def test_chaining_paged_search(topology): """ Test paged search through the chaining db. This would cause a SIGSEGV with paged search which could be triggered by SSSD. :id: 7b29b1f5-26cf-49fa-9fe7-ee29a1408633 :setup: Two standalones in chaining. :steps: 1. Configure chaining between the nodes 2. Do a chaining search (no page) to assert it works 3. Do a paged search through chaining. :expectedresults: 1. Success 2. Success 3. Success """ st1 = topology.ins["standalone1"] st2 = topology.ins["standalone2"] ### We setup so that st1 -> st2 # Clear all the BE in st1 bes1 = Backends(st1) for be in bes1.list(): be.delete() # Setup st1 to chain to st2 chain_plugin_1 = ChainingBackendPlugin(st1) chain_plugin_1.enable() chains = ChainingLinks(st1) chain = chains.create( properties={ 'cn': 'demochain', 'nsslapd-suffix': DEFAULT_SUFFIX, 'nsmultiplexorbinddn': '', 'nsmultiplexorcredentials': '', 'nsfarmserverurl': st2.toLDAPURL(), }) mts = MappingTrees(st1) # Due to a bug in lib389, we need to delete and recreate the mt. for mt in mts.list(): mt.delete() mts.ensure_state( properties={ 'cn': DEFAULT_SUFFIX, 'nsslapd-state': 'backend', 'nsslapd-backend': 'demochain', }) # Restart to enable st1.restart() # Get an anonymous connection. anon = Account(st1, dn='') anon_conn = anon.bind(password='') # Now do a search from st1 -> st2 accs_1 = Accounts(anon_conn, DEFAULT_SUFFIX) assert len(accs_1.list()) > 0 # Allow time to attach lldb if needed. # import time # print("🔥🔥🔥") # time.sleep(45) # Now do a *paged* search from st1 -> st2 assert len(accs_1.list(paged_search=2, paged_critical=False)) > 0
class ChainingLink(DSLdapObject): """Chaining Backend DSLdapObject with: - must attributes = ['cn', 'nsslapd-suffix', 'nsmultiplexorbinddn', 'nsmultiplexorcredentials', 'nsfarmserverurl' - RDN attribute is 'cn' :param instance: An instance :type instance: lib389.DirSrv :param dn: Entry DN :type dn: str """ _must_attributes = ['nsslapd-suffix', 'cn'] def __init__(self, instance, dn=None, rdn=None): super(ChainingLink, self).__init__(instance, dn) self._rdn_attribute = 'cn' self._must_attributes = [ 'nsslapd-suffix', 'cn', 'nsmultiplexorbinddn', 'nsmultiplexorcredentials', 'nsfarmserverurl' ] self._create_objectclasses = [ 'top', 'extensibleObject', BACKEND_OBJECTCLASS_VALUE ] self._protected = False self._basedn = "cn=chaining database,cn=plugins,cn=config" self._mts = MappingTrees(self._instance) def get_monitor(self): """Get a MonitorChaining(DSLdapObject) for the chaining link :returns - chaining monitor entry """ return MonitorChaining(instance=self._instance, dn="cn=monitor,%s" % self._dn) def del_link(self): """ Remove the link from the parent suffix backend entry Delete chaining monitor entry Delete chaining entry """ rdn = self.get_attr_val_utf8_l('cn') try: mt = self._mts.get(selector=rdn) mt.delete() except ldap.NO_SUCH_OBJECT: # Righto, it's already gone! Do nothing ... pass # Delete the monitoring entry monitor = self.get_monitor(rdn) monitor.delete() # Delete the link self.delete() def create(self, rdn=None, properties=None, basedn=None): """Create the link entry, and the mapping tree entry(if needed) """ # Create chaining entry super(ChainingLink, self).create(rdn, properties, basedn) # Create mapping tree entry dn_comps = ldap.explode_dn(properties['nsslapd-suffix'][0]) parent_suffix = ','.join(dn_comps[1:]) mt_properties = { 'cn': properties['nsslapd-suffix'][0], 'nsslapd-state': 'backend', 'nsslapd-backend': properties['cn'][0], 'nsslapd-parent-suffix': parent_suffix } try: self._mts.ensure_state(properties=mt_properties) except ldap.ALREADY_EXISTS: pass
def test_chaining_paged_search(topology): """ Check that when the chaining target has anonymous access disabled that the ping still functions and allows the search to continue with an appropriate bind user. :id: 00bf31db-d93b-4224-8e70-86abb2d4cd17 :setup: Two standalones in chaining. :steps: 1. Configure chaining between the nodes 2. Do a chaining search (w anon allow) to assert it works 3. Configure anon dis allowed on st2 4. Restart both 5. Check search still works :expectedresults: 1. Success 2. Success 3. Success 4. Success 5. Success """ st1 = topology.ins["standalone1"] st2 = topology.ins["standalone2"] ### We setup so that st1 -> st2 # Setup a chaining user on st2 to authenticate to. sa = ServiceAccounts(st2, DEFAULT_SUFFIX).create(properties = { 'cn': 'sa', 'userPassword': PW }) # Add a proxy user. sproxy = ServiceAccounts(st2, DEFAULT_SUFFIX).create(properties = { 'cn': 'proxy', 'userPassword': PW }) # Add the read and proxy ACI dc = Domain(st2, DEFAULT_SUFFIX) dc.add('aci', f"""(targetattr="objectClass || cn || uid")(version 3.0; acl "Enable sa read"; allow (read, search, compare)(userdn="ldap:///{sa.dn}");)""" ) # Add the proxy ACI dc.add('aci', f"""(targetattr="*")(version 3.0; acl "Enable proxy access"; allow (proxy)(userdn="ldap:///{sproxy.dn}");)""" ) # Clear all the BE in st1 bes1 = Backends(st1) for be in bes1.list(): be.delete() # Setup st1 to chain to st2 chain_plugin_1 = ChainingBackendPlugin(st1) chain_plugin_1.enable() # Chain with the proxy user. chains = ChainingLinks(st1) chain = chains.create(properties={ 'cn': 'demochain', 'nsfarmserverurl': st2.toLDAPURL(), 'nsslapd-suffix': DEFAULT_SUFFIX, 'nsmultiplexorbinddn': sproxy.dn, 'nsmultiplexorcredentials': PW, 'nsCheckLocalACI': 'on', 'nsConnectionLife': '30', }) mts = MappingTrees(st1) # Due to a bug in lib389, we need to delete and recreate the mt. for mt in mts.list(): mt.delete() mts.ensure_state(properties={ 'cn': DEFAULT_SUFFIX, 'nsslapd-state': 'backend', 'nsslapd-backend': 'demochain', 'nsslapd-distribution-plugin': 'libreplication-plugin', 'nsslapd-distribution-funct': 'repl_chain_on_update', }) # Enable pwpolicy (Not sure if part of the issue). st1.config.set('passwordIsGlobalPolicy', 'on') st2.config.set('passwordIsGlobalPolicy', 'on') # Restart to enable everything. st1.restart() # Get a proxy auth connection. sa1 = ServiceAccount(st1, sa.dn) sa1_conn = sa1.bind(password=PW) # Now do a search from st1 -> st2 sa1_dc = Domain(sa1_conn, DEFAULT_SUFFIX) assert sa1_dc.exists() # Now on st2 disable anonymous access. st2.config.set('nsslapd-allow-anonymous-access', 'rootdse') # Stop st2 to force the connection to be dead. st2.stop() # Restart st1 - this means it must re-do the ping/keepalive. st1.restart() # do a bind - this should fail, and forces the conn offline. with pytest.raises(ldap.OPERATIONS_ERROR): sa1.bind(password=PW) # Allow time to attach lldb if needed. # print("🔥🔥🔥") # time.sleep(45) # Bring st2 online. st2.start() # Wait a bit time.sleep(5) # Get a proxy auth connection (again) sa1_conn = sa1.bind(password=PW) # Now do a search from st1 -> st2 sa1_dc = Domain(sa1_conn, DEFAULT_SUFFIX) assert sa1_dc.exists()