Skip to content

Commit

Permalink
Issue 6417 - If an entry RDN is identical to the suffix, then Entryrd…
Browse files Browse the repository at this point in the history
…n 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: 389ds#6417

Reviewed by:
  • Loading branch information
tbordaz committed Nov 22, 2024
1 parent 144f933 commit 87eaf73
Show file tree
Hide file tree
Showing 2 changed files with 143 additions and 9 deletions.
106 changes: 105 additions & 1 deletion dirsrvtests/tests/suites/indexes/entryrdn_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -283,6 +288,105 @@ def test_long_rdn(topo_m2):
ou.delete()
assert not ou.exists()

def test_entry_rdn_same_as_suffix(topology_st, request):
"""
Test that a reindex is successful even if an entry
has a RDN that is identical to the suffix
:id: 7f5a38e9-b979-4664-b132-81df0e60f38a
:setup: standalone
:steps:
1. Create a new backend with suffix 'dc=dup_rdn' (ID 1)
2. Create a dummy entry 'ou=my_org,dc=dup_rdn' (ID 2)
3. Create the problematic entry 'dc=dup_rdn,dc=dup_rdn' (ID 3)
4. Create a dummy entry 'ou=my_org,dc=dup_rdn,dc=dup_rdn' (ID 4)
5. Do an offline reindex
6. Check that entryrdn contains the key P3 (parent of ID 3)
7. Check that error log does not contain 'entryrdn_insert_key - Same DN'
:expectedresults:
1. Should succeed
2. Should succeed
3. Should succeed
4. Should succeed
5. Should succeed
6. Should succeed
7. Should succeed
"""
inst = topology_st.standalone

# Create a suffix 'dc=dup_rdn'
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,
})

# Create the domain entry 'dc=dup_rdn'
create_base_domain(inst, suffix)

# Create the org ou=my_org,dc=dup_rdn
ous = OrganizationalUnits(inst, suffix)
ou = ous.create(properties={ 'ou': rdn })

# when reindexing entryrdn the following entry
# (dc=dup_rdn,dc=dup_rdn) Triggers
# this message.
# This is because its RDN (dc=dup_rdn) is also
# the suffix 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)

# Create the domain entry 'dc=dup_rdn,dc=dup_rdn'
trigger_entry = suffix + "," + suffix
topology_st.standalone.add_s(Entry((
trigger_entry, {
"objectClass": "top",
"objectClass": "domain",
"dc": dc_value,
}
)))

# Create the org ou=my_org,dc=dup_rdn,dc=dup_rdn
ous = OrganizationalUnits(inst, trigger_entry)
ou = ous.create(properties={ 'ou': rdn })


# Trigger an offline reindex
log.info('Offline reindex, stopping the server')
topology_st.standalone.stop()

log.info('Reindex all the suffix')
topology_st.standalone.db2index(bename=be_name)

# make sure the key 'P3' (parent of 'dc=dup_rdn,dc=dup_rdn') exists
dbscanout = topology_st.standalone.dbscan(bename=be_name, index='entryrdn')
log.info(dbscanout)
assert(ensure_bytes('P3') in ensure_bytes(dbscanout))

# make sure there is no failure detected/logged in error logs
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
Expand Down
46 changes: 38 additions & 8 deletions ldap/servers/slapd/back-ldbm/ldbm_entryrdn.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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)) {
Expand All @@ -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);
Expand All @@ -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;
Expand Down

0 comments on commit 87eaf73

Please sign in to comment.