Blame SOURCES/0025-Ticket-49184-Overflow-in-memberof.patch

61f723
From 710b0a6aaf1c648bc8fd33d4ab5bcc859a0ed851 Mon Sep 17 00:00:00 2001
61f723
From: Thierry Bordaz <tbordaz@redhat.com>
61f723
Date: Thu, 13 Apr 2017 15:21:49 +0200
61f723
Subject: [PATCH] Ticket 49184 - Overflow in memberof
61f723
61f723
Bug Description:
61f723
    The function memberof_call_foreach_dn can be used to retrieve ancestors of a
61f723
    given entry. (ancestors are groups owning directly or indirectly a given entry).
61f723
61f723
    With the use of group cache in memberof, at the entrance of memberof_call_foreach_dn
61f723
    there is an attempt to get the entry ancestors from the cache.
61f723
61f723
    Before doing so it needs to test if the cache is safe. In fact in case of
61f723
    circular groups the use of the cache is disabled and lookup in the cache should not
61f723
    happend.
61f723
61f723
    To know if the cache is safe it needs to access a flag (use_cache) in callback_data.
61f723
    The callback_data structure is opaque at this level. So accessing it
61f723
    while its structure is unknown is dangerous.
61f723
61f723
    The bug is that we may read an 'int' at an offset that overflow the actual structure.
61f723
    This is just a test and should not trigger a crash.
61f723
61f723
Fix Description:
61f723
    Add a flag to call memberof_call_foreach_dn so that, that indicates if
61f723
    it is valid to use the group cache.
61f723
61f723
https://pagure.io/389-ds-base/issue/49184
61f723
61f723
Reviewed by: William Brown and Mark Reynolds (thanks to you !!)
61f723
61f723
Platforms tested: F23
61f723
61f723
Flag Day: no
61f723
61f723
Doc impact: no
61f723
---
61f723
 dirsrvtests/tests/tickets/ticket49184_test.py | 146 ++++++++++++++++++++++++++
61f723
 ldap/servers/plugins/memberof/memberof.c      |  38 ++++---
61f723
 2 files changed, 167 insertions(+), 17 deletions(-)
61f723
 create mode 100644 dirsrvtests/tests/tickets/ticket49184_test.py
61f723
61f723
diff --git a/dirsrvtests/tests/tickets/ticket49184_test.py b/dirsrvtests/tests/tickets/ticket49184_test.py
61f723
new file mode 100644
61f723
index 0000000..20edfde
61f723
--- /dev/null
61f723
+++ b/dirsrvtests/tests/tickets/ticket49184_test.py
61f723
@@ -0,0 +1,146 @@
61f723
+import time
61f723
+import ldap
61f723
+import logging
61f723
+import pytest
61f723
+from lib389 import DirSrv, Entry, tools, tasks
61f723
+from lib389.tools import DirSrvTools
61f723
+from lib389._constants import *
61f723
+from lib389.properties import *
61f723
+from lib389.tasks import *
61f723
+from lib389.utils import *
61f723
+from lib389.topologies import topology_st as topo
61f723
+
61f723
+DEBUGGING = os.getenv("DEBUGGING", default=False)
61f723
+GROUP_DN_1 = ("cn=group1," + DEFAULT_SUFFIX)
61f723
+GROUP_DN_2 = ("cn=group2," + DEFAULT_SUFFIX)
61f723
+SUPER_GRP1 = ("cn=super_grp1,"  + DEFAULT_SUFFIX)
61f723
+SUPER_GRP2 = ("cn=super_grp2,"  + DEFAULT_SUFFIX)
61f723
+SUPER_GRP3 = ("cn=super_grp3,"  + DEFAULT_SUFFIX)
61f723
+
61f723
+if DEBUGGING:
61f723
+    logging.getLogger(__name__).setLevel(logging.DEBUG)
61f723
+else:
61f723
+    logging.getLogger(__name__).setLevel(logging.INFO)
61f723
+log = logging.getLogger(__name__)
61f723
+
61f723
+def _add_group_with_members(topo, group_dn):
61f723
+    # Create group
61f723
+    try:
61f723
+        topo.standalone.add_s(Entry((group_dn,
61f723
+                                      {'objectclass': 'top groupofnames extensibleObject'.split(),
61f723
+                                       'cn': 'group'})))
61f723
+    except ldap.LDAPError as e:
61f723
+        log.fatal('Failed to add group: error ' + e.message['desc'])
61f723
+        assert False
61f723
+
61f723
+    # Add members to the group - set timeout
61f723
+    log.info('Adding members to the group...')
61f723
+    for idx in range(1, 5):
61f723
+        try:
61f723
+            MEMBER_VAL = ("uid=member%d,%s" % (idx, DEFAULT_SUFFIX))
61f723
+            topo.standalone.modify_s(group_dn,
61f723
+                                      [(ldap.MOD_ADD,
61f723
+                                        'member',
61f723
+                                        MEMBER_VAL)])
61f723
+        except ldap.LDAPError as e:
61f723
+            log.fatal('Failed to update group: member (%s) - error: %s' %
61f723
+                      (MEMBER_VAL, e.message['desc']))
61f723
+            assert False
61f723
+
61f723
+def _check_memberof(topo, member=None, memberof=True, group_dn=None):
61f723
+    # Check that members have memberof attribute on M1
61f723
+    for idx in range(1, 5):
61f723
+        try:
61f723
+            USER_DN = ("uid=member%d,%s" % (idx, DEFAULT_SUFFIX))
61f723
+            ent = topo.standalone.getEntry(USER_DN, ldap.SCOPE_BASE, "(objectclass=*)")
61f723
+            if presence_flag:
61f723
+                assert ent.hasAttr('memberof') and ent.getValue('memberof') == group_dn
61f723
+            else:
61f723
+                assert not ent.hasAttr('memberof')
61f723
+        except ldap.LDAPError as e:
61f723
+            log.fatal('Failed to retrieve user (%s): error %s' % (USER_DN, e.message['desc']))
61f723
+            assert False
61f723
+            
61f723
+def _check_memberof(topo, member=None, memberof=True, group_dn=None):
61f723
+    ent = topo.standalone.getEntry(member, ldap.SCOPE_BASE, "(objectclass=*)")
61f723
+    if memberof:
61f723
+        assert group_dn
61f723
+        assert ent.hasAttr('memberof') and group_dn in ent.getValues('memberof')
61f723
+    else:
61f723
+        if ent.hasAttr('memberof'):
61f723
+            assert group_dn not in ent.getValues('memberof')
61f723
+
61f723
+            
61f723
+def test_ticket49184(topo):
61f723
+    """Write your testcase here...
61f723
+
61f723
+    Also, if you need any testcase initialization,
61f723
+    please, write additional fixture for that(include finalizer).
61f723
+    """
61f723
+    
61f723
+    topo.standalone.plugins.enable(name=PLUGIN_MEMBER_OF)
61f723
+    topo.standalone.restart(timeout=10)
61f723
+
61f723
+    #
61f723
+    #  create some users and a group
61f723
+    #
61f723
+    log.info('create users and group...')
61f723
+    for idx in range(1, 5):
61f723
+        try:
61f723
+            USER_DN = ("uid=member%d,%s" % (idx, DEFAULT_SUFFIX))
61f723
+            topo.standalone.add_s(Entry((USER_DN,
61f723
+                                          {'objectclass': 'top extensibleObject'.split(),
61f723
+                                           'uid': 'member%d' % (idx)})))
61f723
+        except ldap.LDAPError as e:
61f723
+            log.fatal('Failed to add user (%s): error %s' % (USER_DN, e.message['desc']))
61f723
+            assert False
61f723
+
61f723
+    # add all users in GROUP_DN_1 and checks each users is memberof GROUP_DN_1
61f723
+    _add_group_with_members(topo, GROUP_DN_1)
61f723
+    for idx in range(1, 5):
61f723
+        USER_DN = ("uid=member%d,%s" % (idx, DEFAULT_SUFFIX))
61f723
+        _check_memberof(topo, member=USER_DN, memberof=True, group_dn=GROUP_DN_1 )
61f723
+        
61f723
+    # add all users in GROUP_DN_2 and checks each users is memberof GROUP_DN_2
61f723
+    _add_group_with_members(topo, GROUP_DN_2)
61f723
+    for idx in range(1, 5):
61f723
+        USER_DN = ("uid=member%d,%s" % (idx, DEFAULT_SUFFIX))
61f723
+        _check_memberof(topo, member=USER_DN, memberof=True, group_dn=GROUP_DN_2 )
61f723
+    
61f723
+    # add the level 2, 3 and 4 group
61f723
+    for super_grp in (SUPER_GRP1, SUPER_GRP2, SUPER_GRP3):
61f723
+        topo.standalone.add_s(Entry((super_grp,
61f723
+                                          {'objectclass': 'top groupofnames extensibleObject'.split(),
61f723
+                                           'cn': 'super_grp'})))
61f723
+    topo.standalone.modify_s(SUPER_GRP1,
61f723
+                                      [(ldap.MOD_ADD,
61f723
+                                        'member',
61f723
+                                        GROUP_DN_1),
61f723
+                                        (ldap.MOD_ADD,
61f723
+                                        'member',
61f723
+                                        GROUP_DN_2)])
61f723
+    topo.standalone.modify_s(SUPER_GRP2,
61f723
+                                      [(ldap.MOD_ADD,
61f723
+                                        'member',
61f723
+                                        GROUP_DN_1),
61f723
+                                        (ldap.MOD_ADD,
61f723
+                                        'member',
61f723
+                                        GROUP_DN_2)])
61f723
+    return
61f723
+    topo.standalone.delete_s(GROUP_DN_2)
61f723
+    for idx in range(1, 5):
61f723
+        USER_DN = ("uid=member%d,%s" % (idx, DEFAULT_SUFFIX))
61f723
+        _check_memberof(topo, member=USER_DN, memberof=True, group_dn=GROUP_DN_1 )
61f723
+        _check_memberof(topo, member=USER_DN, memberof=False, group_dn=GROUP_DN_2 )
61f723
+    
61f723
+    if DEBUGGING:
61f723
+        # Add debugging steps(if any)...
61f723
+        pass
61f723
+
61f723
+
61f723
+if __name__ == '__main__':
61f723
+    # Run isolated
61f723
+    # -s for DEBUG mode
61f723
+    CURRENT_FILE = os.path.realpath(__file__)
61f723
+    pytest.main("-s %s" % CURRENT_FILE)
61f723
+
61f723
diff --git a/ldap/servers/plugins/memberof/memberof.c b/ldap/servers/plugins/memberof/memberof.c
61f723
index 81ef092..5cd2c01 100644
61f723
--- a/ldap/servers/plugins/memberof/memberof.c
61f723
+++ b/ldap/servers/plugins/memberof/memberof.c
61f723
@@ -159,7 +159,7 @@ static int memberof_qsort_compare(const void *a, const void *b);
61f723
 static void memberof_load_array(Slapi_Value **array, Slapi_Attr *attr);
61f723
 static int memberof_del_dn_from_groups(Slapi_PBlock *pb, MemberOfConfig *config, Slapi_DN *sdn);
61f723
 static int memberof_call_foreach_dn(Slapi_PBlock *pb, Slapi_DN *sdn, MemberOfConfig *config,
61f723
-	char **types, plugin_search_entry_callback callback,  void *callback_data, int *cached);
61f723
+	char **types, plugin_search_entry_callback callback,  void *callback_data, int *cached, PRBool use_grp_cache);
61f723
 static int memberof_is_direct_member(MemberOfConfig *config, Slapi_Value *groupdn,
61f723
 	Slapi_Value *memberdn);
61f723
 static int memberof_is_grouping_attr(char *type, MemberOfConfig *config);
61f723
@@ -659,7 +659,7 @@ memberof_del_dn_from_groups(Slapi_PBlock *pb, MemberOfConfig *config, Slapi_DN *
61f723
 
61f723
 		slapi_log_err(SLAPI_LOG_PLUGIN, MEMBEROF_PLUGIN_SUBSYSTEM, "memberof_del_dn_from_groups: Ancestors of %s\n", slapi_sdn_get_dn(sdn));
61f723
 		rc = memberof_call_foreach_dn(pb, sdn, config, groupattrs,
61f723
-		                              memberof_del_dn_type_callback, &data, &cached);
61f723
+		                              memberof_del_dn_type_callback, &data, &cached, PR_FALSE);
61f723
 	}
61f723
 
61f723
 	return rc;
61f723
@@ -776,8 +776,8 @@ add_ancestors_cbdata(memberof_cached_value *ancestors, void *callback_data)
61f723
  * could want type to be either "member" or "memberOf" depending on the case.
61f723
  */
61f723
 int
61f723
-memberof_call_foreach_dn(Slapi_PBlock *pb, Slapi_DN *sdn,
61f723
-	MemberOfConfig *config, char **types, plugin_search_entry_callback callback, void *callback_data, int *cached)
61f723
+memberof_call_foreach_dn(Slapi_PBlock *pb __attribute__((unused)), Slapi_DN *sdn,
61f723
+	MemberOfConfig *config, char **types, plugin_search_entry_callback callback, void *callback_data, int *cached, PRBool use_grp_cache)
61f723
 {
61f723
 	Slapi_PBlock *search_pb = NULL;
61f723
 	Slapi_DN *base_sdn = NULL;
61f723
@@ -792,9 +792,6 @@ memberof_call_foreach_dn(Slapi_PBlock *pb, Slapi_DN *sdn,
61f723
 	int free_it = 0;
61f723
 	int rc = 0;
61f723
 	int i = 0;
61f723
-	memberof_cached_value *ht_grp = NULL;
61f723
-	memberof_get_groups_data *data = (memberof_get_groups_data*) callback_data;
61f723
-	const char *ndn = slapi_sdn_get_ndn(sdn);
61f723
 
61f723
 	*cached = 0;
61f723
 
61f723
@@ -802,17 +799,24 @@ memberof_call_foreach_dn(Slapi_PBlock *pb, Slapi_DN *sdn,
61f723
 		return (rc);
61f723
 	}
61f723
 
61f723
-	/* Here we will retrieve the ancestor of sdn.
61f723
-	 * The key access is the normalized sdn
61f723
-	 * This is done through recursive internal searches of parents
61f723
-	 * If the ancestors of sdn are already cached, just use
61f723
-	 * this value
61f723
+	/* This flags indicates memberof_call_foreach_dn is called to retrieve ancestors (groups).
61f723
+	 * To improve performance, it can use a cache. (it will not in case of circular groups)
61f723
+	 * When this flag is true it means no circular group are detected (so far) so we can use the cache
61f723
 	 */
61f723
-	if (data && data->use_cache) {
61f723
+	if (use_grp_cache) {
61f723
+		/* Here we will retrieve the ancestor of sdn.
61f723
+		 * The key access is the normalized sdn
61f723
+		 * This is done through recursive internal searches of parents
61f723
+		 * If the ancestors of sdn are already cached, just use
61f723
+		 * this value
61f723
+		 */
61f723
+		memberof_cached_value *ht_grp = NULL;
61f723
+		const char *ndn = slapi_sdn_get_ndn(sdn);
61f723
+		
61f723
 		ht_grp = ancestors_cache_lookup((const void *) ndn);
61f723
 		if (ht_grp) {
61f723
 #if MEMBEROF_CACHE_DEBUG
61f723
-		slapi_log_err(SLAPI_LOG_PLUGIN, MEMBEROF_PLUGIN_SUBSYSTEM, "memberof_call_foreach_dn: Ancestors of %s already cached (%x)\n", ndn, ht_grp);
61f723
+			slapi_log_err(SLAPI_LOG_PLUGIN, MEMBEROF_PLUGIN_SUBSYSTEM, "memberof_call_foreach_dn: Ancestors of %s already cached (%x)\n", ndn, ht_grp);
61f723
 #endif
61f723
 			add_ancestors_cbdata(ht_grp, callback_data);
61f723
 			*cached = 1;
61f723
@@ -1106,7 +1110,7 @@ memberof_replace_dn_from_groups(Slapi_PBlock *pb, MemberOfConfig *config,
61f723
 		slapi_log_err(SLAPI_LOG_PLUGIN, MEMBEROF_PLUGIN_SUBSYSTEM, "memberof_replace_dn_from_groups: Ancestors of %s\n", slapi_sdn_get_dn(post_sdn));
61f723
 		if((ret = memberof_call_foreach_dn(pb, pre_sdn, config, groupattrs,
61f723
 		                                   memberof_replace_dn_type_callback,
61f723
-		                                   &data, &cached)))
61f723
+		                                   &data, &cached, PR_FALSE)))
61f723
 		{
61f723
 			break;
61f723
 		}
61f723
@@ -2383,7 +2387,7 @@ memberof_get_groups_r(MemberOfConfig *config, Slapi_DN *member_sdn,
61f723
 	slapi_log_err(SLAPI_LOG_PLUGIN, MEMBEROF_PLUGIN_SUBSYSTEM, "memberof_get_groups_r: Ancestors of %s\n", slapi_sdn_get_dn(member_sdn));
61f723
 #endif
61f723
 	rc = memberof_call_foreach_dn(NULL, member_sdn, config, config->groupattrs,
61f723
-		memberof_get_groups_callback, &member_data, &cached);
61f723
+		memberof_get_groups_callback, &member_data, &cached, member_data.use_cache);
61f723
 
61f723
 	merge_ancestors(&member_ndn_val, &member_data, data);
61f723
 	if (!cached && member_data.use_cache)
61f723
@@ -2578,7 +2582,7 @@ memberof_test_membership(Slapi_PBlock *pb, MemberOfConfig *config,
61f723
 	int cached = 0;
61f723
 
61f723
 	return memberof_call_foreach_dn(pb, group_sdn, config, attrs,
61f723
-		memberof_test_membership_callback, config, &cached);
61f723
+		memberof_test_membership_callback, config, &cached, PR_FALSE);
61f723
 }
61f723
 
61f723
 /*
61f723
-- 
61f723
2.9.3
61f723