From 0876f74ee39ada8de9e152bbfa50a09d3da580a3 Mon Sep 17 00:00:00 2001 From: Thierry Bordaz Date: Fri, 22 Nov 2024 11:20:19 +0100 Subject: [PATCH] Issue 6417 - If an entry RDN is identical to the suffix, then Entryrdn gets broken during a reindex Bug description: During a reindex, the entryrdn index is built at the end from each entry in the suffix. If one entry has a RDN that is identical to the suffix DN, then entryrdn_lookup_dn may erroneously return the suffix DN as the DN of the entry. Fix description: When the lookup entry has no parent (because index is under work) the loop lookup the entry using the RDN. If this RDN matches the suffix DN, then it exits from the loop with the suffix DN. Before exiting it checks that the original lookup entryID is equal to suffix entryID. If it does not match the function fails and then the DN from the entry will be built from id2enty fixes: #6417 Reviewed by: --- .../tests/suites/indexes/entryrdn_test.py | 75 ++++++++++++++++++- ldap/servers/slapd/back-ldbm/ldbm_entryrdn.c | 46 ++++++++++-- 2 files changed, 112 insertions(+), 9 deletions(-) diff --git a/dirsrvtests/tests/suites/indexes/entryrdn_test.py b/dirsrvtests/tests/suites/indexes/entryrdn_test.py index 345955d4da..05342afcb0 100644 --- a/dirsrvtests/tests/suites/indexes/entryrdn_test.py +++ b/dirsrvtests/tests/suites/indexes/entryrdn_test.py @@ -11,11 +11,16 @@ import pytest import ldap import logging +from lib389 import Entry from lib389._constants import DEFAULT_BENAME, DEFAULT_SUFFIX -from lib389.backend import Backends +from lib389.backend import Backends, Backend +from lib389.mappingTree import MappingTrees +from lib389.configurations.sample import create_base_domain +from lib389.idm.domain import Domain from lib389.idm.user import UserAccounts, UserAccount from lib389.idm.organizationalunit import OrganizationalUnits from lib389.topologies import topology_m2 as topo_m2 +from lib389.topologies import topology_st from lib389.agreement import Agreements from lib389.utils import ds_is_older, ensure_bytes from lib389.tasks import Tasks,ExportTask, ImportTask @@ -283,6 +288,74 @@ def test_long_rdn(topo_m2): ou.delete() assert not ou.exists() +def test_duplicate_rdn(topology_st, request): + inst = topology_st.standalone + inst.config.replace('nsslapd-auditfaillog-logging-enabled', 'on') + be_name = 'domain' + dc_value = 'dup_rdn' + suffix = 'dc=' + dc_value + rdn = 'my_org' + be1 = Backend(inst) + be1.create(properties={ + 'cn': be_name, + 'nsslapd-suffix': suffix, + }, + create_mapping_tree=False + ) + + mts = MappingTrees(inst) + mt = mts.create(properties={ + 'cn': suffix, + 'nsslapd-state': 'backend', + 'nsslapd-backend': be_name, + }) + + # The order of create is important + # it does not hit the bug in the order + # 1 - 3 - 2 - 4 + + # 1 - Create the domain entry 'dc=com' + create_base_domain(inst, suffix) + + # 2 - Create the org ou=my_org,dc=com + ous = OrganizationalUnits(inst, suffix) + ou = ous.create(properties={ 'ou': rdn }) + #org1 = OrganizationalUnits(inst, DEFAULT_SUFFIX).create(properties={'ou': 'Users'}) + # 'originScope': org1.dn, + info_message = 'entryrdn_insert_key - Same DN (dn: %s) is already in the entryrdn file with different' % (ou.dn) + log.info("In case if the bug still exist this line should be in the error log") + log.info(" --> " + info_message) + + # 3 - Create the domain entry 'dc=com,dc=com' + #truc = "dc=dux_rdn,dc=dup_rdn" + truc = "dc=dup_rdn,dc=dup_rdn" + topology_st.standalone.add_s(Entry(( + truc, { + "objectClass": "top", + "objectClass": "domain", + "dc": dc_value, + } + ))) + + # 4 - Create the org ou=my_org,dc=com,dc=com + ous = OrganizationalUnits(inst, truc) + ou = ous.create(properties={ 'ou': rdn }) + + + log.info('Offline reindex, stopping the server') + topology_st.standalone.stop() + + log.info('Reindex all the suffix') + topology_st.standalone.db2index(bename=be_name) + assert not topology_st.standalone.ds_error_log.match(".*entryrdn_insert_key - Same DN.*is already in the entryrdn file with different.*$") + + + def fin(): + topology_st.standalone.restart() + mt.delete() + be1.delete() + + request.addfinalizer(fin) if __name__ == "__main__": # Run isolated diff --git a/ldap/servers/slapd/back-ldbm/ldbm_entryrdn.c b/ldap/servers/slapd/back-ldbm/ldbm_entryrdn.c index ef79219469..421804a3e7 100644 --- a/ldap/servers/slapd/back-ldbm/ldbm_entryrdn.c +++ b/ldap/servers/slapd/back-ldbm/ldbm_entryrdn.c @@ -987,9 +987,11 @@ entryrdn_lookup_dn(backend *be, char *keybuf = NULL; Slapi_RDN *srdn = NULL; char *orignrdn = NULL; + Slapi_DN origin_rdn_sdn = {0}; char *nrdn = NULL; size_t nrdn_len = 0; ID workid = id; /* starting from the given id */ + ID origin_id = id; rdn_elem *elem = NULL; int maybesuffix = 0; @@ -1028,6 +1030,14 @@ entryrdn_lookup_dn(backend *be, slapi_ch_free_string(&orignrdn); } + /* + * Used to be sure we are not returning the suffix DN by mistake + * If the entryrdn is under creation (reindex), there is a risk to + * return the suffix DN if the rdn, of the lookup entry, is equal to suffix. + * (see #6417) + */ + slapi_sdn_init_dn_byval(&origin_rdn_sdn, nrdn); + /* Setting the bulk fetch buffer */ dblayer_value_free(be, &data); dblayer_value_init(be, &data); @@ -1088,14 +1098,6 @@ entryrdn_lookup_dn(backend *be, /* Iterate over the duplicates to get the direct child's ID */ workid = 0; - if (maybesuffix) { - /* it is a suffix, indeed. done. */ - /* generate sdn to return */ - slapi_rdn_get_dn(srdn, dn); - rc = 0; - _ENTRYRDN_DEBUG_GOTO_BAIL(); - goto bail; - } /* found a parent (there should be just one parent :) */ elem = (rdn_elem *)data.data; if (elem && RDN_IS_REDIRECT(elem)) { @@ -1107,6 +1109,33 @@ entryrdn_lookup_dn(backend *be, data.data = elem; } + if (maybesuffix) { + Slapi_DN sdn_returned; + + /* it is a suffix, indeed. done. */ + /* generate sdn to return */ + slapi_rdn_get_dn(srdn, dn); + + /* Check that if we are returning suffix + * the suffix entryID match the original + * provided entryID + */ + slapi_sdn_init_dn_byval(&sdn_returned, *dn); + if ((slapi_sdn_compare(&sdn_returned, &origin_rdn_sdn) == 0) && + (id_stored_to_internal(elem->rdn_elem_id) != origin_id)) { + /* The origin_id is not the suffixID. + * So we are not looking for the suffix DN + */ + slapi_ch_free_string(dn); + rc = -1; + } else { + rc = 0; + } + slapi_sdn_done(&sdn_returned); + + _ENTRYRDN_DEBUG_GOTO_BAIL(); + goto bail; + } _ENTRYRDN_DUMP_RDN_ELEM(elem); slapi_ch_free_string(&nrdn); nrdn = slapi_ch_strdup(elem->rdn_elem_nrdn_rdn); @@ -1131,6 +1160,7 @@ entryrdn_lookup_dn(backend *be, slapi_rdn_free(&srdn); } slapi_ch_free_string(&nrdn); + slapi_sdn_done(&origin_rdn_sdn); slapi_log_err(SLAPI_LOG_TRACE, "entryrdn_lookup_dn", "<-- entryrdn_lookup_dn\n"); return rc;