Blob Blame Raw
From 9ba3240a177c156e365f22c721432321bb0a679e Mon Sep 17 00:00:00 2001
From: Mark Reynolds <mreynolds@redhat.com>
Date: Tue, 4 Aug 2015 12:19:31 -0400
Subject: [PATCH 39/39] Ticket 47931 - memberOf & retrocl deadlocks

Bug Description:  When concurrently updating multiple backends the
                  memberOf and retrocl plugins can deadlock on each
                  other.  This is caused by the required retrocl lock,
                  and the db lock on the changenumber index in the
                  retrocl db.

Fix Description:  Added scoping to the retrocl that allows subtrees/suffixes
                  to be included or excluded.  Also moved the existing
                  memberOf scoping outside of its global lock.

                  Also improved the memberOf config copying to be consistent
                  and more efficient.  Improved the memberOf scoping attributes
                  to be multivalued.  And, properly valdiated new config
                  settings in the preop valdiation function, instead of the
                  "apply config" function.

https://fedorahosted.org/389/ticket/47931

Valgrind: passed

Reviewed by: nhosoi(Thanks!)

(cherry picked from commit fd959ac864d6d86d24928bc2c6f097d1a6031ecd)
(cherry picked from commit d8108476d3bedbcc03f6c61bfb3d50e921faaf42)
---
 ldap/servers/plugins/memberof/memberof.c        | 217 +++++++++++++--------
 ldap/servers/plugins/memberof/memberof.h        |   8 +-
 ldap/servers/plugins/memberof/memberof_config.c | 249 +++++++++++++++++-------
 ldap/servers/plugins/retrocl/retrocl.c          | 183 +++++++++++++++--
 ldap/servers/plugins/retrocl/retrocl.h          |   4 +
 ldap/servers/plugins/retrocl/retrocl_po.c       |  41 +++-
 6 files changed, 516 insertions(+), 186 deletions(-)

diff --git a/ldap/servers/plugins/memberof/memberof.c b/ldap/servers/plugins/memberof/memberof.c
index da52bc8..9b577b9 100644
--- a/ldap/servers/plugins/memberof/memberof.c
+++ b/ldap/servers/plugins/memberof/memberof.c
@@ -116,7 +116,7 @@ static int memberof_compare(MemberOfConfig *config, const void *a, const void *b
 static int memberof_qsort_compare(const void *a, const void *b);
 static void memberof_load_array(Slapi_Value **array, Slapi_Attr *attr);
 static int memberof_del_dn_from_groups(Slapi_PBlock *pb, MemberOfConfig *config, Slapi_DN *sdn);
-static int memberof_call_foreach_dn(Slapi_PBlock *pb, Slapi_DN *sdn,
+static int memberof_call_foreach_dn(Slapi_PBlock *pb, Slapi_DN *sdn, MemberOfConfig *config,
 	char **types, plugin_search_entry_callback callback,  void *callback_data);
 static int memberof_is_direct_member(MemberOfConfig *config, Slapi_Value *groupdn,
 	Slapi_Value *memberdn);
@@ -144,7 +144,7 @@ static const char *fetch_attr(Slapi_Entry *e, const char *attrname,
 static void memberof_fixup_task_thread(void *arg);
 static int memberof_fix_memberof(MemberOfConfig *config, char *dn, char *filter_str);
 static int memberof_fix_memberof_callback(Slapi_Entry *e, void *callback_data);
-
+static int memberof_entry_in_scope(MemberOfConfig *config, Slapi_DN *sdn);
 
 /*** implementation ***/
 
@@ -489,7 +489,8 @@ memberof_get_plugin_area()
 int memberof_postop_del(Slapi_PBlock *pb)
 {
 	int ret = SLAPI_PLUGIN_SUCCESS;
-	MemberOfConfig configCopy = {0, 0, 0, 0};
+	MemberOfConfig *mainConfig = NULL;
+	MemberOfConfig configCopy = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
 	Slapi_DN *sdn;
 	void *caller_id = NULL;
 
@@ -509,12 +510,13 @@ int memberof_postop_del(Slapi_PBlock *pb)
 		struct slapi_entry *e = NULL;
 
 		slapi_pblock_get( pb, SLAPI_ENTRY_PRE_OP, &e );
-
-		/* We need to get the config lock first.  Trying to get the
-		 * config lock after we already hold the op lock can cause
-		 * a deadlock. */
 		memberof_rlock_config();
-		/* copy config so it doesn't change out from under us */
+		mainConfig = memberof_get_config();
+		if(!memberof_entry_in_scope(mainConfig, slapi_entry_get_sdn(e))){
+			/* The entry is not in scope, bail...*/
+			memberof_unlock_config();
+			goto bail;
+		}
 		memberof_copy_config(&configCopy, memberof_get_config());
 		memberof_unlock_config();
 
@@ -529,7 +531,6 @@ int memberof_postop_del(Slapi_PBlock *pb)
 			                "memberof_postop_del: error deleting dn (%s) from group. Error (%d)\n",
 			                slapi_sdn_get_dn(sdn),ret);
 			memberof_unlock();
-			memberof_free_config(&configCopy);
 			goto bail;
 		}
 
@@ -554,10 +555,10 @@ int memberof_postop_del(Slapi_PBlock *pb)
 			}
 		}
 		memberof_unlock();
+bail:
 		memberof_free_config(&configCopy);
 	}
 
-bail:
 	if(ret){
 		slapi_pblock_set(pb, SLAPI_RESULT_CODE, &ret);
 		ret = SLAPI_PLUGIN_FAILURE;
@@ -591,7 +592,7 @@ memberof_del_dn_from_groups(Slapi_PBlock *pb, MemberOfConfig *config, Slapi_DN *
 
 		groupattrs[0] = config->groupattrs[i];
 
-		rc = memberof_call_foreach_dn(pb, sdn, groupattrs,
+		rc = memberof_call_foreach_dn(pb, sdn, config, groupattrs,
 		                              memberof_del_dn_type_callback, &data);
 	}
 
@@ -641,6 +642,20 @@ memberof_del_dn_type_callback(Slapi_Entry *e, void *callback_data)
 	return rc;
 }
 
+/* Check if the the entry include scope is a child of the sdn */
+static Slapi_DN*
+memberof_scope_is_child_of_dn(MemberOfConfig *config, Slapi_DN *sdn)
+{
+	int i = 0;
+
+	while(config->entryScopes && config->entryScopes[i]){
+		if(slapi_sdn_issuffix(config->entryScopes[i], sdn)){
+			return config->entryScopes[i];
+		}
+		i++;
+	}
+	return NULL;
+}
 /*
  * Does a callback search of "type=dn" under the db suffix that "dn" is in,
  * unless all_backends is set, then we look at all the backends.  If "dn"
@@ -649,7 +664,7 @@ memberof_del_dn_type_callback(Slapi_Entry *e, void *callback_data)
  */
 int
 memberof_call_foreach_dn(Slapi_PBlock *pb, Slapi_DN *sdn,
-	char **types, plugin_search_entry_callback callback, void *callback_data)
+	MemberOfConfig *config, char **types, plugin_search_entry_callback callback, void *callback_data)
 {
 	Slapi_PBlock *search_pb = NULL;
 	Slapi_DN *base_sdn = NULL;
@@ -657,9 +672,7 @@ memberof_call_foreach_dn(Slapi_PBlock *pb, Slapi_DN *sdn,
 	char *escaped_filter_val;
 	char *filter_str = NULL;
 	char *cookie = NULL;
-	int all_backends = memberof_config_get_all_backends();
-	Slapi_DN *entry_scope = memberof_config_get_entry_scope();
-	Slapi_DN *entry_scope_exclude_subtree = memberof_config_get_entry_scope_exclude_subtree();
+	int all_backends = config->allBackends;
 	int types_name_len = 0;
 	int num_types = 0;
 	int dn_len = slapi_sdn_get_ndn_len(sdn);
@@ -667,11 +680,7 @@ memberof_call_foreach_dn(Slapi_PBlock *pb, Slapi_DN *sdn,
 	int rc = 0;
 	int i = 0;
 
-	if (entry_scope && !slapi_sdn_issuffix(sdn, entry_scope)) {
-		return (rc);
-	}
-        
-	if (entry_scope_exclude_subtree && slapi_sdn_issuffix(sdn, entry_scope_exclude_subtree)) {
+	if (!memberof_entry_in_scope(config, sdn)) {
 		return (rc);
 	}
 
@@ -728,6 +737,8 @@ memberof_call_foreach_dn(Slapi_PBlock *pb, Slapi_DN *sdn,
 	search_pb = slapi_pblock_new();
 	be = slapi_get_first_backend(&cookie);
 	while(be){
+		Slapi_DN *scope_sdn = NULL;
+
 		if(!all_backends){
 			be = slapi_be_select(sdn);
 			if(be == NULL){
@@ -743,13 +754,14 @@ memberof_call_foreach_dn(Slapi_PBlock *pb, Slapi_DN *sdn,
 				continue;
 			}
 		}
-		if (entry_scope) {
-			if (slapi_sdn_issuffix(base_sdn, entry_scope)) {
+
+		if (config->entryScopes || config->entryScopeExcludeSubtrees) {
+			if (memberof_entry_in_scope(config, base_sdn)) {
 				/* do nothing, entry scope is spanning 
 				 * multiple suffixes, start at suffix */
-			} else if (slapi_sdn_issuffix(entry_scope, base_sdn)) {
+			} else if ((scope_sdn = memberof_scope_is_child_of_dn(config, base_sdn))) {
 				/* scope is below suffix, set search base */
-				base_sdn = entry_scope;
+				base_sdn = scope_sdn;
 			} else if(!all_backends){
 				break;
 			} else {
@@ -767,7 +779,6 @@ memberof_call_foreach_dn(Slapi_PBlock *pb, Slapi_DN *sdn,
 			break;
 		}
 
-
 		if(!all_backends){
 			break;
 		}
@@ -792,10 +803,7 @@ int memberof_postop_modrdn(Slapi_PBlock *pb)
 {
 	int ret = SLAPI_PLUGIN_SUCCESS;
 	void *caller_id = NULL;
-	Slapi_DN *entry_scope = NULL;
-        Slapi_DN *entry_scope_exclude_subtree = memberof_config_get_entry_scope_exclude_subtree();
 
-	entry_scope = memberof_config_get_entry_scope();
 	slapi_log_error( SLAPI_LOG_TRACE, MEMBEROF_PLUGIN_SUBSYSTEM,
 		     "--> memberof_postop_modrdn\n" );
 
@@ -810,7 +818,7 @@ int memberof_postop_modrdn(Slapi_PBlock *pb)
 	if(memberof_oktodo(pb))
 	{
 		MemberOfConfig *mainConfig = 0;
-		MemberOfConfig configCopy = {0, 0, 0, 0};
+		MemberOfConfig configCopy = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
 		struct slapi_entry *pre_e = NULL;
 		struct slapi_entry *post_e = NULL;
 		Slapi_DN *pre_sdn = 0;
@@ -818,7 +826,6 @@ int memberof_postop_modrdn(Slapi_PBlock *pb)
 
 		slapi_pblock_get( pb, SLAPI_ENTRY_PRE_OP, &pre_e );
 		slapi_pblock_get( pb, SLAPI_ENTRY_POST_OP, &post_e );
-		
 		if(pre_e && post_e)
 		{
 			pre_sdn = slapi_entry_get_sdn(pre_e);
@@ -831,11 +838,19 @@ int memberof_postop_modrdn(Slapi_PBlock *pb)
 		memberof_copy_config(&configCopy, mainConfig);
 		memberof_unlock_config();
 
+		/* Need to check both the pre/post entries */
+		if((pre_sdn && !memberof_entry_in_scope(&configCopy, pre_sdn)) &&
+		   (post_sdn && !memberof_entry_in_scope(&configCopy, post_sdn)))
+		{
+			/* The entry is not in scope */
+			goto bail;
+		}
+
 		memberof_lock();
 
 		/*  update any downstream members */
 		if(pre_sdn && post_sdn && configCopy.group_filter &&
-			0 == slapi_filter_test_simple(post_e, configCopy.group_filter))
+		   0 == slapi_filter_test_simple(post_e, configCopy.group_filter))
 		{
 			int i = 0;
 			Slapi_Attr *attr = 0;
@@ -847,7 +862,7 @@ int memberof_postop_modrdn(Slapi_PBlock *pb)
 				if(0 == slapi_entry_attr_find(post_e, configCopy.groupattrs[i], &attr))
 				{
 					if((ret = memberof_moddn_attr_list(pb, &configCopy, pre_sdn,
-					                            post_sdn, attr) != 0))
+					                                   post_sdn, attr) != 0))
 					{
 						slapi_log_error( SLAPI_LOG_FATAL, MEMBEROF_PLUGIN_SUBSYSTEM,
 							"memberof_postop_modrdn - update failed for (%s), error (%d)\n",
@@ -862,49 +877,49 @@ int memberof_postop_modrdn(Slapi_PBlock *pb)
 		 * of other group entries.  We need to update any member
 		 * attributes to refer to the new name. */
 		if (ret == LDAP_SUCCESS && pre_sdn && post_sdn) {
-			if ((entry_scope && !slapi_sdn_issuffix(post_sdn, entry_scope)) || 
-                            (entry_scope_exclude_subtree && slapi_sdn_issuffix(post_sdn, entry_scope_exclude_subtree))) {
+			if (!memberof_entry_in_scope(&configCopy, post_sdn)){
 				if((ret = memberof_del_dn_from_groups(pb, &configCopy, pre_sdn))){
 					slapi_log_error( SLAPI_LOG_FATAL, MEMBEROF_PLUGIN_SUBSYSTEM,
 						"memberof_postop_modrdn - delete dn failed for (%s), error (%d)\n",
 						slapi_sdn_get_dn(pre_sdn), ret);
 				}
 				if(ret == LDAP_SUCCESS && pre_e && configCopy.group_filter &&
-						0 == slapi_filter_test_simple(pre_e, configCopy.group_filter)) {
+				   0 == slapi_filter_test_simple(pre_e, configCopy.group_filter))
+				{
 					/* is the entry of interest as a group? */
-						int i = 0;
-						Slapi_Attr *attr = 0;
-
-						/* Loop through to find each grouping attribute separately. */
-						for (i = 0; configCopy.groupattrs[i] && ret == LDAP_SUCCESS; i++) {
-							if (0 == slapi_entry_attr_find(pre_e, configCopy.groupattrs[i], &attr)) {
-								if((ret = memberof_del_attr_list(pb, &configCopy, pre_sdn, attr))){
-									slapi_log_error( SLAPI_LOG_FATAL, MEMBEROF_PLUGIN_SUBSYSTEM,
-									"memberof_postop_modrdn: error deleting attr list - dn (%s). Error (%d)\n",
-									slapi_sdn_get_dn(pre_sdn),ret);
-								}
+					int i = 0;
+					Slapi_Attr *attr = 0;
 
+					/* Loop through to find each grouping attribute separately. */
+					for (i = 0; configCopy.groupattrs[i] && ret == LDAP_SUCCESS; i++) {
+						if (0 == slapi_entry_attr_find(pre_e, configCopy.groupattrs[i], &attr)) {
+							if((ret = memberof_del_attr_list(pb, &configCopy, pre_sdn, attr))){
+								slapi_log_error( SLAPI_LOG_FATAL, MEMBEROF_PLUGIN_SUBSYSTEM,
+								"memberof_postop_modrdn: error deleting attr list - dn (%s). Error (%d)\n",
+								slapi_sdn_get_dn(pre_sdn),ret);
 							}
+
 						}
-					} 
+					}
+				}
 				if(ret == LDAP_SUCCESS) {
-						memberof_del_dn_data del_data = {0, configCopy.memberof_attr};
-						if((ret = memberof_del_dn_type_callback(post_e, &del_data))){
-							slapi_log_error( SLAPI_LOG_FATAL, MEMBEROF_PLUGIN_SUBSYSTEM,
-								"memberof_postop_modrdn - delete dn callback failed for (%s), error (%d)\n",
-								slapi_entry_get_dn(post_e), ret);
-						}
+					memberof_del_dn_data del_data = {0, configCopy.memberof_attr};
+					if((ret = memberof_del_dn_type_callback(post_e, &del_data))){
+						slapi_log_error( SLAPI_LOG_FATAL, MEMBEROF_PLUGIN_SUBSYSTEM,
+							"memberof_postop_modrdn - delete dn callback failed for (%s), error (%d)\n",
+							slapi_entry_get_dn(post_e), ret);
 					}
+				}
 			} else {
 				if((ret = memberof_replace_dn_from_groups(pb, &configCopy, pre_sdn, post_sdn))){
 					slapi_log_error( SLAPI_LOG_FATAL, MEMBEROF_PLUGIN_SUBSYSTEM,
-						"memberof_postop_modrdn - replace dne failed for (%s), error (%d)\n",
+						"memberof_postop_modrdn - replace dn failed for (%s), error (%d)\n",
 						slapi_sdn_get_dn(pre_sdn), ret);
 				}
 			}
 		}
-
 		memberof_unlock();
+bail:
 		memberof_free_config(&configCopy);
 	}
 
@@ -946,7 +961,7 @@ memberof_replace_dn_from_groups(Slapi_PBlock *pb, MemberOfConfig *config,
 
 		groupattrs[0] = config->groupattrs[i];
 
-		if((ret = memberof_call_foreach_dn(pb, pre_sdn, groupattrs,
+		if((ret = memberof_call_foreach_dn(pb, pre_sdn, config, groupattrs,
 		                                   memberof_replace_dn_type_callback,
 		                                   &data)))
 		{
@@ -1064,12 +1079,11 @@ int memberof_postop_modify(Slapi_PBlock *pb)
 		goto done;
 	}
 
-
-	if(memberof_oktodo(pb) && (sdn = memberof_getsdn(pb)))
+	if(memberof_oktodo(pb))
 	{
 		int config_copied = 0;
 		MemberOfConfig *mainConfig = 0;
-		MemberOfConfig configCopy = {0, 0, 0, 0};
+		MemberOfConfig configCopy = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
 
 		/* get the mod set */
 		slapi_pblock_get(pb, SLAPI_MODIFY_MODS, &mods);
@@ -1088,19 +1102,22 @@ int memberof_postop_modify(Slapi_PBlock *pb)
 			 * only copy the config the first time it's needed so
 			 * it remains the same for all mods in the operation,
 			 * despite any config changes that may be made. */
-			if (!config_copied)
-			{
+			if (!config_copied){
 				memberof_rlock_config();
 				mainConfig = memberof_get_config();
 
 				if (memberof_is_grouping_attr(type, mainConfig))
 				{
 					interested = 1;
+					if (!memberof_entry_in_scope(mainConfig, sdn)){
+						/* Entry is not in scope */
+						memberof_unlock_config();
+						goto bail;
+					}
 					/* copy config so it doesn't change out from under us */
 					memberof_copy_config(&configCopy, mainConfig);
 					config_copied = 1;
 				}
-
 				memberof_unlock_config();
 			} else {
 				if (memberof_is_grouping_attr(type, &configCopy))
@@ -1197,8 +1214,7 @@ int memberof_postop_modify(Slapi_PBlock *pb)
 		}
 
 bail:
-		if (config_copied)
-		{
+		if (config_copied){
 			memberof_free_config(&configCopy);
 		}
 
@@ -1244,22 +1260,25 @@ int memberof_postop_add(Slapi_PBlock *pb)
 
 	if(memberof_oktodo(pb) && (sdn = memberof_getsdn(pb)))
 	{
-		MemberOfConfig *mainConfig = 0;
-		MemberOfConfig configCopy = {0, 0, 0, 0};
 		struct slapi_entry *e = NULL;
-
+		MemberOfConfig configCopy = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
+		MemberOfConfig *mainConfig;
 		slapi_pblock_get( pb, SLAPI_ENTRY_POST_OP, &e );
-		
 
 		/* is the entry of interest? */
 		memberof_rlock_config();
 		mainConfig = memberof_get_config();
 		if(e && mainConfig && mainConfig->group_filter &&
 		   0 == slapi_filter_test_simple(e, mainConfig->group_filter))
+
 		{
 			interested = 1;
-			/* copy config so it doesn't change out from under us */
-			memberof_copy_config(&configCopy, mainConfig);
+			if(!memberof_entry_in_scope(mainConfig, slapi_entry_get_sdn(e))){
+				/* Entry is not in scope */
+				memberof_unlock_config();
+				goto bail;
+			}
+			memberof_copy_config(&configCopy, memberof_get_config());
 		}
 		memberof_unlock_config();
 
@@ -1284,11 +1303,11 @@ int memberof_postop_add(Slapi_PBlock *pb)
 			}
 
 			memberof_unlock();
-
 			memberof_free_config(&configCopy);
 		}
 	}
 
+bail:
 	if(ret){
 		slapi_pblock_set(pb, SLAPI_RESULT_CODE, &ret);
 		ret = SLAPI_PLUGIN_FAILURE;
@@ -1326,26 +1345,61 @@ int memberof_oktodo(Slapi_PBlock *pb)
 	}
 
 	if(slapi_pblock_get(pb, SLAPI_PLUGIN_OPRETURN, &oprc) != 0) 
-        {
+	{
 		slapi_log_error( SLAPI_LOG_FATAL, MEMBEROF_PLUGIN_SUBSYSTEM,
 			"memberof_postop_oktodo: could not get parameters\n" );
 		ret = -1;
 	}
 
-        /* this plugin should only execute if the operation succeeded
-	*/
-        if(oprc != 0)
+	/* this plugin should only execute if the operation succeeded */
+	if(oprc != 0)
 	{
 		ret = 0;
 	}
-	
+
+bail:
 	slapi_log_error( SLAPI_LOG_TRACE, MEMBEROF_PLUGIN_SUBSYSTEM,
 		     "<-- memberof_postop_oktodo\n" );
 
-bail:
 	return ret;
 }
 
+/*
+ * Return 1 if the entry is in the scope.
+ * For MODRDN the caller should check both the preop
+ * and postop entries.  If we are moving out of, or
+ * into scope, we should process it.
+ */
+static int
+memberof_entry_in_scope(MemberOfConfig *config, Slapi_DN *sdn)
+{
+    if (config->entryScopeExcludeSubtrees){
+        int i = 0;
+
+        /* check the excludes */
+        while(config->entryScopeExcludeSubtrees[i]){
+            if (slapi_sdn_issuffix(sdn, config->entryScopeExcludeSubtrees[i])){
+                return 0;
+            }
+            i++;
+        }
+    }
+    if (config->entryScopes){
+        int i = 0;
+
+        /* check the excludes */
+        while(config->entryScopes[i]){
+            if (slapi_sdn_issuffix(sdn, config->entryScopes[i])){
+                return 1;
+            }
+            i++;
+        }
+        return 0;
+    }
+
+    return 1;
+}
+
 static Slapi_DN *
 memberof_getsdn(Slapi_PBlock *pb)
 {
@@ -2013,7 +2067,7 @@ memberof_get_groups_r(MemberOfConfig *config, Slapi_DN *member_sdn,
 {
 	/* Search for any grouping attributes that point to memberdn.
 	 * For each match, add it to the list, recurse and do same search */
-	return memberof_call_foreach_dn(NULL, member_sdn, config->groupattrs,
+	return memberof_call_foreach_dn(NULL, member_sdn, config, config->groupattrs,
 		memberof_get_groups_callback, data);
 }
 
@@ -2030,7 +2084,6 @@ int memberof_get_groups_callback(Slapi_Entry *e, void *callback_data)
 	Slapi_Value *group_dn_val = 0;
 	Slapi_ValueSet *groupvals = *((memberof_get_groups_data*)callback_data)->groupvals;
 	Slapi_ValueSet *group_norm_vals = *((memberof_get_groups_data*)callback_data)->group_norm_vals;
-	Slapi_DN *entry_scope_exclude_subtree = memberof_config_get_entry_scope_exclude_subtree();
 	MemberOfConfig *config = ((memberof_get_groups_data*)callback_data)->config;
 	int rc = 0;
 
@@ -2086,7 +2139,7 @@ int memberof_get_groups_callback(Slapi_Entry *e, void *callback_data)
 	}
 
 	/* if the group does not belong to an excluded subtree, adds it to the valueset */
-	if (!(entry_scope_exclude_subtree && slapi_sdn_issuffix(group_sdn, entry_scope_exclude_subtree))) {
+	if (memberof_entry_in_scope(config, group_sdn)) {
 			/* Push group_dn_val into the valueset.  This memory is now owned
 			 * by the valueset. */
 			group_dn_val = slapi_value_new_string(group_dn);
@@ -2188,8 +2241,8 @@ memberof_test_membership(Slapi_PBlock *pb, MemberOfConfig *config,
 {
 	char *attrs[2] = {config->memberof_attr, 0};
 
-	return memberof_call_foreach_dn(pb, group_sdn, attrs, 
-		memberof_test_membership_callback , config);
+	return memberof_call_foreach_dn(pb, group_sdn, config, attrs,
+		memberof_test_membership_callback, config);
 }
 
 /*
diff --git a/ldap/servers/plugins/memberof/memberof.h b/ldap/servers/plugins/memberof/memberof.h
index 5a70400..9d9d158 100644
--- a/ldap/servers/plugins/memberof/memberof.h
+++ b/ldap/servers/plugins/memberof/memberof.h
@@ -52,8 +52,10 @@ typedef struct memberofconfig {
 	char **groupattrs;
 	char *memberof_attr;
 	int allBackends;
-	Slapi_DN *entryScope;
-	Slapi_DN *entryScopeExcludeSubtree;
+	Slapi_DN **entryScopes;
+	int entryScopeCount;
+	Slapi_DN **entryScopeExcludeSubtrees;
+	int entryExcludeScopeCount;
 	Slapi_Filter *group_filter;
 	Slapi_Attr **group_slapiattrs;
 	int skip_nested;
@@ -74,8 +76,6 @@ void memberof_rlock_config();
 void memberof_wlock_config();
 void memberof_unlock_config();
 int memberof_config_get_all_backends();
-Slapi_DN * memberof_config_get_entry_scope();
-Slapi_DN * memberof_config_get_entry_scope_exclude_subtree();
 void memberof_set_config_area(Slapi_DN *sdn);
 Slapi_DN * memberof_get_config_area();
 void memberof_set_plugin_area(Slapi_DN *sdn);
diff --git a/ldap/servers/plugins/memberof/memberof_config.c b/ldap/servers/plugins/memberof/memberof_config.c
index ac2d045..b4cc941 100644
--- a/ldap/servers/plugins/memberof/memberof_config.c
+++ b/ldap/servers/plugins/memberof/memberof_config.c
@@ -48,7 +48,7 @@ static int memberof_search (Slapi_PBlock *pb, Slapi_Entry* entryBefore, Slapi_En
 /* This is the main configuration which is updated from dse.ldif.  The
  * config will be copied when it is used by the plug-in to prevent it
  * being changed out from under a running memberOf operation. */
-static MemberOfConfig theConfig = {NULL, NULL,0, NULL, NULL, NULL, NULL};
+static MemberOfConfig theConfig = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
 static Slapi_RWLock *memberof_config_lock = 0;
 static int inited = 0;
 
@@ -60,6 +60,19 @@ static int dont_allow_that(Slapi_PBlock *pb, Slapi_Entry* entryBefore, Slapi_Ent
 	return SLAPI_DSE_CALLBACK_ERROR;
 }
 
+static void
+memberof_free_scope(Slapi_DN **scopes, int *count)
+{
+	int i = 0;
+
+	while(scopes && scopes[i]){
+		slapi_sdn_free(&scopes[i]);
+		i++;
+	}
+	slapi_ch_free((void**)&scopes);
+	*count = 0;
+}
+
 /*
  * memberof_config()
  *
@@ -155,17 +168,22 @@ memberof_release_config()
  *
  * Validate the pending changes in the e entry.
  */
-static int 
+int
 memberof_validate_config (Slapi_PBlock *pb, Slapi_Entry* entryBefore, Slapi_Entry* e, 
 	int *returncode, char *returntext, void *arg)
 {
 	Slapi_Attr *memberof_attr = NULL;
 	Slapi_Attr *group_attr = NULL;
 	Slapi_DN *config_sdn = NULL;
+	Slapi_DN **include_dn = NULL;
+	Slapi_DN **exclude_dn = NULL;
 	char *syntaxoid = NULL;
 	char *config_dn = NULL;
 	char *skip_nested = NULL;
+	char **entry_scopes = NULL;
+	char **entry_exclude_scopes = NULL;
 	int not_dn_syntax = 0;
+	int num_vals = 0;
 
 	*returncode = LDAP_UNWILLING_TO_PERFORM; /* be pessimistic */
 
@@ -283,8 +301,112 @@ memberof_validate_config (Slapi_PBlock *pb, Slapi_Entry* entryBefore, Slapi_Entr
 			*returncode = LDAP_UNWILLING_TO_PERFORM;
 		}
 	}
+	/*
+	 * Check the entry scopes
+	 */
+	entry_scopes = slapi_entry_attr_get_charray_ext(e, MEMBEROF_ENTRY_SCOPE_ATTR, &num_vals);
+	if(entry_scopes){
+		int i = 0;
+
+		/* Validate the syntax before we create our DN array */
+		for (i = 0;i < num_vals; i++){
+			if(slapi_dn_syntax_check(pb, entry_scopes[i], 1)){
+				/* invalid dn syntax */
+				PR_snprintf(returntext, SLAPI_DSE_RETURNTEXT_SIZE,
+					"%s: Invalid DN (%s) for include suffix.",
+					MEMBEROF_PLUGIN_SUBSYSTEM, entry_scopes[i]);
+				slapi_ch_array_free(entry_scopes);
+				theConfig.entryScopeCount = 0;
+				*returncode = LDAP_UNWILLING_TO_PERFORM;
+				goto done;
+			}
+		}
+		/* Now create our SDN array for conflict checking */
+		include_dn = (Slapi_DN **)slapi_ch_calloc(sizeof(Slapi_DN *), num_vals+1);
+		for (i = 0;i < num_vals; i++){
+			include_dn[i] = slapi_sdn_new_dn_passin(entry_scopes[i]);
+		}
+	}
+	/*
+	 * Check and process the entry exclude scopes
+	 */
+	entry_exclude_scopes =
+		slapi_entry_attr_get_charray_ext(e, MEMBEROF_ENTRY_SCOPE_EXCLUDE_SUBTREE, &num_vals);
+	if(entry_exclude_scopes){
+		int i = 0;
+
+		/* Validate the syntax before we create our DN array */
+		for (i = 0;i < num_vals; i++){
+			if(slapi_dn_syntax_check(pb, entry_exclude_scopes[i], 1)){
+				/* invalid dn syntax */
+				PR_snprintf(returntext, SLAPI_DSE_RETURNTEXT_SIZE,
+					"%s: Invalid DN (%s) for exclude suffix.",
+					MEMBEROF_PLUGIN_SUBSYSTEM, entry_scopes[i]);
+				slapi_ch_array_free(entry_exclude_scopes);
+				*returncode = LDAP_UNWILLING_TO_PERFORM;
+				goto done;
+			}
+		}
+		/* Now create our SDN array for conflict checking */
+		exclude_dn = (Slapi_DN **)slapi_ch_calloc(sizeof(Slapi_DN *),num_vals+1);
+		for (i = 0;i < num_vals; i++){
+			exclude_dn[i] = slapi_sdn_new_dn_passin(entry_exclude_scopes[i]);
+		}
+	}
+	/*
+	 * Need to do conflict checking
+	 */
+	if(include_dn && exclude_dn){
+		/*
+		 * Make sure we haven't mixed the same suffix, and there are no
+		 * conflicts between the includes and excludes
+		 */
+		int i = 0;
+
+		while(include_dn[i]){
+			int x = 0;
+			while(exclude_dn[x]){
+				if(slapi_sdn_compare(include_dn[i], exclude_dn[x] ) == 0)
+				{
+					/* we have a conflict */
+					PR_snprintf(returntext, SLAPI_DSE_RETURNTEXT_SIZE,
+						"%s: include suffix (%s) is also listed as an exclude suffix list",
+						MEMBEROF_PLUGIN_SUBSYSTEM, slapi_sdn_get_dn(include_dn[i]));
+					*returncode = LDAP_UNWILLING_TO_PERFORM;
+					goto done;
+			   }
+			   x++;
+			}
+			i++;
+		}
+
+		/* Check for parent/child conflicts */
+		i = 0;
+		while(include_dn[i]){
+			int x = 0;
+			while(exclude_dn[x]){
+				if(slapi_sdn_issuffix(include_dn[i], exclude_dn[x]))
+				{
+					/* we have a conflict */
+					PR_snprintf(returntext, SLAPI_DSE_RETURNTEXT_SIZE,
+						"%s: include suffix (%s) is a child of the exclude suffix(%s)",
+						MEMBEROF_PLUGIN_SUBSYSTEM,
+						slapi_sdn_get_dn(include_dn[i]),
+						slapi_sdn_get_dn(exclude_dn[i]));
+					*returncode = LDAP_UNWILLING_TO_PERFORM;
+					goto done;
+			   }
+			   x++;
+			}
+			i++;
+		}
+	}
 
 done:
+	memberof_free_scope(exclude_dn, &num_vals);
+	memberof_free_scope(include_dn,	&num_vals);
+	slapi_ch_free((void**)&entry_scopes);
+	slapi_ch_free((void**)&entry_exclude_scopes);
 	slapi_sdn_free(&config_sdn);
 	slapi_ch_free_string(&config_dn);
 	slapi_ch_free_string(&skip_nested);
@@ -299,7 +421,6 @@ done:
 	}
 }
 
-
 /*
  * memberof_apply_config()
  *
@@ -318,10 +439,11 @@ memberof_apply_config (Slapi_PBlock *pb, Slapi_Entry* entryBefore, Slapi_Entry*
 	int num_groupattrs = 0;
 	int groupattr_name_len = 0;
 	char *allBackends = NULL;
-	char *entryScope = NULL;
-	char *entryScopeExcludeSubtree = NULL;
+	char **entryScopes = NULL;
+	char **entryScopeExcludeSubtrees = NULL;
 	char *sharedcfg = NULL;
 	char *skip_nested = NULL;
+	int num_vals = 0;
 
 	*returncode = LDAP_SUCCESS;
 
@@ -353,8 +475,6 @@ memberof_apply_config (Slapi_PBlock *pb, Slapi_Entry* entryBefore, Slapi_Entry*
 	groupattrs = slapi_entry_attr_get_charray(e, MEMBEROF_GROUP_ATTR);
 	memberof_attr = slapi_entry_attr_get_charptr(e, MEMBEROF_ATTR);
 	allBackends = slapi_entry_attr_get_charptr(e, MEMBEROF_BACKEND_ATTR);
-	entryScope = slapi_entry_attr_get_charptr(e, MEMBEROF_ENTRY_SCOPE_ATTR);
-	entryScopeExcludeSubtree = slapi_entry_attr_get_charptr(e, MEMBEROF_ENTRY_SCOPE_EXCLUDE_SUBTREE);
 	skip_nested = slapi_entry_attr_get_charptr(e, MEMBEROF_SKIP_NESTED_ATTR);
 
 	/*
@@ -480,49 +600,39 @@ memberof_apply_config (Slapi_PBlock *pb, Slapi_Entry* entryBefore, Slapi_Entry*
 		theConfig.allBackends = 0;
 	}
 
-	slapi_sdn_free(&theConfig.entryScope);
-	if (entryScope)
-	{
-        	if (slapi_dn_syntax_check(NULL, entryScope, 1) == 1) {
-            		slapi_log_error(SLAPI_LOG_FATAL, MEMBEROF_PLUGIN_SUBSYSTEM,
-                		"Error: Ignoring invalid DN used as plugin entry scope: [%s]\n",
-                		entryScope);
-			theConfig.entryScope = NULL;
-			slapi_ch_free_string(&entryScope);
-		} else {
-			theConfig.entryScope = slapi_sdn_new_dn_passin(entryScope);
+	/*
+	 * Check and process the entry scopes
+	 */
+	memberof_free_scope(theConfig.entryScopes, &theConfig.entryScopeCount);
+	entryScopes = slapi_entry_attr_get_charray_ext(e, MEMBEROF_ENTRY_SCOPE_ATTR, &num_vals);
+	if(entryScopes){
+		int i = 0;
+
+		/* Validation has already been performed in preop, just build the DN's */
+		theConfig.entryScopes = (Slapi_DN **)slapi_ch_calloc(sizeof(Slapi_DN *), num_vals+1);
+		for (i = 0;i < num_vals; i++){
+			theConfig.entryScopes[i] = slapi_sdn_new_dn_passin(entryScopes[i]);
 		}
-	} else {
-		theConfig.entryScope = NULL;
+		theConfig.entryScopeCount = num_vals; /* shortcut for config copy */
 	}
-        
-        slapi_sdn_free(&theConfig.entryScopeExcludeSubtree);
-        if (entryScopeExcludeSubtree)
-	{
-        	if (theConfig.entryScope == NULL) {
-                        slapi_log_error(SLAPI_LOG_FATAL, MEMBEROF_PLUGIN_SUBSYSTEM,
-                		"Error: Ignoring ExcludeSubtree (%s) because entryScope is not define\n",
-                		entryScopeExcludeSubtree);
-			theConfig.entryScopeExcludeSubtree = NULL;
-			slapi_ch_free_string(&entryScopeExcludeSubtree);
-                } else if (slapi_dn_syntax_check(NULL, entryScopeExcludeSubtree, 1) == 1) {
-            		slapi_log_error(SLAPI_LOG_FATAL, MEMBEROF_PLUGIN_SUBSYSTEM,
-                		"Error: Ignoring invalid DN used as plugin entry exclude subtree: [%s]\n",
-                		entryScopeExcludeSubtree);
-			theConfig.entryScopeExcludeSubtree = NULL;
-			slapi_ch_free_string(&entryScopeExcludeSubtree);
-		} else {
-			theConfig.entryScopeExcludeSubtree = slapi_sdn_new_dn_passin(entryScopeExcludeSubtree);
+	/*
+	 * Check and process the entry exclude scopes
+	 */
+	memberof_free_scope(theConfig.entryScopeExcludeSubtrees,
+	                    &theConfig.entryExcludeScopeCount);
+	entryScopeExcludeSubtrees =
+	    slapi_entry_attr_get_charray_ext(e, MEMBEROF_ENTRY_SCOPE_EXCLUDE_SUBTREE, &num_vals);
+	if(entryScopeExcludeSubtrees){
+		int i = 0;
+
+		/* Validation has already been performed in preop, just build the DN's */
+		theConfig.entryScopeExcludeSubtrees =
+				(Slapi_DN **)slapi_ch_calloc(sizeof(Slapi_DN *),num_vals+1);
+		for (i = 0;i < num_vals; i++){
+			theConfig.entryScopeExcludeSubtrees[i] =
+				slapi_sdn_new_dn_passin(entryScopeExcludeSubtrees[i]);
 		}
-	} else {
-		theConfig.entryScopeExcludeSubtree = NULL;
-	}
-        if (theConfig.entryScopeExcludeSubtree && theConfig.entryScope && !slapi_sdn_issuffix(theConfig.entryScopeExcludeSubtree, theConfig.entryScope)) {
-                slapi_log_error(SLAPI_LOG_FATAL, MEMBEROF_PLUGIN_SUBSYSTEM,
-                        "Error: Ignoring ExcludeSubtree (%s) that is out of the scope (%s)\n",
-                        slapi_sdn_get_dn(theConfig.entryScopeExcludeSubtree),
-                        slapi_sdn_get_dn(theConfig.entryScope));
-                slapi_sdn_free(&theConfig.entryScopeExcludeSubtree);
+		theConfig.entryExcludeScopeCount = num_vals; /* shortcut for config copy */
 	}
 
 	/* release the lock */
@@ -536,6 +646,8 @@ done:
 	slapi_ch_free_string(&memberof_attr);
 	slapi_ch_free_string(&allBackends);
 	slapi_ch_free_string(&skip_nested);
+	slapi_ch_free((void **)&entryScopes);
+	slapi_ch_free((void **)&entryScopeExcludeSubtrees);
 
 	if (*returncode != LDAP_SUCCESS)
 	{
@@ -616,6 +728,23 @@ memberof_copy_config(MemberOfConfig *dest, MemberOfConfig *src)
 		{
 			dest->allBackends = src->allBackends;
 		}
+
+		if(src->entryScopes){
+			int num_vals = 0;
+
+			dest->entryScopes = (Slapi_DN **)slapi_ch_calloc(sizeof(Slapi_DN *),src->entryScopeCount+1);
+			for(num_vals = 0; src->entryScopes[num_vals]; num_vals++){
+				dest->entryScopes[num_vals] = slapi_sdn_dup(src->entryScopes[num_vals]);
+			}
+		}
+		if(src->entryScopeExcludeSubtrees){
+			int num_vals = 0;
+
+			dest->entryScopeExcludeSubtrees = (Slapi_DN **)slapi_ch_calloc(sizeof(Slapi_DN *),src->entryExcludeScopeCount+1);
+			for(num_vals = 0; src->entryScopes[num_vals]; num_vals++){
+				dest->entryScopeExcludeSubtrees[num_vals] = slapi_sdn_dup(src->entryScopeExcludeSubtrees[num_vals]);
+			}
+		}
 	}
 }
 
@@ -641,6 +770,8 @@ memberof_free_config(MemberOfConfig *config)
 		slapi_ch_free((void **)&config->group_slapiattrs);
 
 		slapi_ch_free_string(&config->memberof_attr);
+		memberof_free_scope(config->entryScopes, &config->entryScopeCount);
+		memberof_free_scope(config->entryScopeExcludeSubtrees, &config->entryExcludeScopeCount);
 	}
 }
 
@@ -706,30 +837,6 @@ memberof_config_get_all_backends()
 	return all_backends;
 }
 
-Slapi_DN *
-memberof_config_get_entry_scope()
-{
-	Slapi_DN *entry_scope;
-
-	slapi_rwlock_rdlock(memberof_config_lock);
-	entry_scope = theConfig.entryScope;
-	slapi_rwlock_unlock(memberof_config_lock);
-
-	return entry_scope;
-}
-
-Slapi_DN *
-memberof_config_get_entry_scope_exclude_subtree()
-{
-	Slapi_DN *entry_exclude_subtree;
-
-	slapi_rwlock_rdlock(memberof_config_lock);
-	entry_exclude_subtree = theConfig.entryScopeExcludeSubtree;
-	slapi_rwlock_unlock(memberof_config_lock);
-
-	return entry_exclude_subtree;
-}
-
 /*
  * Check if we are modifying the config, or changing the shared config entry
  */
diff --git a/ldap/servers/plugins/retrocl/retrocl.c b/ldap/servers/plugins/retrocl/retrocl.c
index 78a0c6d..4bcbb38 100644
--- a/ldap/servers/plugins/retrocl/retrocl.c
+++ b/ldap/servers/plugins/retrocl/retrocl.c
@@ -45,6 +45,9 @@ char **retrocl_attributes = NULL;
 char **retrocl_aliases = NULL;
 int retrocl_log_deleted = 0;
 
+static Slapi_DN **retrocl_includes = NULL;
+static Slapi_DN **retrocl_excludes = NULL;
+
 /* ----------------------------- Retrocl Plugin */
 
 static Slapi_PluginDesc retrocldesc = {"retrocl", VENDOR, DS_PACKAGE_VERSION, "Retrocl Plugin"};
@@ -349,6 +352,8 @@ static int retrocl_start (Slapi_PBlock *pb)
     int rc = 0;
     Slapi_Entry *e = NULL;
     char **values = NULL;
+    int num_vals = 0;
+    int i = 0;
 
     retrocl_rootdse_init(pb);
 
@@ -369,6 +374,87 @@ static int retrocl_start (Slapi_PBlock *pb)
         return -1;
     }
 
+    /* Get the exclude suffixes */
+    values = slapi_entry_attr_get_charray_ext(e, CONFIG_CHANGELOG_EXCLUDE_SUFFIX, &num_vals);
+    if(values){
+        /* Validate the syntax before we create our DN array */
+        for (i = 0;i < num_vals; i++){
+            if(slapi_dn_syntax_check(pb, values[i], 1)){
+                /* invalid dn syntax */
+                slapi_log_error(SLAPI_LOG_FATAL, RETROCL_PLUGIN_NAME,
+                        "Invalid DN (%s) for exclude suffix.\n", values[i] );
+                slapi_ch_array_free(values);
+                return -1;
+            }
+        }
+        /* Now create our SDN array */
+        retrocl_excludes = (Slapi_DN **)slapi_ch_calloc(sizeof(Slapi_DN *),num_vals+1);
+        for (i = 0;i < num_vals; i++){
+            retrocl_excludes[i] = slapi_sdn_new_dn_byval(values[i]);
+        }
+        slapi_ch_array_free(values);
+    }
+    /* Get the include suffixes */
+    values = slapi_entry_attr_get_charray_ext(e, CONFIG_CHANGELOG_INCLUDE_SUFFIX, &num_vals);
+    if(values){
+        for (i = 0;i < num_vals; i++){
+            /* Validate the syntax before we create our DN array */
+            if(slapi_dn_syntax_check(pb, values[i], 1)){
+                /* invalid dn syntax */
+                slapi_log_error(SLAPI_LOG_FATAL, RETROCL_PLUGIN_NAME,
+                        "Invalid DN (%s) for include suffix.\n", values[i] );
+                slapi_ch_array_free(values);
+                return -1;
+            }
+        }
+        /* Now create our SDN array */
+        retrocl_includes = (Slapi_DN **)slapi_ch_calloc(sizeof(Slapi_DN *),num_vals+1);
+        for (i = 0;i < num_vals; i++){
+            retrocl_includes[i] = slapi_sdn_new_dn_byval(values[i]);
+        }
+        slapi_ch_array_free(values);
+    }
+    if(retrocl_includes && retrocl_excludes){
+        /*
+         * Make sure we haven't mixed the same suffix, and there are no
+         * conflicts between the includes and excludes
+         */
+        int i = 0;
+
+        while(retrocl_includes[i]){
+            int x = 0;
+            while(retrocl_excludes[x]){
+               if(slapi_sdn_compare(retrocl_includes[i], retrocl_excludes[x] ) == 0){
+                   /* we have a conflict */
+                   slapi_log_error(SLAPI_LOG_FATAL, RETROCL_PLUGIN_NAME,
+                           "include suffix (%s) is also listed in exclude suffix list\n",
+		                   slapi_sdn_get_dn(retrocl_includes[i]));
+                   return -1;
+               }
+               x++;
+            }
+            i++;
+        }
+
+        /* Check for parent/child conflicts */
+        i = 0;
+        while(retrocl_includes[i]){
+            int x = 0;
+            while(retrocl_excludes[x]){
+               if(slapi_sdn_issuffix(retrocl_includes[i], retrocl_excludes[x])){
+                   /* we have a conflict */
+                   slapi_log_error(SLAPI_LOG_FATAL, RETROCL_PLUGIN_NAME,
+                           "include suffix (%s) is a child of the exclude suffix(%s)\n",
+                           slapi_sdn_get_dn(retrocl_includes[i]),
+                           slapi_sdn_get_dn(retrocl_excludes[i]));
+                   return -1;
+               }
+               x++;
+            }
+            i++;
+        }
+    }
+
     values = slapi_entry_attr_get_charray(e, "nsslapd-attribute");
     if (values != NULL) {
         int n = 0;
@@ -434,6 +520,49 @@ static int retrocl_start (Slapi_PBlock *pb)
 }
 
 /*
+ * Check if an entry is in the configured scope.
+ * Return 1 if entry is in the scope, or 0 otherwise.
+ * For MODRDN the caller should check both the preop
+ * and postop entries.  If we are moving out of, or
+ * into scope, we should record it.
+ */
+int
+retrocl_entry_in_scope(Slapi_Entry *e)
+{
+    Slapi_DN *sdn = slapi_entry_get_sdn(e);
+
+    if (e == NULL){
+        return 1;
+    }
+
+    if (retrocl_excludes){
+        int i = 0;
+
+        /* check the excludes */
+        while(retrocl_excludes[i]){
+            if (slapi_sdn_issuffix(sdn, retrocl_excludes[i])){
+                return 0;
+            }
+            i++;
+        }
+    }
+    if (retrocl_includes){
+        int i = 0;
+
+        /* check the excludes */
+        while(retrocl_includes[i]){
+            if (slapi_sdn_issuffix(sdn, retrocl_includes[i])){
+                return 1;
+            }
+            i++;
+        }
+        return 0;
+    }
+
+    return 1;
+}
+
+/*
  * Function: retrocl_stop
  *
  * Returns: 0
@@ -446,26 +575,40 @@ static int retrocl_start (Slapi_PBlock *pb)
 
 static int retrocl_stop (Slapi_PBlock *pb)
 {
-  int rc = 0;
-
-  slapi_ch_array_free(retrocl_attributes);
-  retrocl_attributes = NULL;
-  slapi_ch_array_free(retrocl_aliases);
-  retrocl_aliases = NULL;
-
-  retrocl_stop_trimming();  
-  retrocl_be_changelog = NULL;
-  retrocl_forget_changenumbers();
-  PR_DestroyLock(retrocl_internal_lock);
-  retrocl_internal_lock = NULL;
-  slapi_destroy_rwlock(retrocl_cn_lock);
-  retrocl_cn_lock = NULL;
-  legacy_initialised = 0;
-
-  slapi_config_remove_callback(SLAPI_OPERATION_SEARCH, DSE_FLAG_PREOP, "",
-          LDAP_SCOPE_BASE,"(objectclass=*)", retrocl_rootdse_search);
-
-  return rc;
+    int rc = 0;
+    int i = 0;
+
+    slapi_ch_array_free(retrocl_attributes);
+    retrocl_attributes = NULL;
+    slapi_ch_array_free(retrocl_aliases);
+    retrocl_aliases = NULL;
+
+    while(retrocl_excludes && retrocl_excludes[i]){
+        slapi_sdn_free(&retrocl_excludes[i]);
+        i++;
+    }
+    slapi_ch_free((void**)&retrocl_excludes);
+    i = 0;
+
+    while(retrocl_includes && retrocl_includes[i]){
+        slapi_sdn_free(&retrocl_includes[i]);
+        i++;
+    }
+    slapi_ch_free((void**)&retrocl_includes);
+
+    retrocl_stop_trimming();
+    retrocl_be_changelog = NULL;
+    retrocl_forget_changenumbers();
+    PR_DestroyLock(retrocl_internal_lock);
+    retrocl_internal_lock = NULL;
+    slapi_destroy_rwlock(retrocl_cn_lock);
+    retrocl_cn_lock = NULL;
+    legacy_initialised = 0;
+
+    slapi_config_remove_callback(SLAPI_OPERATION_SEARCH, DSE_FLAG_PREOP, "",
+            LDAP_SCOPE_BASE,"(objectclass=*)", retrocl_rootdse_search);
+
+    return rc;
 }
 
 /*
diff --git a/ldap/servers/plugins/retrocl/retrocl.h b/ldap/servers/plugins/retrocl/retrocl.h
index ae0139c..7edd62f 100644
--- a/ldap/servers/plugins/retrocl/retrocl.h
+++ b/ldap/servers/plugins/retrocl/retrocl.h
@@ -67,6 +67,8 @@ typedef struct _cnumRet {
 /* was originally changelogmaximumage */
 #define CONFIG_CHANGELOG_MAXAGE_ATTRIBUTE     "nsslapd-changelogmaxage"
 #define CONFIG_CHANGELOG_DIRECTORY_ATTRIBUTE  "nsslapd-changelogdir"
+#define CONFIG_CHANGELOG_INCLUDE_SUFFIX       "nsslapd-include-suffix"
+#define CONFIG_CHANGELOG_EXCLUDE_SUFFIX       "nsslapd-exclude-suffix"
 
 #define RETROCL_CHANGELOG_DN "cn=changelog"
 #define RETROCL_MAPPINGTREE_DN "cn=\"cn=changelog\",cn=mapping tree,cn=config"
@@ -140,4 +142,6 @@ extern void retrocl_init_trimming(void);
 extern void retrocl_stop_trimming(void);
 extern char *retrocl_get_config_str(const char *attrt);
 
+int retrocl_entry_in_scope(Slapi_Entry *e);
+
 #endif /* _H_RETROCL */
diff --git a/ldap/servers/plugins/retrocl/retrocl_po.c b/ldap/servers/plugins/retrocl/retrocl_po.c
index 7083d0a..f689373 100644
--- a/ldap/servers/plugins/retrocl/retrocl_po.c
+++ b/ldap/servers/plugins/retrocl/retrocl_po.c
@@ -140,6 +140,7 @@ write_replog_db(
     int			flag,
     time_t		curtime,
     Slapi_Entry         *log_e,
+	Slapi_Entry         *post_entry,
     const char          *newrdn,
     LDAPMod		**modrdn_mods,
     const char          *newsuperior
@@ -156,11 +157,26 @@ write_replog_db(
     int	err = 0;
     int ret = LDAP_SUCCESS;
     int	i;
+    int mark = 0;
 
     if (!dn) {
         slapi_log_error( SLAPI_LOG_PLUGIN, RETROCL_PLUGIN_NAME, "write_replog_db: NULL dn\n");
         return ret;
     }
+    mark = (post_entry && retrocl_entry_in_scope(post_entry));
+    slapi_log_error( SLAPI_LOG_FATAL, RETROCL_PLUGIN_NAME, "post in scope (%d)\n",mark);
+
+    if (post_entry){
+        if(!retrocl_entry_in_scope(log_e) && !retrocl_entry_in_scope(post_entry)){
+            /* modrdn: entry not in scope, just return... */
+            return ret;
+        }
+    } else {
+        if(!retrocl_entry_in_scope(log_e)){
+            /* entry not in scope, just return... */
+            return ret;
+        }
+    }
 
     PR_Lock(retrocl_internal_lock);
     changenum = retrocl_assign_changenumber();
@@ -319,7 +335,7 @@ write_replog_db(
         break;
 
     case OP_DELETE:
-        if (log_e) {
+        if (retrocl_log_deleted) {
             /* we have to log the full entry */
             if ( entry2reple( e, log_e, OP_DELETE ) != 0 ) {
                 err = SLAPI_PLUGIN_FAILURE;
@@ -559,7 +575,8 @@ int retrocl_postob (Slapi_PBlock *pb, int optype)
     char		*dn;
     LDAPMod		**log_m = NULL;
     int			flag = 0;
-    Slapi_Entry		*te = NULL;
+    Slapi_Entry		*entry = NULL;
+    Slapi_Entry		*post_entry = NULL;
     Slapi_Operation     *op = NULL;
     LDAPMod		**modrdn_mods = NULL;
     char *newrdn = NULL;
@@ -624,7 +641,12 @@ int retrocl_postob (Slapi_PBlock *pb, int optype)
         LDAPDebug0Args(LDAP_DEBUG_TRACE,"not applying change for nsTombstone entries\n");
         return SLAPI_PLUGIN_SUCCESS;
     }
-	
+    /*
+     * Start by grabbing the preop entry, ADD will replace it as needed.  Getting the entry
+     * allows up to perform scoping in write_replog_db() for all op types.
+     */
+    (void)slapi_pblock_get(pb, SLAPI_ENTRY_PRE_OP, &entry);
+
     switch ( optype ) {
     case OP_MODIFY:
         (void)slapi_pblock_get( pb, SLAPI_MODIFY_MODS, &log_m );
@@ -634,14 +656,14 @@ int retrocl_postob (Slapi_PBlock *pb, int optype)
          * For adds, we want the unnormalized dn, so we can preserve
          * spacing, case, when replicating it.
          */
-        (void)slapi_pblock_get( pb, SLAPI_ADD_ENTRY, &te );
-        if ( NULL != te ) {
-            dn = slapi_entry_get_dn( te );
+        (void)slapi_pblock_get( pb, SLAPI_ADD_ENTRY, &entry );
+        if ( NULL != entry ) {
+            dn = slapi_entry_get_dn( entry );
         }
         break;
     case OP_DELETE:
         if (retrocl_log_deleted)
-            (void)slapi_pblock_get(pb, SLAPI_ENTRY_PRE_OP, &te);
+            (void)slapi_pblock_get(pb, SLAPI_ENTRY_PRE_OP, &entry);
         break;
     case OP_MODRDN:
         /* newrdn is used just for logging; no need to be normalized */
@@ -649,13 +671,14 @@ int retrocl_postob (Slapi_PBlock *pb, int optype)
         (void)slapi_pblock_get( pb, SLAPI_MODRDN_DELOLDRDN, &flag );
         (void)slapi_pblock_get( pb, SLAPI_MODIFY_MODS, &modrdn_mods );
         (void)slapi_pblock_get( pb, SLAPI_MODRDN_NEWSUPERIOR_SDN, &newsuperior );
+        (void)slapi_pblock_get(pb, SLAPI_ENTRY_POST_OP, &post_entry);
         break;
     }
 
     /* check if we should log change to retro changelog, and
      * if so, do it here */
-    if((rc = write_replog_db( pb, optype, dn, log_m, flag, curtime, te,
-        newrdn, modrdn_mods, slapi_sdn_get_dn(newsuperior) )))
+    if((rc = write_replog_db( pb, optype, dn, log_m, flag, curtime, entry,
+        post_entry, newrdn, modrdn_mods, slapi_sdn_get_dn(newsuperior) )))
     {
         slapi_log_error(SLAPI_LOG_FATAL, "retrocl-plugin",
                         "retrocl_postob: operation failure [%d]\n", rc);
-- 
1.9.3