diff --git a/SOURCES/slapi-0003-slapi-nis-resolve-IPA-groups-with-fully-qualified-su.patch b/SOURCES/slapi-0003-slapi-nis-resolve-IPA-groups-with-fully-qualified-su.patch
index 4b0375b..dfa973f 100644
--- a/SOURCES/slapi-0003-slapi-nis-resolve-IPA-groups-with-fully-qualified-su.patch
+++ b/SOURCES/slapi-0003-slapi-nis-resolve-IPA-groups-with-fully-qualified-su.patch
@@ -1,7 +1,8 @@
-From d263ce2f95fcf6ec8afa5c9528182cce00f57da6 Mon Sep 17 00:00:00 2001
+From 0a5e61c042679679646f6f8f673028f8fbcf3ea7 Mon Sep 17 00:00:00 2001
 From: Alexander Bokovoy <abokovoy@redhat.com>
 Date: Wed, 15 Jun 2016 12:15:46 +0300
-Subject: [PATCH 3/4] slapi-nis: resolve IPA groups with fully qualified suffix
+Subject: [PATCH 03/17] slapi-nis: resolve IPA groups with fully qualified
+ suffix
 
 With SSSD 1.14+ there is a logic change to handling of a default domain
 suffix.
@@ -90,7 +91,7 @@ index bb2aa74..cdd2b3c 100644
 +						if (ret.associated_domain != NULL)
 +							break;
 +					}
-+					slapi_ch_free((void**)&entries);
++					slapi_ch_free((void**)entries);
 +				}
 +			}
 +			slapi_pblock_destroy(pb);
@@ -136,5 +137,5 @@ index 72ba641..c15d1ed 100644
  
  struct backend_entry_data {
 -- 
-2.7.4
+2.13.6
 
diff --git a/SOURCES/slapi-0005-Double-free-on-ldap-entry-during-priming.patch b/SOURCES/slapi-0005-Double-free-on-ldap-entry-during-priming.patch
new file mode 100644
index 0000000..375cfe3
--- /dev/null
+++ b/SOURCES/slapi-0005-Double-free-on-ldap-entry-during-priming.patch
@@ -0,0 +1,39 @@
+From 66177cbab545374ccc0bcacdd7a8ffea1ca7be6d Mon Sep 17 00:00:00 2001
+From: Thierry Bordaz <tbordaz@redhat.com>
+Date: Tue, 12 Jul 2016 11:43:28 +0200
+Subject: [PATCH 05/17] Double free on ldap entry during priming
+
+During Schema-compat cache priming, If it exists an associated domain
+the entry returned by the internal search is freed twice.
+
+This was introduced in order for slapi-nis to resolve IPA groups with
+fully qualified suffix. To support SSSD 1.14+ change of logic to handle
+a default domain suffix.
+---
+ src/back-sch.c | 4 +---
+ 1 file changed, 1 insertion(+), 3 deletions(-)
+
+diff --git a/src/back-sch.c b/src/back-sch.c
+index cdd2b3c..0745329 100644
+--- a/src/back-sch.c
++++ b/src/back-sch.c
+@@ -284,15 +284,13 @@ backend_set_config_read_config(struct plugin_state *state, Slapi_Entry *e,
+ 				slapi_pblock_get(pb, SLAPI_PLUGIN_INTOP_RESULT, &result);
+ 				if (result == 0) {
+ 					slapi_pblock_get(pb, SLAPI_PLUGIN_INTOP_SEARCH_ENTRIES, &entries);
+-					slapi_pblock_set(pb, SLAPI_PLUGIN_INTOP_SEARCH_ENTRIES, NULL);
+ 					for (j=0; entries[j] != NULL; j++) {
+ 						ret.associated_domain = slapi_entry_attr_get_charptr(entries[j], "associatedDomain");
+-						slapi_entry_free(entries[i]);
+ 						if (ret.associated_domain != NULL)
+ 							break;
+ 					}
+-					slapi_ch_free((void**)entries);
+ 				}
++				slapi_free_search_results_internal(pb);
+ 			}
+ 			slapi_pblock_destroy(pb);
+ 			pb = NULL;
+-- 
+2.13.6
+
diff --git a/SOURCES/slapi-0011-Move-a-helper-to-build-DN-to-a-format.c.patch b/SOURCES/slapi-0011-Move-a-helper-to-build-DN-to-a-format.c.patch
new file mode 100644
index 0000000..73c9504
--- /dev/null
+++ b/SOURCES/slapi-0011-Move-a-helper-to-build-DN-to-a-format.c.patch
@@ -0,0 +1,144 @@
+From 96ff6873b024718fcbb7b011eee58aab84c3086f Mon Sep 17 00:00:00 2001
+From: Alexander Bokovoy <abokovoy@redhat.com>
+Date: Mon, 11 Sep 2017 15:32:38 +0300
+Subject: [PATCH 11/17] Move a helper to build DN to a format.c
+
+---
+ src/back-sch-nss.c | 45 ++-------------------------------------------
+ src/format.c       | 41 +++++++++++++++++++++++++++++++++++++++++
+ src/format.h       |  2 ++
+ 3 files changed, 45 insertions(+), 43 deletions(-)
+
+diff --git a/src/back-sch-nss.c b/src/back-sch-nss.c
+index db63e59..e4d027e 100644
+--- a/src/back-sch-nss.c
++++ b/src/back-sch-nss.c
+@@ -186,47 +186,6 @@ backend_search_filter_has_cn_uid(Slapi_Filter *filter, void *arg)
+ 	return SLAPI_FILTER_SCAN_CONTINUE;
+ }
+ 
+-static char *
+-backend_build_dn(const char *attribute, const char *value,
+-		 const char *container_sdn)
+-{
+-	Slapi_RDN *rdn;
+-	Slapi_DN *sdn;
+-	char *val, *dn = NULL;
+-	const char *ndn, *hexchars = "0123456789ABCDEF";
+-	int i;
+-
+-	val = malloc(strlen(value) * 3 + 1);
+-	if (val == NULL) {
+-		return NULL;
+-	}
+-	rdn = slapi_rdn_new();
+-	if (rdn == NULL) {
+-		free(val);
+-		return NULL;
+-	}
+-        for (i = 0; value[i] != '\0'; i++) {
+-		val[i * 3] = '\\';
+-		val[i * 3 + 1] = hexchars[(value[i] & 0xf0) >> 4];
+-		val[i * 3 + 2] = hexchars[value[i] & 0xf];
+-	}
+-	val[i * 3] = '\0';
+-	if (slapi_rdn_add(rdn, attribute, val) == 1) {
+-		sdn = slapi_sdn_new_dn_byval(container_sdn);
+-		if (sdn != NULL) {
+-			sdn = slapi_sdn_add_rdn(sdn, rdn);
+-			ndn = slapi_sdn_get_ndn(sdn);
+-			if (ndn != NULL) {
+-				dn = slapi_ch_strdup(ndn);
+-			}
+-			slapi_sdn_free(&sdn);
+-		}
+-	}
+-	free(val);
+-	slapi_rdn_free(&rdn);
+-	return dn;
+-}
+-
+ static Slapi_Entry *
+ backend_make_user_entry_from_nsswitch_passwd(struct passwd *pwd,
+ 					     char *container_sdn,
+@@ -257,7 +216,7 @@ backend_make_user_entry_from_nsswitch_passwd(struct passwd *pwd,
+ 		return NULL;
+ 	}
+ 
+-	dn = backend_build_dn("uid", name, container_sdn);
++	dn = format_build_dn("uid", name, container_sdn);
+ 	if (dn == NULL) {
+ 		slapi_log_error(SLAPI_LOG_FATAL,
+ 				cbdata->state->plugin_desc->spd_id,
+@@ -556,7 +515,7 @@ backend_make_group_entry_from_nsswitch_group(struct group *grp,
+ 		return NULL;
+ 	}
+ 
+-	dn = backend_build_dn("cn", grp->gr_name, container_sdn);
++	dn = format_build_dn("cn", grp->gr_name, container_sdn);
+ 	if (dn == NULL) {
+ 		slapi_log_error(SLAPI_LOG_FATAL,
+ 				cbdata->state->plugin_desc->spd_id,
+diff --git a/src/format.c b/src/format.c
+index ee6b8f3..ae4824c 100644
+--- a/src/format.c
++++ b/src/format.c
+@@ -4825,3 +4825,44 @@ format_escape_for_filter(const char *unescaped)
+ 	}
+ 	return ret;
+ }
++
++char *
++format_build_dn(const char *attribute, const char *value,
++		const char *container_sdn)
++{
++	Slapi_RDN *rdn;
++	Slapi_DN *sdn;
++	char *val, *dn = NULL;
++	const char *ndn, *hexchars = "0123456789ABCDEF";
++	int i;
++
++	val = malloc(strlen(value) * 3 + 1);
++	if (val == NULL) {
++		return NULL;
++	}
++	rdn = slapi_rdn_new();
++	if (rdn == NULL) {
++		free(val);
++		return NULL;
++	}
++        for (i = 0; value[i] != '\0'; i++) {
++		val[i * 3] = '\\';
++		val[i * 3 + 1] = hexchars[(value[i] & 0xf0) >> 4];
++		val[i * 3 + 2] = hexchars[value[i] & 0xf];
++	}
++	val[i * 3] = '\0';
++	if (slapi_rdn_add(rdn, attribute, val) == 1) {
++		sdn = slapi_sdn_new_dn_byval(container_sdn);
++		if (sdn != NULL) {
++			sdn = slapi_sdn_add_rdn(sdn, rdn);
++			ndn = slapi_sdn_get_ndn(sdn);
++			if (ndn != NULL) {
++				dn = slapi_ch_strdup(ndn);
++			}
++			slapi_sdn_free(&sdn);
++		}
++	}
++	free(val);
++	slapi_rdn_free(&rdn);
++	return dn;
++}
+diff --git a/src/format.h b/src/format.h
+index 4cde2dc..626d4a9 100644
+--- a/src/format.h
++++ b/src/format.h
+@@ -83,4 +83,6 @@ char **format_get_data_set(struct plugin_state *state,
+ 			   unsigned int **data_lengths);
+ 
+ char *format_escape_for_filter(const char *unescaped);
++char *format_build_dn(const char *attribute, const char *value,
++		      const char *container_sdn);
+ #endif
+-- 
+2.13.6
+
diff --git a/SOURCES/slapi-0012-Add-dummy-handler-for-a-related-add-delete-modify-to.patch b/SOURCES/slapi-0012-Add-dummy-handler-for-a-related-add-delete-modify-to.patch
new file mode 100644
index 0000000..e5f1a30
--- /dev/null
+++ b/SOURCES/slapi-0012-Add-dummy-handler-for-a-related-add-delete-modify-to.patch
@@ -0,0 +1,53 @@
+From 79ddc12c93920840e9fbdf5c8ea25db1c4166af5 Mon Sep 17 00:00:00 2001
+From: Alexander Bokovoy <abokovoy@redhat.com>
+Date: Tue, 12 Sep 2017 14:52:21 +0300
+Subject: [PATCH 12/17] Add dummy handler for a related add/delete/modify to
+ NIS plugin
+
+NIS doesn't need to handle ID overrides, it has to always skip related entries
+---
+ src/back-nis.c | 28 ++++++++++++++++++++++++++++
+ 1 file changed, 28 insertions(+)
+
+diff --git a/src/back-nis.c b/src/back-nis.c
+index 7fb191d..244beba 100644
+--- a/src/back-nis.c
++++ b/src/back-nis.c
+@@ -1014,6 +1014,34 @@ backend_check_empty(struct plugin_state *state,
+ 	}
+ }
+ 
++bool_t
++backend_entry_is_add_related(const char *group, const char *set, bool_t flag,
++			     void *shared_set_data,
++			     Slapi_PBlock *pb,
++			     Slapi_Entry *e)
++{
++	return FALSE;
++}
++
++bool_t
++backend_entry_is_modify_related(const char *group, const char *set, bool_t flag,
++				void *shared_set_data,
++				Slapi_PBlock *pb,
++				Slapi_Entry *e_pre,
++				Slapi_Entry *e_post)
++{
++	return FALSE;
++}
++
++bool_t
++backend_entry_is_delete_related(const char *group, const char *set, bool_t flag,
++				void *shared_set_data,
++				Slapi_PBlock *pb,
++				Slapi_Entry *e)
++{
++	return FALSE;
++}
++
+ /* Scan for the list of configured domains and maps. */
+ void
+ backend_startup(Slapi_PBlock *pb, struct plugin_state *state)
+-- 
+2.13.6
+
diff --git a/SOURCES/slapi-0013-track-changes-to-ID-overrides-and-evict-map-cache-en.patch b/SOURCES/slapi-0013-track-changes-to-ID-overrides-and-evict-map-cache-en.patch
new file mode 100644
index 0000000..49465ec
--- /dev/null
+++ b/SOURCES/slapi-0013-track-changes-to-ID-overrides-and-evict-map-cache-en.patch
@@ -0,0 +1,340 @@
+From bcdcb8e762c6a8824ff1dd67f7e068ef519b3952 Mon Sep 17 00:00:00 2001
+From: Alexander Bokovoy <abokovoy@redhat.com>
+Date: Mon, 11 Sep 2017 15:33:24 +0300
+Subject: [PATCH 13/17] track changes to ID overrides and evict map cache
+ entries
+
+Plug into a processing of LDAP add/delete/modify to see if an ID override entry
+was added/deleted/updated. ID overrides aren't directly used to produce
+map cache entries but when AD user or group is resolved, SSSD on IPA
+master amends that information with ID Override from a Default Trust
+View. Since nothing else would remove AD user or group entry from the map cache
+on ID override change, handle their removal here.
+
+Check if we have any nssswitch-generated entry in a map cache that
+corresponds to this entry. Such entries would be evicted from the map
+cache to allow their refresh.
+
+Allow backends to inspect entries related to a map set
+
+Entries may be related to a map set content but not used directly to
+generate it. An example would be ID overrides in FreeIPA. An addition,
+removal or change of an ID override in the Default Trust View should be
+reflected by evicting an entry from the corresponding seti.
+
+Let backends to handle exact logic. NIS backend does not support
+exposing AD users so it provides set of dummy callbacks that always
+return FALSE (entry is not related). Schema Compat backend, on other
+hand, does track ID overrides in a Default Trust View in FreeIPA.
+---
+ src/back-sch.c | 176 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+ src/back-shr.c |  55 ++++++++++++++----
+ src/backend.h  |  20 +++++++
+ 3 files changed, 239 insertions(+), 12 deletions(-)
+
+diff --git a/src/back-sch.c b/src/back-sch.c
+index e15988f..f98b0b4 100644
+--- a/src/back-sch.c
++++ b/src/back-sch.c
+@@ -33,6 +33,7 @@
+ #ifdef HAVE_DIRSRV_SLAPI_PLUGIN_H
+ #include <nspr.h>
+ #include <plhash.h>
++#include <plstr.h>
+ #include <nss.h>
+ #include <dirsrv/slapi-plugin.h>
+ #else
+@@ -2099,6 +2100,181 @@ backend_write_cb(Slapi_PBlock *pb, struct plugin_state *state)
+ 	return ret;
+ }
+ 
++#ifdef USE_IPA_IDVIEWS
++/* Check if ID override applies to an entry in our map cache
++ * and remove the entry in case it does. */
++
++static bool_t
++backend__get_original_uid_and_ndn(Slapi_Entry *e,
++				  char **original_uid,
++				  const char **original_anchor)
++{
++	char **elem = NULL;
++	char *v = NULL;
++	char *ndn = NULL;
++	char *view = NULL;
++	int n_elem = 0;
++	int i = 0;
++
++	if (e == NULL) {
++		return FALSE;
++	}
++
++	elem = slapi_entry_attr_get_charray_ext(e, "objectClass", &n_elem);
++	if (elem == NULL) {
++		/* weird, objectClass should be existing */
++		return FALSE;
++	}
++
++	for (i=0; i < n_elem; i++) {
++		if (strncasecmp(elem[i], "ipaOverrideAnchor", 17) == 0) {
++			break;
++		}
++	}
++
++	slapi_ch_array_free(elem);
++	if (i == n_elem) {
++		/* This is not an override, bail out */
++		return FALSE;
++	}
++
++	ndn = slapi_entry_get_ndn(e);
++	if (ndn == NULL) {
++		return FALSE;
++	}
++
++	view = PL_strcasestr(ndn, "cn=Default Trust View,");
++	if (view == NULL || view == ndn) {
++		return FALSE;
++	}
++
++	/* This is an ID override, we need to search for a referenced ipaOriginalUid or cn in our maps */
++	v = slapi_entry_attr_get_charptr(e, "ipaOriginalUid");
++	if (v == NULL) {
++		v = slapi_entry_attr_get_charptr(e, "cn");
++		if (v == NULL) {
++			return FALSE;
++		}
++	}
++
++	*original_uid = v;
++	*original_anchor = ndn;
++	return TRUE;
++}
++
++bool_t
++backend_entry_evict_if_related(const char *group, const char *set, bool_t flag,
++			       void *shared_set_data,
++			       Slapi_PBlock *pb,
++			       Slapi_Entry *e)
++{
++	struct backend_set_data *set_data;
++	struct plugin_state *state = NULL;
++	char *id = NULL;
++	char *original_uid = NULL;
++	const char *original_anchor = NULL;
++	const char *rdn_attribute[] = {NULL, "uid=%s,%s,%s", "cn=%s,%s,%s"};
++	bool_t result = FALSE;
++
++	set_data = shared_set_data;
++
++	/* We only interested in NSSWITCH-capable maps */
++	if (set_data->check_nsswitch == SCH_NSSWITCH_NONE) {
++		return FALSE;
++	}
++
++	/* See if the entry pre modification had original UID */
++	if (!backend__get_original_uid_and_ndn(e,
++					       &original_uid,
++					       &original_anchor)) {
++		return FALSE;
++	}
++
++	id = slapi_ch_smprintf(rdn_attribute[set_data->check_nsswitch],
++			       original_uid, set, group);
++
++	if (id == NULL) {
++		slapi_ch_free_string(&original_uid);
++		return FALSE;
++	}
++
++	slapi_pblock_get(pb, SLAPI_PLUGIN_PRIVATE, &state);
++	result = map_data_check_entry(state, group, set, id);
++	if (result) {
++		slapi_log_error(SLAPI_LOG_PLUGIN, state->plugin_desc->spd_id,
++				"evicted entry %s due to changed content of ID override %s\n",
++				id, original_anchor);
++
++		/* An entry corresponding to our target is found, evict it */
++		map_data_unset_entry(state, group, set, id);
++	}
++
++	slapi_ch_free_string(&id);
++	slapi_ch_free_string(&original_uid);
++	return result;
++}
++
++bool_t
++backend_entry_is_add_related(const char *group, const char *set, bool_t flag,
++			     void *shared_set_data,
++			     Slapi_PBlock *pb,
++			     Slapi_Entry *e)
++{
++	return backend_entry_evict_if_related(group, set, flag, shared_set_data, pb, e);
++}
++
++
++bool_t
++backend_entry_is_modify_related(const char *group, const char *set, bool_t flag,
++				void *shared_set_data,
++				Slapi_PBlock *pb,
++				Slapi_Entry *e_pre,
++				Slapi_Entry *e_post)
++{
++	return backend_entry_evict_if_related(group, set, flag, shared_set_data, pb, e_pre);
++}
++
++bool_t
++backend_entry_is_delete_related(const char *group, const char *set, bool_t flag,
++				void *shared_set_data,
++				Slapi_PBlock *pb,
++				Slapi_Entry *e)
++{
++	return backend_entry_evict_if_related(group, set, flag, shared_set_data, pb, e);
++}
++
++#else
++
++bool_t
++backend_entry_is_add_related(const char *group, const char *set, bool_t flag,
++			     void *shared_set_data,
++			     Slapi_PBlock *pb,
++			     Slapi_Entry *e)
++{
++	return FALSE;
++}
++
++bool_t
++backend_entry_is_modify_related(const char *group, const char *set, bool_t flag,
++				void *shared_set_data,
++				Slapi_PBlock *pb,
++				Slapi_Entry *e_pre,
++				Slapi_Entry *e_post)
++{
++	return FALSE;
++}
++
++bool_t
++backend_entry_is_delete_related(const char *group, const char *set, bool_t flag,
++				void *shared_set_data,
++				Slapi_PBlock *pb,
++				Slapi_Entry *e)
++{
++	return FALSE;
++}
++
++#endif
++
+ static int
+ backend_pre_write_cb(Slapi_PBlock *pb)
+ {
+diff --git a/src/back-shr.c b/src/back-shr.c
+index 7842e05..a7ea92f 100644
+--- a/src/back-shr.c
++++ b/src/back-shr.c
+@@ -1856,11 +1856,21 @@ backend_shr_add_entry_cb(const char *group, const char *set, bool_t secure,
+ 
+ 	/* If the entry doesn't match the set, skip it. */
+ 	if (!backend_shr_entry_matches_set(set_data, cbdata->pb, cbdata->e)) {
+-		slapi_log_error(SLAPI_LOG_PLUGIN,
+-				cbdata->state->plugin_desc->spd_id,
+-				"entry \"%s\" does not belong in "
+-				"\"%s\"/\"%s\"\n",
+-				cbdata->ndn, group, set);
++		/* Give backend a chance to perform other operations on the add op.
++		 * For example, ID override addition in FreeIPA need to be noted to
++		 * evict AD user/group entries from a map cache while ID overrides
++		 * themselves aren't present in any map.
++		 * Note that we could do this as a part of backend_shr_entry_matches_set()
++		 * but it is better to isolate this operation in a separate call. */
++		if (!backend_entry_is_add_related(group, set, secure,
++						  set_data, cbdata->pb,
++						  cbdata->e)) {
++			slapi_log_error(SLAPI_LOG_PLUGIN,
++					cbdata->state->plugin_desc->spd_id,
++					"entry \"%s\" does not belong in "
++					"\"%s\"/\"%s\"\n",
++					cbdata->ndn, group, set);
++		}
+ 		return TRUE;
+ 	}
+ 
+@@ -2010,13 +2020,24 @@ backend_shr_modify_entry_cb(const char *group, const char *set, bool_t flag,
+ 					   cbdata->e_post) &&
+ 	    !backend_shr_entry_matches_set(set_data, cbdata->pb,
+ 					   cbdata->e_pre)) {
+-		slapi_log_error(SLAPI_LOG_PLUGIN,
+-				cbdata->state->plugin_desc->spd_id,
+-				"\"%s\" not in \"%s\"/\"%s\", "
+-				"before or after modify\n",
+-				cbdata->ndn,
+-				set_data->group,
+-				set_data->set);
++		/* Give backend a chance to perform other operations on the modify op.
++		 * For example, ID override updates in FreeIPA need to be noted to
++		 * evict AD user/group entries from a map cache while ID overrides
++		 * themselves aren't present in any map.
++		 * Note that we could do this as a part of backend_shr_entry_matches_set()
++		 * but it is better to isolate this operation in a separate call. */
++		if (!backend_entry_is_modify_related(group, set, flag,
++						     set_data, cbdata->pb,
++						     cbdata->e_pre,
++						     cbdata->e_post)) {
++			slapi_log_error(SLAPI_LOG_PLUGIN,
++					cbdata->state->plugin_desc->spd_id,
++					"\"%s\" not in \"%s\"/\"%s\", "
++					"before or after modify\n",
++					cbdata->ndn,
++					set_data->group,
++					set_data->set);
++		}
+ 		return TRUE;
+ 	}
+ 	if (set_data->skip_uninteresting_updates &&
+@@ -2631,6 +2652,16 @@ backend_shr_delete_entry_cb(const char *group, const char *set, bool_t flag,
+ 				group, set, set_data->group, set_data->set,
+ 				cbdata->ndn);
+ 		map_data_unset_entry(cbdata->state, group, set, cbdata->ndn);
++	} else {
++		/* Give backend a chance to perform other operations on the delete op.
++		 * For example, ID override removal in FreeIPA need to be noted to
++		 * evict AD user/group entries from a map cache while ID overrides
++		 * themselves aren't present in any map.
++		 * Note that we could do this as a part of backend_shr_entry_matches_set()
++		 * but it is better to isolate this operation in a separate call. */
++		(void) backend_entry_is_delete_related(group, set, flag,
++						       set_data, cbdata->pb,
++						       cbdata->e);
+ 	}
+ 	return TRUE;
+ }
+diff --git a/src/backend.h b/src/backend.h
+index 4608d2d..f0a5bbb 100644
+--- a/src/backend.h
++++ b/src/backend.h
+@@ -115,4 +115,24 @@ void backend_update_params(Slapi_PBlock *pb, struct plugin_state *state);
+ bool_t backend_shr_is_caller(struct plugin_state *state,
+ 			     struct slapi_pblock *pb);
+ 
++/* Check if an operation is performed on an entry that is related to
++ * any entry in the set. This allows to catch changes of the entries that
++ * aren't directly included in the map set but should affect the set. */
++bool_t
++backend_entry_is_modify_related(const char *group, const char *set, bool_t flag,
++				void *shared_set_data,
++				Slapi_PBlock *pb,
++				Slapi_Entry *e_pre,
++				Slapi_Entry *e_post);
++bool_t
++backend_entry_is_add_related(const char *group, const char *set, bool_t flag,
++			     void *shared_set_data,
++			     Slapi_PBlock *pb,
++			     Slapi_Entry *e);
++bool_t
++backend_entry_is_delete_related(const char *group, const char *set, bool_t flag,
++				void *shared_set_data,
++				Slapi_PBlock *pb,
++				Slapi_Entry *e);
++
+ #endif
+-- 
+2.13.6
+
diff --git a/SOURCES/slapi-0015-configure.ac-detect-extended-NSS-API-provided-by-SSS.patch b/SOURCES/slapi-0015-configure.ac-detect-extended-NSS-API-provided-by-SSS.patch
new file mode 100644
index 0000000..76c6a0b
--- /dev/null
+++ b/SOURCES/slapi-0015-configure.ac-detect-extended-NSS-API-provided-by-SSS.patch
@@ -0,0 +1,53 @@
+From 6c4e8869ba6121ddbc6e1eca880c39b0af3391e0 Mon Sep 17 00:00:00 2001
+From: Alexander Bokovoy <abokovoy@redhat.com>
+Date: Wed, 25 Oct 2017 11:38:55 +0300
+Subject: [PATCH 15/17] configure.ac: detect extended NSS API provided by SSSD
+
+SSSD exposes an extended NSS API via libsss_nss_idmap. This API allows
+to query getpwnam()/getgrnam()/getgruid()/getpwuid()/getgrouplist()
+information with a timeout per request. As result, an application has
+possibility to cancel too long request.
+
+This API also allows to ignore SSSD cache or invalidate it when
+requesting certain information. slapi-nis needs this functionality when
+invalidating own entries as result of changes done by other LDAP clients
+in the areas which slapi-nis doesn't track directly.
+
+For example, an update of ID override in the Default Trust View should
+invalidate user or group entry for that AD object. Since retrieval of
+the user/group information relies on SSSD, SSSD needs to be notified
+that there is a change in ID override and evict the entry from its cache
+as well.
+---
+ configure.ac | 6 ++++++
+ 1 file changed, 6 insertions(+)
+
+diff --git a/configure.ac b/configure.ac
+index f82a47e..a958607 100644
+--- a/configure.ac
++++ b/configure.ac
+@@ -361,8 +361,13 @@ if test "x$use_nsswitch" != xno ; then
+ 			SSS_NSS_IDMAP_LIBS=
+ 		fi
+ 	fi
++
+ 	AC_SUBST(SSS_NSS_IDMAP_CFLAGS)
+ 	AC_SUBST(SSS_NSS_IDMAP_LIBS)
++	AC_CHECK_LIB(sss_nss_idmap,sss_nss_getpwnam_timeout)
++	if test "x$ac_cv_lib_sss_nss_idmap_sss_nss_getpwnam_timeout" = xyes ; then
++		AC_DEFINE(USE_SSS_NSS_TIMEOUT,1,[Use extended NSS API provided by SSSD])
++	fi
+ 
+ 	if test "x$use_pam" != xno ; then
+ 		AC_CHECK_HEADERS(security/pam_appl.h)
+@@ -384,6 +389,7 @@ if test "x$use_nsswitch" != xno ; then
+ 	fi
+ 	AC_DEFINE(USE_NSSWITCH,1,[Use nsswitch API to lookup users and groups not found in the LDAP tree])
+ fi
++AM_CONDITIONAL([USE_SSS_NSS_TIMEOUT], [test "x$ac_cv_lib_sss_nss_idmap_sss_nss_getpwnam_timeout" = xyes])
+ 
+ use_idviews=true
+ AC_ARG_WITH(idviews,
+-- 
+2.13.6
+
diff --git a/SOURCES/slapi-0016-schema-compat-add-support-for-timeout-based-NSS-quer.patch b/SOURCES/slapi-0016-schema-compat-add-support-for-timeout-based-NSS-quer.patch
new file mode 100644
index 0000000..972b735
--- /dev/null
+++ b/SOURCES/slapi-0016-schema-compat-add-support-for-timeout-based-NSS-quer.patch
@@ -0,0 +1,933 @@
+From 6fbf5891e9169142fc0ea37eb8f897a645b82d6f Mon Sep 17 00:00:00 2001
+From: Alexander Bokovoy <abokovoy@redhat.com>
+Date: Wed, 1 Nov 2017 10:29:41 +0200
+Subject: [PATCH 16/17] schema-compat: add support for timeout-based NSS
+ queries with libsss_nss_idmap
+
+In case libsss_nss_idmap provides timeout-enabled NSS API, use it.
+This solves a problem of too long queries to an NSS backend with
+traditional POSIX NSS API. In case SSSD takes too long to respond
+to a query, corresponding 389-ds thread running schema-compat plugin
+would stuck waiting that response. It can lead to an exhaustion of
+389-ds threads.
+
+A refactored interface to NSS backends is introduced with this commit.
+A backend API looks like an API an NSS plugin has to implement in glibc
+but also allows to handle timeout-based requests internally.
+
+If backend implements timeout-enabled calls, then
+backend_nss_set_timeout() function can be used to modify a per-context
+state. There is no need for a caller to know whether backend supports
+timeout-enabled calls because either way these calls are synchronous
+and backend choice is done at compile-time.
+
+schema-compat plugin uses 10 seconds as its default timeout. One can
+change it via 'slapi-nss-timeout' attribute in the plugin config entry.
+---
+ src/Makefile.am          |  11 ++-
+ src/back-sch-nss.c       | 187 +++++-------------------------------
+ src/back-sch-nss.h       |  70 ++++++++++++++
+ src/back-sch-nss_sss.c   | 231 +++++++++++++++++++++++++++++++++++++++++++++
+ src/back-sch-sss_idmap.c | 239 +++++++++++++++++++++++++++++++++++++++++++++++
+ src/back-sch.h           |   9 +-
+ src/plug-sch.c           |   7 ++
+ 7 files changed, 588 insertions(+), 166 deletions(-)
+ create mode 100644 src/back-sch-nss.h
+ create mode 100644 src/back-sch-nss_sss.c
+ create mode 100644 src/back-sch-sss_idmap.c
+
+diff --git a/src/Makefile.am b/src/Makefile.am
+index 6f4926e..cd1efc2 100644
+--- a/src/Makefile.am
++++ b/src/Makefile.am
+@@ -68,8 +68,17 @@ schemacompat_plugin_la_LIBADD = $(LDAP_LIBS) $(RUNTIME_LIBS) $(LIBPTHREAD) $(CON
+ 
+ if USE_NSSWITCH
+ schemacompat_plugin_la_CFLAGS += $(SSS_NSS_IDMAP_CFLAGS)
+-schemacompat_plugin_la_SOURCES += back-sch-nss.c
++schemacompat_plugin_la_SOURCES += back-sch-nss.c back-sch-nss.h
+ schemacompat_plugin_la_LIBADD += $(SSS_NSS_IDMAP_LIBS)
++# We have two backends for nss operations:
++# (1) directly loading nss_sss.so.2
++# (2) using timeout-enabled API from libsss_nss_idmap
++# We prefer (2) if available
++if USE_SSS_NSS_TIMEOUT
++schemacompat_plugin_la_SOURCES += back-sch-sss_idmap.c
++else
++schemacompat_plugin_la_SOURCES += back-sch-nss_sss.c
++endif
+ endif
+ 
+ if USE_PAM
+diff --git a/src/back-sch-nss.c b/src/back-sch-nss.c
+index e4d027e..e5f91b2 100644
+--- a/src/back-sch-nss.c
++++ b/src/back-sch-nss.c
+@@ -28,7 +28,6 @@
+ #include <string.h>
+ #include <time.h>
+ #include <unistd.h>
+-#include <dlfcn.h>
+ #include <errno.h>
+ #include <pwd.h>
+ #include <grp.h>
+@@ -52,6 +51,7 @@
+ #include "map.h"
+ #include "back-sch.h"
+ #include "format.h"
++#include "back-sch-nss.h"
+ 
+ static int
+ bvstrprefix(const struct berval *bval, const char *s)
+@@ -294,143 +294,6 @@ backend_make_user_entry_from_nsswitch_passwd(struct passwd *pwd,
+ 	return entry;
+ }
+ 
+-/* Possible results of lookup using a nss_* function.
+- * Note: don't include nss.h as its path gets overriden by NSS library */
+-enum nss_status
+-{
+-  NSS_STATUS_TRYAGAIN = -2,
+-  NSS_STATUS_UNAVAIL,
+-  NSS_STATUS_NOTFOUND,
+-  NSS_STATUS_SUCCESS,
+-  NSS_STATUS_RETURN
+-};
+-
+-struct nss_ops_ctx {
+-	void *dl_handle;
+-
+-	enum nss_status (*getpwnam_r)(const char *name, struct passwd *result,
+-			  char *buffer, size_t buflen, int *errnop);
+-	enum nss_status (*getpwuid_r)(uid_t uid, struct passwd *result,
+-			  char *buffer, size_t buflen, int *errnop);
+-	enum nss_status (*setpwent)(void);
+-	enum nss_status (*getpwent_r)(struct passwd *result,
+-			  char *buffer, size_t buflen, int *errnop);
+-	enum nss_status (*endpwent)(void);
+-
+-	enum nss_status (*getgrnam_r)(const char *name, struct group *result,
+-			  char *buffer, size_t buflen, int *errnop);
+-	enum nss_status (*getgrgid_r)(gid_t gid, struct group *result,
+-			  char *buffer, size_t buflen, int *errnop);
+-	enum nss_status (*setgrent)(void);
+-	enum nss_status (*getgrent_r)(struct group *result,
+-			  char *buffer, size_t buflen, int *errnop);
+-	enum nss_status (*endgrent)(void);
+-
+-	enum nss_status (*initgroups_dyn)(const char *user, gid_t group,
+-			      long int *start, long int *size,
+-			      gid_t **groups, long int limit,
+-			      int *errnop);
+-};
+-
+-void backend_nss_init_context(struct nss_ops_ctx **nss_context)
+-{
+-	struct nss_ops_ctx *ctx = NULL;
+-
+-	if (nss_context == NULL) {
+-		return;
+-	}
+-
+-	ctx = calloc(1, sizeof(struct nss_ops_ctx));
+-
+-	*nss_context = ctx;
+-	if (ctx == NULL) {
+-		return;
+-	}
+-
+-	ctx->dl_handle = dlopen("libnss_sss.so.2", RTLD_NOW);
+-	if (ctx->dl_handle == NULL) {
+-		goto fail;
+-	}
+-
+-	ctx->getpwnam_r = dlsym(ctx->dl_handle, "_nss_sss_getpwnam_r");
+-	if (ctx->getpwnam_r == NULL) {
+-		goto fail;
+-	}
+-
+-	ctx->getpwuid_r = dlsym(ctx->dl_handle, "_nss_sss_getpwuid_r");
+-	if (ctx->getpwuid_r == NULL) {
+-		goto fail;
+-	}
+-
+-	ctx->setpwent = dlsym(ctx->dl_handle, "_nss_sss_setpwent");
+-	if (ctx->setpwent == NULL) {
+-		goto fail;
+-	}
+-
+-	ctx->getpwent_r = dlsym(ctx->dl_handle, "_nss_sss_getpwent_r");
+-	if (ctx->getpwent_r == NULL) {
+-		goto fail;
+-	}
+-
+-	ctx->endpwent = dlsym(ctx->dl_handle, "_nss_sss_endpwent");
+-	if (ctx->endpwent == NULL) {
+-		goto fail;
+-	}
+-
+-	ctx->getgrnam_r = dlsym(ctx->dl_handle, "_nss_sss_getgrnam_r");
+-	if (ctx->getgrnam_r == NULL) {
+-		goto fail;
+-	}
+-
+-	ctx->getgrgid_r = dlsym(ctx->dl_handle, "_nss_sss_getgrgid_r");
+-	if (ctx->getgrgid_r == NULL) {
+-		goto fail;
+-	}
+-
+-	ctx->setgrent = dlsym(ctx->dl_handle, "_nss_sss_setgrent");
+-	if (ctx->setgrent == NULL) {
+-		goto fail;
+-	}
+-
+-	ctx->getgrent_r = dlsym(ctx->dl_handle, "_nss_sss_getgrent_r");
+-	if (ctx->getgrent_r == NULL) {
+-		goto fail;
+-	}
+-
+-	ctx->endgrent = dlsym(ctx->dl_handle, "_nss_sss_endgrent");
+-	if (ctx->endgrent == NULL) {
+-		goto fail;
+-	}
+-
+-	ctx->initgroups_dyn = dlsym(ctx->dl_handle, "_nss_sss_initgroups_dyn");
+-	if (ctx->initgroups_dyn == NULL) {
+-		goto fail;
+-	}
+-
+-	return;
+-
+-fail:
+-	backend_nss_free_context(nss_context);
+-
+-	return;
+-}
+-
+-void
+-backend_nss_free_context(struct nss_ops_ctx **nss_context)
+-{
+-	if (nss_context == NULL) {
+-		return;
+-	}
+-
+-	if ((*nss_context)->dl_handle != NULL) {
+-		dlclose((*nss_context)->dl_handle);
+-	}
+-
+-	free((*nss_context));
+-	*nss_context = NULL;
+-}
+-
+-
+ 
+ static Slapi_Entry **
+ backend_retrieve_user_entry_from_nsswitch(char *user_name, bool_t is_uid,
+@@ -456,13 +319,13 @@ repeat:
+ 	}
+ 
+ 	if (is_uid) {
+-		rc = ctx->getpwuid_r((uid_t) atoll(user_name), &pwd,
+-				     cbdata->nsswitch_buffer,
+-				     cbdata->nsswitch_buffer_len, &lerrno);
++		rc = backend_nss_getpwuid(ctx, (uid_t) atoll(user_name), &pwd,
++					  cbdata->nsswitch_buffer,
++					  cbdata->nsswitch_buffer_len, &result, &lerrno);
+ 	} else {
+-		rc = ctx->getpwnam_r(user_name, &pwd,
+-				     cbdata->nsswitch_buffer,
+-				     cbdata->nsswitch_buffer_len, &lerrno);
++		rc = backend_nss_getpwnam(ctx, user_name, &pwd,
++					  cbdata->nsswitch_buffer,
++					  cbdata->nsswitch_buffer_len, &result, &lerrno);
+ 	}
+ 
+ 	if ((rc != NSS_STATUS_SUCCESS)) {
+@@ -591,13 +454,13 @@ repeat:
+ 	}
+ 
+ 	if (is_gid) {
+-		rc = ctx->getgrgid_r((gid_t) atoll(group_name), &grp,
+-				     cbdata->nsswitch_buffer,
+-				     cbdata->nsswitch_buffer_len, &lerrno);
++		rc = backend_nss_getgrgid(ctx, (gid_t) atoll(group_name), &grp,
++					  cbdata->nsswitch_buffer,
++					  cbdata->nsswitch_buffer_len, &result, &lerrno);
+ 	} else {
+-		rc = ctx->getgrnam_r(group_name, &grp,
+-				     cbdata->nsswitch_buffer,
+-				     cbdata->nsswitch_buffer_len, &lerrno);
++		rc = backend_nss_getgrnam(ctx, group_name, &grp,
++					  cbdata->nsswitch_buffer,
++					  cbdata->nsswitch_buffer_len, &result, &lerrno);
+ 	}
+ 	if ((rc != NSS_STATUS_SUCCESS)) {
+ 		if (lerrno == ERANGE) {
+@@ -651,9 +514,9 @@ repeat:
+ 		return NULL;
+ 	}
+ 
+-	rc = ctx->getgrgid_r(gid, &grp,
+-			     cbdata->nsswitch_buffer,
+-			     cbdata->nsswitch_buffer_len, &lerrno);
++	rc = backend_nss_getgrgid(ctx, gid, &grp,
++				  cbdata->nsswitch_buffer,
++				  cbdata->nsswitch_buffer_len, &result, &lerrno);
+ 
+ 	if ((rc != NSS_STATUS_SUCCESS)) {
+ 		if (lerrno == ERANGE) {
+@@ -689,7 +552,7 @@ backend_retrieve_group_list_from_nsswitch(char *user_name, char *container_sdn,
+ 	int i, idx;
+ 	struct nss_ops_ctx *ctx = NULL;
+ 	int lerrno = 0;
+-	long int ngroups = 0;
++	int ngroups = 0;
+ 	long int start = 0;
+ 	enum nss_status rc;
+ 
+@@ -702,9 +565,9 @@ repeat:
+ 		return NULL;
+ 	}
+ 
+-	rc = ctx->getpwnam_r(user_name, &pwd,
+-			     cbdata->nsswitch_buffer,
+-			     cbdata->nsswitch_buffer_len, &lerrno);
++	rc = backend_nss_getpwnam(ctx, user_name, &pwd,
++				  cbdata->nsswitch_buffer,
++				  cbdata->nsswitch_buffer_len, &pwd_result, &lerrno);
+ 
+ 	if ((rc != NSS_STATUS_SUCCESS)) {
+ 		if (lerrno == ERANGE) {
+@@ -723,19 +586,15 @@ repeat:
+ 	}
+ 
+ 	ngroups = 32;
+-	start = 0;
+ 	grouplist = malloc(sizeof(gid_t) * ngroups);
+ 	if (grouplist == NULL) {
+ 		return NULL;
+ 	}
+ 
+-	grouplist[0] = pwd.pw_gid;
+-	start++;
+-
+ 	do {
+-		rc = ctx->initgroups_dyn(user_name, pwd.pw_gid,
+-					 &start, &ngroups, &grouplist,
+-					 -1, &lerrno);
++		rc = backend_nss_getgrouplist(ctx, user_name, pwd.pw_gid,
++					      grouplist, &ngroups,
++					      &lerrno);
+ 		if ((rc != NSS_STATUS_SUCCESS)) {
+ 			tmp_list = realloc(grouplist, ngroups * sizeof(gid_t));
+ 			if (tmp_list == NULL) {
+diff --git a/src/back-sch-nss.h b/src/back-sch-nss.h
+new file mode 100644
+index 0000000..54a3c07
+--- /dev/null
++++ b/src/back-sch-nss.h
+@@ -0,0 +1,70 @@
++/*
++ * Copyright 2017 Red Hat, Inc.
++ *
++ * This Program is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License as published by
++ * the Free Software Foundation; version 2 of the License.
++ *
++ * This Program is distributed in the hope that it will be useful, but
++ * WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
++ * General Public License for more details.
++ *
++ * You should have received a copy of the GNU General Public License
++ * along with this Program; if not, write to the
++ *
++ *   Free Software Foundation, Inc.
++ *   59 Temple Place, Suite 330
++ *   Boston, MA 02111-1307 USA
++ *
++ */
++
++#ifndef back_sch_nss_h
++#define back_sch_nss_h
++#include <unistd.h>
++#include <pwd.h>
++#include <grp.h>
++
++/* Possible results of lookup using a nss_* function.
++ * Note: don't include nss.h as its path gets overriden by NSS library */
++enum nss_status
++{
++  NSS_STATUS_TRYAGAIN = -2,
++  NSS_STATUS_UNAVAIL,
++  NSS_STATUS_NOTFOUND,
++  NSS_STATUS_SUCCESS,
++  NSS_STATUS_RETURN
++};
++
++struct nss_ops_ctx;
++
++enum nss_status backend_nss_getpwnam(struct nss_ops_ctx *nss_context,
++				     const char *name, struct passwd *pwd,
++				     char *buffer, size_t buflen,
++				     struct passwd **result,
++				     int *lerrno);
++
++enum nss_status backend_nss_getpwuid(struct nss_ops_ctx *nss_context,
++				     uid_t uid, struct passwd *pwd,
++				     char *buffer, size_t buflen,
++				     struct passwd **result,
++				     int *lerrno);
++
++enum nss_status backend_nss_getgrnam(struct nss_ops_ctx *nss_context,
++				     const char *name, struct group *grp,
++				     char *buffer, size_t buflen,
++				     struct group **result,
++				     int *lerrno);
++
++enum nss_status backend_nss_getgrgid(struct nss_ops_ctx *nss_context,
++				     gid_t gid, struct group *grp,
++				     char *buffer, size_t buflen,
++				     struct group **result,
++				     int *lerrno);
++
++enum nss_status backend_nss_getgrouplist(struct nss_ops_ctx *nss_context,
++					 const char *name, gid_t group,
++					 gid_t *groups, int *ngroups,
++					 int *lerrno);
++
++#endif /* back_sch_nss_h */
+diff --git a/src/back-sch-nss_sss.c b/src/back-sch-nss_sss.c
+new file mode 100644
+index 0000000..e3e6628
+--- /dev/null
++++ b/src/back-sch-nss_sss.c
+@@ -0,0 +1,231 @@
++/*
++ * Copyright 2013-2017 Red Hat, Inc.
++ *
++ * This Program is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License as published by
++ * the Free Software Foundation; version 2 of the License.
++ *
++ * This Program is distributed in the hope that it will be useful, but
++ * WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
++ * General Public License for more details.
++ *
++ * You should have received a copy of the GNU General Public License
++ * along with this Program; if not, write to the
++ *
++ *   Free Software Foundation, Inc.
++ *   59 Temple Place, Suite 330
++ *   Boston, MA 02111-1307 USA
++ *
++ */
++
++#ifdef HAVE_CONFIG_H
++#include "config.h"
++#endif
++
++#include <sys/types.h>
++#include <stdlib.h>
++#include <string.h>
++#include <time.h>
++#include <unistd.h>
++#include <dlfcn.h>
++#include <errno.h>
++#include <pwd.h>
++#include <grp.h>
++#include "back-sch-nss.h"
++
++struct nss_ops_ctx {
++	void *dl_handle;
++	long int initgroups_start;
++
++	enum nss_status (*getpwnam_r)(const char *name, struct passwd *result,
++			  char *buffer, size_t buflen, int *errnop);
++	enum nss_status (*getpwuid_r)(uid_t uid, struct passwd *result,
++			  char *buffer, size_t buflen, int *errnop);
++	enum nss_status (*getgrnam_r)(const char *name, struct group *result,
++			  char *buffer, size_t buflen, int *errnop);
++	enum nss_status (*getgrgid_r)(gid_t gid, struct group *result,
++			  char *buffer, size_t buflen, int *errnop);
++	enum nss_status (*initgroups_dyn)(const char *user, gid_t group,
++			      long int *start, long int *size,
++			      gid_t **groups, long int limit,
++			      int *errnop);
++};
++
++void backend_nss_init_context(struct nss_ops_ctx **nss_context)
++{
++	struct nss_ops_ctx *ctx = NULL;
++
++	if (nss_context == NULL) {
++		return;
++	}
++
++	ctx = calloc(1, sizeof(struct nss_ops_ctx));
++
++	*nss_context = ctx;
++	if (ctx == NULL) {
++		return;
++	}
++
++	ctx->dl_handle = dlopen("libnss_sss.so.2", RTLD_NOW);
++	if (ctx->dl_handle == NULL) {
++		goto fail;
++	}
++
++	ctx->getpwnam_r = dlsym(ctx->dl_handle, "_nss_sss_getpwnam_r");
++	if (ctx->getpwnam_r == NULL) {
++		goto fail;
++	}
++
++	ctx->getpwuid_r = dlsym(ctx->dl_handle, "_nss_sss_getpwuid_r");
++	if (ctx->getpwuid_r == NULL) {
++		goto fail;
++	}
++
++	ctx->getgrnam_r = dlsym(ctx->dl_handle, "_nss_sss_getgrnam_r");
++	if (ctx->getgrnam_r == NULL) {
++		goto fail;
++	}
++
++	ctx->getgrgid_r = dlsym(ctx->dl_handle, "_nss_sss_getgrgid_r");
++	if (ctx->getgrgid_r == NULL) {
++		goto fail;
++	}
++
++	ctx->initgroups_dyn = dlsym(ctx->dl_handle, "_nss_sss_initgroups_dyn");
++	if (ctx->initgroups_dyn == NULL) {
++		goto fail;
++	}
++
++	return;
++
++fail:
++	backend_nss_free_context(nss_context);
++
++	return;
++}
++
++void
++backend_nss_free_context(struct nss_ops_ctx **nss_context)
++{
++	if (nss_context == NULL) {
++		return;
++	}
++
++	if ((*nss_context)->dl_handle != NULL) {
++		dlclose((*nss_context)->dl_handle);
++	}
++
++	free((*nss_context));
++	*nss_context = NULL;
++}
++
++
++/* Following three functions cannot be implemented with nss_sss.so.2
++ * As result, we simply do nothing here */
++
++void backend_nss_set_timeout(struct nss_ops_ctx **nss_context,
++			     unsigned int timeout) {
++	/* no operation */
++}
++
++void backend_nss_evict_user(struct nss_ops_ctx **nss_context,
++			    const char *name) {
++	/* no operation */
++}
++
++void backend_nss_evict_group(struct nss_ops_ctx **nss_context,
++			     const char *name) {
++	/* no operation */
++}
++
++enum nss_status backend_nss_getpwnam(struct nss_ops_ctx *nss_context,
++				     const char *name, struct passwd *pwd,
++				     char *buffer, size_t buflen,
++				     struct passwd **result,
++				     int *lerrno) {
++
++	if (nss_context == NULL) {
++		return NSS_STATUS_UNAVAIL;
++	}
++
++	return (enum nss_status)
++		nss_context->getpwnam_r(name, pwd,
++					buffer, buflen,
++					result, lerrno);
++}
++
++enum nss_status backend_nss_getpwuid(struct nss_ops_ctx *nss_context,
++				     uid_t uid, struct passwd *pwd,
++				     char *buffer, size_t buflen,
++				     struct passwd **result,
++				     int *lerrno) {
++
++	if (nss_context == NULL) {
++		return NSS_STATUS_UNAVAIL;
++	}
++
++	return (enum nss_status)
++		nss_context->getpwuid_r(uid, pwd,
++					buffer, buflen,
++					result, lerrno);
++}
++
++enum nss_status backend_nss_getgrnam(struct nss_ops_ctx *nss_context,
++				     const char *name, struct group *grp,
++				     char *buffer, size_t buflen,
++				     struct group **result,
++				     int *lerrno) {
++
++	if (nss_context == NULL) {
++		return NSS_STATUS_UNAVAIL;
++	}
++
++	return (enum nss_status)
++		nss_context->getgrnam_r(name, grp,
++					buffer, buflen,
++					result, lerrno);
++}
++
++enum nss_status backend_nss_getgrgid(struct nss_ops_ctx *nss_context,
++				     gid_t gid, struct group *grp,
++				     char *buffer, size_t buflen,
++				     struct group **result,
++				     int *lerrno) {
++
++	if (nss_context == NULL) {
++		return NSS_STATUS_UNAVAIL;
++	}
++
++	return (enum nss_status)
++		nss_context->getgrgid_r(gid, grp,
++					buffer, buflen,
++					result, lerrno);
++}
++
++enum nss_status backend_nss_getgrouplist(struct nss_ops_ctx *nss_context,
++					 const char *name, gid_t group,
++					 gid_t *groups, int *ngroups,
++					 int *lerrno) {
++	enum nss_status ret = NSS_STATUS_UNAVAIL;
++
++	if (nss_context == NULL) {
++		return NSS_STATUS_UNAVAIL;
++	}
++
++	if (nss_context->initgroups_start == 0) {
++		groups[0] = group;
++		nss_context->initgroups_start++;
++	}
++
++	ret = nss_context->initgroups_dyn(name, group,
++					  &nss_context->initgroups_start,
++					  &ngroups, &groups,
++					  -1, &lerrno);
++	if (ret == NSS_STATUS_SUCCESS) {
++		nss_context->initgroups_start = 0;
++	}
++
++	return ret;
++}
++
+diff --git a/src/back-sch-sss_idmap.c b/src/back-sch-sss_idmap.c
+new file mode 100644
+index 0000000..6a31267
+--- /dev/null
++++ b/src/back-sch-sss_idmap.c
+@@ -0,0 +1,239 @@
++/*
++ * Copyright 2013-2017 Red Hat, Inc.
++ *
++ * This Program is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License as published by
++ * the Free Software Foundation; version 2 of the License.
++ *
++ * This Program is distributed in the hope that it will be useful, but
++ * WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
++ * General Public License for more details.
++ *
++ * You should have received a copy of the GNU General Public License
++ * along with this Program; if not, write to the
++ *
++ *   Free Software Foundation, Inc.
++ *   59 Temple Place, Suite 330
++ *   Boston, MA 02111-1307 USA
++ *
++ */
++
++#ifdef HAVE_CONFIG_H
++#include "config.h"
++#endif
++
++#include <sys/types.h>
++#include <stdlib.h>
++#include <string.h>
++#include <time.h>
++#include <unistd.h>
++#include <errno.h>
++#include <pwd.h>
++#include <grp.h>
++#include "back-sch-nss.h"
++
++/* SSSD only exposes *_timeout() variants if the following symbol is defined */
++#define IPA_389DS_PLUGIN_HELPER_CALLS
++#include <sss_nss_idmap.h>
++
++struct nss_ops_ctx {
++	unsigned int timeout;
++};
++
++static enum nss_status __convert_sss_nss2nss_status(int errcode) {
++	enum nss_status ret = NSS_STATUS_UNAVAIL;
++
++	if (errcode == 0) {
++		ret = NSS_STATUS_SUCCESS;
++	} else if (errcode == ENOENT) {
++		ret = NSS_STATUS_NOTFOUND;
++	} else if(errcode == ERANGE) {
++		ret = NSS_STATUS_TRYAGAIN;
++	} else if(errcode == ETIMEDOUT) {
++		ret = NSS_STATUS_UNAVAIL;
++	} else if(errcode == ETIME) {
++		ret = NSS_STATUS_TRYAGAIN;
++	};
++
++	return ret;
++}
++
++void backend_nss_init_context(struct nss_ops_ctx **nss_context)
++{
++	struct nss_ops_ctx *ctx = NULL;
++
++	if (nss_context == NULL) {
++		return;
++	}
++
++	ctx = calloc(1, sizeof(struct nss_ops_ctx));
++
++	*nss_context = ctx;
++}
++
++void backend_nss_free_context(struct nss_ops_ctx **nss_context)
++{
++	if (nss_context == NULL) {
++		return;
++	}
++
++	free((*nss_context));
++	*nss_context = NULL;
++}
++
++
++void backend_nss_set_timeout(struct nss_ops_ctx *nss_context,
++			     unsigned int timeout) {
++	if (nss_context == NULL) {
++		return;
++	}
++
++	nss_context->timeout = timeout;
++}
++
++/* TODO: handle buffers and memory allocation in this function */
++void backend_nss_evict_user(struct nss_ops_ctx *nss_context,
++			    const char *name) {
++	if (nss_context == NULL) {
++		return;
++	}
++
++	(void) sss_nss_getpwnam_timeout(name, NULL,
++					NULL, 0,
++					NULL,
++					SSS_NSS_EX_FLAG_INVALIDATE_CACHE,
++					nss_context->timeout);
++}
++
++/* TODO: handle buffers and memory allocation in this function */
++void backend_nss_evict_group(struct nss_ops_ctx *nss_context,
++			     const char *name) {
++	if (nss_context == NULL) {
++		return;
++	}
++
++	(void) sss_nss_getgrnam_timeout(name, NULL,
++					NULL, 0,
++					NULL,
++					SSS_NSS_EX_FLAG_INVALIDATE_CACHE,
++					nss_context->timeout);
++}
++
++enum nss_status backend_nss_getpwnam(struct nss_ops_ctx *nss_context,
++				     const char *name, struct passwd *pwd,
++				     char *buffer, size_t buflen,
++				     struct passwd **result,
++				     int *lerrno) {
++	int ret = 0;
++
++	if (nss_context == NULL) {
++		return NSS_STATUS_UNAVAIL;
++	}
++
++	ret = sss_nss_getpwnam_timeout(name, pwd,
++				       buffer, buflen,
++				       result,
++				       SSS_NSS_EX_FLAG_NO_FLAGS,
++				       nss_context->timeout);
++
++	if (ret != 0 && lerrno != NULL) {
++		/* SSSD translates errno into return code */
++		*lerrno = ret;
++	}
++	return __convert_sss_nss2nss_status(ret);
++}
++
++enum nss_status backend_nss_getpwuid(struct nss_ops_ctx *nss_context,
++				     uid_t uid, struct passwd *pwd,
++				     char *buffer, size_t buflen,
++				     struct passwd **result,
++				     int *lerrno) {
++
++	int ret = 0;
++
++	if (nss_context == NULL) {
++		return NSS_STATUS_UNAVAIL;
++	}
++
++	ret = sss_nss_getpwuid_timeout(uid, pwd,
++				       buffer, buflen,
++				       result,
++				       SSS_NSS_EX_FLAG_NO_FLAGS,
++				       nss_context->timeout);
++	if (ret != 0 && lerrno != NULL) {
++		/* SSSD translates errno into return code */
++		*lerrno = ret;
++	}
++	return __convert_sss_nss2nss_status(ret);
++}
++
++enum nss_status backend_nss_getgrnam(struct nss_ops_ctx *nss_context,
++				     const char *name, struct group *grp,
++				     char *buffer, size_t buflen,
++				     struct group **result,
++				     int *lerrno) {
++
++	int ret = 0;
++
++	if (nss_context == NULL) {
++		return NSS_STATUS_UNAVAIL;
++	}
++
++	ret = sss_nss_getgrnam_timeout(name, grp,
++				       buffer, buflen,
++				       result,
++				       SSS_NSS_EX_FLAG_NO_FLAGS,
++				       nss_context->timeout);
++	if (ret != 0 && lerrno != NULL) {
++		/* SSSD translates errno into return code */
++		*lerrno = ret;
++	}
++	return __convert_sss_nss2nss_status(ret);
++}
++
++enum nss_status backend_nss_getgrgid(struct nss_ops_ctx *nss_context,
++				     gid_t gid, struct group *grp,
++				     char *buffer, size_t buflen,
++				     struct group **result,
++				     int *lerrno) {
++
++	int ret = 0;
++
++	if (nss_context == NULL) {
++		return NSS_STATUS_UNAVAIL;
++	}
++
++	ret = sss_nss_getgrgid_timeout(gid, grp,
++				       buffer, buflen,
++				       result,
++				       SSS_NSS_EX_FLAG_NO_FLAGS,
++				       nss_context->timeout);
++	if (ret != 0 && lerrno != NULL) {
++		/* SSSD translates errno into return code */
++		*lerrno = ret;
++	}
++	return __convert_sss_nss2nss_status(ret);
++}
++
++enum nss_status backend_nss_getgrouplist(struct nss_ops_ctx *nss_context,
++					 const char *name, gid_t group,
++					 gid_t *groups, int *ngroups,
++					 int *lerrno) {
++	int ret = 0;
++
++	if (nss_context == NULL) {
++		return NSS_STATUS_UNAVAIL;
++	}
++
++	ret = sss_nss_getgrouplist_timeout(name, group,
++					   groups, ngroups,
++					   SSS_NSS_EX_FLAG_NO_FLAGS,
++					   nss_context->timeout);
++	if (ret != 0 && lerrno != NULL) {
++		/* SSSD translates errno into return code */
++		*lerrno = ret;
++	}
++	return __convert_sss_nss2nss_status(ret);
++}
++
+diff --git a/src/back-sch.h b/src/back-sch.h
+index 9a9abc7..a400419 100644
+--- a/src/back-sch.h
++++ b/src/back-sch.h
+@@ -152,10 +152,17 @@ typedef struct backend_extop_handlers {
+ 
+ int backend_analyze_search_filter(Slapi_Filter *filter, struct backend_search_filter_config *config);
+ 
+-/* Operations against nsswitch API */
++/* NSS backend operations implemented using either nss_sss.so.2 or libsss_nss_idmap API */
+ struct nss_ops_ctx;
++
+ void backend_nss_init_context(struct nss_ops_ctx **nss_context);
+ void backend_nss_free_context(struct nss_ops_ctx **nss_context);
++void backend_nss_set_timeout(struct nss_ops_ctx **nss_context,
++			     unsigned int timeout);
++void backend_nss_evict_user(struct nss_ops_ctx **nss_context,
++			    const char *name);
++void backend_nss_evict_group(struct nss_ops_ctx **nss_context,
++			     const char *name);
+ 
+ void backend_search_nsswitch(struct backend_set_data *set_data,
+ 			     struct backend_search_cbdata *cbdata);
+diff --git a/src/plug-sch.c b/src/plug-sch.c
+index 00e7041..6ee4042 100644
+--- a/src/plug-sch.c
++++ b/src/plug-sch.c
+@@ -104,6 +104,7 @@ plugin_startup(Slapi_PBlock *pb)
+ 	struct plugin_state *state;
+ 	Slapi_Entry *plugin_entry = NULL;
+ 	Slapi_DN *pluginsdn = NULL;
++	unsigned int nss_timeout = 10000;
+ 
+ 	slapi_pblock_get(pb, SLAPI_PLUGIN_PRIVATE, &state);
+ 	slapi_pblock_get(pb, SLAPI_TARGET_SDN, &pluginsdn);
+@@ -130,7 +131,13 @@ plugin_startup(Slapi_PBlock *pb)
+ 		state->use_entry_cache = backend_shr_get_vattr_boolean(state, plugin_entry,
+ 									"slapi-entry-cache",
+ 									1);
++		nss_timeout = backend_shr_get_vattr_uint(state, plugin_entry,
++							 "slapi-nss-timeout",
++							 10000);
++
+ 	}
++	backend_nss_set_timeout(state->nss_context, nss_timeout);
++
+ 	state->cached_entries_lock = wrap_new_rwlock();
+ 	wrap_rwlock_wrlock(state->cached_entries_lock);
+ 	state->cached_entries = PL_NewHashTable(0, PL_HashString, PL_CompareStrings, PL_CompareValues, 0, 0);
+-- 
+2.13.6
+
diff --git a/SOURCES/slapi-0017-Fix-nss_sss-callers.patch b/SOURCES/slapi-0017-Fix-nss_sss-callers.patch
new file mode 100644
index 0000000..d90305d
--- /dev/null
+++ b/SOURCES/slapi-0017-Fix-nss_sss-callers.patch
@@ -0,0 +1,185 @@
+From 53f7f75ebf2a5086d63989bdf1da0d42983d6448 Mon Sep 17 00:00:00 2001
+From: Alexander Bokovoy <abokovoy@redhat.com>
+Date: Fri, 3 Nov 2017 23:49:19 +0200
+Subject: [PATCH 17/18] Fix nss_sss callers
+
+---
+ src/back-sch-nss_sss.c | 82 +++++++++++++++++++++++++++++++++++---------------
+ 1 file changed, 57 insertions(+), 25 deletions(-)
+
+diff --git a/src/back-sch-nss_sss.c b/src/back-sch-nss_sss.c
+index e3e6628..2b1b8b0 100644
+--- a/src/back-sch-nss_sss.c
++++ b/src/back-sch-nss_sss.c
+@@ -52,6 +52,20 @@ struct nss_ops_ctx {
+ 			      int *errnop);
+ };
+ 
++void backend_nss_free_context(struct nss_ops_ctx **nss_context)
++{
++	if (nss_context == NULL) {
++		return;
++	}
++
++	if ((*nss_context)->dl_handle != NULL) {
++		dlclose((*nss_context)->dl_handle);
++	}
++
++	free((*nss_context));
++	*nss_context = NULL;
++}
++
+ void backend_nss_init_context(struct nss_ops_ctx **nss_context)
+ {
+ 	struct nss_ops_ctx *ctx = NULL;
+@@ -105,21 +119,6 @@ fail:
+ 	return;
+ }
+ 
+-void
+-backend_nss_free_context(struct nss_ops_ctx **nss_context)
+-{
+-	if (nss_context == NULL) {
+-		return;
+-	}
+-
+-	if ((*nss_context)->dl_handle != NULL) {
+-		dlclose((*nss_context)->dl_handle);
+-	}
+-
+-	free((*nss_context));
+-	*nss_context = NULL;
+-}
+-
+ 
+ /* Following three functions cannot be implemented with nss_sss.so.2
+  * As result, we simply do nothing here */
+@@ -144,15 +143,22 @@ enum nss_status backend_nss_getpwnam(struct nss_ops_ctx *nss_context,
+ 				     char *buffer, size_t buflen,
+ 				     struct passwd **result,
+ 				     int *lerrno) {
++	enum nss_status ret;
+ 
+ 	if (nss_context == NULL) {
+ 		return NSS_STATUS_UNAVAIL;
+ 	}
+ 
+-	return (enum nss_status)
++	ret = (enum nss_status)
+ 		nss_context->getpwnam_r(name, pwd,
+ 					buffer, buflen,
+-					result, lerrno);
++					lerrno);
++
++	if ((ret == NSS_STATUS_SUCCESS) && (result != NULL)) {
++		*result = pwd;
++	}
++
++	return ret;
+ }
+ 
+ enum nss_status backend_nss_getpwuid(struct nss_ops_ctx *nss_context,
+@@ -160,15 +166,22 @@ enum nss_status backend_nss_getpwuid(struct nss_ops_ctx *nss_context,
+ 				     char *buffer, size_t buflen,
+ 				     struct passwd **result,
+ 				     int *lerrno) {
++	enum nss_status ret;
+ 
+ 	if (nss_context == NULL) {
+ 		return NSS_STATUS_UNAVAIL;
+ 	}
+ 
+-	return (enum nss_status)
++	ret = (enum nss_status)
+ 		nss_context->getpwuid_r(uid, pwd,
+ 					buffer, buflen,
+-					result, lerrno);
++					lerrno);
++
++	if ((ret == NSS_STATUS_SUCCESS) && (result != NULL)) {
++		*result = pwd;
++	}
++
++	return ret;
+ }
+ 
+ enum nss_status backend_nss_getgrnam(struct nss_ops_ctx *nss_context,
+@@ -176,15 +189,22 @@ enum nss_status backend_nss_getgrnam(struct nss_ops_ctx *nss_context,
+ 				     char *buffer, size_t buflen,
+ 				     struct group **result,
+ 				     int *lerrno) {
++	enum nss_status ret;
+ 
+ 	if (nss_context == NULL) {
+ 		return NSS_STATUS_UNAVAIL;
+ 	}
+ 
+-	return (enum nss_status)
++	ret = (enum nss_status)
+ 		nss_context->getgrnam_r(name, grp,
+ 					buffer, buflen,
+-					result, lerrno);
++					lerrno);
++
++	if ((ret == NSS_STATUS_SUCCESS) && (result != NULL)) {
++		*result = grp;
++	}
++
++	return ret;
+ }
+ 
+ enum nss_status backend_nss_getgrgid(struct nss_ops_ctx *nss_context,
+@@ -193,21 +213,31 @@ enum nss_status backend_nss_getgrgid(struct nss_ops_ctx *nss_context,
+ 				     struct group **result,
+ 				     int *lerrno) {
+ 
++	enum nss_status ret;
++
+ 	if (nss_context == NULL) {
+ 		return NSS_STATUS_UNAVAIL;
+ 	}
+ 
+-	return (enum nss_status)
++	ret = (enum nss_status)
+ 		nss_context->getgrgid_r(gid, grp,
+ 					buffer, buflen,
+-					result, lerrno);
++					lerrno);
++
++	if ((ret == NSS_STATUS_SUCCESS) && (result != NULL)) {
++		*result = grp;
++	}
++
++	return ret;
+ }
+ 
+ enum nss_status backend_nss_getgrouplist(struct nss_ops_ctx *nss_context,
+ 					 const char *name, gid_t group,
+ 					 gid_t *groups, int *ngroups,
+ 					 int *lerrno) {
++
+ 	enum nss_status ret = NSS_STATUS_UNAVAIL;
++	long int size = 0;
+ 
+ 	if (nss_context == NULL) {
+ 		return NSS_STATUS_UNAVAIL;
+@@ -220,12 +250,14 @@ enum nss_status backend_nss_getgrouplist(struct nss_ops_ctx *nss_context,
+ 
+ 	ret = nss_context->initgroups_dyn(name, group,
+ 					  &nss_context->initgroups_start,
+-					  &ngroups, &groups,
+-					  -1, &lerrno);
++					  &size, &groups,
++					  -1, lerrno);
+ 	if (ret == NSS_STATUS_SUCCESS) {
+ 		nss_context->initgroups_start = 0;
+ 	}
+ 
++	*ngroups = (int) size;
++
+ 	return ret;
+ }
+ 
+-- 
+2.13.6
+
diff --git a/SOURCES/slapi-0017-back-sch-cancel-memberof-retrieval-in-case-of-a-dirs.patch b/SOURCES/slapi-0017-back-sch-cancel-memberof-retrieval-in-case-of-a-dirs.patch
new file mode 100644
index 0000000..7c5dd7f
--- /dev/null
+++ b/SOURCES/slapi-0017-back-sch-cancel-memberof-retrieval-in-case-of-a-dirs.patch
@@ -0,0 +1,29 @@
+From 46649808918c027865dfaf78869aeeaddf97f47c Mon Sep 17 00:00:00 2001
+From: Alexander Bokovoy <abokovoy@redhat.com>
+Date: Thu, 2 Nov 2017 17:14:14 +0200
+Subject: [PATCH 17/17] back-sch: cancel memberof retrieval in case of a dirsrv
+ shutdown
+
+Do not wait for SSSD to become online if directory server is going
+for shutdown. Since it is guaranteed that SSSD will not be able to
+function with 389-ds offline, it makes no sense to continue a loop.
+---
+ src/back-sch.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/src/back-sch.c b/src/back-sch.c
+index f98b0b4..1998765 100644
+--- a/src/back-sch.c
++++ b/src/back-sch.c
+@@ -507,7 +507,7 @@ backend_set_process_external_members(Slapi_PBlock *pb,
+ 		/* 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) {
++		if (!is_group_exists && !slapi_is_shutting_down()) {
+ 			slapi_log_error(SLAPI_LOG_FATAL, plugin_id,
+ 					"group \"%s\" does not exist because SSSD is offline.\n",
+ 					staged.name);
+-- 
+2.13.6
+
diff --git a/SOURCES/slapi-0018-Clean-up-unused-code.patch b/SOURCES/slapi-0018-Clean-up-unused-code.patch
new file mode 100644
index 0000000..8fab1e3
--- /dev/null
+++ b/SOURCES/slapi-0018-Clean-up-unused-code.patch
@@ -0,0 +1,68 @@
+From 9f8158841cc5ee3017de8f718203707d27ae87e1 Mon Sep 17 00:00:00 2001
+From: Alexander Bokovoy <abokovoy@redhat.com>
+Date: Sat, 4 Nov 2017 07:35:22 +0200
+Subject: [PATCH 18/18] Clean up unused code
+
+---
+ src/back-sch-idview.c | 2 +-
+ src/back-sch-nss.c    | 4 +---
+ src/back-sch.h        | 1 -
+ 3 files changed, 2 insertions(+), 5 deletions(-)
+
+diff --git a/src/back-sch-idview.c b/src/back-sch-idview.c
+index 8842906..e83fb1d 100644
+--- a/src/back-sch-idview.c
++++ b/src/back-sch-idview.c
+@@ -121,7 +121,7 @@ idview_process_overrides(struct backend_search_cbdata *cbdata,
+ 					 "createtimestamp", "modifytimestamp", "parentid",
+ 					 "entryusn", "entryid", "entrydn", "ipaoriginaluid",
+ 					 "ipaanchoruuid", "nsuniqueid", "ipasshpubkey", NULL };
+-	char *new_dn = NULL, *new_key = NULL, *sep = NULL, *new_val = NULL;
++	char *new_dn = NULL, *sep = NULL, *new_val = NULL;
+ 	char *override_type = NULL;
+ 	Slapi_Entry *override_entry = NULL;
+ 	Slapi_Attr *anchor = NULL, *id_attr = NULL;
+diff --git a/src/back-sch-nss.c b/src/back-sch-nss.c
+index e5f91b2..df04a96 100644
+--- a/src/back-sch-nss.c
++++ b/src/back-sch-nss.c
+@@ -57,7 +57,6 @@ static int
+ bvstrprefix(const struct berval *bval, const char *s)
+ {
+ 	size_t len;
+-	int c;
+ 
+ 	len = strlen(s);
+ 	if (len < bval->bv_len) {
+@@ -553,7 +552,6 @@ backend_retrieve_group_list_from_nsswitch(char *user_name, char *container_sdn,
+ 	struct nss_ops_ctx *ctx = NULL;
+ 	int lerrno = 0;
+ 	int ngroups = 0;
+-	long int start = 0;
+ 	enum nss_status rc;
+ 
+ 	ctx = cbdata->state->nss_context;
+@@ -674,7 +672,7 @@ void
+ backend_search_nsswitch(struct backend_set_data *set_data,
+ 			struct backend_search_cbdata *cbdata)
+ {
+-	int result, rc;
++	int result;
+ 	struct backend_search_filter_config config =
+ 		{FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, NULL, NULL, NULL};
+ 	struct backend_staged_search *staged = NULL;
+diff --git a/src/back-sch.h b/src/back-sch.h
+index a400419..483e288 100644
+--- a/src/back-sch.h
++++ b/src/back-sch.h
+@@ -137,7 +137,6 @@ struct backend_search_filter_config {
+ #define LDAP_EXTOP_PASSMOD_TAG_NEWPWD	0x82U
+ 
+ typedef int (*IFP)();
+-static int backend_passwdmod_extop(Slapi_PBlock *pb);
+ typedef struct backend_extop_handlers {
+     char *oid;
+     IFP extop_fct;
+-- 
+2.13.6
+
diff --git a/SOURCES/slapi-0019-Synchronize-nsswitch-backend-code-with-freeIPA.patch b/SOURCES/slapi-0019-Synchronize-nsswitch-backend-code-with-freeIPA.patch
new file mode 100644
index 0000000..d7daa01
--- /dev/null
+++ b/SOURCES/slapi-0019-Synchronize-nsswitch-backend-code-with-freeIPA.patch
@@ -0,0 +1,905 @@
+From 1229089c4023757ae15ff1866e4d89069325d2f0 Mon Sep 17 00:00:00 2001
+From: Alexander Bokovoy <abokovoy@redhat.com>
+Date: Thu, 7 Dec 2017 12:33:24 +0200
+Subject: [PATCH] Synchronize nsswitch backend code with freeIPA
+
+FreeIPA ipa-extdom-extop plugin uses the same logic as slapi-nis
+schema-compat plugin to handle requests to SSSD. Thus, we keep the
+code synchronized across both code bases. Since both plugins are loaded
+into the same address space we currently rename functions to allow them
+to co-exist. In future we'd move some of common code to a shared
+library.
+---
+ src/back-sch-nss_sss.c   | 365 ++++++++++++++++++++++++-----------------------
+ src/back-sch-sss_idmap.c | 335 +++++++++++++++++++++++--------------------
+ src/back-sch.h           |   8 +-
+ src/plug-sch.c           |  32 +++--
+ 4 files changed, 390 insertions(+), 350 deletions(-)
+
+diff --git a/src/back-sch-nss_sss.c b/src/back-sch-nss_sss.c
+index 2b1b8b0..5ee911e 100644
+--- a/src/back-sch-nss_sss.c
++++ b/src/back-sch-nss_sss.c
+@@ -32,232 +32,245 @@
+ #include <errno.h>
+ #include <pwd.h>
+ #include <grp.h>
++#include <sys/param.h>
+ #include "back-sch-nss.h"
+ 
+ struct nss_ops_ctx {
+-	void *dl_handle;
+-	long int initgroups_start;
+-
+-	enum nss_status (*getpwnam_r)(const char *name, struct passwd *result,
+-			  char *buffer, size_t buflen, int *errnop);
+-	enum nss_status (*getpwuid_r)(uid_t uid, struct passwd *result,
+-			  char *buffer, size_t buflen, int *errnop);
+-	enum nss_status (*getgrnam_r)(const char *name, struct group *result,
+-			  char *buffer, size_t buflen, int *errnop);
+-	enum nss_status (*getgrgid_r)(gid_t gid, struct group *result,
+-			  char *buffer, size_t buflen, int *errnop);
+-	enum nss_status (*initgroups_dyn)(const char *user, gid_t group,
+-			      long int *start, long int *size,
+-			      gid_t **groups, long int limit,
+-			      int *errnop);
++    void *dl_handle;
++    long int initgroups_start;
++
++    enum nss_status (*getpwnam_r)(const char *name, struct passwd *result,
++                                  char *buffer, size_t buflen, int *errnop);
++    enum nss_status (*getpwuid_r)(uid_t uid, struct passwd *result,
++                                  char *buffer, size_t buflen, int *errnop);
++    enum nss_status (*getgrnam_r)(const char *name, struct group *result,
++                                  char *buffer, size_t buflen, int *errnop);
++    enum nss_status (*getgrgid_r)(gid_t gid, struct group *result,
++                                  char *buffer, size_t buflen, int *errnop);
++    enum nss_status (*initgroups_dyn)(const char *user, gid_t group,
++                                      long int *start, long int *size,
++                                      gid_t **groups, long int limit,
++                                      int *errnop);
+ };
+ 
+ void backend_nss_free_context(struct nss_ops_ctx **nss_context)
+ {
+-	if (nss_context == NULL) {
+-		return;
+-	}
++    if ((nss_context == NULL) || (*nss_context == NULL)) {
++        return;
++    }
+ 
+-	if ((*nss_context)->dl_handle != NULL) {
+-		dlclose((*nss_context)->dl_handle);
+-	}
++    if ((*nss_context)->dl_handle != NULL) {
++        dlclose((*nss_context)->dl_handle);
++    }
+ 
+-	free((*nss_context));
+-	*nss_context = NULL;
++    free((*nss_context));
++    *nss_context = NULL;
+ }
+ 
+-void backend_nss_init_context(struct nss_ops_ctx **nss_context)
++int backend_nss_init_context(struct nss_ops_ctx **nss_context)
+ {
+-	struct nss_ops_ctx *ctx = NULL;
+-
+-	if (nss_context == NULL) {
+-		return;
+-	}
+-
+-	ctx = calloc(1, sizeof(struct nss_ops_ctx));
+-
+-	*nss_context = ctx;
+-	if (ctx == NULL) {
+-		return;
+-	}
+-
+-	ctx->dl_handle = dlopen("libnss_sss.so.2", RTLD_NOW);
+-	if (ctx->dl_handle == NULL) {
+-		goto fail;
+-	}
+-
+-	ctx->getpwnam_r = dlsym(ctx->dl_handle, "_nss_sss_getpwnam_r");
+-	if (ctx->getpwnam_r == NULL) {
+-		goto fail;
+-	}
+-
+-	ctx->getpwuid_r = dlsym(ctx->dl_handle, "_nss_sss_getpwuid_r");
+-	if (ctx->getpwuid_r == NULL) {
+-		goto fail;
+-	}
+-
+-	ctx->getgrnam_r = dlsym(ctx->dl_handle, "_nss_sss_getgrnam_r");
+-	if (ctx->getgrnam_r == NULL) {
+-		goto fail;
+-	}
+-
+-	ctx->getgrgid_r = dlsym(ctx->dl_handle, "_nss_sss_getgrgid_r");
+-	if (ctx->getgrgid_r == NULL) {
+-		goto fail;
+-	}
+-
+-	ctx->initgroups_dyn = dlsym(ctx->dl_handle, "_nss_sss_initgroups_dyn");
+-	if (ctx->initgroups_dyn == NULL) {
+-		goto fail;
+-	}
+-
+-	return;
++    struct nss_ops_ctx *ctx = NULL;
++
++    if (nss_context == NULL) {
++        return EINVAL;
++    }
++
++    ctx = calloc(1, sizeof(struct nss_ops_ctx));
++    if (ctx == NULL) {
++        return ENOMEM;
++    }
++    *nss_context = ctx;
++
++    ctx->dl_handle = dlopen("libnss_sss.so.2", RTLD_NOW);
++    if (ctx->dl_handle == NULL) {
++        goto fail;
++    }
++
++    ctx->getpwnam_r = dlsym(ctx->dl_handle, "_nss_sss_getpwnam_r");
++    if (ctx->getpwnam_r == NULL) {
++        goto fail;
++    }
++
++    ctx->getpwuid_r = dlsym(ctx->dl_handle, "_nss_sss_getpwuid_r");
++    if (ctx->getpwuid_r == NULL) {
++        goto fail;
++    }
++
++    ctx->getgrnam_r = dlsym(ctx->dl_handle, "_nss_sss_getgrnam_r");
++    if (ctx->getgrnam_r == NULL) {
++        goto fail;
++    }
++
++    ctx->getgrgid_r = dlsym(ctx->dl_handle, "_nss_sss_getgrgid_r");
++    if (ctx->getgrgid_r == NULL) {
++        goto fail;
++    }
++
++    ctx->initgroups_dyn = dlsym(ctx->dl_handle, "_nss_sss_initgroups_dyn");
++    if (ctx->initgroups_dyn == NULL) {
++        goto fail;
++    }
++
++    return 0;
+ 
+ fail:
+-	backend_nss_free_context(nss_context);
++    backend_nss_free_context(nss_context);
+ 
+-	return;
++    return EINVAL;
+ }
+ 
+ 
+ /* Following three functions cannot be implemented with nss_sss.so.2
+  * As result, we simply do nothing here */
+ 
+-void backend_nss_set_timeout(struct nss_ops_ctx **nss_context,
+-			     unsigned int timeout) {
+-	/* no operation */
++void backend_nss_set_timeout(struct nss_ops_ctx *nss_context,
++                             unsigned int timeout) {
++        /* no operation */
+ }
+ 
+-void backend_nss_evict_user(struct nss_ops_ctx **nss_context,
+-			    const char *name) {
+-	/* no operation */
++void backend_nss_evict_user(struct nss_ops_ctx *nss_context,
++                            const char *name) {
++        /* no operation */
+ }
+ 
+-void backend_nss_evict_group(struct nss_ops_ctx **nss_context,
+-			     const char *name) {
+-	/* no operation */
++void backend_nss_evict_group(struct nss_ops_ctx *nss_context,
++                             const char *name) {
++        /* no operation */
+ }
+ 
+ enum nss_status backend_nss_getpwnam(struct nss_ops_ctx *nss_context,
+-				     const char *name, struct passwd *pwd,
+-				     char *buffer, size_t buflen,
+-				     struct passwd **result,
+-				     int *lerrno) {
+-	enum nss_status ret;
+-
+-	if (nss_context == NULL) {
+-		return NSS_STATUS_UNAVAIL;
+-	}
+-
+-	ret = (enum nss_status)
+-		nss_context->getpwnam_r(name, pwd,
+-					buffer, buflen,
+-					lerrno);
+-
+-	if ((ret == NSS_STATUS_SUCCESS) && (result != NULL)) {
+-		*result = pwd;
+-	}
+-
+-	return ret;
++                                     const char *name, struct passwd *pwd,
++                                     char *buffer, size_t buflen,
++                                     struct passwd **result,
++                                     int *lerrno) {
++    enum nss_status ret;
++
++    if (nss_context == NULL) {
++        return NSS_STATUS_UNAVAIL;
++    }
++
++    ret = nss_context->getpwnam_r(name, pwd,
++                                  buffer, buflen,
++                                  lerrno);
++
++    if ((ret == NSS_STATUS_SUCCESS) && (result != NULL)) {
++        *result = pwd;
++        *lerrno = 0;
++    }
++
++    return ret;
+ }
+ 
+ enum nss_status backend_nss_getpwuid(struct nss_ops_ctx *nss_context,
+-				     uid_t uid, struct passwd *pwd,
+-				     char *buffer, size_t buflen,
+-				     struct passwd **result,
+-				     int *lerrno) {
+-	enum nss_status ret;
+-
+-	if (nss_context == NULL) {
+-		return NSS_STATUS_UNAVAIL;
+-	}
+-
+-	ret = (enum nss_status)
+-		nss_context->getpwuid_r(uid, pwd,
+-					buffer, buflen,
+-					lerrno);
+-
+-	if ((ret == NSS_STATUS_SUCCESS) && (result != NULL)) {
+-		*result = pwd;
+-	}
+-
+-	return ret;
++                                     uid_t uid, struct passwd *pwd,
++                                     char *buffer, size_t buflen,
++                                     struct passwd **result,
++                                     int *lerrno) {
++    enum nss_status ret;
++
++    if (nss_context == NULL) {
++        return NSS_STATUS_UNAVAIL;
++    }
++
++    ret = nss_context->getpwuid_r(uid, pwd,
++                                  buffer, buflen,
++                                  lerrno);
++
++    if ((ret == NSS_STATUS_SUCCESS) && (result != NULL)) {
++        *result = pwd;
++        *lerrno = 0;
++    }
++
++    return ret;
+ }
+ 
+ enum nss_status backend_nss_getgrnam(struct nss_ops_ctx *nss_context,
+-				     const char *name, struct group *grp,
+-				     char *buffer, size_t buflen,
+-				     struct group **result,
+-				     int *lerrno) {
+-	enum nss_status ret;
+-
+-	if (nss_context == NULL) {
+-		return NSS_STATUS_UNAVAIL;
+-	}
+-
+-	ret = (enum nss_status)
+-		nss_context->getgrnam_r(name, grp,
+-					buffer, buflen,
+-					lerrno);
+-
+-	if ((ret == NSS_STATUS_SUCCESS) && (result != NULL)) {
+-		*result = grp;
+-	}
+-
+-	return ret;
++                                     const char *name, struct group *grp,
++                                     char *buffer, size_t buflen,
++                                     struct group **result,
++                                     int *lerrno) {
++    enum nss_status ret;
++
++    if (nss_context == NULL) {
++        return NSS_STATUS_UNAVAIL;
++    }
++
++    ret = nss_context->getgrnam_r(name, grp,
++                                  buffer, buflen,
++                                  lerrno);
++
++    if ((ret == NSS_STATUS_SUCCESS) && (result != NULL)) {
++        *result = grp;
++        *lerrno = 0;
++    }
++
++    return ret;
+ }
+ 
+ enum nss_status backend_nss_getgrgid(struct nss_ops_ctx *nss_context,
+-				     gid_t gid, struct group *grp,
+-				     char *buffer, size_t buflen,
+-				     struct group **result,
+-				     int *lerrno) {
++                                     gid_t gid, struct group *grp,
++                                     char *buffer, size_t buflen,
++                                     struct group **result,
++                                     int *lerrno) {
+ 
+-	enum nss_status ret;
++    enum nss_status ret;
+ 
+-	if (nss_context == NULL) {
+-		return NSS_STATUS_UNAVAIL;
+-	}
++    if (nss_context == NULL) {
++        return NSS_STATUS_UNAVAIL;
++    }
+ 
+-	ret = (enum nss_status)
+-		nss_context->getgrgid_r(gid, grp,
+-					buffer, buflen,
+-					lerrno);
++    ret = nss_context->getgrgid_r(gid, grp,
++                                  buffer, buflen,
++                                  lerrno);
+ 
+-	if ((ret == NSS_STATUS_SUCCESS) && (result != NULL)) {
+-		*result = grp;
+-	}
++    if ((ret == NSS_STATUS_SUCCESS) && (result != NULL)) {
++        *result = grp;
++        *lerrno = 0;
++    }
+ 
+-	return ret;
++    return ret;
+ }
+ 
+ enum nss_status backend_nss_getgrouplist(struct nss_ops_ctx *nss_context,
+-					 const char *name, gid_t group,
+-					 gid_t *groups, int *ngroups,
+-					 int *lerrno) {
++                                         const char *name, gid_t group,
++                                         gid_t *groups, int *ngroups,
++                                         int *lerrno) {
++
++    enum nss_status ret = NSS_STATUS_UNAVAIL;
++    long int tsize = MAX (1, *ngroups);
++    gid_t *newgroups = NULL;
++
++    if (nss_context == NULL) {
++        return NSS_STATUS_UNAVAIL;
++    }
++
++    newgroups = (gid_t *) calloc (tsize, sizeof (gid_t));
++    if (newgroups == NULL) {
++        *lerrno = ENOMEM;
++        return NSS_STATUS_TRYAGAIN;
++    }
++
++    newgroups[0] = group;
++    nss_context->initgroups_start = 1;
+ 
+-	enum nss_status ret = NSS_STATUS_UNAVAIL;
+-	long int size = 0;
++    ret = nss_context->initgroups_dyn(name, group,
++                                      &nss_context->initgroups_start,
++                                      &tsize, &newgroups,
++                                      -1, lerrno);
+ 
+-	if (nss_context == NULL) {
+-		return NSS_STATUS_UNAVAIL;
+-	}
++    (void) memcpy(groups, newgroups,
++                  MIN(*ngroups, nss_context->initgroups_start) * sizeof(gid_t));
++    free(newgroups);
+ 
+-	if (nss_context->initgroups_start == 0) {
+-		groups[0] = group;
+-		nss_context->initgroups_start++;
+-	}
++    if (*ngroups < nss_context->initgroups_start) {
++        ret = NSS_STATUS_TRYAGAIN;
++        *lerrno = ERANGE;
++    }
+ 
+-	ret = nss_context->initgroups_dyn(name, group,
+-					  &nss_context->initgroups_start,
+-					  &size, &groups,
+-					  -1, lerrno);
+-	if (ret == NSS_STATUS_SUCCESS) {
+-		nss_context->initgroups_start = 0;
+-	}
++    *ngroups = (int) nss_context->initgroups_start;
+ 
+-	*ngroups = (int) size;
++    nss_context->initgroups_start = 0;
+ 
+-	return ret;
++    return ret;
+ }
+ 
+diff --git a/src/back-sch-sss_idmap.c b/src/back-sch-sss_idmap.c
+index 6a31267..452ff4f 100644
+--- a/src/back-sch-sss_idmap.c
++++ b/src/back-sch-sss_idmap.c
+@@ -38,202 +38,223 @@
+ #include <sss_nss_idmap.h>
+ 
+ struct nss_ops_ctx {
+-	unsigned int timeout;
++    unsigned int timeout;
+ };
+ 
+ static enum nss_status __convert_sss_nss2nss_status(int errcode) {
+-	enum nss_status ret = NSS_STATUS_UNAVAIL;
+-
+-	if (errcode == 0) {
+-		ret = NSS_STATUS_SUCCESS;
+-	} else if (errcode == ENOENT) {
+-		ret = NSS_STATUS_NOTFOUND;
+-	} else if(errcode == ERANGE) {
+-		ret = NSS_STATUS_TRYAGAIN;
+-	} else if(errcode == ETIMEDOUT) {
+-		ret = NSS_STATUS_UNAVAIL;
+-	} else if(errcode == ETIME) {
+-		ret = NSS_STATUS_TRYAGAIN;
+-	};
+-
+-	return ret;
++    switch(errcode) {
++    case 0:
++        return NSS_STATUS_SUCCESS;
++    case ENOENT:
++        return NSS_STATUS_NOTFOUND;
++    case ETIME:
++        /* fall-through */
++    case ERANGE:
++        return NSS_STATUS_TRYAGAIN;
++    case ETIMEDOUT:
++        /* fall-through */
++    default:
++        return NSS_STATUS_UNAVAIL;
++    }
++    return NSS_STATUS_UNAVAIL;
+ }
+ 
+-void backend_nss_init_context(struct nss_ops_ctx **nss_context)
++int backend_nss_init_context(struct nss_ops_ctx **nss_context)
+ {
+-	struct nss_ops_ctx *ctx = NULL;
++    struct nss_ops_ctx *ctx = NULL;
+ 
+-	if (nss_context == NULL) {
+-		return;
+-	}
++    if (nss_context == NULL) {
++        return EINVAL;
++    }
+ 
+-	ctx = calloc(1, sizeof(struct nss_ops_ctx));
++    ctx = calloc(1, sizeof(struct nss_ops_ctx));
+ 
+-	*nss_context = ctx;
++    if (ctx == NULL) {
++        return ENOMEM;
++    }
++    *nss_context = ctx;
++    return 0;
+ }
+ 
+ void backend_nss_free_context(struct nss_ops_ctx **nss_context)
+ {
+-	if (nss_context == NULL) {
+-		return;
+-	}
++    if ((nss_context == NULL) || (*nss_context == NULL)) {
++        return;
++    }
+ 
+-	free((*nss_context));
+-	*nss_context = NULL;
++    free((*nss_context));
++    *nss_context = NULL;
+ }
+ 
+ 
+ void backend_nss_set_timeout(struct nss_ops_ctx *nss_context,
+-			     unsigned int timeout) {
+-	if (nss_context == NULL) {
+-		return;
+-	}
++                             unsigned int timeout) {
++    if (nss_context == NULL) {
++        return;
++    }
+ 
+-	nss_context->timeout = timeout;
++    nss_context->timeout = timeout;
+ }
+ 
+-/* TODO: handle buffers and memory allocation in this function */
+ void backend_nss_evict_user(struct nss_ops_ctx *nss_context,
+-			    const char *name) {
+-	if (nss_context == NULL) {
+-		return;
+-	}
+-
+-	(void) sss_nss_getpwnam_timeout(name, NULL,
+-					NULL, 0,
+-					NULL,
+-					SSS_NSS_EX_FLAG_INVALIDATE_CACHE,
+-					nss_context->timeout);
++                            const char *name) {
++    if (nss_context == NULL) {
++        return;
++    }
++
++    (void) sss_nss_getpwnam_timeout(name, NULL,
++                                    NULL, 0,
++                                    NULL,
++                                    SSS_NSS_EX_FLAG_INVALIDATE_CACHE,
++                                    nss_context->timeout);
+ }
+ 
+-/* TODO: handle buffers and memory allocation in this function */
+ void backend_nss_evict_group(struct nss_ops_ctx *nss_context,
+-			     const char *name) {
+-	if (nss_context == NULL) {
+-		return;
+-	}
+-
+-	(void) sss_nss_getgrnam_timeout(name, NULL,
+-					NULL, 0,
+-					NULL,
+-					SSS_NSS_EX_FLAG_INVALIDATE_CACHE,
+-					nss_context->timeout);
++                             const char *name) {
++    if (nss_context == NULL) {
++            return;
++    }
++
++    (void) sss_nss_getgrnam_timeout(name, NULL,
++                                    NULL, 0,
++                                    NULL,
++                                    SSS_NSS_EX_FLAG_INVALIDATE_CACHE,
++                                    nss_context->timeout);
+ }
+ 
+ enum nss_status backend_nss_getpwnam(struct nss_ops_ctx *nss_context,
+-				     const char *name, struct passwd *pwd,
+-				     char *buffer, size_t buflen,
+-				     struct passwd **result,
+-				     int *lerrno) {
+-	int ret = 0;
+-
+-	if (nss_context == NULL) {
+-		return NSS_STATUS_UNAVAIL;
+-	}
+-
+-	ret = sss_nss_getpwnam_timeout(name, pwd,
+-				       buffer, buflen,
+-				       result,
+-				       SSS_NSS_EX_FLAG_NO_FLAGS,
+-				       nss_context->timeout);
+-
+-	if (ret != 0 && lerrno != NULL) {
+-		/* SSSD translates errno into return code */
+-		*lerrno = ret;
+-	}
+-	return __convert_sss_nss2nss_status(ret);
++                                     const char *name, struct passwd *pwd,
++                                     char *buffer, size_t buflen,
++                                     struct passwd **result,
++                                     int *lerrno) {
++    int ret = 0;
++
++    if (nss_context == NULL) {
++        return NSS_STATUS_UNAVAIL;
++    }
++
++    ret = sss_nss_getpwnam_timeout(name, pwd,
++                                   buffer, buflen,
++                                   result,
++                                   SSS_NSS_EX_FLAG_NO_FLAGS,
++                                   nss_context->timeout);
++
++    /* SSSD uses the same infrastructure to handle sss_nss_get* calls
++     * as nss_sss.so.2 module where 'int *errno' is passed to the helper
++     * but writes down errno into return code so we propagate it in case
++     * of error and translate the return code */
++    if (lerrno != NULL) {
++        *lerrno = ret;
++    }
++    return __convert_sss_nss2nss_status(ret);
+ }
+ 
+ enum nss_status backend_nss_getpwuid(struct nss_ops_ctx *nss_context,
+-				     uid_t uid, struct passwd *pwd,
+-				     char *buffer, size_t buflen,
+-				     struct passwd **result,
+-				     int *lerrno) {
+-
+-	int ret = 0;
+-
+-	if (nss_context == NULL) {
+-		return NSS_STATUS_UNAVAIL;
+-	}
+-
+-	ret = sss_nss_getpwuid_timeout(uid, pwd,
+-				       buffer, buflen,
+-				       result,
+-				       SSS_NSS_EX_FLAG_NO_FLAGS,
+-				       nss_context->timeout);
+-	if (ret != 0 && lerrno != NULL) {
+-		/* SSSD translates errno into return code */
+-		*lerrno = ret;
+-	}
+-	return __convert_sss_nss2nss_status(ret);
++                                     uid_t uid, struct passwd *pwd,
++                                     char *buffer, size_t buflen,
++                                     struct passwd **result,
++                                     int *lerrno) {
++
++    int ret = 0;
++
++    if (nss_context == NULL) {
++        return NSS_STATUS_UNAVAIL;
++    }
++
++    ret = sss_nss_getpwuid_timeout(uid, pwd,
++                                   buffer, buflen,
++                                   result,
++                                   SSS_NSS_EX_FLAG_NO_FLAGS,
++                                   nss_context->timeout);
++
++    /* SSSD uses the same infrastructure to handle sss_nss_get* calls
++     * as nss_sss.so.2 module where 'int *errno' is passed to the helper
++     * but writes down errno into return code so we propagate it in case
++     * of error and translate the return code */
++    if (lerrno != NULL) {
++        *lerrno = ret;
++    }
++    return __convert_sss_nss2nss_status(ret);
+ }
+ 
+ enum nss_status backend_nss_getgrnam(struct nss_ops_ctx *nss_context,
+-				     const char *name, struct group *grp,
+-				     char *buffer, size_t buflen,
+-				     struct group **result,
+-				     int *lerrno) {
+-
+-	int ret = 0;
+-
+-	if (nss_context == NULL) {
+-		return NSS_STATUS_UNAVAIL;
+-	}
+-
+-	ret = sss_nss_getgrnam_timeout(name, grp,
+-				       buffer, buflen,
+-				       result,
+-				       SSS_NSS_EX_FLAG_NO_FLAGS,
+-				       nss_context->timeout);
+-	if (ret != 0 && lerrno != NULL) {
+-		/* SSSD translates errno into return code */
+-		*lerrno = ret;
+-	}
+-	return __convert_sss_nss2nss_status(ret);
++                                     const char *name, struct group *grp,
++                                     char *buffer, size_t buflen,
++                                     struct group **result,
++                                     int *lerrno) {
++
++    int ret = 0;
++
++    if (nss_context == NULL) {
++        return NSS_STATUS_UNAVAIL;
++    }
++
++    ret = sss_nss_getgrnam_timeout(name, grp,
++                                   buffer, buflen,
++                                   result,
++                                   SSS_NSS_EX_FLAG_NO_FLAGS,
++                                   nss_context->timeout);
++
++    /* SSSD uses the same infrastructure to handle sss_nss_get* calls
++     * as nss_sss.so.2 module where 'int *errno' is passed to the helper
++     * but writes down errno into return code so we propagate it in case
++     * of error and translate the return code */
++    if (lerrno != NULL) {
++        *lerrno = ret;
++    }
++    return __convert_sss_nss2nss_status(ret);
+ }
+ 
+ enum nss_status backend_nss_getgrgid(struct nss_ops_ctx *nss_context,
+-				     gid_t gid, struct group *grp,
+-				     char *buffer, size_t buflen,
+-				     struct group **result,
+-				     int *lerrno) {
+-
+-	int ret = 0;
+-
+-	if (nss_context == NULL) {
+-		return NSS_STATUS_UNAVAIL;
+-	}
+-
+-	ret = sss_nss_getgrgid_timeout(gid, grp,
+-				       buffer, buflen,
+-				       result,
+-				       SSS_NSS_EX_FLAG_NO_FLAGS,
+-				       nss_context->timeout);
+-	if (ret != 0 && lerrno != NULL) {
+-		/* SSSD translates errno into return code */
+-		*lerrno = ret;
+-	}
+-	return __convert_sss_nss2nss_status(ret);
++                                     gid_t gid, struct group *grp,
++                                     char *buffer, size_t buflen,
++                                     struct group **result,
++                                     int *lerrno) {
++
++    int ret = 0;
++
++    if (nss_context == NULL) {
++        return NSS_STATUS_UNAVAIL;
++    }
++
++    ret = sss_nss_getgrgid_timeout(gid, grp,
++                                   buffer, buflen,
++                                   result,
++                                   SSS_NSS_EX_FLAG_NO_FLAGS,
++                                   nss_context->timeout);
++
++    /* SSSD uses the same infrastructure to handle sss_nss_get* calls
++     * as nss_sss.so.2 module where 'int *errno' is passed to the helper
++     * but writes down errno into return code so we propagate it in case
++     * of error and translate the return code */
++    if (lerrno != NULL) {
++        *lerrno = ret;
++    }
++    return __convert_sss_nss2nss_status(ret);
+ }
+ 
+ enum nss_status backend_nss_getgrouplist(struct nss_ops_ctx *nss_context,
+-					 const char *name, gid_t group,
+-					 gid_t *groups, int *ngroups,
+-					 int *lerrno) {
+-	int ret = 0;
+-
+-	if (nss_context == NULL) {
+-		return NSS_STATUS_UNAVAIL;
+-	}
+-
+-	ret = sss_nss_getgrouplist_timeout(name, group,
+-					   groups, ngroups,
+-					   SSS_NSS_EX_FLAG_NO_FLAGS,
+-					   nss_context->timeout);
+-	if (ret != 0 && lerrno != NULL) {
+-		/* SSSD translates errno into return code */
+-		*lerrno = ret;
+-	}
+-	return __convert_sss_nss2nss_status(ret);
++                                         const char *name, gid_t group,
++                                         gid_t *groups, int *ngroups,
++                                         int *lerrno) {
++    int ret = 0;
++
++    if (nss_context == NULL) {
++        return NSS_STATUS_UNAVAIL;
++    }
++
++    ret = sss_nss_getgrouplist_timeout(name, group,
++                                       groups, ngroups,
++                                       SSS_NSS_EX_FLAG_NO_FLAGS,
++                                       nss_context->timeout);
++
++    /* SSSD uses the same infrastructure to handle sss_nss_get* calls
++     * as nss_sss.so.2 module where 'int *errno' is passed to the helper
++     * but writes down errno into return code so we propagate it in case
++     * of error and translate the return code */
++    if (lerrno != NULL) {
++        *lerrno = ret;
++    }
++    return __convert_sss_nss2nss_status(ret);
+ }
+ 
+diff --git a/src/back-sch.h b/src/back-sch.h
+index 483e288..2d73f34 100644
+--- a/src/back-sch.h
++++ b/src/back-sch.h
+@@ -154,13 +154,13 @@ int backend_analyze_search_filter(Slapi_Filter *filter, struct backend_search_fi
+ /* NSS backend operations implemented using either nss_sss.so.2 or libsss_nss_idmap API */
+ struct nss_ops_ctx;
+ 
+-void backend_nss_init_context(struct nss_ops_ctx **nss_context);
++int backend_nss_init_context(struct nss_ops_ctx **nss_context);
+ void backend_nss_free_context(struct nss_ops_ctx **nss_context);
+-void backend_nss_set_timeout(struct nss_ops_ctx **nss_context,
++void backend_nss_set_timeout(struct nss_ops_ctx *nss_context,
+ 			     unsigned int timeout);
+-void backend_nss_evict_user(struct nss_ops_ctx **nss_context,
++void backend_nss_evict_user(struct nss_ops_ctx *nss_context,
+ 			    const char *name);
+-void backend_nss_evict_group(struct nss_ops_ctx **nss_context,
++void backend_nss_evict_group(struct nss_ops_ctx *nss_context,
+ 			     const char *name);
+ 
+ void backend_search_nsswitch(struct backend_set_data *set_data,
+diff --git a/src/plug-sch.c b/src/plug-sch.c
+index 6ee4042..0c20a07 100644
+--- a/src/plug-sch.c
++++ b/src/plug-sch.c
+@@ -105,27 +105,33 @@ plugin_startup(Slapi_PBlock *pb)
+ 	Slapi_Entry *plugin_entry = NULL;
+ 	Slapi_DN *pluginsdn = NULL;
+ 	unsigned int nss_timeout = 10000;
++	int ret = 0;
+ 
+ 	slapi_pblock_get(pb, SLAPI_PLUGIN_PRIVATE, &state);
+ 	slapi_pblock_get(pb, SLAPI_TARGET_SDN, &pluginsdn);
+ 	/* plugin base need to be duplicated because it will be destroyed
+ 	 * when pblock is destroyed but we need to use it in a separate thread */
+ 	if (NULL == pluginsdn || 0 == slapi_sdn_get_ndn_len(pluginsdn)) {
+-        slapi_log_error(SLAPI_LOG_FATAL, state->plugin_desc->spd_id,
+-                        "scheman compat plugin_startup: unable to retrieve plugin DN\n");
+-		return -1;
+-
+-    } else {
+-        state->plugin_base = slapi_ch_strdup(slapi_sdn_get_dn(pluginsdn));
+-		slapi_log_error(SLAPI_LOG_PLUGIN, state->plugin_desc->spd_id,
+-				"configuration entry is %s%s%s\n",
+-				state->plugin_base ? "\"" : "",
+-				state->plugin_base ? state->plugin_base : "NULL",
+-				state->plugin_base ? "\"" : "");
+-    }
++            slapi_log_error(SLAPI_LOG_FATAL, state->plugin_desc->spd_id,
++                            "scheman compat plugin_startup: unable to retrieve plugin DN\n");
++	    return -1;
++        } else {
++            state->plugin_base = slapi_ch_strdup(slapi_sdn_get_dn(pluginsdn));
++	    slapi_log_error(SLAPI_LOG_PLUGIN, state->plugin_desc->spd_id,
++			    "configuration entry is %s%s%s\n",
++			    state->plugin_base ? "\"" : "",
++			    state->plugin_base ? state->plugin_base : "NULL",
++			    state->plugin_base ? "\"" : "");
++        }
+ 
+ 	state->pam_lock = wrap_new_rwlock();
+-	backend_nss_init_context((struct nss_ops_ctx**) &state->nss_context);
++	ret = backend_nss_init_context((struct nss_ops_ctx**) &state->nss_context);
++	if (ret != 0) {
++	    slapi_log_error(SLAPI_LOG_PLUGIN, state->plugin_desc->spd_id,
++			    "failed to intiialize nsswitch backend: [%d]!\n",
++			    ret);
++	    return -1;
++	}
+ 	if ((slapi_pblock_get(pb, SLAPI_PLUGIN_CONFIG_ENTRY, &plugin_entry) == 0) &&
+ 	    (plugin_entry != NULL)) {
+ 		state->use_entry_cache = backend_shr_get_vattr_boolean(state, plugin_entry,
+-- 
+2.14.3
+
diff --git a/SOURCES/slapi-0020-Use-extended-SSSD-API-to-signal-that-an-entry-should.patch b/SOURCES/slapi-0020-Use-extended-SSSD-API-to-signal-that-an-entry-should.patch
new file mode 100644
index 0000000..f91c79d
--- /dev/null
+++ b/SOURCES/slapi-0020-Use-extended-SSSD-API-to-signal-that-an-entry-should.patch
@@ -0,0 +1,43 @@
+From 4cd8ef26ceb3f28d103d9b8381b025d9d3f087e5 Mon Sep 17 00:00:00 2001
+From: Alexander Bokovoy <abokovoy@redhat.com>
+Date: Thu, 7 Dec 2017 12:41:15 +0200
+Subject: [PATCH] Use extended SSSD API to signal that an entry should not be
+ cached anymore
+
+When ID override is changed, we remove affected entry from the schema
+compat subtrees. However, we should also signal to SSSD that ID override
+did change and thus SSSD should stop caching the entry. As result, next
+look up of the affected entry should cause a refresh of the data in
+SSSD.
+
+This is important for cases when group membership changes for AD users.
+---
+ src/back-sch.c | 12 ++++++++++++
+ 1 file changed, 12 insertions(+)
+
+diff --git a/src/back-sch.c b/src/back-sch.c
+index 1998765..4612051 100644
+--- a/src/back-sch.c
++++ b/src/back-sch.c
+@@ -2207,6 +2207,18 @@ backend_entry_evict_if_related(const char *group, const char *set, bool_t flag,
+ 
+ 		/* An entry corresponding to our target is found, evict it */
+ 		map_data_unset_entry(state, group, set, id);
++
++		/* Signal to SSSD that this entry is not cached anymore */
++		switch(set_data->check_nsswitch) {
++                case SCH_NSSWITCH_USER:
++			backend_nss_evict_user(state->nss_context, original_uid);
++			break;
++		case SCH_NSSWITCH_GROUP:
++			backend_nss_evict_group(state->nss_context, original_uid);
++			break;
++		default:
++			break;
++		}
+ 	}
+ 
+ 	slapi_ch_free_string(&id);
+-- 
+2.14.3
+
diff --git a/SPECS/slapi-nis.spec b/SPECS/slapi-nis.spec
index 6b79862..ca86aee 100644
--- a/SPECS/slapi-nis.spec
+++ b/SPECS/slapi-nis.spec
@@ -11,7 +11,7 @@
 
 Name:		slapi-nis
 Version:	0.56.0
-Release:	4%{?dist}
+Release:	8%{?dist}
 Summary:	NIS Server and Schema Compatibility plugins for Directory Server
 Group:		System Environment/Daemons
 License:	GPLv2
@@ -22,14 +22,27 @@ Patch1:		slapi-0001-Move-advance-definition-of-backend_passwdmod_extop-b.patch
 Patch2:		slapi-0002-Initialize-ret-before-use.patch
 Patch3:		slapi-0003-slapi-nis-resolve-IPA-groups-with-fully-qualified-su.patch
 Patch4:		slapi-0004-Declare-int-backend_init_extop-for-reuse-in-plug-sch.patch
+Patch5:		slapi-0005-Double-free-on-ldap-entry-during-priming.patch
 Patch6:		slapi-0006-back-sch-do-not-clobber-target-of-the-pblock-for-idv.patch
 Patch7:		slapi-0007-back-sch-nss-for-users-with-aliases-return-alias-as-.patch
+Patch11:	slapi-0011-Move-a-helper-to-build-DN-to-a-format.c.patch
+Patch12:	slapi-0012-Add-dummy-handler-for-a-related-add-delete-modify-to.patch
+Patch13:	slapi-0013-track-changes-to-ID-overrides-and-evict-map-cache-en.patch
+Patch15:	slapi-0015-configure.ac-detect-extended-NSS-API-provided-by-SSS.patch
+Patch16:	slapi-0016-schema-compat-add-support-for-timeout-based-NSS-quer.patch
+Patch17:	slapi-0017-back-sch-cancel-memberof-retrieval-in-case-of-a-dirs.patch
+Patch18:	slapi-0017-Fix-nss_sss-callers.patch
+Patch19:	slapi-0018-Clean-up-unused-code.patch
+Patch20:	slapi-0019-Synchronize-nsswitch-backend-code-with-freeIPA.patch
+Patch21:	slapi-0020-Use-extended-SSSD-API-to-signal-that-an-entry-should.patch
+
 BuildRoot:	%{_tmppath}/%{name}-%{version}-%{release}-root-%(%{__id_u} -n)
 
+BuildRequires: libtool, automake, autoconf
 BuildRequires:	389-ds-base-devel >= 1.3.5.6, %{ldap_impl}-devel
 BuildRequires:	nspr-devel, nss-devel, /usr/bin/rpcgen
 %if 0%{?fedora} > 18 || 0%{?rhel} > 6
-BuildRequires:	libsss_nss_idmap-devel
+BuildRequires:	libsss_nss_idmap-devel >= 1.16.0-11
 %define sss_nss_opts --with-sss-nss-idmap
 %else
 %define sss_nss_opts %{nil}
@@ -47,6 +60,9 @@ BuildRequires:	libtirpc-devel
 ExclusiveArch:	x86_64 %{ix86}
 %endif
 Requires: 389-ds-base >= 1.3.5.6
+%if 0%{?fedora} > 18 || 0%{?rhel} > 6
+Requires: libsss_nss_idmap >= 1.16.0-11
+%endif
 
 %description
 This package provides two plugins for Red Hat and 389 Directory Server.
@@ -67,11 +83,26 @@ for attributes from multiple entries in the tree.
 %patch2 -p1
 %patch3 -p1
 %patch4 -p1
-# patch 5 is part of patch 3
+%patch5 -p1
 %patch6 -p1
 %patch7 -p1
+%patch11 -p1
+%patch12 -p1
+%patch13 -p1
+%patch15 -p1
+%patch16 -p1
+%patch17 -p1
+%patch18 -p1
+%patch19 -p1
+%patch20 -p1
+%patch21 -p1
 
 %build
+libtoolize -f -c
+aclocal --force -I m4
+autoheader
+automake -f -a -i
+autoconf -f -i
 %configure --disable-static --with-tcp-wrappers --with-ldap=%{ldap_impl} \
 	--with-nsswitch --with-pam --with-pam-service=system-auth \
 	%{sss_nss_opts} %{betxn_opts}
@@ -100,6 +131,27 @@ rm -rf $RPM_BUILD_ROOT
 %{_sbindir}/nisserver-plugin-defs
 
 %changelog
+* Fri Dec 08 2017 Alexander Bokovoy <abokovoy@redhat.com> - 0.56.0-8
+- Fixed: #1473572
+- Changes of ID overrides now force clearing of affected SSSD cache entries on IPA master
+- Related: #1473577
+- Synchronize timeout-enabled code with ipa-extdom-extop
+
+* Mon Nov 06 2017 Alexander Bokovoy <abokovoy@redhat.com> - 0.56.0-7
+- Related: #1473577
+- Force at least SSSD 1.16.0-3 for timeout-enabled NSS API
+
+* Fri Nov 03 2017 Alexander Bokovoy <abokovoy@redhat.com> - 0.56.0-6
+- Related: #1473577
+- Update configure and make files over the old release
+- Fix nss_sss usage
+
+* Fri Nov 03 2017 Alexander Bokovoy <abokovoy@redhat.com> - 0.56.0-5
+- Resolves: #1473572
+- Fixed: Make changes in overrides available in the compat tree at runtime
+- Resolves: #1473577
+- Fixed: slapi-nis should use requests to SSSD with ability to cancel them with a timeout
+
 * Tue Aug 09 2016 Alexander Bokovoy <abokovoy@redhat.com> - 0.56.0-4
 - Fixed: UPN-based search for AD users does not match an entry in slapi-nis map cache
 - Resolves: #1361123