diff --git a/Makefile.am b/Makefile.am index c448694c96..e402378b7e 100644 --- a/Makefile.am +++ b/Makefile.am @@ -1738,6 +1738,7 @@ libsyntax_plugin_la_SOURCES = ldap/servers/plugins/syntaxes/bin.c \ ldap/servers/plugins/syntaxes/facsimile.c \ ldap/servers/plugins/syntaxes/guide.c \ ldap/servers/plugins/syntaxes/int.c \ + ldap/servers/plugins/syntaxes/inchain.c \ ldap/servers/plugins/syntaxes/nameoptuid.c \ ldap/servers/plugins/syntaxes/numericstring.c \ ldap/servers/plugins/syntaxes/phonetic.c \ diff --git a/dirsrvtests/tests/suites/filter/inchain_test.py b/dirsrvtests/tests/suites/filter/inchain_test.py new file mode 100644 index 0000000000..4af71e7a2c --- /dev/null +++ b/dirsrvtests/tests/suites/filter/inchain_test.py @@ -0,0 +1,815 @@ +# --- BEGIN COPYRIGHT BLOCK --- +# Copyright (C) 2019 RED Hat, Inc. +# All rights reserved. +# +# License: GPL (version 3 or any later version). +# See LICENSE for details. +# --- END COPYRIGHT BLOCK ---- + +import pytest, os, re +from lib389.tasks import * +from lib389.utils import * +from ldap import SCOPE_SUBTREE, ALREADY_EXISTS + +from lib389._constants import DEFAULT_SUFFIX, PW_DM, PLUGIN_MEMBER_OF +from lib389.topologies import topology_st as topo +from lib389.plugins import MemberOfPlugin + +from lib389.idm.user import UserAccount, UserAccounts +from lib389.idm.account import Accounts +from lib389.idm.account import Anonymous + +INCHAIN_OID = "1.2.840.113556.1.4.1941" + +pytestmark = pytest.mark.tier0 + +@pytest.fixture(scope="function") +def provision_inchain(topo): + """fixture that provision a hierachical tree + with 'manager' membership relation + + 3_1 + |__ 2_1 + | |__ 1_1 + | | |__ 100 + | | |__ 101 + | | |__ 102 + | | |__ 103 + | | |__ 104 + | | + | |__ 1_2 + | | |__ 200 + | | |__ 201 + | | |__ 202 + | | |__ 203 + | | |__ 204 + | + |__ 2_2 + |__ 1_3 + | |__ 300 + | |__ 301 + | |__ 302 + | |__ 303 + | |__ 304 + | + |__ 1_4 + |__ 400 + |__ 401 + |__ 402 + |__ 403 + |__ 404 + """ + user = UserAccounts(topo.standalone, DEFAULT_SUFFIX) + try: + manager_lvl_3_1 = user.create_test_user(uid=31) + except ALREADY_EXISTS: + manager_lvl_3_1 = None + pass + + try: + manager_lvl_2_1 = user.create_test_user(uid=21) + if manager_lvl_3_1: + manager_lvl_2_1.set("manager", manager_lvl_3_1.dn) + else: + manager_lvl_2_1.set("manager", "uid=test_user_31,ou=People,%s" % DEFAULT_SUFFIX) + except ALREADY_EXISTS: + manager_lvl_2_1 = None + pass + + try: + manager_lvl_2_2 = user.create_test_user(uid=22) + if manager_lvl_3_1: + manager_lvl_2_2.set("manager", manager_lvl_3_1.dn) + else: + manager_lvl_2_2.set("manager", "uid=test_user_31,ou=People,%s" % DEFAULT_SUFFIX) + except ALREADY_EXISTS: + manager_lvl_2_2 = None + pass + + try: + manager_lvl_1_1 = user.create_test_user(uid=11) + if manager_lvl_2_1: + manager_lvl_1_1.set("manager", manager_lvl_2_1.dn) + else: + manager_lvl_1_1.set("manager", "uid=test_user_21,ou=People,%s" % DEFAULT_SUFFIX) + except ALREADY_EXISTS: + manager_lvl_1_1 = None + pass + + try: + manager_lvl_1_2 = user.create_test_user(uid=12) + if manager_lvl_2_1: + manager_lvl_1_2.set("manager", manager_lvl_2_1.dn) + else: + manager_lvl_1_2.set("manager", "uid=test_user_21,ou=People,%s" % DEFAULT_SUFFIX) + except ALREADY_EXISTS: + manager_lvl_1_2 = None + pass + + try: + manager_lvl_1_3 = user.create_test_user(uid=13) + if manager_lvl_2_2: + manager_lvl_1_3.set("manager", manager_lvl_2_2.dn) + else: + manager_lvl_1_3.set("manager", "uid=test_user_22,ou=People,%s" % DEFAULT_SUFFIX) + except ALREADY_EXISTS: + manager_lvl_1_3 = None + pass + + try: + manager_lvl_1_4 = user.create_test_user(uid=14) + if manager_lvl_2_2: + manager_lvl_1_4.set("manager", manager_lvl_2_2.dn) + else: + manager_lvl_1_4.set("manager", "uid=test_user_22,ou=People,%s" % DEFAULT_SUFFIX) + except ALREADY_EXISTS: + manager_lvl_1_4 = None + pass + + for i in range(100, 105): + try: + user1 = user.create_test_user(uid=i) + if manager_lvl_1_1: + user1.set("manager", manager_lvl_1_1.dn) + else: + user1.set("manager", "uid=test_user_11,ou=People,%s" % DEFAULT_SUFFIX) + except ALREADY_EXISTS: + pass + + for i in range(200, 205): + try: + user1 = user.create_test_user(uid=i) + if manager_lvl_1_2: + user1.set("manager", manager_lvl_1_2.dn) + else: + user1.set("manager", "uid=test_user_12,ou=People,%s" % DEFAULT_SUFFIX) + except ALREADY_EXISTS: + pass + + for i in range(300, 305): + try: + user1 = user.create_test_user(uid=i) + if manager_lvl_1_3: + user1.set("manager", manager_lvl_1_3.dn) + else: + user1.set("manager", "uid=test_user_13,ou=People,%s" % DEFAULT_SUFFIX) + except ALREADY_EXISTS: + pass + + for i in range(400, 405): + try: + user1 = user.create_test_user(uid=i) + if manager_lvl_1_4: + user1.set("manager", manager_lvl_1_4.dn) + else: + user1.set("manager", "uid=test_user_14,ou=People,%s" % DEFAULT_SUFFIX) + except ALREADY_EXISTS: + pass + +def check_subordinates(topo, uid, expected): + """Test filter can search attributes + + :id: 9a1b0a4b-111c-4105-866d-4288f143ee07 + :setup: Standalone instance + :steps: + 1. Add test entry + 2. make search + :expectedresults: + 1. Entry should be added + 2. Operation should succeed + """ + manager = "uid=%s,ou=People,%s" % (uid, DEFAULT_SUFFIX) + topo.standalone.log.info("Subordinate of manager %s" % manager) + + subordinates = topo.standalone.search_s(DEFAULT_SUFFIX, SCOPE_SUBTREE, "(manager:%s:=%s)" % (INCHAIN_OID, manager)) + found = [] + for sub in subordinates: + p =re.compile("uid=(.*),ou.*$") + res = p.search(sub.dn) + found.append(res.group(1)) + topo.standalone.log.info("Subordinate found : %s" % res.group(1)) + + + for sub in expected: + assert sub in found + + for sub in found: + assert sub in expected + + +def test_manager_lvl_1(topo, provision_inchain): + """Test that it succeeds to retrieve the subordinate + of level 1 manager + + :id: 193040ef-861e-41bd-84c7-c07a53a74e18 + :setup: Standalone instance + :steps: + 1. fixture provision a hierachical tree + 2. Check subordinates of 1_4 entry + |__ 1_4 + |__ 400 + |__ 401 + |__ 402 + |__ 403 + |__ 404 + + 3. Check subordinates of 1_3 entry + |__ 1_3 + | |__ 300 + | |__ 301 + | |__ 302 + | |__ 303 + | |__ 304 + + 4. Check subordinates of 1_2 entry + | |__ 1_2 + | | |__ 200 + | | |__ 201 + | | |__ 202 + | | |__ 203 + | | |__ 204 + + 5. Check subordinates of 1_1 entry + | |__ 1_1 + | | |__ 100 + | | |__ 101 + | | |__ 102 + | | |__ 103 + | | |__ 104 + :expectedresults: + 1. provisioning done + 2. found subordinates should match expected ones + 3. found subordinates should match expected ones + 4. found subordinates should match expected ones + 5. found subordinates should match expected ones + """ + + # Check subordinates of user_14 + uid = "test_user_14" + topo.standalone.log.info("Subordinate of uid=%s" % uid) + expected = [] + for i in range(400, 405): + uid_expected = "test_user_%s" % i + expected.append(uid_expected) + topo.standalone.log.info("Subordinate expected: %s" % uid_expected) + + check_subordinates(topo, uid, expected) + + # Check subordinates of user_13 + uid = "test_user_13" + topo.standalone.log.info("Subordinate of uid=%s" % uid) + expected = [] + for i in range(300, 305): + uid_expected = "test_user_%s" % i + expected.append(uid_expected) + topo.standalone.log.info("Subordinate expected: %s" % uid_expected) + + check_subordinates(topo, uid, expected) + + # Check subordinates of user_12 + uid = "test_user_12" + topo.standalone.log.info("Subordinate of uid=%s" % uid) + expected = [] + for i in range(200, 205): + uid_expected = "test_user_%s" % i + expected.append(uid_expected) + topo.standalone.log.info("Subordinate expected: %s" % uid_expected) + + check_subordinates(topo, uid, expected) + + # Check subordinates of user_11 + uid = "test_user_11" + topo.standalone.log.info("Subordinate of uid=%s" % uid) + expected = [] + for i in range(100, 105): + uid_expected = "test_user_%s" % i + expected.append(uid_expected) + topo.standalone.log.info("Subordinate expected: %s" % uid_expected) + + check_subordinates(topo, uid, expected) + +def test_manager_lvl_2(topo, provision_inchain): + """Test that it succeeds to retrieve the subordinate + of level 2 manager + + :id: d0c98211-3b90-4764-913d-f55ea5479029 + :setup: Standalone instance + :steps: + 1. fixture provision a hierachical tree + 2. Check subordinates of 2_1 entry + |__ 2_1 + | |__ 1_1 + | | |__ 100 + | | |__ 101 + | | |__ 102 + | | |__ 103 + | | |__ 104 + | | + | |__ 1_2 + | | |__ 200 + | | |__ 201 + | | |__ 202 + | | |__ 203 + | | |__ 204 + + 3. Check subordinates of 2_2 entry + | + |__ 2_2 + |__ 1_3 + | |__ 300 + | |__ 301 + | |__ 302 + | |__ 303 + | |__ 304 + | + |__ 1_4 + |__ 400 + |__ 401 + |__ 402 + |__ 403 + |__ 404 + :expectedresults: + 1. provisioning done + 2. found subordinates should match expected ones + 3. found subordinates should match expected ones + """ + + # Check subordinates of user_22 + uid = "test_user_22" + topo.standalone.log.info("Subordinate of uid=%s" % uid) + expected = [] + + # it contains user_14 and below + uid_expected = "test_user_14" + topo.standalone.log.info("Subordinate expected: %s" % uid_expected) + expected.append(uid_expected) + for i in range(400, 405): + uid_expected = "test_user_%s" % i + expected.append(uid_expected) + topo.standalone.log.info("Subordinate expected: %s" % uid_expected) + + # it contains user_13 and below + uid_expected = "test_user_13" + topo.standalone.log.info("Subordinate expected: %s" % uid_expected) + expected.append(uid_expected) + for i in range(300, 305): + uid_expected = "test_user_%s" % i + expected.append(uid_expected) + topo.standalone.log.info("Subordinate expected: %s" % uid_expected) + + check_subordinates(topo, uid, expected) + + # Check subordinates of user_21 + uid = "test_user_21" + topo.standalone.log.info("Subordinate of uid=%s" % uid) + expected = [] + + # it contains user_12 and below + uid_expected = "test_user_12" + topo.standalone.log.info("Subordinate expected: %s" % uid_expected) + expected.append(uid_expected) + for i in range(200, 205): + uid_expected = "test_user_%s" % i + expected.append(uid_expected) + topo.standalone.log.info("Subordinate expected: %s" % uid_expected) + + # it contains user_11 and below + uid_expected = "test_user_11" + topo.standalone.log.info("Subordinate expected: %s" % uid_expected) + expected.append(uid_expected) + for i in range(100, 105): + uid_expected = "test_user_%s" % i + expected.append(uid_expected) + topo.standalone.log.info("Subordinate expected: %s" % uid_expected) + + check_subordinates(topo, uid, expected) + +def test_manager_lvl_3(topo, provision_inchain): + """Test that it succeeds to retrieve the subordinate + of level 3 manager + + :id: d3708a39-7901-4c88-b4af-272aed2aa846 + :setup: Standalone instance + :steps: + 1. fixture provision a hierachical tree + 2. Check subordinates of 3_1 entry + + 3_1 + |__ 2_1 + | |__ 1_1 + | | |__ 100 + | | |__ 101 + | | |__ 102 + | | |__ 103 + | | |__ 104 + | | + | |__ 1_2 + | | |__ 200 + | | |__ 201 + | | |__ 202 + | | |__ 203 + | | |__ 204 + | + |__ 2_2 + |__ 1_3 + | |__ 300 + | |__ 301 + | |__ 302 + | |__ 303 + | |__ 304 + | + |__ 1_4 + |__ 400 + |__ 401 + |__ 402 + |__ 403 + |__ 404 + :expectedresults: + 1. provisioning done + 2. found subordinates should match expected ones + """ + + # Check subordinates of user_31 + uid = "test_user_31" + topo.standalone.log.info("Subordinate of uid=%s" % uid) + expected = [] + + # it contains user_22 and below + uid_expected = "test_user_22" + topo.standalone.log.info("Subordinate expected: %s" % uid_expected) + expected.append(uid_expected) + + # it contains user_14 and below + uid_expected = "test_user_14" + topo.standalone.log.info("Subordinate expected: %s" % uid_expected) + expected.append(uid_expected) + for i in range(400, 405): + uid_expected = "test_user_%s" % i + expected.append(uid_expected) + topo.standalone.log.info("Subordinate expected: %s" % uid_expected) + + # it contains user_13 and below + uid_expected = "test_user_13" + topo.standalone.log.info("Subordinate expected: %s" % uid_expected) + expected.append(uid_expected) + for i in range(300, 305): + uid_expected = "test_user_%s" % i + expected.append(uid_expected) + topo.standalone.log.info("Subordinate expected: %s" % uid_expected) + + + # it contains user_21 and below + uid_expected = "test_user_21" + topo.standalone.log.info("Subordinate expected: %s" % uid_expected) + expected.append(uid_expected) + + # it contains user_12 and below + uid_expected = "test_user_12" + topo.standalone.log.info("Subordinate expected: %s" % uid_expected) + expected.append(uid_expected) + for i in range(200, 205): + uid_expected = "test_user_%s" % i + expected.append(uid_expected) + topo.standalone.log.info("Subordinate expected: %s" % uid_expected) + + # it contains user_11 and below + uid_expected = "test_user_11" + topo.standalone.log.info("Subordinate expected: %s" % uid_expected) + expected.append(uid_expected) + for i in range(100, 105): + uid_expected = "test_user_%s" % i + expected.append(uid_expected) + topo.standalone.log.info("Subordinate expected: %s" % uid_expected) + + check_subordinates(topo, uid, expected) + +def test_recompute_del(topo, provision_inchain): + """Test that if we delete a subordinate + the subordinate list is correctly updated + + :id: 6865876d-64b5-41a2-ae6e-4453aae5caab + :setup: Standalone instance + :steps: + 1. fixture provision a hierachical tree + 2. Check subordinates of 1_1 entry + + | |__ 1_1 + | | |__ 100 + | | |__ 101 + | | |__ 102 + | | |__ 103 + | | |__ 104 + + 3. Delete user_100 + 4. Check subordinates of 1_1 entry + | |__ 1_1 + | | |__ 101 + | | |__ 102 + | | |__ 103 + | | |__ 104 + :expectedresults: + 1. provisioning done + 2. found subordinates should match expected ones + """ + + # Check subordinates of user_11 + uid = "test_user_11" + topo.standalone.log.info("Subordinate of uid=%s" % uid) + expected = [] + for i in range(100, 105): + uid_expected = "test_user_%s" % i + expected.append(uid_expected) + topo.standalone.log.info("Subordinate expected: %s" % uid_expected) + + check_subordinates(topo, uid, expected) + + del_dn = "uid=test_user_100,ou=People,%s" % (DEFAULT_SUFFIX) + user = UserAccount(topo.standalone, del_dn) + topo.standalone.log.info("Delete: %s" % del_dn) + user.delete() + + topo.standalone.log.info("Subordinate of uid=%s" % uid) + expected = [] + for i in range(101, 105): + uid_expected = "test_user_%s" % i + expected.append(uid_expected) + topo.standalone.log.info("Subordinate expected: %s" % uid_expected) + + check_subordinates(topo, uid, expected) + +def test_recompute_add(topo, provision_inchain, request): + """Test that if we add a subordinate + the subordinate list is correctly updated + + :id: 60d10233-37c2-400b-ac6e-d29b68706216 + :setup: Standalone instance + :steps: + 1. fixture provision a hierachical tree + 2. Check subordinates of 1_1 entry + + | |__ 1_1 + | | |__ 100 + | | |__ 101 + | | |__ 102 + | | |__ 103 + | | |__ 104 + + 3. Delete user_100 + 4. Check subordinates of 1_1 entry + | |__ 1_1 + | | |__ 101 + | | |__ 102 + | | |__ 103 + | | |__ 104 + :expectedresults: + 1. provisioning done + 2. found subordinates should match expected ones + """ + + # Check subordinates of user_11 + uid = "test_user_11" + topo.standalone.log.info("Subordinate of uid=%s" % uid) + expected = [] + for i in range(100, 105): + uid_expected = "test_user_%s" % i + expected.append(uid_expected) + topo.standalone.log.info("Subordinate expected: %s" % uid_expected) + + check_subordinates(topo, uid, expected) + + # add a new subordinate of user_11 + users = UserAccounts(topo.standalone, DEFAULT_SUFFIX) + user_added = users.create_test_user(uid=105) + topo.standalone.log.info("Add: %s" % user_added.dn) + user_added.set("manager", "uid=%s,ou=People,%s" % (uid, DEFAULT_SUFFIX)) + + topo.standalone.log.info("Subordinate of uid=%s" % uid) + expected = [] + for i in range(100, 106): + uid_expected = "test_user_%s" % i + expected.append(uid_expected) + topo.standalone.log.info("Subordinate expected: %s" % uid_expected) + + check_subordinates(topo, uid, expected) + + def fin(): + user_added.delete() + + request.addfinalizer(fin) + + +def test_anonymous_inchain(topo, provision_inchain): + """Test that anonymous connection can not + retrieve subordinates + + :id: d6c41cc1-7c36-4a3f-bcdf-f479c12310e2 + :setup: Standalone instance + :steps: + 1. fixture provision a hierachical tree + 2. bound anonymous + 3. Check subordinates of 1_2 entry is empty although hierarchy + | |__ 1_2 + | | |__ 200 + | | |__ 201 + | | |__ 202 + | | |__ 203 + | | |__ 204 + + :expectedresults: + 1. provisioning done + 2. succeed + 3. succeeds but 0 subordinates + """ + + # create an anonymous connection + topo.standalone.log.info("Bind as anonymous user") + conn = Anonymous(topo.standalone).bind() + + # Check that there are no subordinates of test_user_12 + uid = "test_user_12" + manager = "uid=%s,ou=People,%s" % (uid, DEFAULT_SUFFIX) + topo.standalone.log.info("Subordinate of manager %s on anonymous connection" % manager) + + subordinates = conn.search_s(DEFAULT_SUFFIX, SCOPE_SUBTREE, "(manager:%s:=%s)" % (INCHAIN_OID, manager)) + assert len(subordinates) == 0 + + # Check the ACI right failure + assert topo.standalone.ds_error_log.match('.*inchain - Requestor is not allowed to use InChain Matching rule$') + +def test_authenticated_inchain(topo, provision_inchain, request): + """Test that bound connection can not + retrieve subordinates (only DM is allowed by default) + + :id: 17ebee0d-86e2-4f0d-95fe-8a4cf566a493 + :setup: Standalone instance + :steps: + 1. fixture provision a hierachical tree + 2. create a test user + 3. create a bound connection + 4. Check subordinates of 1_2 entry is empty although hierarchy + | |__ 1_2 + | | |__ 200 + | | |__ 201 + | | |__ 202 + | | |__ 203 + | | |__ 204 + + :expectedresults: + 1. provisioning done + 2. succeed + 3. succeed + 4. succeeds but 0 subordinates + """ + + # create a user + RDN = "test_bound_user" + test_user = UserAccount(topo.standalone, "uid=%s,ou=People,%s" % (RDN, DEFAULT_SUFFIX)) + test_user.create(properties={ + 'uid': RDN, + 'cn': RDN, + 'sn': RDN, + 'userPassword': "password", + 'uidNumber' : '1000', + 'gidNumber' : '2000', + 'homeDirectory' : '/home/inchain', + }) + + # create a bound connection + conn = test_user.bind("password") + + # Check that there are no subordinates of test_user_12 + uid = "test_user_12" + manager = "uid=%s,ou=People,%s" % (uid, DEFAULT_SUFFIX) + topo.standalone.log.info("Subordinate of manager %s on bound connection" % manager) + + subordinates = conn.search_s(DEFAULT_SUFFIX, SCOPE_SUBTREE, "(manager:%s:=%s)" % (INCHAIN_OID, manager)) + assert len(subordinates) == 0 + + # Check the ACI right failure + assert topo.standalone.ds_error_log.match('.*inchain - Requestor is not allowed to use InChain Matching rule$') + + def fin(): + test_user.delete() + + request.addfinalizer(fin) + + +def _create_user(topology_st, ext): + user_dn = "uid=%s,ou=People,%s" % (ext, DEFAULT_SUFFIX) + topology_st.standalone.add_s(Entry((user_dn, { + 'objectclass': 'top extensibleObject'.split(), + 'uid': ext + }))) + topology_st.standalone.log.info("Create user %s" % user_dn) + return ensure_bytes(user_dn) + +def _create_group(topology_st, ext): + group_dn = "ou=%s,ou=People,%s" % (ext, DEFAULT_SUFFIX) + topology_st.standalone.add_s(Entry((group_dn, { + 'objectclass': 'top groupOfNames extensibleObject'.split(), + 'ou': ext, + 'cn': ext + }))) + topology_st.standalone.log.info("Create group %s" % group_dn) + return ensure_bytes(group_dn) + +def test_reuse_memberof(topo, request): + """Check that slapi_memberof successfully + compute the membership either using 'memberof' attribute + or either recomputing it. + + :id: e52bd21a-3ff6-493f-9ec5-8cf4e76696a0 + :setup: Standalone instance + :steps: + 1. Enable the plugin + 2. Create a user belonging in cascade to 3 groups + 3. Check that slapi_member re-computes membership + 4. Check that slapi_member retrieve membership from memberof + :expectedresults: + 1. Success + 2. Success + 3. Success + 4. Success + """ + + # enable the plugin + topo.standalone.log.info("Enable MemberOf plugin") + topo.standalone.plugins.enable(name=PLUGIN_MEMBER_OF) + topo.standalone.restart() + + # Create a user belonging to 3 goups + # in cascade + user1 = _create_user(topo, 'user1') + + group1 = _create_group(topo, 'group1') + mods = [(ldap.MOD_ADD, 'member', user1)] + topo.standalone.modify_s(ensure_str(group1), mods) + + group2 = _create_group(topo, 'group2') + mods = [(ldap.MOD_ADD, 'member', group1)] + topo.standalone.modify_s(ensure_str(group2), mods) + + group3 = _create_group(topo, 'group3') + mods = [(ldap.MOD_ADD, 'member', group2)] + topo.standalone.modify_s(ensure_str(group3), mods) + + # Call slapi_member that does *not* reuse the memberof + # because of 'memberOfEntryScope' not being set + topo.standalone.log.info("Groups that %s is memberof" % ensure_str(user1)) + + topo.standalone.config.set('nsslapd-errorlog-level', '65536') # plugin logging + memberof = topo.standalone.search_s(DEFAULT_SUFFIX, SCOPE_SUBTREE, "(member:%s:=%s)" % (INCHAIN_OID, ensure_str(user1))) + topo.standalone.config.set('nsslapd-errorlog-level', '0') + for sub in memberof: + topo.standalone.log.info("memberof found : %s" % sub.dn) + assert sub.dn in [ensure_str(group1), ensure_str(group2), ensure_str(group3)] + assert topo.standalone.ds_error_log.match('.*sm_compare_memberof_config: fails because requested include scope is not empty.*') + assert not topo.standalone.ds_error_log.match('.*slapi_memberof - sm_compare_memberof_config: succeeds. requested options match config.*') + + # Call slapi_member that does reuse the memberof + # because of 'memberOfEntryScope' being set + memberof = MemberOfPlugin(topo.standalone) + memberof.replace('memberOfEntryScope', DEFAULT_SUFFIX) + topo.standalone.restart() + topo.standalone.config.set('nsslapd-errorlog-level', '65536') # plugin logging + memberof = topo.standalone.search_s(DEFAULT_SUFFIX, SCOPE_SUBTREE, "(member:%s:=%s)" % (INCHAIN_OID, ensure_str(user1))) + topo.standalone.config.set('nsslapd-errorlog-level', '0') + for sub in memberof: + topo.standalone.log.info("memberof found : %s" % sub.dn) + assert sub.dn in [ensure_str(group1), ensure_str(group2), ensure_str(group3)] + assert topo.standalone.ds_error_log.match('.*slapi_memberof - sm_compare_memberof_config: succeeds. requested options match config.*') + + def fin(): + topo.standalone.delete_s(ensure_str(user1)) + topo.standalone.delete_s(ensure_str(group1)) + topo.standalone.delete_s(ensure_str(group2)) + topo.standalone.delete_s(ensure_str(group3)) + + request.addfinalizer(fin) + +def test_invalid_assertion(topo): + """Check that with invalid assertion + there is no returned entries + + :id: 0a204b81-e7c0-41a0-97cc-7d9425a603c2 + :setup: Standalone instance + :steps: + 1. Search with invalid assertion '..:=foo' + 2. Search with not existing entry '..:=' + :expectedresults: + 1. Success + 2. Success + """ + topo.standalone.log.info("Search with an invalid assertion") + memberof = topo.standalone.search_s(DEFAULT_SUFFIX, SCOPE_SUBTREE, "(member:%s:=foo)" % (INCHAIN_OID)) + assert len(memberof) == 0 + + topo.standalone.log.info("Search with an none exisiting entry") + + user = "uid=not_existing_entry,ou=People,%s" % (DEFAULT_SUFFIX) + memberof = topo.standalone.search_s(DEFAULT_SUFFIX, SCOPE_SUBTREE, "(member:%s:=%s)" % (INCHAIN_OID, user)) + assert len(memberof) == 0 + +if __name__ == "__main__": + CURRENT_FILE = os.path.realpath(__file__) + pytest.main("-s -v %s" % CURRENT_FILE) diff --git a/ldap/ldif/template-dse-minimal.ldif.in b/ldap/ldif/template-dse-minimal.ldif.in index 9f7c9929dd..d2b02f8be9 100644 --- a/ldap/ldif/template-dse-minimal.ldif.in +++ b/ldap/ldif/template-dse-minimal.ldif.in @@ -34,6 +34,14 @@ objectclass: top objectclass: nsContainer cn: features +dn: oid=1.2.840.113556.1.4.1941,cn=features,cn=config +objectClass: top +objectClass: directoryServerFeature +oid: 1.2.840.113556.1.4.1941 +cn: InChain Matching Rule +aci: (targetattr != "aci")(version 3.0; acl "InChain Matching Rule"; deny( all ) + userdn = "ldap:///anyone";) + dn: oid=1.3.6.1.4.1.42.2.27.9.5.8,cn=features,cn=config objectClass: top objectClass: directoryServerFeature @@ -439,6 +447,16 @@ nsslapd-plugininitfunc: int_init nsslapd-plugintype: syntax nsslapd-pluginenabled: on +dn: cn=In Chain,cn=plugins,cn=config +objectclass: top +objectclass: nsSlapdPlugin +objectclass: extensibleObject +cn: In Chain +nsslapd-pluginpath: libsyntax-plugin +nsslapd-plugininitfunc: inchain_init +nsslapd-plugintype: syntax +nsslapd-pluginenabled: on + dn: cn=Distinguished Name Syntax,cn=plugins,cn=config objectclass: top objectclass: nsSlapdPlugin diff --git a/ldap/ldif/template-dse.ldif.in b/ldap/ldif/template-dse.ldif.in index 3361a73fcf..b1736ccc28 100644 --- a/ldap/ldif/template-dse.ldif.in +++ b/ldap/ldif/template-dse.ldif.in @@ -50,6 +50,14 @@ objectclass: top objectclass: nsContainer cn: features +dn: oid=1.2.840.113556.1.4.1941,cn=features,cn=config +objectClass: top +objectClass: directoryServerFeature +oid: 1.2.840.113556.1.4.1941 +cn: InChain Matching Rule +aci: (targetattr != "aci")(version 3.0; acl "InChain Matching Rule"; deny( all ) + userdn = "ldap:///anyone";) + dn: oid=1.3.6.1.4.1.42.2.27.9.5.8,cn=features,cn=config objectClass: top objectClass: directoryServerFeature @@ -498,6 +506,16 @@ nsslapd-plugininitfunc: int_init nsslapd-plugintype: syntax nsslapd-pluginenabled: on +dn: cn=In Chain,cn=plugins,cn=config +objectclass: top +objectclass: nsSlapdPlugin +objectclass: extensibleObject +cn: In Chain +nsslapd-pluginpath: libsyntax-plugin +nsslapd-plugininitfunc: inchain_init +nsslapd-plugintype: syntax +nsslapd-pluginenabled: on + dn: cn=Distinguished Name Syntax,cn=plugins,cn=config objectclass: top objectclass: nsSlapdPlugin diff --git a/ldap/servers/plugins/syntaxes/inchain.c b/ldap/servers/plugins/syntaxes/inchain.c new file mode 100644 index 0000000000..52d0c49946 --- /dev/null +++ b/ldap/servers/plugins/syntaxes/inchain.c @@ -0,0 +1,416 @@ +/** BEGIN COPYRIGHT BLOCK + * Copyright (C) 2023 Red Hat, Inc. + * All rights reserved. + * + * License: GPL (version 3 or any later version). + * See LICENSE for details. + * END COPYRIGHT BLOCK **/ + +#ifdef HAVE_CONFIG_H +#include +#endif + +/* inchain.c - in_chain syntax routines + see https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-adts/1e889adc-b503-4423-8985-c28d5c7d4887 + */ + +#include +#include +#include +#include "syntax.h" +#include "slapi-plugin.h" + +int inchain_filter_ava(Slapi_PBlock *pb, struct berval *bvfilter, Slapi_Value **bvals, int ftype, Slapi_Value **retVal); +int inchain_filter_sub(Slapi_PBlock *pb, char *initial, char **any, char * final, Slapi_Value **bvals); +int inchain_values2keys(Slapi_PBlock *pb, Slapi_Value **val, Slapi_Value ***ivals, int ftype); +int inchain_assertion2keys_ava(Slapi_PBlock *pb, Slapi_Value *val, Slapi_Value ***ivals, int ftype); +int inchain_assertion2keys_sub(Slapi_PBlock *pb, char *initial, char **any, char * final, Slapi_Value ***ivals); +int inchain_validate(struct berval *val); +void inchain_normalize( + Slapi_PBlock *pb, + char *s, + int trim_spaces, + char **alt); + +/* the first name is the official one from RFC 4517 */ +static char *names[] = {"inchain", "inchain", LDAP_MATCHING_RULE_IN_CHAIN_OID, 0}; + +static Slapi_PluginDesc pdesc = {"inchain-matching-rule", VENDOR, DS_PACKAGE_VERSION, + "inchain matching rule plugin"}; + +static const char *inchainMatch_names[] = {"inchainMatch", "1.2.840.113556.1.4.1941", NULL}; + +static struct mr_plugin_def mr_plugin_table[] = { + { + { + "1.2.840.113556.1.4.1941", + NULL, + "inchainMatch", + "The distinguishedNameMatch rule compares an assertion value of the DN " + "syntax to an attribute value of a syntax (e.g., the DN syntax) whose " + "corresponding ASN.1 type is DistinguishedName. " + "The rule evaluates to TRUE if and only if the attribute value and the " + "assertion value have the same number of relative distinguished names " + "and corresponding relative distinguished names (by position) are the " + "same. A relative distinguished name (RDN) of the assertion value is " + "the same as an RDN of the attribute value if and only if they have " + "the same number of attribute value assertions and each attribute " + "value assertion (AVA) of the first RDN is the same as the AVA of the " + "second RDN with the same attribute type. The order of the AVAs is " + "not significant. Also note that a particular attribute type may " + "appear in at most one AVA in an RDN. Two AVAs with the same " + "attribute type are the same if their values are equal according to " + "the equality matching rule of the attribute type. If one or more of " + "the AVA comparisons evaluate to Undefined and the remaining AVA " + "comparisons return TRUE then the distinguishedNameMatch rule " + "evaluates to Undefined.", + NULL, + 0, + NULL /* dn only for now */ + }, /* matching rule desc */ + { + "inchainMatch-mr", + VENDOR, + DS_PACKAGE_VERSION, + "inchain matching rule plugin"}, /* plugin desc */ + inchainMatch_names, /* matching rule name/oid/aliases */ + NULL, + NULL, + inchain_filter_ava, + NULL, + inchain_values2keys, + inchain_assertion2keys_ava, + NULL, + NULL, + NULL /* mr_nomalise */ + }, +}; + +static size_t mr_plugin_table_size = sizeof(mr_plugin_table) / sizeof(mr_plugin_table[0]); + +static int +matching_rule_plugin_init(Slapi_PBlock *pb) +{ + return syntax_matching_rule_plugin_init(pb, mr_plugin_table, mr_plugin_table_size); +} + +static int +register_matching_rule_plugins(void) +{ + return syntax_register_matching_rule_plugins(mr_plugin_table, mr_plugin_table_size, matching_rule_plugin_init); +} + +static int +inchain_feature_allowed(Slapi_PBlock *pb) +{ + int isroot = 0; + int ldapcode = LDAP_SUCCESS; + + slapi_pblock_get(pb, SLAPI_REQUESTOR_ISROOT, &isroot); + if (!isroot) { + char *dn; + Slapi_Entry *feature = NULL; + + /* Fetch the feature entry and see if the requestor is allowed access. */ + dn = slapi_ch_smprintf("dn: oid=%s,cn=features,cn=config", LDAP_MATCHING_RULE_IN_CHAIN_OID); + if ((feature = slapi_str2entry(dn, 0)) != NULL) { + char *dummy_attr = "1.1"; + Slapi_Backend *be = NULL; + + be = slapi_mapping_tree_find_backend_for_sdn(slapi_entry_get_sdn(feature)); + if (NULL == be) { + ldapcode = LDAP_INSUFFICIENT_ACCESS; + } else { + slapi_pblock_set(pb, SLAPI_BACKEND, be); + ldapcode = slapi_access_allowed(pb, feature, dummy_attr, NULL, SLAPI_ACL_READ); + } + } + + /* If the feature entry does not exist, deny use of the control. Only + * the root DN will be allowed to use the control in this case. */ + if ((feature == NULL) || (ldapcode != LDAP_SUCCESS)) { + ldapcode = LDAP_INSUFFICIENT_ACCESS; + } + slapi_ch_free((void **)&dn); + slapi_entry_free(feature); + } + return (ldapcode); +} + +int +inchain_init(Slapi_PBlock *pb) +{ + int rc; + + slapi_log_err(SLAPI_LOG_PLUGIN, SYNTAX_PLUGIN_SUBSYSTEM, "=> inchain_init\n"); + + rc = slapi_pblock_set(pb, SLAPI_PLUGIN_VERSION, + (void *)SLAPI_PLUGIN_VERSION_01); + rc |= slapi_pblock_set(pb, SLAPI_PLUGIN_DESCRIPTION, + (void *)&pdesc); + rc |= slapi_pblock_set(pb, SLAPI_PLUGIN_SYNTAX_FILTER_AVA, + (void *)inchain_filter_ava); + rc |= slapi_pblock_set(pb, SLAPI_PLUGIN_SYNTAX_FILTER_SUB, + (void *)inchain_filter_sub); + rc |= slapi_pblock_set(pb, SLAPI_PLUGIN_SYNTAX_VALUES2KEYS, + (void *)inchain_values2keys); + rc |= slapi_pblock_set(pb, SLAPI_PLUGIN_SYNTAX_ASSERTION2KEYS_AVA, + (void *)inchain_assertion2keys_ava); + rc |= slapi_pblock_set(pb, SLAPI_PLUGIN_SYNTAX_ASSERTION2KEYS_SUB, + (void *)inchain_assertion2keys_sub); + rc |= slapi_pblock_set(pb, SLAPI_PLUGIN_SYNTAX_NAMES, + (void *)names); + rc |= slapi_pblock_set(pb, SLAPI_PLUGIN_SYNTAX_OID, + (void *)LDAP_MATCHING_RULE_IN_CHAIN_OID); + rc |= slapi_pblock_set(pb, SLAPI_PLUGIN_SYNTAX_VALIDATE, + (void *)inchain_validate); + rc |= slapi_pblock_set(pb, SLAPI_PLUGIN_SYNTAX_NORMALIZE, + (void *)inchain_normalize); + + rc |= register_matching_rule_plugins(); + slapi_log_err(SLAPI_LOG_PLUGIN, SYNTAX_PLUGIN_SUBSYSTEM, "<= inchain_init %d\n", rc); + return (rc); +} + +int +inchain_filter_ava(Slapi_PBlock *pb, struct berval *bvfilter, Slapi_Value **bvals, int ftype, Slapi_Value **retVal) +{ + /* always true because candidate entries are valid */ + /* in theory we should check the filter but with inchain MR + * inchain_values2keys select candidates where membership attribute/value + * are not systematically present in the candidate entry (recursive call) + * this is the reason why this usual check does not apply + */ +#if 0 + int syntax = SYNTAX_CIS | SYNTAX_DN; + return (string_filter_ava(bvfilter, bvals, syntax, ftype, retVal)); +#else + return(0); +#endif +} + +int +inchain_filter_sub(Slapi_PBlock *pb, char *initial, char **any, char * final, Slapi_Value **bvals) +{ + return(1); +} + + +static PRIntn memberof_hash_compare_keys(const void *v1, const void *v2) +{ + PRIntn rc; + if (0 == strcasecmp((const char *) v1, (const char *) v2)) { + rc = 1; + } else { + rc = 0; + } + return rc; +} + +static PRIntn memberof_hash_compare_values(const void *v1, const void *v2) +{ + PRIntn rc; + if ((char *) v1 == (char *) v2) { + rc = 1; + } else { + rc = 0; + } + return rc; +} + +/* + * Hashing function using Bernstein's method + */ +static PLHashNumber memberof_hash_fn(const void *key) +{ + PLHashNumber hash = 5381; + unsigned char *x = (unsigned char *)key; + int c; + + while ((c = *x++)){ + hash = ((hash << 5) + hash) ^ c; + } + return hash; +} + +/* allocates the plugin hashtable + * This hash table is used by operation and is protected from + * concurrent operations with the memberof_lock (if not usetxn, memberof_lock + * is not implemented and the hash table will be not used. + * + * The hash table contains all the DN of the entries for which the memberof + * attribute has been computed/updated during the current operation + * + * hash table should be empty at the beginning and end of the plugin callback + */ +PLHashTable *hashtable_new(int usetxn) +{ + if (!usetxn) { + return NULL; + } + + return PL_NewHashTable(1000, + memberof_hash_fn, + memberof_hash_compare_keys, + memberof_hash_compare_values, NULL, NULL); +} +int +inchain_values2keys(Slapi_PBlock *pb, Slapi_Value **vals, Slapi_Value ***ivals, int ftype) +{ + Slapi_MemberOfResult groupvals = {0}; + Slapi_ValueSet *groupdn_vals; + Slapi_Value **result; + int nbvalues; + Slapi_Value *v; + Slapi_MemberOfConfig config = {0}; + Slapi_DN *member_sdn; + Slapi_DN *base_sdn = NULL; + size_t idx = 0; + char *mrTYPE; +#if 0 + char *filter_str; +#endif + char error_msg[1024] = {0}; + int rc; + + slapi_pblock_get(pb, SLAPI_PLUGIN_MR_TYPE, &mrTYPE); + slapi_pblock_get(pb, SLAPI_SEARCH_TARGET_SDN, &base_sdn); + + if (! slapi_attr_is_dn_syntax_type(mrTYPE)) { + slapi_log_err(SLAPI_LOG_ERR, "inchain", "Requires distinguishedName syntax. AttributeDescription %s is not distinguishedName\n"); + result = (Slapi_Value **)slapi_ch_calloc(1, sizeof(Slapi_Value *)); + *ivals = result; + return(0); + } + + /* check if the user is allowed to perform inChain matching */ + if (inchain_feature_allowed(pb) != LDAP_SUCCESS) { + slapi_log_err(SLAPI_LOG_ERR, "inchain", "Requestor is not allowed to use InChain Matching rule\n"); + result = (Slapi_Value **)slapi_ch_calloc(1, sizeof(Slapi_Value *)); + *ivals = result; + return(0); + } + + /* it is used only in case of MEMBEROF_REUSE_ONLY of MEMBEROF_REUSE_IF_POSSIBLE + * to reuse potential results from memberof plugin + * So its value is only "memberof" + */ + config.memberof_attr = "memberof"; + config.groupattrs = (char **) slapi_ch_calloc(sizeof(char*), 2); + config.groupattrs[0] = mrTYPE; + config.groupattrs[1] = NULL; + config.subtree_search = PR_FALSE; + config.allBackends = 0; + config.entryScopes = (Slapi_DN **)slapi_ch_calloc(sizeof(Slapi_DN *), 2); + /* only looking in the base search scope */ + config.entryScopes[0] = slapi_sdn_dup((const Slapi_DN *) base_sdn); + + /* no exclusion for inchain */ + config.entryScopeExcludeSubtrees = NULL; + +#if 0 + filter_str = slapi_ch_smprintf("(%s=*)", "manager"); + config.group_filter = slapi_str2filter(filter_str); + + config.group_slapiattrs = (Slapi_Attr **)slapi_ch_calloc(sizeof(Slapi_Attr *), 3); + config.group_slapiattrs[0] = slapi_attr_new(); + config.group_slapiattrs[1] = slapi_attr_new(); + slapi_attr_init(config.group_slapiattrs[0], "manager"); + slapi_attr_init(config.group_slapiattrs[1], "nsuniqueid"); +#endif + config.recurse = PR_TRUE; + config.maxgroups = 0; + config.flag = MEMBEROF_REUSE_IF_POSSIBLE; + config.error_msg = error_msg; + config.errot_msg_lenght = sizeof(error_msg); + + member_sdn = slapi_sdn_new_dn_byval((const char*) vals[0]->bv.bv_val); + rc = slapi_memberof(&config, member_sdn, &groupvals); + if (rc) { + slapi_log_err(SLAPI_LOG_ERR, "inchain", " slapi_memberof fails %d (msg=%s)\n", rc, error_msg); + } +#if 0 + slapi_filter_free(config.group_filter, 1); + slapi_attr_free(&config.group_slapiattrs[0]); + slapi_attr_free(&config.group_slapiattrs[1]); +#endif + groupdn_vals = groupvals.nsuniqueid_vals; + idx = slapi_valueset_first_value(groupdn_vals, &v); + for (; groupdn_vals && v; idx = slapi_valueset_next_value(groupdn_vals, idx, &v)) { + char value[1000]; + strncpy(value, v->bv.bv_val, v->bv.bv_len); + value[v->bv.bv_len] = '\0'; + slapi_log_err(SLAPI_LOG_FILTER, "inchain", " groupvals = %s\n", value); + + } + +#if 1 + + nbvalues = slapi_valueset_count(groupdn_vals); + result = (Slapi_Value **)slapi_ch_calloc(nbvalues + 1, sizeof(Slapi_Value *)); + for(idx = 0; idx < slapi_valueset_count(groupdn_vals); idx++) { + char value[1000]; + + result[idx] = slapi_value_dup(groupdn_vals->va[idx]); + strncpy(value, result[idx]->bv.bv_val, result[idx]->bv.bv_len); + value[result[idx]->bv.bv_len] = '\0'; + slapi_log_err(SLAPI_LOG_FILTER, "inchain", "copy key %s \n", value); + } + if (groupvals.dn_vals) { + slapi_valueset_free(groupvals.dn_vals); + groupvals.dn_vals = NULL; + } + if (groupvals.nsuniqueid_vals) { + slapi_valueset_free(groupvals.nsuniqueid_vals); + groupvals.nsuniqueid_vals = NULL; + } + *ivals = result; + return(0); +#else + return (string_values2keys(pb, vals, ivals, SYNTAX_CIS | SYNTAX_DN, + ftype)); +#endif +} + +int +inchain_assertion2keys_ava(Slapi_PBlock *pb, Slapi_Value *val, Slapi_Value ***ivals, int ftype) +{ + slapi_log_err(SLAPI_LOG_ERR, "inchain", "inchain_assertion2keys_ava \n"); + return (string_assertion2keys_ava(pb, val, ivals, + SYNTAX_CIS | SYNTAX_DN, ftype)); +} + +int +inchain_assertion2keys_sub(Slapi_PBlock *pb, char *initial, char **any, char * final, Slapi_Value ***ivals) +{ + slapi_log_err(SLAPI_LOG_ERR, "inchain", "inchain_assertion2keys_sub \n"); + return (string_assertion2keys_sub(pb, initial, any, final, ivals, + SYNTAX_CIS | SYNTAX_DN)); +} + +int +inchain_validate(struct berval *val) +{ + int rc = 0; /* Assume value is valid */ + + /* A 0 length value is valid for the DN syntax. */ + if (val == NULL) { + rc = 1; + } else if (val->bv_len > 0) { + rc = distinguishedname_validate(val->bv_val, &(val->bv_val[val->bv_len - 1])); + } + + return rc; +} + +void +inchain_normalize( + Slapi_PBlock *pb __attribute__((unused)), + char *s, + int trim_spaces, + char **alt) +{ + slapi_log_err(SLAPI_LOG_ERR, "inchain", "inchain_normalize %s \n", s); + value_normalize_ext(s, SYNTAX_CIS | SYNTAX_DN, trim_spaces, alt); + return; +} diff --git a/ldap/servers/slapd/back-ldbm/filterindex.c b/ldap/servers/slapd/back-ldbm/filterindex.c index 3bcd715840..d498053792 100644 --- a/ldap/servers/slapd/back-ldbm/filterindex.c +++ b/ldap/servers/slapd/back-ldbm/filterindex.c @@ -451,6 +451,7 @@ extensible_candidates( Slapi_PBlock *pb = slapi_pblock_new(); int mrOP = 0; Slapi_Operation *op = NULL; + Connection *conn = NULL; back_txn txn = {NULL}; slapi_log_err(SLAPI_LOG_TRACE, "extensible_candidates", "=> \n"); slapi_pblock_get(glob_pb, SLAPI_TXN, &txn.back_txn_txn); @@ -470,8 +471,11 @@ extensible_candidates( /* set the pb->pb_op to glob_pb->pb_op to catch the abandon req. * in case the operation is interrupted. */ slapi_pblock_get(glob_pb, SLAPI_OPERATION, &op); + slapi_pblock_get(glob_pb, SLAPI_CONNECTION, &conn); /* coverity[var_deref_model] */ slapi_pblock_set(pb, SLAPI_OPERATION, op); + slapi_pblock_set(pb, SLAPI_CONNECTION, conn); + slapi_pblock_set(pb, SLAPI_REQUESTOR_ISROOT, &op->o_isroot); slapi_pblock_get(pb, SLAPI_PLUGIN_MR_INDEX_FN, &mrINDEX); slapi_pblock_get(pb, SLAPI_PLUGIN_OBJECT, &mrOBJECT); @@ -516,16 +520,36 @@ extensible_candidates( } else if (keys == NULL || keys[0] == NULL) { /* no keys */ idl_free(&idl); - idl = idl_allids(be); + if (strcmp(mrOID, LDAP_MATCHING_RULE_IN_CHAIN_OID) == 0) { + /* we need to return no candidate else, inchain_filter_ava + * matching all candidates, the search returns invalid results + */ + idl = idl_alloc(0); + } else { + idl = idl_allids(be); + } } else { IDList *idl2 = NULL; struct berval **key; +#define KEY_STR_LGHT 35 /* stollen from nsuniqueid.c UIDSTR_SIZE 35 */ + char key_str[KEY_STR_LGHT + 1]; /* only used for debug logging */ for (key = keys; *key != NULL; ++key) { int unindexed = 0; IDList *idl3 = (mrOP == SLAPI_OP_EQUAL) ? index_read_ext_allids(pb, be, mrTYPE, mrOID, *key, &txn, err, &unindexed, allidslimit) : index_range_read_ext(pb, be, mrTYPE, mrOID, mrOP, *key, NULL, 0, &txn, err, allidslimit); + if (slapi_is_loglevel_set(SLAPI_LOG_FILTER)) { + int lenght_str = key[0]->bv_len; + + if (key[0]->bv_len > KEY_STR_LGHT) { + lenght_str = KEY_STR_LGHT; + } + + strncpy(key_str, key[0]->bv_val, lenght_str); + key_str[lenght_str] = '\0'; + slapi_log_err(SLAPI_LOG_FILTER, "extensible_candidates", "=> idl (%s) = (%d)\n", key_str, idl3->b_ids[0]); + } if (unindexed) { int pr_idx = -1; slapi_pblock_set_flag_operation_notes(pb, SLAPI_OP_NOTE_UNINDEXED); @@ -542,7 +566,12 @@ extensible_candidates( /* first iteration */ idl2 = idl3; } else { - IDList *tmp = idl_intersection(be, idl2, idl3); + IDList *tmp; + if (strcmp(mrOID, LDAP_MATCHING_RULE_IN_CHAIN_OID) == 0) { + tmp = idl_union(be, idl2, idl3); + } else { + tmp = idl_intersection(be, idl2, idl3); + } idl_free(&idl2); idl_free(&idl3); idl2 = tmp; @@ -575,8 +604,11 @@ extensible_candidates( } return_idl: op = NULL; + conn = NULL; + /* coverity[var_deref_model] */ slapi_pblock_set(pb, SLAPI_OPERATION, op); + slapi_pblock_set(pb, SLAPI_CONNECTION, conn); slapi_pblock_destroy(pb); slapi_log_err(SLAPI_LOG_TRACE, "extensible_candidates", "<= %lu\n", (u_long)IDL_NIDS(idl)); diff --git a/ldap/servers/slapd/back-ldbm/index.c b/ldap/servers/slapd/back-ldbm/index.c index 5b34a1e879..86bc825feb 100644 --- a/ldap/servers/slapd/back-ldbm/index.c +++ b/ldap/servers/slapd/back-ldbm/index.c @@ -924,6 +924,10 @@ index_read_ext_allids( *err = 0; + if (strcmp(indextype, LDAP_MATCHING_RULE_IN_CHAIN_OID) == 0) { + type = "nsuniqueid"; + indextype = indextype_EQUALITY; + } if (unindexed != NULL) *unindexed = 0; prefix = index_index2prefix(indextype); diff --git a/ldap/servers/slapd/slap.h b/ldap/servers/slapd/slap.h index 40f5cebb07..aea052983c 100644 --- a/ldap/servers/slapd/slap.h +++ b/ldap/servers/slapd/slap.h @@ -751,6 +751,7 @@ typedef int (*SyntaxEnumFunc)(char **names, Slapi_PluginDesc *plugindesc, void * #define INTEGERORDERINGMATCH_OID "2.5.13.15" /* integerOrderingMatch */ #define INTFIRSTCOMPMATCH_OID "2.5.13.29" /* integerFirstComponentMatch */ #define OIDFIRSTCOMPMATCH_OID "2.5.13.30" /* objectIdentifierFirstComponentMatch */ +#define LDAP_MATCHING_RULE_IN_CHAIN_OID "1.2.840.113556.1.4.1941" /* Names for some commonly used matching rules */ #define DNMATCH_NAME "distinguishedNameMatch" @@ -759,6 +760,7 @@ typedef int (*SyntaxEnumFunc)(char **names, Slapi_PluginDesc *plugindesc, void * #define INTEGERORDERINGMATCH_NAME "integerOrderingMatch" #define INTFIRSTCOMPMATCH_NAME "integerFirstComponentMatch" #define OIDFIRSTCOMPMATCH_NAME "objectIdentifierFirstComponentMatch" +#define LDAP_MATCHING_RULE_IN_CHAIN_NAME "ancestryDNMatch" #define ATTR_STANDARD_STRING "Standard Attribute" #define ATTR_USERDEF_STRING "User Defined Attribute" diff --git a/ldap/servers/slapd/slapi-memberof.c b/ldap/servers/slapd/slapi-memberof.c index 6f39965bbe..f71fd8854b 100644 --- a/ldap/servers/slapd/slapi-memberof.c +++ b/ldap/servers/slapd/slapi-memberof.c @@ -784,12 +784,12 @@ sm_compare_memberof_config(const char *memberof_attr, char **groupattrs, PRBool int32_t cnt1, cnt2; if ((memberof_config == NULL) || (memberof_config->enabled == PR_FALSE)) { - slapi_log_err(SLAPI_LOG_ERR, "slapi_memberof", "sm_compare_memberof_config: config not initialized or disabled\n"); + slapi_log_err(SLAPI_LOG_PLUGIN, "slapi_memberof", "sm_compare_memberof_config: config not initialized or disabled\n"); return PR_FALSE; } if (enabled_only) { - slapi_log_err(SLAPI_LOG_ERR, "slapi_memberof", "sm_compare_memberof_config: check the plugin is enabled that is %s\n", + slapi_log_err(SLAPI_LOG_PLUGIN, "slapi_memberof", "sm_compare_memberof_config: check the plugin is enabled that is %s\n", memberof_config->enabled ? "SUCCEEDS" : "FAILS"); if (memberof_config->enabled) { return PR_TRUE; @@ -801,7 +801,7 @@ sm_compare_memberof_config(const char *memberof_attr, char **groupattrs, PRBool /* Check direct flags */ if ((all_backends != memberof_config->all_backends) || (skip_nested != memberof_config->skip_nested)) { /* If those flags do not match the current set of 'memberof' values is invalid */ - slapi_log_err(SLAPI_LOG_ERR, "slapi_memberof", "sm_compare_memberof_config: fails (allbackend %d vs %d, skip_nested %d vs %d)\n", + slapi_log_err(SLAPI_LOG_PLUGIN, "slapi_memberof", "sm_compare_memberof_config: fails (allbackend %d vs %d, skip_nested %d vs %d)\n", all_backends, memberof_config->all_backends, skip_nested, memberof_config->skip_nested); return PR_FALSE; } @@ -811,7 +811,7 @@ sm_compare_memberof_config(const char *memberof_attr, char **groupattrs, PRBool */ if ((memberof_attr == NULL) || (memberof_config->memberof_attr == NULL) || (strcasecmp(memberof_attr, memberof_config->memberof_attr))) { /* just be conservative, we should speak about the same attribute */ - slapi_log_err(SLAPI_LOG_ERR, "slapi_memberof", + slapi_log_err(SLAPI_LOG_PLUGIN, "slapi_memberof", "sm_compare_memberof_config: fails memberof attribute differs (require '%s' vs config '%s')\n", memberof_attr ? memberof_attr : "NULL", memberof_config->memberof_attr ? memberof_config->memberof_attr : NULL); @@ -823,12 +823,12 @@ sm_compare_memberof_config(const char *memberof_attr, char **groupattrs, PRBool */ if (groupattrs == NULL) { /* This is a mandatory parameter */ - slapi_log_err(SLAPI_LOG_ERR, "slapi_memberof", "sm_compare_memberof_config: fails because requested group attributes is empty\n"); + slapi_log_err(SLAPI_LOG_PLUGIN, "slapi_memberof", "sm_compare_memberof_config: fails because requested group attributes is empty\n"); return PR_FALSE; } for (cnt1 = 0; groupattrs[cnt1]; cnt1++) { if (charray_inlist(memberof_config->groupattrs, groupattrs[cnt1]) == 0) { - slapi_log_err(SLAPI_LOG_ERR, "slapi_memberof", + slapi_log_err(SLAPI_LOG_PLUGIN, "slapi_memberof", "sm_compare_memberof_config: fails because requested group attribute '%s' is not configured\n", groupattrs[cnt1]); return PR_FALSE; @@ -837,26 +837,26 @@ sm_compare_memberof_config(const char *memberof_attr, char **groupattrs, PRBool for (cnt2 = 0; memberof_config->groupattrs && memberof_config->groupattrs[cnt2]; cnt2++); if (cnt1 != cnt2) { /* make sure groupattrs is not a subset of memberof_config->groupattrs */ - slapi_log_err(SLAPI_LOG_ERR, "slapi_memberof", "sm_compare_memberof_config: fails because number of requested group attributes differs from config\n"); + slapi_log_err(SLAPI_LOG_PLUGIN, "slapi_memberof", "sm_compare_memberof_config: fails because number of requested group attributes differs from config\n"); return PR_FALSE; } /* check Include scope that is optional */ if (include_scope == NULL) { if (memberof_config->include_scope) { - slapi_log_err(SLAPI_LOG_ERR, "slapi_memberof", "sm_compare_memberof_config: fails because requested include scope is empty that differs config\n"); + slapi_log_err(SLAPI_LOG_PLUGIN, "slapi_memberof", "sm_compare_memberof_config: fails because requested include scope is empty that differs config\n"); return PR_FALSE; } } else { if (memberof_config->include_scope == NULL) { - slapi_log_err(SLAPI_LOG_ERR, "slapi_memberof", "sm_compare_memberof_config: fails because requested include scope is not empty that differs config\n"); + slapi_log_err(SLAPI_LOG_PLUGIN, "slapi_memberof", "sm_compare_memberof_config: fails because requested include scope is not empty that differs config\n"); return PR_FALSE; } } /* here include scopes are both NULL or both not NULL */ for (cnt1 = 0; include_scope && include_scope[cnt1]; cnt1++) { if (charray_inlist(memberof_config->include_scope, (char *) slapi_sdn_get_ndn(include_scope[cnt1])) == 0) { - slapi_log_err(SLAPI_LOG_ERR, "slapi_memberof", "sm_compare_memberof_config: fails because requested include scope (%s) is not in config\n", + slapi_log_err(SLAPI_LOG_PLUGIN, "slapi_memberof", "sm_compare_memberof_config: fails because requested include scope (%s) is not in config\n", slapi_sdn_get_ndn(include_scope[cnt1])); return PR_FALSE; } @@ -864,26 +864,26 @@ sm_compare_memberof_config(const char *memberof_attr, char **groupattrs, PRBool for (cnt2 = 0; memberof_config->include_scope && memberof_config->include_scope[cnt2]; cnt2++); if (cnt1 != cnt2) { /* make sure include_scope is not a subset of memberof_config->include_scope */ - slapi_log_err(SLAPI_LOG_ERR, "slapi_memberof", "sm_compare_memberof_config: fails because number of requested included scopes differs from config\n"); + slapi_log_err(SLAPI_LOG_PLUGIN, "slapi_memberof", "sm_compare_memberof_config: fails because number of requested included scopes differs from config\n"); return PR_FALSE; } /* check Exclude scope that is optional */ if (exclude_scope == NULL) { if (memberof_config->exclude_scope) { - slapi_log_err(SLAPI_LOG_ERR, "slapi_memberof", "sm_compare_memberof_config: fails because requested exclude scope is empty that differs config\n"); + slapi_log_err(SLAPI_LOG_PLUGIN, "slapi_memberof", "sm_compare_memberof_config: fails because requested exclude scope is empty that differs config\n"); return PR_FALSE; } } else { if (memberof_config->exclude_scope == NULL) { - slapi_log_err(SLAPI_LOG_ERR, "slapi_memberof", "sm_compare_memberof_config: fails because requested exclude scope is not empty that differs config\n"); + slapi_log_err(SLAPI_LOG_PLUGIN, "slapi_memberof", "sm_compare_memberof_config: fails because requested exclude scope is not empty that differs config\n"); return PR_FALSE; } } /* here exclude scopes are both NULL or both not NULL */ for (cnt1 = 0; exclude_scope && exclude_scope[cnt1]; cnt1++) { if (charray_inlist(memberof_config->exclude_scope, (char *) slapi_sdn_get_ndn(exclude_scope[cnt1])) == 0) { - slapi_log_err(SLAPI_LOG_ERR, "slapi_memberof", "sm_compare_memberof_config: fails because requested exclude scope (%s) is not in config\n", + slapi_log_err(SLAPI_LOG_PLUGIN, "slapi_memberof", "sm_compare_memberof_config: fails because requested exclude scope (%s) is not in config\n", slapi_sdn_get_ndn(exclude_scope[cnt1])); return PR_FALSE; } @@ -891,10 +891,10 @@ sm_compare_memberof_config(const char *memberof_attr, char **groupattrs, PRBool for (cnt2 = 0; memberof_config->exclude_scope && memberof_config->exclude_scope[cnt2]; cnt2++); if (cnt1 != cnt2) { /* make sure exclude_scope is not a subset of memberof_config->exclude_scope */ - slapi_log_err(SLAPI_LOG_ERR, "slapi_memberof", "sm_compare_memberof_config: fails because number of requested exclude scopes differs from config\n"); + slapi_log_err(SLAPI_LOG_PLUGIN, "slapi_memberof", "sm_compare_memberof_config: fails because number of requested exclude scopes differs from config\n"); return PR_FALSE; } - slapi_log_err(SLAPI_LOG_ERR, "slapi_memberof", "sm_compare_memberof_config: succeeds. requested options match config\n"); + slapi_log_err(SLAPI_LOG_PLUGIN, "slapi_memberof", "sm_compare_memberof_config: succeeds. requested options match config\n"); return PR_TRUE; }