Compare commits

...

No commits in common. 'c9' and 'c8-stream-1.4' have entirely different histories.

@ -1,2 +1,3 @@
274dec37976c1efde9cbeb458d50bbcd6b244974 SOURCES/389-ds-base-2.5.2.tar.bz2 bd9aab32d9cbf9231058d585479813f3420dc872 SOURCES/389-ds-base-1.4.3.39.tar.bz2
1c8f2d0dfbf39fa8cd86363bf3314351ab21f8d4 SOURCES/jemalloc-5.3.0.tar.bz2 1c8f2d0dfbf39fa8cd86363bf3314351ab21f8d4 SOURCES/jemalloc-5.3.0.tar.bz2
978b7c5e4a9e5784fddb23ba1abe4dc5a071589f SOURCES/vendor-1.4.3.39-1.tar.gz

3
.gitignore vendored

@ -1,2 +1,3 @@
SOURCES/389-ds-base-2.5.2.tar.bz2 SOURCES/389-ds-base-1.4.3.39.tar.bz2
SOURCES/jemalloc-5.3.0.tar.bz2 SOURCES/jemalloc-5.3.0.tar.bz2
SOURCES/vendor-1.4.3.39-1.tar.gz

@ -1,60 +0,0 @@
From 0ff5aa641d619bdcc154c2c94f8f8180bcaec776 Mon Sep 17 00:00:00 2001
From: tbordaz <tbordaz@redhat.com>
Date: Thu, 29 Aug 2024 10:49:57 +0200
Subject: [PATCH] Issue 6312 - In branch 2.5, healthcheck report an invalid
warning regarding BDB deprecation (#6313)
Bug description:
during healthcheck, _lint_backend_implementation checks that
the instance is not running a BDB backend.
This check only applies for instance after 3.0.0
Fix description:
If the instance is newer than 3.0.0 the health check
just returns
relates: #6312
Reviewed by:
---
dirsrvtests/tests/suites/healthcheck/healthcheck_test.py | 1 +
src/lib389/lib389/backend.py | 4 ++--
2 files changed, 3 insertions(+), 2 deletions(-)
diff --git a/dirsrvtests/tests/suites/healthcheck/healthcheck_test.py b/dirsrvtests/tests/suites/healthcheck/healthcheck_test.py
index 29cca187e..66cf3c7d3 100644
--- a/dirsrvtests/tests/suites/healthcheck/healthcheck_test.py
+++ b/dirsrvtests/tests/suites/healthcheck/healthcheck_test.py
@@ -556,6 +556,7 @@ def test_lint_backend_implementation_wrong_files(topology_st):
@pytest.mark.skipif(get_default_db_lib() == "mdb", reason="Not needed for mdb")
+@pytest.mark.skipif(ds_is_older("3.0.0"), reason="mdb and bdb are both supported")
def test_lint_backend_implementation(topology_st):
"""Test the lint for backend implementation mismatch
diff --git a/src/lib389/lib389/backend.py b/src/lib389/lib389/backend.py
index caee88e6a..0ed00a4a7 100644
--- a/src/lib389/lib389/backend.py
+++ b/src/lib389/lib389/backend.py
@@ -14,7 +14,7 @@ from lib389._constants import DN_LDBM, DN_CHAIN, DN_PLUGIN, DEFAULT_BENAME
from lib389.properties import BACKEND_OBJECTCLASS_VALUE, BACKEND_PROPNAME_TO_ATTRNAME, BACKEND_CHAIN_BIND_DN, \
BACKEND_CHAIN_BIND_PW, BACKEND_CHAIN_URLS, BACKEND_PROPNAME_TO_ATTRNAME, BACKEND_NAME, \
BACKEND_SUFFIX, BACKEND_SAMPLE_ENTRIES, TASK_WAIT
-from lib389.utils import normalizeDN, ensure_str, assert_c
+from lib389.utils import normalizeDN, ensure_str, assert_c, ds_is_newer
from lib389 import Entry
# Need to fix this ....
@@ -513,7 +513,7 @@ class Backend(DSLdapObject):
def _lint_backend_implementation(self):
backend_impl = self._instance.get_db_lib()
- if backend_impl == 'bdb':
+ if backend_impl == 'bdb' and ds_is_newer('3.0.0', instance=self._instance):
result = DSBLE0006
result['items'] = [self.lint_uid()]
yield result
--
2.46.0

@ -0,0 +1,119 @@
From dddb14210b402f317e566b6387c76a8e659bf7fa Mon Sep 17 00:00:00 2001
From: progier389 <progier@redhat.com>
Date: Tue, 14 Feb 2023 13:34:10 +0100
Subject: [PATCH 1/2] issue 5647 - covscan: memory leak in audit log when
adding entries (#5650)
covscan reported an issue about "vals" variable in auditlog.c:231 and indeed a charray_free is missing.
Issue: 5647
Reviewed by: @mreynolds389, @droideck
---
ldap/servers/slapd/auditlog.c | 71 +++++++++++++++++++----------------
1 file changed, 38 insertions(+), 33 deletions(-)
diff --git a/ldap/servers/slapd/auditlog.c b/ldap/servers/slapd/auditlog.c
index 68cbc674d..3128e0497 100644
--- a/ldap/servers/slapd/auditlog.c
+++ b/ldap/servers/slapd/auditlog.c
@@ -177,6 +177,40 @@ write_auditfail_log_entry(Slapi_PBlock *pb)
slapi_ch_free_string(&audit_config);
}
+/*
+ * Write the attribute values to the audit log as "comments"
+ *
+ * Slapi_Attr *entry - the attribute begin logged.
+ * char *attrname - the attribute name.
+ * lenstr *l - the audit log buffer
+ *
+ * Resulting output in the log:
+ *
+ * #ATTR: VALUE
+ * #ATTR: VALUE
+ */
+static void
+log_entry_attr(Slapi_Attr *entry_attr, char *attrname, lenstr *l)
+{
+ Slapi_Value **vals = attr_get_present_values(entry_attr);
+ for(size_t i = 0; vals && vals[i]; i++) {
+ char log_val[256] = "";
+ const struct berval *bv = slapi_value_get_berval(vals[i]);
+ if (bv->bv_len >= 256) {
+ strncpy(log_val, bv->bv_val, 252);
+ strcpy(log_val+252, "...");
+ } else {
+ strncpy(log_val, bv->bv_val, bv->bv_len);
+ log_val[bv->bv_len] = 0;
+ }
+ addlenstr(l, "#");
+ addlenstr(l, attrname);
+ addlenstr(l, ": ");
+ addlenstr(l, log_val);
+ addlenstr(l, "\n");
+ }
+}
+
/*
* Write "requested" attributes from the entry to the audit log as "comments"
*
@@ -212,21 +246,9 @@ add_entry_attrs(Slapi_Entry *entry, lenstr *l)
for (req_attr = ldap_utf8strtok_r(display_attrs, ", ", &last); req_attr;
req_attr = ldap_utf8strtok_r(NULL, ", ", &last))
{
- char **vals = slapi_entry_attr_get_charray(entry, req_attr);
- for(size_t i = 0; vals && vals[i]; i++) {
- char log_val[256] = {0};
-
- if (strlen(vals[i]) > 256) {
- strncpy(log_val, vals[i], 252);
- strcat(log_val, "...");
- } else {
- strcpy(log_val, vals[i]);
- }
- addlenstr(l, "#");
- addlenstr(l, req_attr);
- addlenstr(l, ": ");
- addlenstr(l, log_val);
- addlenstr(l, "\n");
+ slapi_entry_attr_find(entry, req_attr, &entry_attr);
+ if (entry_attr) {
+ log_entry_attr(entry_attr, req_attr, l);
}
}
} else {
@@ -234,7 +256,6 @@ add_entry_attrs(Slapi_Entry *entry, lenstr *l)
for (; entry_attr; entry_attr = entry_attr->a_next) {
Slapi_Value **vals = attr_get_present_values(entry_attr);
char *attr = NULL;
- const char *val = NULL;
slapi_attr_get_type(entry_attr, &attr);
if (strcmp(attr, PSEUDO_ATTR_UNHASHEDUSERPASSWORD) == 0) {
@@ -251,23 +272,7 @@ add_entry_attrs(Slapi_Entry *entry, lenstr *l)
addlenstr(l, ": ****************************\n");
continue;
}
-
- for(size_t i = 0; vals && vals[i]; i++) {
- char log_val[256] = {0};
-
- val = slapi_value_get_string(vals[i]);
- if (strlen(val) > 256) {
- strncpy(log_val, val, 252);
- strcat(log_val, "...");
- } else {
- strcpy(log_val, val);
- }
- addlenstr(l, "#");
- addlenstr(l, attr);
- addlenstr(l, ": ");
- addlenstr(l, log_val);
- addlenstr(l, "\n");
- }
+ log_entry_attr(entry_attr, attr, l);
}
}
slapi_ch_free_string(&display_attrs);
--
2.43.0

@ -0,0 +1,27 @@
From be7c2b82958e91ce08775bf6b5da3c311d3b00e5 Mon Sep 17 00:00:00 2001
From: progier389 <progier@redhat.com>
Date: Mon, 20 Feb 2023 16:14:05 +0100
Subject: [PATCH 2/2] Issue 5647 - Fix unused variable warning from previous
commit (#5670)
* issue 5647 - memory leak in audit log when adding entries
* Issue 5647 - Fix unused variable warning from previous commit
---
ldap/servers/slapd/auditlog.c | 1 -
1 file changed, 1 deletion(-)
diff --git a/ldap/servers/slapd/auditlog.c b/ldap/servers/slapd/auditlog.c
index 3128e0497..0597ecc6f 100644
--- a/ldap/servers/slapd/auditlog.c
+++ b/ldap/servers/slapd/auditlog.c
@@ -254,7 +254,6 @@ add_entry_attrs(Slapi_Entry *entry, lenstr *l)
} else {
/* Return all attributes */
for (; entry_attr; entry_attr = entry_attr->a_next) {
- Slapi_Value **vals = attr_get_present_values(entry_attr);
char *attr = NULL;
slapi_attr_get_type(entry_attr, &attr);
--
2.43.0

@ -1,237 +0,0 @@
From af27f433ec14bcaf070108ab0b6af64ad1153a11 Mon Sep 17 00:00:00 2001
From: progier389 <progier@redhat.com>
Date: Fri, 6 Sep 2024 18:07:17 +0200
Subject: [PATCH] Issue 6316 - lmdb reindex is broken if index type is
specified (#6318)
While reindexing using task or offline reindex, if the attribute name contains the index type (for example :eq,pres)
Then the attribute is not reindexed. Problem occurs when lmdb is used, things are working fine with bdb.
Solution: strip the index type in reindex as it is done in bdb case.
Anyway the reindex design requires that for a given attribute all the configured index types must be rebuild.
Issue: #6316
Reviewed by: @tbordaz, @droideck (Thanks!)
---
.../tests/suites/indexes/regression_test.py | 141 +++++++++++++++++-
.../slapd/back-ldbm/db-mdb/mdb_import.c | 10 +-
2 files changed, 147 insertions(+), 4 deletions(-)
diff --git a/dirsrvtests/tests/suites/indexes/regression_test.py b/dirsrvtests/tests/suites/indexes/regression_test.py
index c385f5ca4..b077b529a 100644
--- a/dirsrvtests/tests/suites/indexes/regression_test.py
+++ b/dirsrvtests/tests/suites/indexes/regression_test.py
@@ -10,6 +10,9 @@ import time
import os
import pytest
import ldap
+import logging
+import glob
+import re
from lib389._constants import DEFAULT_BENAME, DEFAULT_SUFFIX
from lib389.backend import Backend, Backends, DatabaseConfig
from lib389.cos import CosClassicDefinition, CosClassicDefinitions, CosTemplate
@@ -31,6 +34,8 @@ SUFFIX2 = 'dc=example2,dc=com'
BENAME2 = 'be2'
DEBUGGING = os.getenv("DEBUGGING", default=False)
+logging.getLogger(__name__).setLevel(logging.INFO)
+log = logging.getLogger(__name__)
@pytest.fixture(scope="function")
@@ -83,6 +88,7 @@ def add_a_group_with_users(request, topo):
'cn': USER_NAME,
'uidNumber': f'{num}',
'gidNumber': f'{num}',
+ 'description': f'Description for {USER_NAME}',
'homeDirectory': f'/home/{USER_NAME}'
})
users_list.append(user)
@@ -95,9 +101,10 @@ def add_a_group_with_users(request, topo):
# If the server crashed, start it again to do the cleanup
if not topo.standalone.status():
topo.standalone.start()
- for user in users_list:
- user.delete()
- group.delete()
+ if not DEBUGGING:
+ for user in users_list:
+ user.delete()
+ group.delete()
request.addfinalizer(fin)
@@ -124,6 +131,38 @@ def set_small_idlistscanlimit(request, topo):
request.addfinalizer(fin)
+
+@pytest.fixture(scope="function")
+def set_description_index(request, topo, add_a_group_with_users):
+ """
+ Set some description values and description index without reindexing.
+ """
+ inst = topo.standalone
+ backends = Backends(inst)
+ backend = backends.get(DEFAULT_BENAME)
+ indexes = backend.get_indexes()
+ attr = 'description'
+
+ def fin(always=False):
+ if always or not DEBUGGING:
+ try:
+ idx = indexes.get(attr)
+ idx.delete()
+ except ldap.NO_SUCH_OBJECT:
+ pass
+
+ request.addfinalizer(fin)
+ fin(always=True)
+ index = indexes.create(properties={
+ 'cn': attr,
+ 'nsSystemIndex': 'false',
+ 'nsIndexType': ['eq', 'pres', 'sub']
+ })
+ # Restart needed with lmdb (to open the dbi handle)
+ inst.restart()
+ return (indexes, attr)
+
+
#unstable or unstatus tests, skipped for now
@pytest.mark.flaky(max_runs=2, min_passes=1)
@pytest.mark.skipif(ds_is_older("1.4.4.4"), reason="Not implemented")
@@ -347,6 +386,102 @@ def test_task_status(topo):
assert reindex_task.get_exit_code() == 0
+def count_keys(inst, bename, attr, prefix=''):
+ indexfile = os.path.join(inst.dbdir, bename, attr + '.db')
+ # (bdb - we should also accept a version number for .db suffix)
+ for f in glob.glob(f'{indexfile}*'):
+ indexfile = f
+
+ inst.stop()
+ output = inst.dbscan(None, None, args=['-f', indexfile, '-A'], stopping=False).decode()
+ inst.start()
+ count = 0
+ regexp = f'^KEY: {re.escape(prefix)}'
+ for match in re.finditer(regexp, output, flags=re.MULTILINE):
+ count += 1
+ log.info(f"count_keys found {count} keys starting with '{prefix}' in {indexfile}")
+ return count
+
+
+def test_reindex_task_with_type(topo, set_description_index):
+ """Check that reindex task works as expected when index type is specified.
+
+ :id: 0c7f2fda-69f6-11ef-9eb8-083a88554478
+ :setup: Standalone instance
+ - with 100 users having description attribute
+ - with description:eq,pres,sub index entry but not yet reindexed
+ :steps:
+ 1. Set description in suffix entry
+ 2. Count number of equality keys in description index
+ 3. Start a Reindex task on description:eq,pres and wait for completion
+ 4. Check the task status and exit code
+ 5. Count the equality, presence and substring keys in description index
+ 6. Start a Reindex task on description and wait for completion
+ 7. Check the task status and exit code
+ 8. Count the equality, presence and substring keys in description index
+
+ :expectedresults:
+ 1. Success
+ 2. Should be either no key (bdb) or a single one (lmdb)
+ 3. Success
+ 4. Success
+ 5. Should have: more equality keys than in step 2
+ one presence key
+ some substrings keys
+ 6. Success
+ 7. Success
+ 8. Should have same counts than in step 5
+ """
+ (indexes, attr) = set_description_index
+ inst = topo.standalone
+ if not inst.is_dbi_supported():
+ pytest.skip('This test requires that dbscan supports -A option')
+ # modify indexed value
+ Domain(inst, DEFAULT_SUFFIX).replace(attr, f'test_before_reindex')
+
+ keys1 = count_keys(inst, DEFAULT_BENAME, attr, prefix='=')
+ assert keys1 <= 1
+
+ tasks = Tasks(topo.standalone)
+ # completed reindex tasks MUST have a status because freeipa check it.
+
+ # Reindex attr with eq,pres types
+ log.info(f'Reindex {attr} with eq,pres types')
+ tasks.reindex(
+ suffix=DEFAULT_SUFFIX,
+ attrname=f'{attr}:eq,pres',
+ args={TASK_WAIT: True}
+ )
+ reindex_task = Task(topo.standalone, tasks.dn)
+ assert reindex_task.status()
+ assert reindex_task.get_exit_code() == 0
+
+ keys2e = count_keys(inst, DEFAULT_BENAME, attr, prefix='=')
+ keys2p = count_keys(inst, DEFAULT_BENAME, attr, prefix='+')
+ keys2s = count_keys(inst, DEFAULT_BENAME, attr, prefix='*')
+ assert keys2e > keys1
+ assert keys2p > 0
+ assert keys2s > 0
+
+ # Reindex attr without types
+ log.info(f'Reindex {attr} without types')
+ tasks.reindex(
+ suffix=DEFAULT_SUFFIX,
+ attrname=attr,
+ args={TASK_WAIT: True}
+ )
+ reindex_task = Task(topo.standalone, tasks.dn)
+ assert reindex_task.status()
+ assert reindex_task.get_exit_code() == 0
+
+ keys3e = count_keys(inst, DEFAULT_BENAME, attr, prefix='=')
+ keys3p = count_keys(inst, DEFAULT_BENAME, attr, prefix='+')
+ keys3s = count_keys(inst, DEFAULT_BENAME, attr, prefix='*')
+ assert keys3e == keys2e
+ assert keys3p == keys2p
+ assert keys3s == keys2s
+
+
def test_task_and_be(topo, add_backend_and_ldif_50K_users):
"""Check that backend is writable after finishing a tasks
diff --git a/ldap/servers/slapd/back-ldbm/db-mdb/mdb_import.c b/ldap/servers/slapd/back-ldbm/db-mdb/mdb_import.c
index d57146953..ce2151174 100644
--- a/ldap/servers/slapd/back-ldbm/db-mdb/mdb_import.c
+++ b/ldap/servers/slapd/back-ldbm/db-mdb/mdb_import.c
@@ -1150,6 +1150,8 @@ process_db2index_attrs(Slapi_PBlock *pb, ImportCtx_t *ctx)
* TBD
*/
char **attrs = NULL;
+ char *attrname = NULL;
+ char *pt = NULL;
int i;
slapi_pblock_get(pb, SLAPI_DB2INDEX_ATTRS, &attrs);
@@ -1157,7 +1159,13 @@ process_db2index_attrs(Slapi_PBlock *pb, ImportCtx_t *ctx)
for (i = 0; attrs && attrs[i]; i++) {
switch (attrs[i][0]) {
case 't': /* attribute type to index */
- slapi_ch_array_add(&ctx->indexAttrs, slapi_ch_strdup(attrs[i] + 1));
+ attrname = slapi_ch_strdup(attrs[i] + 1);
+ /* Strip index type */
+ pt = strchr(attrname, ':');
+ if (pt != NULL) {
+ *pt = '\0';
+ }
+ slapi_ch_array_add(&ctx->indexAttrs, attrname);
break;
case 'T': /* VLV Search to index */
slapi_ch_array_add(&ctx->indexVlvs, get_vlv_dbname(attrs[i] + 1));
--
2.46.0

@ -0,0 +1,147 @@
From 692c4cec6cc5c0086cf58f83bcfa690c766c9887 Mon Sep 17 00:00:00 2001
From: Thierry Bordaz <tbordaz@redhat.com>
Date: Fri, 2 Feb 2024 14:14:28 +0100
Subject: [PATCH] Issue 5407 - sync_repl crashes if enabled while dynamic
plugin is enabled (#5411)
Bug description:
When dynamic plugin is enabled, if a MOD enables sync_repl plugin
then sync_repl init function registers the postop callback
that will be called for the MOD itself while the preop
has not been called.
postop expects preop to be called and so primary operation
to be set. When it is not set it crashes
Fix description:
If the primary operation is not set, just return
relates: #5407
---
.../suites/syncrepl_plugin/basic_test.py | 68 +++++++++++++++++++
ldap/servers/plugins/sync/sync_persist.c | 23 ++++++-
2 files changed, 90 insertions(+), 1 deletion(-)
diff --git a/dirsrvtests/tests/suites/syncrepl_plugin/basic_test.py b/dirsrvtests/tests/suites/syncrepl_plugin/basic_test.py
index eb3770b78..cdf35eeaa 100644
--- a/dirsrvtests/tests/suites/syncrepl_plugin/basic_test.py
+++ b/dirsrvtests/tests/suites/syncrepl_plugin/basic_test.py
@@ -592,6 +592,74 @@ def test_sync_repl_cenotaph(topo_m2, request):
request.addfinalizer(fin)
+def test_sync_repl_dynamic_plugin(topology, request):
+ """Test sync_repl with dynamic plugin
+
+ :id: d4f84913-c18a-459f-8525-110f610ca9e6
+ :setup: install a standalone instance
+ :steps:
+ 1. reset instance to standard (no retroCL, no sync_repl, no dynamic plugin)
+ 2. Enable dynamic plugin
+ 3. Enable retroCL/content_sync
+ 4. Establish a sync_repl req
+ :expectedresults:
+ 1. Should succeeds
+ 2. Should succeeds
+ 3. Should succeeds
+ 4. Should succeeds
+ """
+
+ # Reset the instance in a default config
+ # Disable content sync plugin
+ topology.standalone.plugins.disable(name=PLUGIN_REPL_SYNC)
+
+ # Disable retro changelog
+ topology.standalone.plugins.disable(name=PLUGIN_RETRO_CHANGELOG)
+
+ # Disable dynamic plugins
+ topology.standalone.modify_s(DN_CONFIG, [(ldap.MOD_REPLACE, 'nsslapd-dynamic-plugins', b'off')])
+ topology.standalone.restart()
+
+ # Now start the test
+ # Enable dynamic plugins
+ try:
+ topology.standalone.modify_s(DN_CONFIG, [(ldap.MOD_REPLACE, 'nsslapd-dynamic-plugins', b'on')])
+ except ldap.LDAPError as e:
+ log.error('Failed to enable dynamic plugin! {}'.format(e.args[0]['desc']))
+ assert False
+
+ # Enable retro changelog
+ topology.standalone.plugins.enable(name=PLUGIN_RETRO_CHANGELOG)
+
+ # Enbale content sync plugin
+ topology.standalone.plugins.enable(name=PLUGIN_REPL_SYNC)
+
+ # create a sync repl client and wait 5 seconds to be sure it is running
+ sync_repl = Sync_persist(topology.standalone)
+ sync_repl.start()
+ time.sleep(5)
+
+ # create users
+ users = UserAccounts(topology.standalone, DEFAULT_SUFFIX)
+ users_set = []
+ for i in range(10001, 10004):
+ users_set.append(users.create_test_user(uid=i))
+
+ time.sleep(10)
+ # delete users, that automember/memberof will generate nested updates
+ for user in users_set:
+ user.delete()
+ # stop the server to get the sync_repl result set (exit from while loop).
+ # Only way I found to acheive that.
+ # and wait a bit to let sync_repl thread time to set its result before fetching it.
+ topology.standalone.stop()
+ sync_repl.get_result()
+ sync_repl.join()
+ log.info('test_sync_repl_dynamic_plugin: PASS\n')
+
+ # Success
+ log.info('Test complete')
+
def test_sync_repl_invalid_cookie(topology, request):
"""Test sync_repl with invalid cookie
diff --git a/ldap/servers/plugins/sync/sync_persist.c b/ldap/servers/plugins/sync/sync_persist.c
index d2210b64c..283607361 100644
--- a/ldap/servers/plugins/sync/sync_persist.c
+++ b/ldap/servers/plugins/sync/sync_persist.c
@@ -156,6 +156,17 @@ ignore_op_pl(Slapi_PBlock *pb)
* This is the same for ident
*/
prim_op = get_thread_primary_op();
+ if (prim_op == NULL) {
+ /* This can happen if the PRE_OP (sync_update_persist_betxn_pre_op) was not called.
+ * The only known case it happens is with dynamic plugin enabled and an
+ * update that enable the sync_repl plugin. In such case sync_repl registers
+ * the postop (sync_update_persist_op) that is called while the preop was not called
+ */
+ slapi_log_err(SLAPI_LOG_PLUGIN, SYNC_PLUGIN_SUBSYSTEM,
+ "ignore_op_pl - Operation without primary op set (0x%lx)\n",
+ (ulong) op);
+ return;
+ }
ident = sync_persist_get_operation_extension(pb);
if (ident) {
@@ -232,8 +243,18 @@ sync_update_persist_op(Slapi_PBlock *pb, Slapi_Entry *e, Slapi_Entry *eprev, ber
prim_op = get_thread_primary_op();
+ if (prim_op == NULL) {
+ /* This can happen if the PRE_OP (sync_update_persist_betxn_pre_op) was not called.
+ * The only known case it happens is with dynamic plugin enabled and an
+ * update that enable the sync_repl plugin. In such case sync_repl registers
+ * the postop (sync_update_persist_op) that is called while the preop was not called
+ */
+ slapi_log_err(SLAPI_LOG_PLUGIN, SYNC_PLUGIN_SUBSYSTEM,
+ "sync_update_persist_op - Operation without primary op set (0x%lx)\n",
+ (ulong) pb_op);
+ return;
+ }
ident = sync_persist_get_operation_extension(pb);
- PR_ASSERT(prim_op);
if ((ident == NULL) && operation_is_flag_set(pb_op, OP_FLAG_NOOP)) {
/* This happens for URP (add cenotaph, fixup rename, tombstone resurrect)
--
2.43.0

@ -0,0 +1,840 @@
From 8dc61a176323f0d41df730abd715ccff3034c2be Mon Sep 17 00:00:00 2001
From: Mark Reynolds <mreynolds@redhat.com>
Date: Sun, 27 Nov 2022 09:37:19 -0500
Subject: [PATCH] Issue 5547 - automember plugin improvements
Description:
Rebuild task has the following improvements:
- Only one task allowed at a time
- Do not cleanup previous members by default. Add new CLI option to intentionally
cleanup memberships before rebuilding from scratch.
- Add better task logging to show fixup progress
To prevent automember from being called in a nested be_txn loop thread storage is
used to check and skip these loops.
relates: https://github.com/389ds/389-ds-base/issues/5547
Reviewed by: spichugi(Thanks!)
---
.../automember_plugin/automember_mod_test.py | 43 +++-
ldap/servers/plugins/automember/automember.c | 232 ++++++++++++++----
ldap/servers/slapd/back-ldbm/ldbm_add.c | 11 +-
ldap/servers/slapd/back-ldbm/ldbm_delete.c | 10 +-
ldap/servers/slapd/back-ldbm/ldbm_modify.c | 11 +-
.../lib389/cli_conf/plugins/automember.py | 10 +-
src/lib389/lib389/plugins.py | 7 +-
src/lib389/lib389/tasks.py | 9 +-
8 files changed, 250 insertions(+), 83 deletions(-)
diff --git a/dirsrvtests/tests/suites/automember_plugin/automember_mod_test.py b/dirsrvtests/tests/suites/automember_plugin/automember_mod_test.py
index 8d25384bf..7a0ed3275 100644
--- a/dirsrvtests/tests/suites/automember_plugin/automember_mod_test.py
+++ b/dirsrvtests/tests/suites/automember_plugin/automember_mod_test.py
@@ -5,12 +5,13 @@
# License: GPL (version 3 or any later version).
# See LICENSE for details.
# --- END COPYRIGHT BLOCK ---
-#
+import ldap
import logging
import pytest
import os
+import time
from lib389.utils import ds_is_older
-from lib389._constants import *
+from lib389._constants import DEFAULT_SUFFIX
from lib389.plugins import AutoMembershipPlugin, AutoMembershipDefinitions
from lib389.idm.user import UserAccounts
from lib389.idm.group import Groups
@@ -41,6 +42,11 @@ def automember_fixture(topo, request):
user_accts = UserAccounts(topo.standalone, DEFAULT_SUFFIX)
user = user_accts.create_test_user()
+ # Create extra users
+ users = UserAccounts(topo.standalone, DEFAULT_SUFFIX)
+ for i in range(0, 100):
+ users.create_test_user(uid=i)
+
# Create automember definitions and regex rules
automember_prop = {
'cn': 'testgroup_definition',
@@ -59,7 +65,7 @@ def automember_fixture(topo, request):
automemberplugin.enable()
topo.standalone.restart()
- return (user, groups)
+ return user, groups
def test_mods(automember_fixture, topo):
@@ -72,19 +78,21 @@ def test_mods(automember_fixture, topo):
2. Update user that should add it to group[1]
3. Update user that should add it to group[2]
4. Update user that should add it to group[0]
- 5. Test rebuild task correctly moves user to group[1]
+ 5. Test rebuild task adds user to group[1]
+ 6. Test rebuild task cleanups groups and only adds it to group[1]
:expectedresults:
1. Success
2. Success
3. Success
4. Success
5. Success
+ 6. Success
"""
(user, groups) = automember_fixture
# Update user which should go into group[0]
user.replace('cn', 'whatever')
- groups[0].is_member(user.dn)
+ assert groups[0].is_member(user.dn)
if groups[1].is_member(user.dn):
assert False
if groups[2].is_member(user.dn):
@@ -92,7 +100,7 @@ def test_mods(automember_fixture, topo):
# Update user0 which should go into group[1]
user.replace('cn', 'mark')
- groups[1].is_member(user.dn)
+ assert groups[1].is_member(user.dn)
if groups[0].is_member(user.dn):
assert False
if groups[2].is_member(user.dn):
@@ -100,7 +108,7 @@ def test_mods(automember_fixture, topo):
# Update user which should go into group[2]
user.replace('cn', 'simon')
- groups[2].is_member(user.dn)
+ assert groups[2].is_member(user.dn)
if groups[0].is_member(user.dn):
assert False
if groups[1].is_member(user.dn):
@@ -108,7 +116,7 @@ def test_mods(automember_fixture, topo):
# Update user which should go back into group[0] (full circle)
user.replace('cn', 'whatever')
- groups[0].is_member(user.dn)
+ assert groups[0].is_member(user.dn)
if groups[1].is_member(user.dn):
assert False
if groups[2].is_member(user.dn):
@@ -128,12 +136,24 @@ def test_mods(automember_fixture, topo):
automemberplugin.enable()
topo.standalone.restart()
- # Run rebuild task
+ # Run rebuild task (no cleanup)
task = automemberplugin.fixup(DEFAULT_SUFFIX, "objectclass=posixaccount")
+ with pytest.raises(ldap.UNWILLING_TO_PERFORM):
+ # test only one fixup task is allowed at a time
+ automemberplugin.fixup(DEFAULT_SUFFIX, "objectclass=top")
task.wait()
- # Test membership
- groups[1].is_member(user.dn)
+ # Test membership (user should still be in groups[0])
+ assert groups[1].is_member(user.dn)
+ if not groups[0].is_member(user.dn):
+ assert False
+
+ # Run rebuild task with cleanup
+ task = automemberplugin.fixup(DEFAULT_SUFFIX, "objectclass=posixaccount", cleanup=True)
+ task.wait()
+
+ # Test membership (user should only be in groups[1])
+ assert groups[1].is_member(user.dn)
if groups[0].is_member(user.dn):
assert False
if groups[2].is_member(user.dn):
@@ -148,4 +168,3 @@ if __name__ == '__main__':
# -s for DEBUG mode
CURRENT_FILE = os.path.realpath(__file__)
pytest.main(["-s", CURRENT_FILE])
-
diff --git a/ldap/servers/plugins/automember/automember.c b/ldap/servers/plugins/automember/automember.c
index 3494d0343..419adb052 100644
--- a/ldap/servers/plugins/automember/automember.c
+++ b/ldap/servers/plugins/automember/automember.c
@@ -1,5 +1,5 @@
/** BEGIN COPYRIGHT BLOCK
- * Copyright (C) 2011 Red Hat, Inc.
+ * Copyright (C) 2022 Red Hat, Inc.
* All rights reserved.
*
* License: GPL (version 3 or any later version).
@@ -14,7 +14,7 @@
* Auto Membership Plug-in
*/
#include "automember.h"
-
+#include <pthread.h>
/*
* Plug-in globals
@@ -22,7 +22,9 @@
static PRCList *g_automember_config = NULL;
static Slapi_RWLock *g_automember_config_lock = NULL;
static uint64_t abort_rebuild_task = 0;
-
+static pthread_key_t td_automem_block_nested;
+static PRBool fixup_running = PR_FALSE;
+static PRLock *fixup_lock = NULL;
static void *_PluginID = NULL;
static Slapi_DN *_PluginDN = NULL;
static Slapi_DN *_ConfigAreaDN = NULL;
@@ -93,9 +95,43 @@ static void automember_task_export_destructor(Slapi_Task *task);
static void automember_task_map_destructor(Slapi_Task *task);
#define DEFAULT_FILE_MODE PR_IRUSR | PR_IWUSR
+#define FIXUP_PROGRESS_LIMIT 1000
static uint64_t plugin_do_modify = 0;
static uint64_t plugin_is_betxn = 0;
+/* automember_plugin fixup task and add operations should block other be_txn
+ * plugins from calling automember_post_op_mod() */
+static int32_t
+slapi_td_block_nested_post_op(void)
+{
+ int32_t val = 12345;
+
+ if (pthread_setspecific(td_automem_block_nested, (void *)&val) != 0) {
+ return PR_FAILURE;
+ }
+ return PR_SUCCESS;
+}
+
+static int32_t
+slapi_td_unblock_nested_post_op(void)
+{
+ if (pthread_setspecific(td_automem_block_nested, NULL) != 0) {
+ return PR_FAILURE;
+ }
+ return PR_SUCCESS;
+}
+
+static int32_t
+slapi_td_is_post_op_nested(void)
+{
+ int32_t *value = pthread_getspecific(td_automem_block_nested);
+
+ if (value == NULL) {
+ return 0;
+ }
+ return 1;
+}
+
/*
* Config cache locking functions
*/
@@ -317,6 +353,14 @@ automember_start(Slapi_PBlock *pb)
return -1;
}
+ if (fixup_lock == NULL) {
+ if ((fixup_lock = PR_NewLock()) == NULL) {
+ slapi_log_err(SLAPI_LOG_ERR, AUTOMEMBER_PLUGIN_SUBSYSTEM,
+ "automember_start - Failed to create fixup lock.\n");
+ return -1;
+ }
+ }
+
/*
* Get the plug-in target dn from the system
* and store it for future use. */
@@ -360,6 +404,11 @@ automember_start(Slapi_PBlock *pb)
}
}
+ if (pthread_key_create(&td_automem_block_nested, NULL) != 0) {
+ slapi_log_err(SLAPI_LOG_ERR, AUTOMEMBER_PLUGIN_SUBSYSTEM,
+ "automember_start - pthread_key_create failed\n");
+ }
+
slapi_log_err(SLAPI_LOG_PLUGIN, AUTOMEMBER_PLUGIN_SUBSYSTEM,
"automember_start - ready for service\n");
slapi_log_err(SLAPI_LOG_TRACE, AUTOMEMBER_PLUGIN_SUBSYSTEM,
@@ -394,6 +443,8 @@ automember_close(Slapi_PBlock *pb __attribute__((unused)))
slapi_sdn_free(&_ConfigAreaDN);
slapi_destroy_rwlock(g_automember_config_lock);
g_automember_config_lock = NULL;
+ PR_DestroyLock(fixup_lock);
+ fixup_lock = NULL;
slapi_log_err(SLAPI_LOG_TRACE, AUTOMEMBER_PLUGIN_SUBSYSTEM,
"<-- automember_close\n");
@@ -1619,7 +1670,6 @@ out:
return rc;
}
-
/*
* automember_update_member_value()
*
@@ -1634,7 +1684,7 @@ automember_update_member_value(Slapi_Entry *member_e, const char *group_dn, char
LDAPMod *mods[2];
char *vals[2];
char *member_value = NULL;
- int rc = 0;
+ int rc = LDAP_SUCCESS;
Slapi_DN *group_sdn;
/* First thing check that the group still exists */
@@ -1653,7 +1703,7 @@ automember_update_member_value(Slapi_Entry *member_e, const char *group_dn, char
"automember_update_member_value - group (default or target) can not be retrieved (%s) err=%d\n",
group_dn, rc);
}
- return rc;
+ goto out;
}
/* If grouping_value is dn, we need to fetch the dn instead. */
@@ -1879,6 +1929,13 @@ automember_mod_post_op(Slapi_PBlock *pb)
PRCList *list = NULL;
int rc = SLAPI_PLUGIN_SUCCESS;
+ if (slapi_td_is_post_op_nested()) {
+ /* don't process op twice in the same thread */
+ return rc;
+ } else {
+ slapi_td_block_nested_post_op();
+ }
+
slapi_log_err(SLAPI_LOG_TRACE, AUTOMEMBER_PLUGIN_SUBSYSTEM,
"--> automember_mod_post_op\n");
@@ -2005,6 +2062,7 @@ automember_mod_post_op(Slapi_PBlock *pb)
}
}
}
+ slapi_td_unblock_nested_post_op();
slapi_log_err(SLAPI_LOG_TRACE, AUTOMEMBER_PLUGIN_SUBSYSTEM,
"<-- automember_mod_post_op (%d)\n", rc);
@@ -2024,6 +2082,13 @@ automember_add_post_op(Slapi_PBlock *pb)
slapi_log_err(SLAPI_LOG_TRACE, AUTOMEMBER_PLUGIN_SUBSYSTEM,
"--> automember_add_post_op\n");
+ if (slapi_td_is_post_op_nested()) {
+ /* don't process op twice in the same thread */
+ return rc;
+ } else {
+ slapi_td_block_nested_post_op();
+ }
+
/* Reload config if a config entry was added. */
if ((sdn = automember_get_sdn(pb))) {
if (automember_dn_is_config(sdn)) {
@@ -2039,7 +2104,7 @@ automember_add_post_op(Slapi_PBlock *pb)
/* If replication, just bail. */
if (automember_isrepl(pb)) {
- return SLAPI_PLUGIN_SUCCESS;
+ goto bail;
}
/* Get the newly added entry. */
@@ -2052,7 +2117,7 @@ automember_add_post_op(Slapi_PBlock *pb)
tombstone);
slapi_value_free(&tombstone);
if (is_tombstone) {
- return SLAPI_PLUGIN_SUCCESS;
+ goto bail;
}
/* Check if a config entry applies
@@ -2063,21 +2128,19 @@ automember_add_post_op(Slapi_PBlock *pb)
list = PR_LIST_HEAD(g_automember_config);
while (list != g_automember_config) {
config = (struct configEntry *)list;
-
/* Does the entry meet scope and filter requirements? */
if (slapi_dn_issuffix(slapi_sdn_get_dn(sdn), config->scope) &&
- (slapi_filter_test_simple(e, config->filter) == 0)) {
+ (slapi_filter_test_simple(e, config->filter) == 0))
+ {
/* Find out what membership changes are needed and make them. */
if (automember_update_membership(config, e, NULL) == SLAPI_PLUGIN_FAILURE) {
rc = SLAPI_PLUGIN_FAILURE;
break;
}
}
-
list = PR_NEXT_LINK(list);
}
}
-
automember_config_unlock();
} else {
slapi_log_err(SLAPI_LOG_PLUGIN, AUTOMEMBER_PLUGIN_SUBSYSTEM,
@@ -2098,6 +2161,7 @@ bail:
slapi_pblock_set(pb, SLAPI_RESULT_CODE, &result);
slapi_pblock_set(pb, SLAPI_PB_RESULT_TEXT, &errtxt);
}
+ slapi_td_unblock_nested_post_op();
return rc;
}
@@ -2138,6 +2202,7 @@ typedef struct _task_data
Slapi_DN *base_dn;
char *bind_dn;
int scope;
+ PRBool cleanup;
} task_data;
static void
@@ -2270,6 +2335,7 @@ automember_task_abort_thread(void *arg)
* basedn: dc=example,dc=com
* filter: (uid=*)
* scope: sub
+ * cleanup: yes/on (default is off)
*
* basedn and filter are required. If scope is omitted, the default is sub
*/
@@ -2284,9 +2350,22 @@ automember_task_add(Slapi_PBlock *pb, Slapi_Entry *e, Slapi_Entry *eAfter __attr
const char *base_dn;
const char *filter;
const char *scope;
+ const char *cleanup_str;
+ PRBool cleanup = PR_FALSE;
*returncode = LDAP_SUCCESS;
+ PR_Lock(fixup_lock);
+ if (fixup_running) {
+ PR_Unlock(fixup_lock);
+ *returncode = LDAP_UNWILLING_TO_PERFORM;
+ slapi_log_err(SLAPI_LOG_ERR, AUTOMEMBER_PLUGIN_SUBSYSTEM,
+ "automember_task_add - there is already a fixup task running\n");
+ rv = SLAPI_DSE_CALLBACK_ERROR;
+ goto out;
+ }
+ PR_Unlock(fixup_lock);
+
/*
* Grab the task params
*/
@@ -2300,6 +2379,12 @@ automember_task_add(Slapi_PBlock *pb, Slapi_Entry *e, Slapi_Entry *eAfter __attr
rv = SLAPI_DSE_CALLBACK_ERROR;
goto out;
}
+ if ((cleanup_str = slapi_entry_attr_get_ref(e, "cleanup"))) {
+ if (strcasecmp(cleanup_str, "yes") == 0 || strcasecmp(cleanup_str, "on")) {
+ cleanup = PR_TRUE;
+ }
+ }
+
scope = slapi_fetch_attr(e, "scope", "sub");
/*
* setup our task data
@@ -2315,6 +2400,7 @@ automember_task_add(Slapi_PBlock *pb, Slapi_Entry *e, Slapi_Entry *eAfter __attr
mytaskdata->bind_dn = slapi_ch_strdup(bind_dn);
mytaskdata->base_dn = slapi_sdn_new_dn_byval(base_dn);
mytaskdata->filter_str = slapi_ch_strdup(filter);
+ mytaskdata->cleanup = cleanup;
if (scope) {
if (strcasecmp(scope, "sub") == 0) {
@@ -2334,6 +2420,9 @@ automember_task_add(Slapi_PBlock *pb, Slapi_Entry *e, Slapi_Entry *eAfter __attr
task = slapi_plugin_new_task(slapi_entry_get_ndn(e), arg);
slapi_task_set_destructor_fn(task, automember_task_destructor);
slapi_task_set_data(task, mytaskdata);
+ PR_Lock(fixup_lock);
+ fixup_running = PR_TRUE;
+ PR_Unlock(fixup_lock);
/*
* Start the task as a separate thread
*/
@@ -2345,6 +2434,9 @@ automember_task_add(Slapi_PBlock *pb, Slapi_Entry *e, Slapi_Entry *eAfter __attr
"automember_task_add - Unable to create task thread!\n");
*returncode = LDAP_OPERATIONS_ERROR;
slapi_task_finish(task, *returncode);
+ PR_Lock(fixup_lock);
+ fixup_running = PR_FALSE;
+ PR_Unlock(fixup_lock);
rv = SLAPI_DSE_CALLBACK_ERROR;
} else {
rv = SLAPI_DSE_CALLBACK_OK;
@@ -2372,6 +2464,9 @@ automember_rebuild_task_thread(void *arg)
PRCList *list = NULL;
PRCList *include_list = NULL;
int result = 0;
+ int64_t fixup_progress_count = 0;
+ int64_t fixup_progress_elapsed = 0;
+ int64_t fixup_start_time = 0;
size_t i = 0;
/* Reset abort flag */
@@ -2380,6 +2475,7 @@ automember_rebuild_task_thread(void *arg)
if (!task) {
return; /* no task */
}
+
slapi_task_inc_refcount(task);
slapi_log_err(SLAPI_LOG_PLUGIN, AUTOMEMBER_PLUGIN_SUBSYSTEM,
"automember_rebuild_task_thread - Refcount incremented.\n");
@@ -2393,9 +2489,11 @@ automember_rebuild_task_thread(void *arg)
slapi_task_log_status(task, "Automember rebuild task starting (base dn: (%s) filter (%s)...",
slapi_sdn_get_dn(td->base_dn), td->filter_str);
/*
- * Set the bind dn in the local thread data
+ * Set the bind dn in the local thread data, and block post op mods
*/
slapi_td_set_dn(slapi_ch_strdup(td->bind_dn));
+ slapi_td_block_nested_post_op();
+ fixup_start_time = slapi_current_rel_time_t();
/*
* Take the config lock now and search the database
*/
@@ -2426,6 +2524,21 @@ automember_rebuild_task_thread(void *arg)
* Loop over the entries
*/
for (i = 0; entries && (entries[i] != NULL); i++) {
+ fixup_progress_count++;
+ if (fixup_progress_count % FIXUP_PROGRESS_LIMIT == 0 ) {
+ slapi_task_log_notice(task,
+ "Processed %ld entries in %ld seconds (+%ld seconds)",
+ fixup_progress_count,
+ slapi_current_rel_time_t() - fixup_start_time,
+ slapi_current_rel_time_t() - fixup_progress_elapsed);
+ slapi_task_log_status(task,
+ "Processed %ld entries in %ld seconds (+%ld seconds)",
+ fixup_progress_count,
+ slapi_current_rel_time_t() - fixup_start_time,
+ slapi_current_rel_time_t() - fixup_progress_elapsed);
+ slapi_task_inc_progress(task);
+ fixup_progress_elapsed = slapi_current_rel_time_t();
+ }
if (slapi_atomic_load_64(&abort_rebuild_task, __ATOMIC_ACQUIRE) == 1) {
/* The task was aborted */
slapi_task_log_notice(task, "Automember rebuild task was intentionally aborted");
@@ -2443,48 +2556,66 @@ automember_rebuild_task_thread(void *arg)
if (slapi_dn_issuffix(slapi_entry_get_dn(entries[i]), config->scope) &&
(slapi_filter_test_simple(entries[i], config->filter) == 0))
{
- /* First clear out all the defaults groups */
- for (size_t ii = 0; config->default_groups && config->default_groups[ii]; ii++) {
- if ((result = automember_update_member_value(entries[i], config->default_groups[ii],
- config->grouping_attr, config->grouping_value, NULL, DEL_MEMBER)))
- {
- slapi_task_log_notice(task, "Automember rebuild membership task unable to delete "
- "member from default group (%s) error (%d)",
- config->default_groups[ii], result);
- slapi_task_log_status(task, "Automember rebuild membership task unable to delete "
- "member from default group (%s) error (%d)",
- config->default_groups[ii], result);
- slapi_log_err(SLAPI_LOG_ERR, AUTOMEMBER_PLUGIN_SUBSYSTEM,
- "automember_rebuild_task_thread - Unable to unable to delete from (%s) error (%d)\n",
- config->default_groups[ii], result);
- goto out;
- }
- }
-
- /* Then clear out the non-default group */
- if (config->inclusive_rules && !PR_CLIST_IS_EMPTY((PRCList *)config->inclusive_rules)) {
- include_list = PR_LIST_HEAD((PRCList *)config->inclusive_rules);
- while (include_list != (PRCList *)config->inclusive_rules) {
- struct automemberRegexRule *curr_rule = (struct automemberRegexRule *)include_list;
- if ((result = automember_update_member_value(entries[i], slapi_sdn_get_dn(curr_rule->target_group_dn),
- config->grouping_attr, config->grouping_value, NULL, DEL_MEMBER)))
+ if (td->cleanup) {
+
+ slapi_log_err(SLAPI_LOG_PLUGIN, AUTOMEMBER_PLUGIN_SUBSYSTEM,
+ "automember_rebuild_task_thread - Cleaning up groups (config %s)\n",
+ config->dn);
+ /* First clear out all the defaults groups */
+ for (size_t ii = 0; config->default_groups && config->default_groups[ii]; ii++) {
+ if ((result = automember_update_member_value(entries[i],
+ config->default_groups[ii],
+ config->grouping_attr,
+ config->grouping_value,
+ NULL, DEL_MEMBER)))
{
slapi_task_log_notice(task, "Automember rebuild membership task unable to delete "
- "member from group (%s) error (%d)",
- slapi_sdn_get_dn(curr_rule->target_group_dn), result);
+ "member from default group (%s) error (%d)",
+ config->default_groups[ii], result);
slapi_task_log_status(task, "Automember rebuild membership task unable to delete "
- "member from group (%s) error (%d)",
- slapi_sdn_get_dn(curr_rule->target_group_dn), result);
+ "member from default group (%s) error (%d)",
+ config->default_groups[ii], result);
slapi_log_err(SLAPI_LOG_ERR, AUTOMEMBER_PLUGIN_SUBSYSTEM,
"automember_rebuild_task_thread - Unable to unable to delete from (%s) error (%d)\n",
- slapi_sdn_get_dn(curr_rule->target_group_dn), result);
+ config->default_groups[ii], result);
goto out;
}
- include_list = PR_NEXT_LINK(include_list);
}
+
+ /* Then clear out the non-default group */
+ if (config->inclusive_rules && !PR_CLIST_IS_EMPTY((PRCList *)config->inclusive_rules)) {
+ include_list = PR_LIST_HEAD((PRCList *)config->inclusive_rules);
+ while (include_list != (PRCList *)config->inclusive_rules) {
+ struct automemberRegexRule *curr_rule = (struct automemberRegexRule *)include_list;
+ if ((result = automember_update_member_value(entries[i],
+ slapi_sdn_get_dn(curr_rule->target_group_dn),
+ config->grouping_attr,
+ config->grouping_value,
+ NULL, DEL_MEMBER)))
+ {
+ slapi_task_log_notice(task, "Automember rebuild membership task unable to delete "
+ "member from group (%s) error (%d)",
+ slapi_sdn_get_dn(curr_rule->target_group_dn), result);
+ slapi_task_log_status(task, "Automember rebuild membership task unable to delete "
+ "member from group (%s) error (%d)",
+ slapi_sdn_get_dn(curr_rule->target_group_dn), result);
+ slapi_log_err(SLAPI_LOG_ERR, AUTOMEMBER_PLUGIN_SUBSYSTEM,
+ "automember_rebuild_task_thread - Unable to unable to delete from (%s) error (%d)\n",
+ slapi_sdn_get_dn(curr_rule->target_group_dn), result);
+ goto out;
+ }
+ include_list = PR_NEXT_LINK(include_list);
+ }
+ }
+ slapi_log_err(SLAPI_LOG_PLUGIN, AUTOMEMBER_PLUGIN_SUBSYSTEM,
+ "automember_rebuild_task_thread - Finished cleaning up groups (config %s)\n",
+ config->dn);
}
/* Update the memberships for this entries */
+ slapi_log_err(SLAPI_LOG_PLUGIN, AUTOMEMBER_PLUGIN_SUBSYSTEM,
+ "automember_rebuild_task_thread - Updating membership (config %s)\n",
+ config->dn);
if (slapi_is_shutting_down() ||
automember_update_membership(config, entries[i], NULL) == SLAPI_PLUGIN_FAILURE)
{
@@ -2508,15 +2639,22 @@ out:
slapi_task_log_notice(task, "Automember rebuild task aborted. Error (%d)", result);
slapi_task_log_status(task, "Automember rebuild task aborted. Error (%d)", result);
} else {
- slapi_task_log_notice(task, "Automember rebuild task finished. Processed (%d) entries.", (int32_t)i);
- slapi_task_log_status(task, "Automember rebuild task finished. Processed (%d) entries.", (int32_t)i);
+ slapi_task_log_notice(task, "Automember rebuild task finished. Processed (%ld) entries in %ld seconds",
+ (int64_t)i, slapi_current_rel_time_t() - fixup_start_time);
+ slapi_task_log_status(task, "Automember rebuild task finished. Processed (%ld) entries in %ld seconds",
+ (int64_t)i, slapi_current_rel_time_t() - fixup_start_time);
}
slapi_task_inc_progress(task);
slapi_task_finish(task, result);
slapi_task_dec_refcount(task);
slapi_atomic_store_64(&abort_rebuild_task, 0, __ATOMIC_RELEASE);
+ slapi_td_unblock_nested_post_op();
+ PR_Lock(fixup_lock);
+ fixup_running = PR_FALSE;
+ PR_Unlock(fixup_lock);
+
slapi_log_err(SLAPI_LOG_PLUGIN, AUTOMEMBER_PLUGIN_SUBSYSTEM,
- "automember_rebuild_task_thread - Refcount decremented.\n");
+ "automember_rebuild_task_thread - task finished, refcount decremented.\n");
}
/*
diff --git a/ldap/servers/slapd/back-ldbm/ldbm_add.c b/ldap/servers/slapd/back-ldbm/ldbm_add.c
index ba2d73a84..ce4c314a1 100644
--- a/ldap/servers/slapd/back-ldbm/ldbm_add.c
+++ b/ldap/servers/slapd/back-ldbm/ldbm_add.c
@@ -1,6 +1,6 @@
/** BEGIN COPYRIGHT BLOCK
* Copyright (C) 2001 Sun Microsystems, Inc. Used by permission.
- * Copyright (C) 2005 Red Hat, Inc.
+ * Copyright (C) 2022 Red Hat, Inc.
* Copyright (C) 2009 Hewlett-Packard Development Company, L.P.
* All rights reserved.
*
@@ -1264,10 +1264,6 @@ ldbm_back_add(Slapi_PBlock *pb)
goto common_return;
error_return:
- /* Revert the caches if this is the parent operation */
- if (parent_op && betxn_callback_fails) {
- revert_cache(inst, &parent_time);
- }
if (addingentry_id_assigned) {
next_id_return(be, addingentry->ep_id);
}
@@ -1376,6 +1372,11 @@ diskfull_return:
if (!not_an_error) {
rc = SLAPI_FAIL_GENERAL;
}
+
+ /* Revert the caches if this is the parent operation */
+ if (parent_op && betxn_callback_fails) {
+ revert_cache(inst, &parent_time);
+ }
}
common_return:
diff --git a/ldap/servers/slapd/back-ldbm/ldbm_delete.c b/ldap/servers/slapd/back-ldbm/ldbm_delete.c
index de23190c3..27f0ac58a 100644
--- a/ldap/servers/slapd/back-ldbm/ldbm_delete.c
+++ b/ldap/servers/slapd/back-ldbm/ldbm_delete.c
@@ -1407,11 +1407,6 @@ commit_return:
goto common_return;
error_return:
- /* Revert the caches if this is the parent operation */
- if (parent_op && betxn_callback_fails) {
- revert_cache(inst, &parent_time);
- }
-
if (tombstone) {
if (cache_is_in_cache(&inst->inst_cache, tombstone)) {
tomb_ep_id = tombstone->ep_id; /* Otherwise, tombstone might have been freed. */
@@ -1496,6 +1491,11 @@ error_return:
conn_id, op_id, parent_modify_c.old_entry, parent_modify_c.new_entry, myrc);
}
+ /* Revert the caches if this is the parent operation */
+ if (parent_op && betxn_callback_fails) {
+ revert_cache(inst, &parent_time);
+ }
+
common_return:
if (orig_entry) {
/* NOTE: #define SLAPI_DELETE_BEPREOP_ENTRY SLAPI_ENTRY_PRE_OP */
diff --git a/ldap/servers/slapd/back-ldbm/ldbm_modify.c b/ldap/servers/slapd/back-ldbm/ldbm_modify.c
index 537369055..64b293001 100644
--- a/ldap/servers/slapd/back-ldbm/ldbm_modify.c
+++ b/ldap/servers/slapd/back-ldbm/ldbm_modify.c
@@ -1,6 +1,6 @@
/** BEGIN COPYRIGHT BLOCK
* Copyright (C) 2001 Sun Microsystems, Inc. Used by permission.
- * Copyright (C) 2005 Red Hat, Inc.
+ * Copyright (C) 2022 Red Hat, Inc.
* Copyright (C) 2009 Hewlett-Packard Development Company, L.P.
* All rights reserved.
*
@@ -1043,11 +1043,6 @@ ldbm_back_modify(Slapi_PBlock *pb)
goto common_return;
error_return:
- /* Revert the caches if this is the parent operation */
- if (parent_op && betxn_callback_fails) {
- revert_cache(inst, &parent_time);
- }
-
if (postentry != NULL) {
slapi_entry_free(postentry);
postentry = NULL;
@@ -1103,6 +1098,10 @@ error_return:
if (!not_an_error) {
rc = SLAPI_FAIL_GENERAL;
}
+ /* Revert the caches if this is the parent operation */
+ if (parent_op && betxn_callback_fails) {
+ revert_cache(inst, &parent_time);
+ }
}
/* if ec is in cache, remove it, then add back e if we still have it */
diff --git a/src/lib389/lib389/cli_conf/plugins/automember.py b/src/lib389/lib389/cli_conf/plugins/automember.py
index 15b00c633..568586ad8 100644
--- a/src/lib389/lib389/cli_conf/plugins/automember.py
+++ b/src/lib389/lib389/cli_conf/plugins/automember.py
@@ -155,7 +155,7 @@ def fixup(inst, basedn, log, args):
log.info('Attempting to add task entry... This will fail if Automembership plug-in is not enabled.')
if not plugin.status():
log.error("'%s' is disabled. Rebuild membership task can't be executed" % plugin.rdn)
- fixup_task = plugin.fixup(args.DN, args.filter)
+ fixup_task = plugin.fixup(args.DN, args.filter, args.cleanup)
if args.wait:
log.info(f'Waiting for fixup task "{fixup_task.dn}" to complete. You can safely exit by pressing Control C ...')
fixup_task.wait(timeout=args.timeout)
@@ -225,8 +225,8 @@ def create_parser(subparsers):
subcommands = automember.add_subparsers(help='action')
add_generic_plugin_parsers(subcommands, AutoMembershipPlugin)
- list = subcommands.add_parser('list', help='List Automembership definitions or regex rules.')
- subcommands_list = list.add_subparsers(help='action')
+ automember_list = subcommands.add_parser('list', help='List Automembership definitions or regex rules.')
+ subcommands_list = automember_list.add_subparsers(help='action')
list_definitions = subcommands_list.add_parser('definitions', help='Lists Automembership definitions.')
list_definitions.set_defaults(func=definition_list)
list_regexes = subcommands_list.add_parser('regexes', help='List Automembership regex rules.')
@@ -269,6 +269,8 @@ def create_parser(subparsers):
fixup_task.add_argument('-f', '--filter', required=True, help='Sets the LDAP filter for entries to fix up')
fixup_task.add_argument('-s', '--scope', required=True, choices=['sub', 'base', 'one'], type=str.lower,
help='Sets the LDAP search scope for entries to fix up')
+ fixup_task.add_argument('--cleanup', action='store_true',
+ help="Clean up previous group memberships before rebuilding")
fixup_task.add_argument('--wait', action='store_true',
help="Wait for the task to finish, this could take a long time")
fixup_task.add_argument('--timeout', default=0, type=int,
@@ -279,7 +281,7 @@ def create_parser(subparsers):
fixup_status.add_argument('--dn', help="The task entry's DN")
fixup_status.add_argument('--show-log', action='store_true', help="Display the task log")
fixup_status.add_argument('--watch', action='store_true',
- help="Watch the task's status and wait for it to finish")
+ help="Watch the task's status and wait for it to finish")
abort_fixup = subcommands.add_parser('abort-fixup', help='Abort the rebuild membership task.')
abort_fixup.set_defaults(func=abort)
diff --git a/src/lib389/lib389/plugins.py b/src/lib389/lib389/plugins.py
index 52691a44c..a1ad0a45b 100644
--- a/src/lib389/lib389/plugins.py
+++ b/src/lib389/lib389/plugins.py
@@ -1141,13 +1141,15 @@ class AutoMembershipPlugin(Plugin):
def __init__(self, instance, dn="cn=Auto Membership Plugin,cn=plugins,cn=config"):
super(AutoMembershipPlugin, self).__init__(instance, dn)
- def fixup(self, basedn, _filter=None):
+ def fixup(self, basedn, _filter=None, cleanup=False):
"""Create an automember rebuild membership task
:param basedn: Basedn to fix up
:type basedn: str
:param _filter: a filter for entries to fix up
:type _filter: str
+ :param cleanup: cleanup old group memberships
+ :type cleanup: boolean
:returns: an instance of Task(DSLdapObject)
"""
@@ -1156,6 +1158,9 @@ class AutoMembershipPlugin(Plugin):
task_properties = {'basedn': basedn}
if _filter is not None:
task_properties['filter'] = _filter
+ if cleanup:
+ task_properties['cleanup'] = "yes"
+
task.create(properties=task_properties)
return task
diff --git a/src/lib389/lib389/tasks.py b/src/lib389/lib389/tasks.py
index 1a16bbb83..193805780 100644
--- a/src/lib389/lib389/tasks.py
+++ b/src/lib389/lib389/tasks.py
@@ -1006,12 +1006,13 @@ class Tasks(object):
return exitCode
def automemberRebuild(self, suffix=DEFAULT_SUFFIX, scope='sub',
- filterstr='objectclass=top', args=None):
+ filterstr='objectclass=top', cleanup=False, args=None):
'''
- @param suffix - The suffix the task should examine - defualt is
+ @param suffix - The suffix the task should examine - default is
"dc=example,dc=com"
@param scope - The scope of the search to find entries
- @param fitlerstr - THe search filter to find entries
+ @param fitlerstr - The search filter to find entries
+ @param cleanup - reset/clear the old group mmeberships prior to rebuilding
@param args - is a dictionary that contains modifier of the task
wait: True/[False] - If True, waits for the completion of
the task before to return
@@ -1027,6 +1028,8 @@ class Tasks(object):
entry.setValues('basedn', suffix)
entry.setValues('filter', filterstr)
entry.setValues('scope', scope)
+ if cleanup:
+ entry.setValues('cleanup', 'yes')
# start the task and possibly wait for task completion
try:
--
2.43.0

@ -0,0 +1,83 @@
From 9319d5b022918f14cacb00e3faef85a6ab730a26 Mon Sep 17 00:00:00 2001
From: Simon Pichugin <spichugi@redhat.com>
Date: Tue, 27 Feb 2024 16:30:47 -0800
Subject: [PATCH] Issue 3527 - Support HAProxy and Instance on the same machine
configuration (#6107)
Description: Improve how we handle HAProxy connections to work better when
the DS and HAProxy are on the same machine.
Ensure the client and header destination IPs are checked against the trusted IP list.
Additionally, this change will also allow configuration having
HAProxy is listening on a different subnet than the one used to forward the request.
Related: https://github.com/389ds/389-ds-base/issues/3527
Reviewed by: @progier389, @jchapma (Thanks!)
---
ldap/servers/slapd/connection.c | 35 +++++++++++++++++++++++++--------
1 file changed, 27 insertions(+), 8 deletions(-)
diff --git a/ldap/servers/slapd/connection.c b/ldap/servers/slapd/connection.c
index d28a39bf7..10a8cc577 100644
--- a/ldap/servers/slapd/connection.c
+++ b/ldap/servers/slapd/connection.c
@@ -1187,6 +1187,8 @@ connection_read_operation(Connection *conn, Operation *op, ber_tag_t *tag, int *
char str_ip[INET6_ADDRSTRLEN + 1] = {0};
char str_haproxy_ip[INET6_ADDRSTRLEN + 1] = {0};
char str_haproxy_destip[INET6_ADDRSTRLEN + 1] = {0};
+ int trusted_matches_ip_found = 0;
+ int trusted_matches_destip_found = 0;
struct berval **bvals = NULL;
int proxy_connection = 0;
@@ -1245,21 +1247,38 @@ connection_read_operation(Connection *conn, Operation *op, ber_tag_t *tag, int *
normalize_IPv4(conn->cin_addr, buf_ip, sizeof(buf_ip), str_ip, sizeof(str_ip));
normalize_IPv4(&pr_netaddr_dest, buf_haproxy_destip, sizeof(buf_haproxy_destip),
str_haproxy_destip, sizeof(str_haproxy_destip));
+ size_t ip_len = strlen(buf_ip);
+ size_t destip_len = strlen(buf_haproxy_destip);
/* Now, reset RC and set it to 0 only if a match is found */
haproxy_rc = -1;
- /* Allow only:
- * Trusted IP == Original Client IP == HAProxy Header Destination IP */
+ /*
+ * We need to allow a configuration where DS instance and HAProxy are on the same machine.
+ * In this case, we need to check if
+ * the HAProxy client IP (which will be a loopback address) matches one of the the trusted IP addresses,
+ * while still checking that
+ * the HAProxy header destination IP address matches one of the trusted IP addresses.
+ * Additionally, this change will also allow configuration having
+ * HAProxy listening on a different subnet than one used to forward the request.
+ */
for (size_t i = 0; bvals[i] != NULL; ++i) {
- if ((strlen(bvals[i]->bv_val) == strlen(buf_ip)) &&
- (strlen(bvals[i]->bv_val) == strlen(buf_haproxy_destip)) &&
- (strncasecmp(bvals[i]->bv_val, buf_ip, strlen(buf_ip)) == 0) &&
- (strncasecmp(bvals[i]->bv_val, buf_haproxy_destip, strlen(buf_haproxy_destip)) == 0)) {
- haproxy_rc = 0;
- break;
+ size_t bval_len = strlen(bvals[i]->bv_val);
+
+ /* Check if the Client IP (HAProxy's machine IP) address matches the trusted IP address */
+ if (!trusted_matches_ip_found) {
+ trusted_matches_ip_found = (bval_len == ip_len) && (strncasecmp(bvals[i]->bv_val, buf_ip, ip_len) == 0);
+ }
+ /* Check if the HAProxy header destination IP address matches the trusted IP address */
+ if (!trusted_matches_destip_found) {
+ trusted_matches_destip_found = (bval_len == destip_len) && (strncasecmp(bvals[i]->bv_val, buf_haproxy_destip, destip_len) == 0);
}
}
+
+ if (trusted_matches_ip_found && trusted_matches_destip_found) {
+ haproxy_rc = 0;
+ }
+
if (haproxy_rc == -1) {
slapi_log_err(SLAPI_LOG_CONNS, "connection_read_operation", "HAProxy header received from unknown source.\n");
disconnect_server_nomutex(conn, conn->c_connid, -1, SLAPD_DISCONNECT_PROXY_UNKNOWN, EPROTO);
--
2.45.0

@ -0,0 +1,108 @@
From 016a2b6bd3e27cbff36609824a75b020dfd24823 Mon Sep 17 00:00:00 2001
From: James Chapman <jachapma@redhat.com>
Date: Wed, 1 May 2024 15:01:33 +0100
Subject: [PATCH] CVE-2024-2199
---
.../tests/suites/password/password_test.py | 56 +++++++++++++++++++
ldap/servers/slapd/modify.c | 8 ++-
2 files changed, 62 insertions(+), 2 deletions(-)
diff --git a/dirsrvtests/tests/suites/password/password_test.py b/dirsrvtests/tests/suites/password/password_test.py
index 38079476a..b3ff08904 100644
--- a/dirsrvtests/tests/suites/password/password_test.py
+++ b/dirsrvtests/tests/suites/password/password_test.py
@@ -65,6 +65,62 @@ def test_password_delete_specific_password(topology_st):
log.info('test_password_delete_specific_password: PASSED')
+def test_password_modify_non_utf8(topology_st):
+ """Attempt a modify of the userPassword attribute with
+ an invalid non utf8 value
+
+ :id: a31af9d5-d665-42b9-8d6e-fea3d0837d36
+ :setup: Standalone instance
+ :steps:
+ 1. Add a user if it doesnt exist and set its password
+ 2. Verify password with a bind
+ 3. Modify userPassword attr with invalid value
+ 4. Attempt a bind with invalid password value
+ 5. Verify original password with a bind
+ :expectedresults:
+ 1. The user with userPassword should be added successfully
+ 2. Operation should be successful
+ 3. Server returns ldap.UNWILLING_TO_PERFORM
+ 4. Server returns ldap.INVALID_CREDENTIALS
+ 5. Operation should be successful
+ """
+
+ log.info('Running test_password_modify_non_utf8...')
+
+ # Create user and set password
+ standalone = topology_st.standalone
+ users = UserAccounts(standalone, DEFAULT_SUFFIX)
+ if not users.exists(TEST_USER_PROPERTIES['uid'][0]):
+ user = users.create(properties=TEST_USER_PROPERTIES)
+ else:
+ user = users.get(TEST_USER_PROPERTIES['uid'][0])
+ user.set('userpassword', PASSWORD)
+
+ # Verify password
+ try:
+ user.bind(PASSWORD)
+ except ldap.LDAPError as e:
+ log.fatal('Failed to bind as {}, error: '.format(user.dn) + e.args[0]['desc'])
+ assert False
+
+ # Modify userPassword with an invalid value
+ password = b'tes\x82t-password' # A non UTF-8 encoded password
+ with pytest.raises(ldap.UNWILLING_TO_PERFORM):
+ user.replace('userpassword', password)
+
+ # Verify a bind fails with invalid pasword
+ with pytest.raises(ldap.INVALID_CREDENTIALS):
+ user.bind(password)
+
+ # Verify we can still bind with original password
+ try:
+ user.bind(PASSWORD)
+ except ldap.LDAPError as e:
+ log.fatal('Failed to bind as {}, error: '.format(user.dn) + e.args[0]['desc'])
+ assert False
+
+ log.info('test_password_modify_non_utf8: PASSED')
+
if __name__ == '__main__':
# Run isolated
# -s for DEBUG mode
diff --git a/ldap/servers/slapd/modify.c b/ldap/servers/slapd/modify.c
index 5ca78539c..669bb104c 100644
--- a/ldap/servers/slapd/modify.c
+++ b/ldap/servers/slapd/modify.c
@@ -765,8 +765,10 @@ op_shared_modify(Slapi_PBlock *pb, int pw_change, char *old_pw)
* flagged - leave mod attributes alone */
if (!repl_op && !skip_modified_attrs && lastmod) {
modify_update_last_modified_attr(pb, &smods);
+ slapi_pblock_set(pb, SLAPI_MODIFY_MODS, slapi_mods_get_ldapmods_byref(&smods));
}
+
if (0 == slapi_mods_get_num_mods(&smods)) {
/* nothing to do - no mods - this is not an error - just
send back LDAP_SUCCESS */
@@ -933,8 +935,10 @@ op_shared_modify(Slapi_PBlock *pb, int pw_change, char *old_pw)
/* encode password */
if (pw_encodevals_ext(pb, sdn, va)) {
- slapi_log_err(SLAPI_LOG_CRIT, "op_shared_modify", "Unable to hash userPassword attribute for %s.\n", slapi_entry_get_dn_const(e));
- send_ldap_result(pb, LDAP_UNWILLING_TO_PERFORM, NULL, "Unable to store attribute \"userPassword\" correctly\n", 0, NULL);
+ slapi_log_err(SLAPI_LOG_CRIT, "op_shared_modify", "Unable to hash userPassword attribute for %s, "
+ "check value is utf8 string.\n", slapi_entry_get_dn_const(e));
+ send_ldap_result(pb, LDAP_UNWILLING_TO_PERFORM, NULL, "Unable to hash \"userPassword\" attribute, "
+ "check value is utf8 string.\n", 0, NULL);
valuearray_free(&va);
goto free_and_return;
}
--
2.45.0

@ -0,0 +1,213 @@
From d5bbe52fbe84a7d3b5938bf82d5c4af15061a8e2 Mon Sep 17 00:00:00 2001
From: Pierre Rogier <progier@redhat.com>
Date: Wed, 17 Apr 2024 18:18:04 +0200
Subject: [PATCH] CVE-2024-3657
---
.../tests/suites/filter/large_filter_test.py | 34 +++++-
ldap/servers/slapd/back-ldbm/index.c | 111 ++++++++++--------
2 files changed, 92 insertions(+), 53 deletions(-)
diff --git a/dirsrvtests/tests/suites/filter/large_filter_test.py b/dirsrvtests/tests/suites/filter/large_filter_test.py
index ecc7bf979..40526bb16 100644
--- a/dirsrvtests/tests/suites/filter/large_filter_test.py
+++ b/dirsrvtests/tests/suites/filter/large_filter_test.py
@@ -13,19 +13,29 @@ verify and testing Filter from a search
import os
import pytest
+import ldap
-from lib389._constants import PW_DM
+from lib389._constants import PW_DM, DEFAULT_SUFFIX, ErrorLog
from lib389.topologies import topology_st as topo
from lib389.idm.user import UserAccounts, UserAccount
from lib389.idm.account import Accounts
from lib389.backend import Backends
from lib389.idm.domain import Domain
+from lib389.utils import get_ldapurl_from_serverid
SUFFIX = 'dc=anuj,dc=com'
pytestmark = pytest.mark.tier1
+def open_new_ldapi_conn(dsinstance):
+ ldapurl, certdir = get_ldapurl_from_serverid(dsinstance)
+ assert 'ldapi://' in ldapurl
+ conn = ldap.initialize(ldapurl)
+ conn.sasl_interactive_bind_s("", ldap.sasl.external())
+ return conn
+
+
@pytest.fixture(scope="module")
def _create_entries(request, topo):
"""
@@ -160,6 +170,28 @@ def test_large_filter(topo, _create_entries, real_value):
assert len(Accounts(conn, SUFFIX).filter(real_value)) == 3
+def test_long_filter_value(topo):
+ """Exercise large eq filter with dn syntax attributes
+
+ :id: b069ef72-fcc3-11ee-981c-482ae39447e5
+ :setup: Standalone
+ :steps:
+ 1. Try to pass filter rules as per the condition.
+ :expectedresults:
+ 1. Pass
+ """
+ inst = topo.standalone
+ conn = open_new_ldapi_conn(inst.serverid)
+ inst.config.loglevel(vals=(ErrorLog.DEFAULT,ErrorLog.TRACE,ErrorLog.SEARCH_FILTER))
+ filter_value = "a\x1Edmin" * 1025
+ conn.search_s(DEFAULT_SUFFIX, ldap.SCOPE_SUBTREE, f'(cn={filter_value})')
+ filter_value = "aAdmin" * 1025
+ conn.search_s(DEFAULT_SUFFIX, ldap.SCOPE_SUBTREE, f'(cn={filter_value})')
+ filter_value = "*"
+ conn.search_s(DEFAULT_SUFFIX, ldap.SCOPE_SUBTREE, f'(cn={filter_value})')
+ inst.config.loglevel(vals=(ErrorLog.DEFAULT,))
+
+
if __name__ == '__main__':
CURRENT_FILE = os.path.realpath(__file__)
pytest.main("-s -v %s" % CURRENT_FILE)
diff --git a/ldap/servers/slapd/back-ldbm/index.c b/ldap/servers/slapd/back-ldbm/index.c
index 410db23d1..30fa09ebb 100644
--- a/ldap/servers/slapd/back-ldbm/index.c
+++ b/ldap/servers/slapd/back-ldbm/index.c
@@ -71,6 +71,32 @@ typedef struct _index_buffer_handle index_buffer_handle;
#define INDEX_BUFFER_FLAG_SERIALIZE 1
#define INDEX_BUFFER_FLAG_STATS 2
+/*
+ * space needed to encode a byte:
+ * 0x00-0x31 and 0x7f-0xff requires 3 bytes: \xx
+ * 0x22 and 0x5C requires 2 bytes: \" and \\
+ * other requires 1 byte: c
+ */
+static char encode_size[] = {
+ /* 0x00 */ 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
+ /* 0x10 */ 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
+ /* 0x20 */ 1, 1, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ /* 0x30 */ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ /* 0x40 */ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ /* 0x50 */ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 1, 1, 1,
+ /* 0x60 */ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ /* 0x70 */ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 3,
+ /* 0x80 */ 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
+ /* 0x90 */ 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
+ /* 0xA0 */ 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
+ /* 0xB0 */ 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
+ /* 0xC0 */ 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
+ /* 0xD0 */ 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
+ /* 0xE0 */ 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
+ /* 0xF0 */ 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
+};
+
+
/* Index buffering functions */
static int
@@ -799,65 +825,46 @@ index_add_mods(
/*
* Convert a 'struct berval' into a displayable ASCII string
+ * returns the printable string
*/
-
-#define SPECIAL(c) (c < 32 || c > 126 || c == '\\' || c == '"')
-
const char *
encode(const struct berval *data, char buf[BUFSIZ])
{
- char *s;
- char *last;
- if (data == NULL || data->bv_len == 0)
- return "";
- last = data->bv_val + data->bv_len - 1;
- for (s = data->bv_val; s < last; ++s) {
- if (SPECIAL(*s)) {
- char *first = data->bv_val;
- char *bufNext = buf;
- size_t bufSpace = BUFSIZ - 4;
- while (1) {
- /* printf ("%lu bytes ASCII\n", (unsigned long)(s - first)); */
- if (bufSpace < (size_t)(s - first))
- s = first + bufSpace - 1;
- if (s != first) {
- memcpy(bufNext, first, s - first);
- bufNext += (s - first);
- bufSpace -= (s - first);
- }
- do {
- if (bufSpace) {
- *bufNext++ = '\\';
- --bufSpace;
- }
- if (bufSpace < 2) {
- memcpy(bufNext, "..", 2);
- bufNext += 2;
- goto bail;
- }
- if (*s == '\\' || *s == '"') {
- *bufNext++ = *s;
- --bufSpace;
- } else {
- sprintf(bufNext, "%02x", (unsigned)*(unsigned char *)s);
- bufNext += 2;
- bufSpace -= 2;
- }
- } while (++s <= last && SPECIAL(*s));
- if (s > last)
- break;
- first = s;
- while (!SPECIAL(*s) && s <= last)
- ++s;
- }
- bail:
- *bufNext = '\0';
- /* printf ("%lu chars in buffer\n", (unsigned long)(bufNext - buf)); */
+ if (!data || !data->bv_val) {
+ strcpy(buf, "<NULL>");
+ return buf;
+ }
+ char *endbuff = &buf[BUFSIZ-4]; /* Reserve space to append "...\0" */
+ char *ptout = buf;
+ unsigned char *ptin = (unsigned char*) data->bv_val;
+ unsigned char *endptin = ptin+data->bv_len;
+
+ while (ptin < endptin) {
+ if (ptout >= endbuff) {
+ /*
+ * BUFSIZ(8K) > SLAPI_LOG_BUFSIZ(2K) so the error log message will be
+ * truncated anyway. So there is no real interrest to test if the original
+ * data contains no special characters and return it as is.
+ */
+ strcpy(endbuff, "...");
return buf;
}
+ switch (encode_size[*ptin]) {
+ case 1:
+ *ptout++ = *ptin++;
+ break;
+ case 2:
+ *ptout++ = '\\';
+ *ptout++ = *ptin++;
+ break;
+ case 3:
+ sprintf(ptout, "\\%02x", *ptin++);
+ ptout += 3;
+ break;
+ }
}
- /* printf ("%lu bytes, all ASCII\n", (unsigned long)(s - data->bv_val)); */
- return data->bv_val;
+ *ptout = 0;
+ return buf;
}
static const char *
--
2.45.0

@ -0,0 +1,143 @@
From 6e5f03d5872129963106024f53765234a282406c Mon Sep 17 00:00:00 2001
From: James Chapman <jachapma@redhat.com>
Date: Fri, 16 Feb 2024 11:13:16 +0000
Subject: [PATCH] Issue 6096 - Improve connection timeout error logging (#6097)
Bug description: When a paged result search is run with a time limit,
if the time limit is exceed the server closes the connection with
closed IO timeout (nsslapd-ioblocktimeout) - T2. This error message
is incorrect as the reason the connection has been closed was because
the specified time limit on a paged result search has been exceeded.
Fix description: Correct error message
Relates: https://github.com/389ds/389-ds-base/issues/6096
Reviewed by: @tbordaz (Thank you)
---
ldap/admin/src/logconv.pl | 24 ++++++++++++++++++-
ldap/servers/slapd/daemon.c | 4 ++--
ldap/servers/slapd/disconnect_error_strings.h | 1 +
ldap/servers/slapd/disconnect_errors.h | 2 +-
4 files changed, 27 insertions(+), 4 deletions(-)
diff --git a/ldap/admin/src/logconv.pl b/ldap/admin/src/logconv.pl
index 7698c383a..2a933c4a3 100755
--- a/ldap/admin/src/logconv.pl
+++ b/ldap/admin/src/logconv.pl
@@ -267,7 +267,7 @@ my $optimeAvg = 0;
my %cipher = ();
my @removefiles = ();
-my @conncodes = qw(A1 B1 B4 T1 T2 B2 B3 R1 P1 P2 U1);
+my @conncodes = qw(A1 B1 B4 T1 T2 T3 B2 B3 R1 P1 P2 U1);
my %conn = ();
map {$conn{$_} = $_} @conncodes;
@@ -355,6 +355,7 @@ $connmsg{"B1"} = "Bad Ber Tag Encountered";
$connmsg{"B4"} = "Server failed to flush data (response) back to Client";
$connmsg{"T1"} = "Idle Timeout Exceeded";
$connmsg{"T2"} = "IO Block Timeout Exceeded or NTSSL Timeout";
+$connmsg{"T3"} = "Paged Search Time Limit Exceeded";
$connmsg{"B2"} = "Ber Too Big";
$connmsg{"B3"} = "Ber Peek";
$connmsg{"R1"} = "Revents";
@@ -1723,6 +1724,10 @@ if ($usage =~ /j/i || $verb eq "yes"){
print "\n $recCount. You have some coonections that are being closed by the ioblocktimeout setting. You may want to increase the ioblocktimeout.\n";
$recCount++;
}
+ if (defined($conncount->{"T3"}) and $conncount->{"T3"} > 0){
+ print "\n $recCount. You have some connections that are being closed because a paged result search limit has been exceeded. You may want to increase the search time limit.\n";
+ $recCount++;
+ }
# compare binds to unbinds, if the difference is more than 30% of the binds, then report a issue
if (($bindCount - $unbindCount) > ($bindCount*.3)){
print "\n $recCount. You have a significant difference between binds and unbinds. You may want to investigate this difference.\n";
@@ -2366,6 +2371,7 @@ sub parseLineNormal
$brokenPipeCount++;
if (m/- T1/){ $hashes->{rc}->{"T1"}++; }
elsif (m/- T2/){ $hashes->{rc}->{"T2"}++; }
+ elsif (m/- T3/){ $hashes->{rc}->{"T3"}++; }
elsif (m/- A1/){ $hashes->{rc}->{"A1"}++; }
elsif (m/- B1/){ $hashes->{rc}->{"B1"}++; }
elsif (m/- B4/){ $hashes->{rc}->{"B4"}++; }
@@ -2381,6 +2387,7 @@ sub parseLineNormal
$connResetByPeerCount++;
if (m/- T1/){ $hashes->{src}->{"T1"}++; }
elsif (m/- T2/){ $hashes->{src}->{"T2"}++; }
+ elsif (m/- T3/){ $hashes->{src}->{"T3"}++; }
elsif (m/- A1/){ $hashes->{src}->{"A1"}++; }
elsif (m/- B1/){ $hashes->{src}->{"B1"}++; }
elsif (m/- B4/){ $hashes->{src}->{"B4"}++; }
@@ -2396,6 +2403,7 @@ sub parseLineNormal
$resourceUnavailCount++;
if (m/- T1/){ $hashes->{rsrc}->{"T1"}++; }
elsif (m/- T2/){ $hashes->{rsrc}->{"T2"}++; }
+ elsif (m/- T3/){ $hashes->{rsrc}->{"T3"}++; }
elsif (m/- A1/){ $hashes->{rsrc}->{"A1"}++; }
elsif (m/- B1/){ $hashes->{rsrc}->{"B1"}++; }
elsif (m/- B4/){ $hashes->{rsrc}->{"B4"}++; }
@@ -2494,6 +2502,20 @@ sub parseLineNormal
}
}
}
+ if (m/- T3/){
+ if ($_ =~ /conn= *([0-9A-Z]+)/i) {
+ $exc = "no";
+ $ip = getIPfromConn($1, $serverRestartCount);
+ for (my $xxx = 0; $xxx < $#excludeIP; $xxx++){
+ if ($ip eq $excludeIP[$xxx]){$exc = "yes";}
+ }
+ if ($exc ne "yes"){
+ $hashes->{T3}->{$ip}++;
+ $hashes->{conncount}->{"T3"}++;
+ $connCodeCount++;
+ }
+ }
+ }
if (m/- B2/){
if ($_ =~ /conn= *([0-9A-Z]+)/i) {
$exc = "no";
diff --git a/ldap/servers/slapd/daemon.c b/ldap/servers/slapd/daemon.c
index 5a48aa66f..bb80dae36 100644
--- a/ldap/servers/slapd/daemon.c
+++ b/ldap/servers/slapd/daemon.c
@@ -1599,9 +1599,9 @@ setup_pr_read_pds(Connection_Table *ct)
int add_fd = 1;
/* check timeout for PAGED RESULTS */
if (pagedresults_is_timedout_nolock(c)) {
- /* Exceeded the timelimit; disconnect the client */
+ /* Exceeded the paged search timelimit; disconnect the client */
disconnect_server_nomutex(c, c->c_connid, -1,
- SLAPD_DISCONNECT_IO_TIMEOUT,
+ SLAPD_DISCONNECT_PAGED_SEARCH_LIMIT,
0);
connection_table_move_connection_out_of_active_list(ct,
c);
diff --git a/ldap/servers/slapd/disconnect_error_strings.h b/ldap/servers/slapd/disconnect_error_strings.h
index f7a31d728..c2d9e283b 100644
--- a/ldap/servers/slapd/disconnect_error_strings.h
+++ b/ldap/servers/slapd/disconnect_error_strings.h
@@ -27,6 +27,7 @@ ER2(SLAPD_DISCONNECT_BER_FLUSH, "B4")
ER2(SLAPD_DISCONNECT_IDLE_TIMEOUT, "T1")
ER2(SLAPD_DISCONNECT_REVENTS, "R1")
ER2(SLAPD_DISCONNECT_IO_TIMEOUT, "T2")
+ER2(SLAPD_DISCONNECT_PAGED_SEARCH_LIMIT, "T3")
ER2(SLAPD_DISCONNECT_PLUGIN, "P1")
ER2(SLAPD_DISCONNECT_UNBIND, "U1")
ER2(SLAPD_DISCONNECT_POLL, "P2")
diff --git a/ldap/servers/slapd/disconnect_errors.h b/ldap/servers/slapd/disconnect_errors.h
index a0484f1c2..e118f674c 100644
--- a/ldap/servers/slapd/disconnect_errors.h
+++ b/ldap/servers/slapd/disconnect_errors.h
@@ -35,6 +35,6 @@
#define SLAPD_DISCONNECT_SASL_FAIL SLAPD_DISCONNECT_ERROR_BASE + 12
#define SLAPD_DISCONNECT_PROXY_INVALID_HEADER SLAPD_DISCONNECT_ERROR_BASE + 13
#define SLAPD_DISCONNECT_PROXY_UNKNOWN SLAPD_DISCONNECT_ERROR_BASE + 14
-
+#define SLAPD_DISCONNECT_PAGED_SEARCH_LIMIT SLAPD_DISCONNECT_ERROR_BASE + 15
#endif /* __DISCONNECT_ERRORS_H_ */
--
2.45.0

@ -0,0 +1,44 @@
From a112394af3a20787755029804684d57a9c3ffa9a Mon Sep 17 00:00:00 2001
From: James Chapman <jachapma@redhat.com>
Date: Wed, 21 Feb 2024 12:43:03 +0000
Subject: [PATCH] Issue 6103 - New connection timeout error breaks errormap
(#6104)
Bug description: A recent addition to the connection disconnect error
messaging, conflicts with how errormap.c maps error codes/strings.
Fix description: errormap expects error codes/strings to be in ascending
order. Moved the new error code to the bottom of the list.
Relates: https://github.com/389ds/389-ds-base/issues/6103
Reviewed by: @droideck. @progier389 (Thank you)
---
ldap/servers/slapd/disconnect_error_strings.h | 5 +++--
1 file changed, 3 insertions(+), 2 deletions(-)
diff --git a/ldap/servers/slapd/disconnect_error_strings.h b/ldap/servers/slapd/disconnect_error_strings.h
index c2d9e283b..f603a08ce 100644
--- a/ldap/servers/slapd/disconnect_error_strings.h
+++ b/ldap/servers/slapd/disconnect_error_strings.h
@@ -14,7 +14,8 @@
/* disconnect_error_strings.h
*
* Strings describing the errors used in logging the reason a connection
- * was closed.
+ * was closed. Ensure definitions are in the same order as the error codes
+ * defined in disconnect_errors.h
*/
#ifndef __DISCONNECT_ERROR_STRINGS_H_
#define __DISCONNECT_ERROR_STRINGS_H_
@@ -35,6 +36,6 @@ ER2(SLAPD_DISCONNECT_NTSSL_TIMEOUT, "T2")
ER2(SLAPD_DISCONNECT_SASL_FAIL, "S1")
ER2(SLAPD_DISCONNECT_PROXY_INVALID_HEADER, "P3")
ER2(SLAPD_DISCONNECT_PROXY_UNKNOWN, "P4")
-
+ER2(SLAPD_DISCONNECT_PAGED_SEARCH_LIMIT, "T3")
#endif /* __DISCONNECT_ERROR_STRINGS_H_ */
--
2.45.0

@ -0,0 +1,30 @@
From edd9abc8901604dde1d739d87ca2906734d53dd3 Mon Sep 17 00:00:00 2001
From: Viktor Ashirov <vashirov@redhat.com>
Date: Thu, 13 Jun 2024 13:35:09 +0200
Subject: [PATCH] Issue 6103 - New connection timeout error breaks errormap
Description:
Remove duplicate SLAPD_DISCONNECT_PAGED_SEARCH_LIMIT error code.
Fixes: https://github.com/389ds/389-ds-base/issues/6103
Reviewed by: @tbordaz (Thanks!)
---
ldap/servers/slapd/disconnect_error_strings.h | 1 -
1 file changed, 1 deletion(-)
diff --git a/ldap/servers/slapd/disconnect_error_strings.h b/ldap/servers/slapd/disconnect_error_strings.h
index f603a08ce..d49cc79a2 100644
--- a/ldap/servers/slapd/disconnect_error_strings.h
+++ b/ldap/servers/slapd/disconnect_error_strings.h
@@ -28,7 +28,6 @@ ER2(SLAPD_DISCONNECT_BER_FLUSH, "B4")
ER2(SLAPD_DISCONNECT_IDLE_TIMEOUT, "T1")
ER2(SLAPD_DISCONNECT_REVENTS, "R1")
ER2(SLAPD_DISCONNECT_IO_TIMEOUT, "T2")
-ER2(SLAPD_DISCONNECT_PAGED_SEARCH_LIMIT, "T3")
ER2(SLAPD_DISCONNECT_PLUGIN, "P1")
ER2(SLAPD_DISCONNECT_UNBIND, "U1")
ER2(SLAPD_DISCONNECT_POLL, "P2")
--
2.45.0

@ -0,0 +1,220 @@
From 8cf981c00ae18d3efaeb10819282cd991621e9a2 Mon Sep 17 00:00:00 2001
From: tbordaz <tbordaz@redhat.com>
Date: Wed, 22 May 2024 11:29:05 +0200
Subject: [PATCH] Issue 6172 - RFE: improve the performance of evaluation of
filter component when tested against a large valueset (like group members)
(#6173)
Bug description:
Before returning an entry (to a SRCH) the server checks that the entry matches the SRCH filter.
If a filter component (equality) is testing the value (ava) against a
large valueset (like uniquemember values), it takes a long time because
of the large number of values and required normalization of the values.
This can be improved taking benefit of sorted valueset. Those sorted
valueset were created to improve updates of large valueset (groups) but
at that time not implemented in SRCH path.
Fix description:
In case of LDAP_FILTER_EQUALITY component, the server can get
benefit of the sorted valuearray.
To limit the risk of regression, we use the sorted valuearray
only for the DN syntax attribute. Indeed the sorted valuearray was
designed for those type of attribute.
With those two limitations, there is no need of a toggle and
the call to plugin_call_syntax_filter_ava can be replaced by
a call to slapi_valueset_find.
In both cases, sorted valueset and plugin_call_syntax_filter_ava, ava and
values are normalized.
In sorted valueset, the values have been normalized to insert the index
in the sorted array and then comparison is done on normalized values.
In plugin_call_syntax_filter_ava, all values in valuearray (of valueset) are normalized
before comparison.
relates: #6172
Reviewed by: Pierre Rogier, Simon Pichugin (Big Thanks !!!)
---
.../tests/suites/filter/filter_test.py | 125 ++++++++++++++++++
ldap/servers/slapd/filterentry.c | 22 ++-
2 files changed, 146 insertions(+), 1 deletion(-)
diff --git a/dirsrvtests/tests/suites/filter/filter_test.py b/dirsrvtests/tests/suites/filter/filter_test.py
index d6bfa5a3b..4baaf04a7 100644
--- a/dirsrvtests/tests/suites/filter/filter_test.py
+++ b/dirsrvtests/tests/suites/filter/filter_test.py
@@ -9,7 +9,11 @@
import logging
import pytest
+import time
+from lib389.dirsrv_log import DirsrvAccessLog
from lib389.tasks import *
+from lib389.backend import Backends, Backend
+from lib389.dbgen import dbgen_users, dbgen_groups
from lib389.topologies import topology_st
from lib389._constants import PASSWORD, DEFAULT_SUFFIX, DN_DM, SUFFIX
from lib389.utils import *
@@ -304,6 +308,127 @@ def test_extended_search(topology_st):
ents = topology_st.standalone.search_s(SUFFIX, ldap.SCOPE_SUBTREE, myfilter)
assert len(ents) == 1
+def test_match_large_valueset(topology_st):
+ """Test that when returning a big number of entries
+ and that we need to match the filter from a large valueset
+ we get benefit to use the sorted valueset
+
+ :id: 7db5aa88-50e0-4c31-85dd-1d2072cb674c
+
+ :setup: Standalone instance
+
+ :steps:
+ 1. Create a users and groups backends and tune them
+ 2. Generate a test ldif (2k users and 1K groups with all users)
+ 3. Import test ldif file using Offline import (ldif2db).
+ 4. Prim the 'groups' entrycache with a "fast" search
+ 5. Search the 'groups' with a difficult matching value
+ 6. check that etime from step 5 is less than a second
+
+ :expectedresults:
+ 1. Create a users and groups backends should PASS
+ 2. Generate LDIF should PASS.
+ 3. Offline import should PASS.
+ 4. Priming should PASS.
+ 5. Performance search should PASS.
+ 6. Etime of performance search should PASS.
+ """
+
+ log.info('Running test_match_large_valueset...')
+ #
+ # Test online/offline LDIF imports
+ #
+ inst = topology_st.standalone
+ inst.start()
+ backends = Backends(inst)
+ users_suffix = "ou=users,%s" % DEFAULT_SUFFIX
+ users_backend = 'users'
+ users_ldif = 'users_import.ldif'
+ groups_suffix = "ou=groups,%s" % DEFAULT_SUFFIX
+ groups_backend = 'groups'
+ groups_ldif = 'groups_import.ldif'
+ groups_entrycache = '200000000'
+ users_number = 2000
+ groups_number = 1000
+
+
+ # For priming the cache we just want to be fast
+ # taking the first value in the valueset is good
+ # whether the valueset is sorted or not
+ priming_user_rdn = "user0001"
+
+ # For performance testing, this is important to use
+ # user1000 rather then user0001
+ # Because user0001 is the first value in the valueset
+ # whether we use the sorted valuearray or non sorted
+ # valuearray the performance will be similar.
+ # With middle value user1000, the performance boost of
+ # the sorted valuearray will make the difference.
+ perf_user_rdn = "user1000"
+
+ # Step 1. Prepare the backends and tune the groups entrycache
+ try:
+ be_users = backends.create(properties={'parent': DEFAULT_SUFFIX, 'nsslapd-suffix': users_suffix, 'name': users_backend})
+ be_groups = backends.create(properties={'parent': DEFAULT_SUFFIX, 'nsslapd-suffix': groups_suffix, 'name': groups_backend})
+
+ # set the entry cache to 200Mb as the 1K groups of 2K users require at least 170Mb
+ be_groups.replace('nsslapd-cachememsize', groups_entrycache)
+ except:
+ raise
+
+ # Step 2. Generate a test ldif (10k users entries)
+ log.info("Generating users LDIF...")
+ ldif_dir = inst.get_ldif_dir()
+ users_import_ldif = "%s/%s" % (ldif_dir, users_ldif)
+ groups_import_ldif = "%s/%s" % (ldif_dir, groups_ldif)
+ dbgen_users(inst, users_number, users_import_ldif, suffix=users_suffix, generic=True, parent=users_suffix)
+
+ # Generate a test ldif (800 groups with 10k members) that fit in 700Mb entry cache
+ props = {
+ "name": "group",
+ "suffix": groups_suffix,
+ "parent": groups_suffix,
+ "number": groups_number,
+ "numMembers": users_number,
+ "createMembers": False,
+ "memberParent": users_suffix,
+ "membershipAttr": "uniquemember",
+ }
+ dbgen_groups(inst, groups_import_ldif, props)
+
+ # Step 3. Do the both offline imports
+ inst.stop()
+ if not inst.ldif2db(users_backend, None, None, None, users_import_ldif):
+ log.fatal('test_basic_import_export: Offline users import failed')
+ assert False
+ if not inst.ldif2db(groups_backend, None, None, None, groups_import_ldif):
+ log.fatal('test_basic_import_export: Offline groups import failed')
+ assert False
+ inst.start()
+
+ # Step 4. first prime the cache
+ # Just request the 'DN'. We are interested by the time of matching not by the time of transfert
+ entries = topology_st.standalone.search_s(groups_suffix, ldap.SCOPE_SUBTREE, "(&(objectclass=groupOfUniqueNames)(uniquemember=uid=%s,%s))" % (priming_user_rdn, users_suffix), ['dn'])
+ assert len(entries) == groups_number
+
+ # Step 5. Now do the real performance checking it should take less than a second
+ # Just request the 'DN'. We are interested by the time of matching not by the time of transfert
+ search_start = time.time()
+ entries = topology_st.standalone.search_s(groups_suffix, ldap.SCOPE_SUBTREE, "(&(objectclass=groupOfUniqueNames)(uniquemember=uid=%s,%s))" % (perf_user_rdn, users_suffix), ['dn'])
+ duration = time.time() - search_start
+ log.info("Duration of the search was %f", duration)
+
+ # Step 6. Gather the etime from the access log
+ inst.stop()
+ access_log = DirsrvAccessLog(inst)
+ search_result = access_log.match(".*RESULT err=0 tag=101 nentries=%s.*" % groups_number)
+ log.info("Found patterns are %s", search_result[0])
+ log.info("Found patterns are %s", search_result[1])
+ etime = float(search_result[1].split('etime=')[1])
+ log.info("Duration of the search from access log was %f", etime)
+ assert len(entries) == groups_number
+ assert (etime < 1)
+
if __name__ == '__main__':
# Run isolated
# -s for DEBUG mode
diff --git a/ldap/servers/slapd/filterentry.c b/ldap/servers/slapd/filterentry.c
index fd8fdda9f..cae5c7edc 100644
--- a/ldap/servers/slapd/filterentry.c
+++ b/ldap/servers/slapd/filterentry.c
@@ -296,7 +296,27 @@ test_ava_filter(
rc = -1;
for (; a != NULL; a = a->a_next) {
if (slapi_attr_type_cmp(ava->ava_type, a->a_type, SLAPI_TYPE_CMP_SUBTYPE) == 0) {
- rc = plugin_call_syntax_filter_ava(a, ftype, ava);
+ if ((ftype == LDAP_FILTER_EQUALITY) &&
+ (slapi_attr_is_dn_syntax_type(a->a_type))) {
+ /* This path is for a performance improvement */
+
+ /* In case of equality filter we can get benefit of the
+ * sorted valuearray (from valueset).
+ * This improvement is limited to DN syntax attributes for
+ * which the sorted valueset was designed.
+ */
+ Slapi_Value *sval = NULL;
+ sval = slapi_value_new_berval(&ava->ava_value);
+ if (slapi_valueset_find((const Slapi_Attr *)a, &a->a_present_values, sval)) {
+ rc = 0;
+ }
+ slapi_value_free(&sval);
+ } else {
+ /* When sorted valuearray optimization cannot be used
+ * lets filter the value according to its syntax
+ */
+ rc = plugin_call_syntax_filter_ava(a, ftype, ava);
+ }
if (rc == 0) {
break;
}
--
2.46.0

@ -0,0 +1,163 @@
From 57051154bafaf50b83fc27dadbd89a49fd1c8c36 Mon Sep 17 00:00:00 2001
From: Pierre Rogier <progier@redhat.com>
Date: Fri, 14 Jun 2024 13:27:10 +0200
Subject: [PATCH] Security fix for CVE-2024-5953
Description:
A denial of service vulnerability was found in the 389 Directory Server.
This issue may allow an authenticated user to cause a server denial
of service while attempting to log in with a user with a malformed hash
in their password.
Fix Description:
To prevent buffer overflow when a bind request is processed, the bind fails
if the hash size is not coherent without even attempting to process further
the hashed password.
References:
- https://nvd.nist.gov/vuln/detail/CVE-2024-5953
- https://access.redhat.com/security/cve/CVE-2024-5953
- https://bugzilla.redhat.com/show_bug.cgi?id=2292104
---
.../tests/suites/password/regression_test.py | 54 ++++++++++++++++++-
ldap/servers/plugins/pwdstorage/md5_pwd.c | 9 +++-
ldap/servers/plugins/pwdstorage/pbkdf2_pwd.c | 6 +++
3 files changed, 66 insertions(+), 3 deletions(-)
diff --git a/dirsrvtests/tests/suites/password/regression_test.py b/dirsrvtests/tests/suites/password/regression_test.py
index 8f1facb6d..1fa581643 100644
--- a/dirsrvtests/tests/suites/password/regression_test.py
+++ b/dirsrvtests/tests/suites/password/regression_test.py
@@ -7,12 +7,14 @@
#
import pytest
import time
+import glob
+import base64
from lib389._constants import PASSWORD, DN_DM, DEFAULT_SUFFIX
from lib389._constants import SUFFIX, PASSWORD, DN_DM, DN_CONFIG, PLUGIN_RETRO_CHANGELOG, DEFAULT_SUFFIX, DEFAULT_CHANGELOG_DB
from lib389 import Entry
from lib389.topologies import topology_m1 as topo_supplier
-from lib389.idm.user import UserAccounts
-from lib389.utils import ldap, os, logging, ensure_bytes, ds_is_newer
+from lib389.idm.user import UserAccounts, UserAccount
+from lib389.utils import ldap, os, logging, ensure_bytes, ds_is_newer, ds_supports_new_changelog
from lib389.topologies import topology_st as topo
from lib389.idm.organizationalunit import OrganizationalUnits
@@ -39,6 +41,13 @@ TEST_PASSWORDS += ['CNpwtest1ZZZZ', 'ZZZZZCNpwtest1',
TEST_PASSWORDS2 = (
'CN12pwtest31', 'SN3pwtest231', 'UID1pwtest123', 'MAIL2pwtest12@redhat.com', '2GN1pwtest123', 'People123')
+SUPPORTED_SCHEMES = (
+ "{SHA}", "{SSHA}", "{SHA256}", "{SSHA256}",
+ "{SHA384}", "{SSHA384}", "{SHA512}", "{SSHA512}",
+ "{crypt}", "{NS-MTA-MD5}", "{clear}", "{MD5}",
+ "{SMD5}", "{PBKDF2_SHA256}", "{PBKDF2_SHA512}",
+ "{GOST_YESCRYPT}", "{PBKDF2-SHA256}", "{PBKDF2-SHA512}" )
+
def _check_unhashed_userpw(inst, user_dn, is_present=False):
"""Check if unhashed#user#password attribute is present or not in the changelog"""
unhashed_pwd_attribute = 'unhashed#user#password'
@@ -319,6 +328,47 @@ def test_unhashed_pw_switch(topo_supplier):
# Add debugging steps(if any)...
pass
+@pytest.mark.parametrize("scheme", SUPPORTED_SCHEMES )
+def test_long_hashed_password(topo, create_user, scheme):
+ """Check that hashed password with very long value does not cause trouble
+
+ :id: 252a1f76-114b-11ef-8a7a-482ae39447e5
+ :setup: standalone Instance
+ :parametrized: yes
+ :steps:
+ 1. Add a test user user
+ 2. Set a long password with requested scheme
+ 3. Bind on that user using a wrong password
+ 4. Check that instance is still alive
+ 5. Remove the added user
+ :expectedresults:
+ 1. Success
+ 2. Success
+ 3. Should get ldap.INVALID_CREDENTIALS exception
+ 4. Success
+ 5. Success
+ """
+ inst = topo.standalone
+ inst.simple_bind_s(DN_DM, PASSWORD)
+ users = UserAccounts(inst, DEFAULT_SUFFIX)
+ # Make sure that server is started as this test may crash it
+ inst.start()
+ # Adding Test user (It may already exists if previous test failed)
+ user2 = UserAccount(inst, dn='uid=test_user_1002,ou=People,dc=example,dc=com')
+ if not user2.exists():
+ user2 = users.create_test_user(uid=1002, gid=2002)
+ # Setting hashed password
+ passwd = 'A'*4000
+ hashed_passwd = scheme.encode('utf-8') + base64.b64encode(passwd.encode('utf-8'))
+ user2.replace('userpassword', hashed_passwd)
+ # Bind on that user using a wrong password
+ with pytest.raises(ldap.INVALID_CREDENTIALS):
+ conn = user2.bind(PASSWORD)
+ # Check that instance is still alive
+ assert inst.status()
+ # Remove the added user
+ user2.delete()
+
if __name__ == '__main__':
# Run isolated
diff --git a/ldap/servers/plugins/pwdstorage/md5_pwd.c b/ldap/servers/plugins/pwdstorage/md5_pwd.c
index 1e2cf58e7..b9a48d5ca 100644
--- a/ldap/servers/plugins/pwdstorage/md5_pwd.c
+++ b/ldap/servers/plugins/pwdstorage/md5_pwd.c
@@ -37,6 +37,7 @@ md5_pw_cmp(const char *userpwd, const char *dbpwd)
unsigned char hash_out[MD5_HASH_LEN];
unsigned char b2a_out[MD5_HASH_LEN * 2]; /* conservative */
SECItem binary_item;
+ size_t dbpwd_len = strlen(dbpwd);
ctx = PK11_CreateDigestContext(SEC_OID_MD5);
if (ctx == NULL) {
@@ -45,6 +46,12 @@ md5_pw_cmp(const char *userpwd, const char *dbpwd)
goto loser;
}
+ if (dbpwd_len >= sizeof b2a_out) {
+ slapi_log_err(SLAPI_LOG_PLUGIN, MD5_SUBSYSTEM_NAME,
+ "The hashed password stored in the user entry is longer than any valid md5 hash");
+ goto loser;
+ }
+
/* create the hash */
PK11_DigestBegin(ctx);
PK11_DigestOp(ctx, (const unsigned char *)userpwd, strlen(userpwd));
@@ -57,7 +64,7 @@ md5_pw_cmp(const char *userpwd, const char *dbpwd)
bver = NSSBase64_EncodeItem(NULL, (char *)b2a_out, sizeof b2a_out, &binary_item);
/* bver points to b2a_out upon success */
if (bver) {
- rc = slapi_ct_memcmp(bver, dbpwd, strlen(dbpwd));
+ rc = slapi_ct_memcmp(bver, dbpwd, dbpwd_len);
} else {
slapi_log_err(SLAPI_LOG_PLUGIN, MD5_SUBSYSTEM_NAME,
"Could not base64 encode hashed value for password compare");
diff --git a/ldap/servers/plugins/pwdstorage/pbkdf2_pwd.c b/ldap/servers/plugins/pwdstorage/pbkdf2_pwd.c
index dcac4fcdd..82b8c9501 100644
--- a/ldap/servers/plugins/pwdstorage/pbkdf2_pwd.c
+++ b/ldap/servers/plugins/pwdstorage/pbkdf2_pwd.c
@@ -255,6 +255,12 @@ pbkdf2_sha256_pw_cmp(const char *userpwd, const char *dbpwd)
passItem.data = (unsigned char *)userpwd;
passItem.len = strlen(userpwd);
+ if (pwdstorage_base64_decode_len(dbpwd, dbpwd_len) > sizeof dbhash) {
+ /* Hashed value is too long and cannot match any value generated by pbkdf2_sha256_hash */
+ slapi_log_err(SLAPI_LOG_ERR, (char *)schemeName, "Unable to base64 decode dbpwd value. (hashed value is too long)\n");
+ return result;
+ }
+
/* Decode the DBpwd to bytes from b64 */
if (PL_Base64Decode(dbpwd, dbpwd_len, dbhash) == NULL) {
slapi_log_err(SLAPI_LOG_ERR, (char *)schemeName, "Unable to base64 decode dbpwd value\n");
--
2.46.0

@ -1,3 +0,0 @@
#Type Name ID GECOS Home directory Shell
g dirsrv 389
u dirsrv 389:389 "user for 389-ds-base" /usr/share/dirsrv/ /sbin/nologin

@ -0,0 +1,933 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
version = 3
[[package]]
name = "addr2line"
version = "0.21.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8a30b2e23b9e17a9f90641c7ab1549cd9b44f296d3ccbf309d2863cfe398a0cb"
dependencies = [
"gimli",
]
[[package]]
name = "adler"
version = "1.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe"
[[package]]
name = "ahash"
version = "0.7.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5a824f2aa7e75a0c98c5a504fceb80649e9c35265d44525b5f94de4771a395cd"
dependencies = [
"getrandom",
"once_cell",
"version_check",
]
[[package]]
name = "ansi_term"
version = "0.12.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d52a9bb7ec0cf484c551830a7ce27bd20d67eac647e1befb56b0be4ee39a55d2"
dependencies = [
"winapi",
]
[[package]]
name = "atty"
version = "0.2.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8"
dependencies = [
"hermit-abi",
"libc",
"winapi",
]
[[package]]
name = "autocfg"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa"
[[package]]
name = "backtrace"
version = "0.3.69"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2089b7e3f35b9dd2d0ed921ead4f6d318c27680d4a5bd167b3ee120edb105837"
dependencies = [
"addr2line",
"cc",
"cfg-if",
"libc",
"miniz_oxide",
"object",
"rustc-demangle",
]
[[package]]
name = "base64"
version = "0.13.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8"
[[package]]
name = "bitflags"
version = "1.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
[[package]]
name = "bitflags"
version = "2.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "327762f6e5a765692301e5bb513e0d9fef63be86bbc14528052b1cd3e6f03e07"
[[package]]
name = "byteorder"
version = "1.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b"
[[package]]
name = "cbindgen"
version = "0.9.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9daec6140ab4dcd38c3dd57e580b59a621172a526ac79f1527af760a55afeafd"
dependencies = [
"clap",
"log",
"proc-macro2",
"quote",
"serde",
"serde_json",
"syn 1.0.109",
"tempfile",
"toml",
]
[[package]]
name = "cc"
version = "1.0.83"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f1174fb0b6ec23863f8b971027804a42614e347eafb0a95bf0b12cdae21fc4d0"
dependencies = [
"jobserver",
"libc",
]
[[package]]
name = "cfg-if"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
[[package]]
name = "clap"
version = "2.34.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a0610544180c38b88101fecf2dd634b174a62eef6946f84dfc6a7127512b381c"
dependencies = [
"ansi_term",
"atty",
"bitflags 1.3.2",
"strsim",
"textwrap",
"unicode-width",
"vec_map",
]
[[package]]
name = "concread"
version = "0.2.21"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dcc9816f5ac93ebd51c37f7f9a6bf2b40dfcd42978ad2aea5d542016e9244cf6"
dependencies = [
"ahash",
"crossbeam",
"crossbeam-epoch",
"crossbeam-utils",
"lru",
"parking_lot",
"rand",
"smallvec",
"tokio",
]
[[package]]
name = "crossbeam"
version = "0.8.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1137cd7e7fc0fb5d3c5a8678be38ec56e819125d8d7907411fe24ccb943faca8"
dependencies = [
"crossbeam-channel",
"crossbeam-deque",
"crossbeam-epoch",
"crossbeam-queue",
"crossbeam-utils",
]
[[package]]
name = "crossbeam-channel"
version = "0.5.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "176dc175b78f56c0f321911d9c8eb2b77a78a4860b9c19db83835fea1a46649b"
dependencies = [
"crossbeam-utils",
]
[[package]]
name = "crossbeam-deque"
version = "0.8.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "613f8cc01fe9cf1a3eb3d7f488fd2fa8388403e97039e2f73692932e291a770d"
dependencies = [
"crossbeam-epoch",
"crossbeam-utils",
]
[[package]]
name = "crossbeam-epoch"
version = "0.9.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5b82ac4a3c2ca9c3460964f020e1402edd5753411d7737aa39c3714ad1b5420e"
dependencies = [
"crossbeam-utils",
]
[[package]]
name = "crossbeam-queue"
version = "0.3.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "df0346b5d5e76ac2fe4e327c5fd1118d6be7c51dfb18f9b7922923f287471e35"
dependencies = [
"crossbeam-utils",
]
[[package]]
name = "crossbeam-utils"
version = "0.8.19"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "248e3bacc7dc6baa3b21e405ee045c3047101a49145e7e9eca583ab4c2ca5345"
[[package]]
name = "entryuuid"
version = "0.1.0"
dependencies = [
"cc",
"libc",
"paste",
"slapi_r_plugin",
"uuid",
]
[[package]]
name = "entryuuid_syntax"
version = "0.1.0"
dependencies = [
"cc",
"libc",
"paste",
"slapi_r_plugin",
"uuid",
]
[[package]]
name = "errno"
version = "0.3.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a258e46cdc063eb8519c00b9fc845fc47bcfca4130e2f08e88665ceda8474245"
dependencies = [
"libc",
"windows-sys",
]
[[package]]
name = "fastrand"
version = "2.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "25cbce373ec4653f1a01a31e8a5e5ec0c622dc27ff9c4e6606eefef5cbbed4a5"
[[package]]
name = "fernet"
version = "0.1.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "93804560e638370a8be6d59ce71ed803e55e230abdbf42598e666b41adda9b1f"
dependencies = [
"base64",
"byteorder",
"getrandom",
"openssl",
"zeroize",
]
[[package]]
name = "foreign-types"
version = "0.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1"
dependencies = [
"foreign-types-shared",
]
[[package]]
name = "foreign-types-shared"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b"
[[package]]
name = "getrandom"
version = "0.2.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "190092ea657667030ac6a35e305e62fc4dd69fd98ac98631e5d3a2b1575a12b5"
dependencies = [
"cfg-if",
"libc",
"wasi",
]
[[package]]
name = "gimli"
version = "0.28.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4271d37baee1b8c7e4b708028c57d816cf9d2434acb33a549475f78c181f6253"
[[package]]
name = "hashbrown"
version = "0.12.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888"
dependencies = [
"ahash",
]
[[package]]
name = "hermit-abi"
version = "0.1.19"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33"
dependencies = [
"libc",
]
[[package]]
name = "instant"
version = "0.1.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c"
dependencies = [
"cfg-if",
]
[[package]]
name = "itoa"
version = "1.0.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b1a46d1a171d865aa5f83f92695765caa047a9b4cbae2cbf37dbd613a793fd4c"
[[package]]
name = "jobserver"
version = "0.1.27"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8c37f63953c4c63420ed5fd3d6d398c719489b9f872b9fa683262f8edd363c7d"
dependencies = [
"libc",
]
[[package]]
name = "libc"
version = "0.2.152"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "13e3bf6590cbc649f4d1a3eefc9d5d6eb746f5200ffb04e5e142700b8faa56e7"
[[package]]
name = "librnsslapd"
version = "0.1.0"
dependencies = [
"cbindgen",
"libc",
"slapd",
]
[[package]]
name = "librslapd"
version = "0.1.0"
dependencies = [
"cbindgen",
"concread",
"libc",
"slapd",
]
[[package]]
name = "linux-raw-sys"
version = "0.4.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c4cd1a83af159aa67994778be9070f0ae1bd732942279cabb14f86f986a21456"
[[package]]
name = "lock_api"
version = "0.4.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3c168f8615b12bc01f9c17e2eb0cc07dcae1940121185446edc3744920e8ef45"
dependencies = [
"autocfg",
"scopeguard",
]
[[package]]
name = "log"
version = "0.4.20"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f"
[[package]]
name = "lru"
version = "0.7.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e999beba7b6e8345721bd280141ed958096a2e4abdf74f67ff4ce49b4b54e47a"
dependencies = [
"hashbrown",
]
[[package]]
name = "memchr"
version = "2.7.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "523dc4f511e55ab87b694dc30d0f820d60906ef06413f93d4d7a1385599cc149"
[[package]]
name = "miniz_oxide"
version = "0.7.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e7810e0be55b428ada41041c41f32c9f1a42817901b4ccf45fa3d4b6561e74c7"
dependencies = [
"adler",
]
[[package]]
name = "object"
version = "0.32.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a6a622008b6e321afc04970976f62ee297fdbaa6f95318ca343e3eebb9648441"
dependencies = [
"memchr",
]
[[package]]
name = "once_cell"
version = "1.19.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92"
[[package]]
name = "openssl"
version = "0.10.62"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8cde4d2d9200ad5909f8dac647e29482e07c3a35de8a13fce7c9c7747ad9f671"
dependencies = [
"bitflags 2.4.1",
"cfg-if",
"foreign-types",
"libc",
"once_cell",
"openssl-macros",
"openssl-sys",
]
[[package]]
name = "openssl-macros"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.48",
]
[[package]]
name = "openssl-sys"
version = "0.9.98"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c1665caf8ab2dc9aef43d1c0023bd904633a6a05cb30b0ad59bec2ae986e57a7"
dependencies = [
"cc",
"libc",
"pkg-config",
"vcpkg",
]
[[package]]
name = "parking_lot"
version = "0.11.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7d17b78036a60663b797adeaee46f5c9dfebb86948d1255007a1d6be0271ff99"
dependencies = [
"instant",
"lock_api",
"parking_lot_core",
]
[[package]]
name = "parking_lot_core"
version = "0.8.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "60a2cfe6f0ad2bfc16aefa463b497d5c7a5ecd44a23efa72aa342d90177356dc"
dependencies = [
"cfg-if",
"instant",
"libc",
"redox_syscall 0.2.16",
"smallvec",
"winapi",
]
[[package]]
name = "paste"
version = "0.1.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "45ca20c77d80be666aef2b45486da86238fabe33e38306bd3118fe4af33fa880"
dependencies = [
"paste-impl",
"proc-macro-hack",
]
[[package]]
name = "paste-impl"
version = "0.1.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d95a7db200b97ef370c8e6de0088252f7e0dfff7d047a28528e47456c0fc98b6"
dependencies = [
"proc-macro-hack",
]
[[package]]
name = "pin-project-lite"
version = "0.2.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8afb450f006bf6385ca15ef45d71d2288452bc3683ce2e2cacc0d18e4be60b58"
[[package]]
name = "pkg-config"
version = "0.3.28"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "69d3587f8a9e599cc7ec2c00e331f71c4e69a5f9a4b8a6efd5b07466b9736f9a"
[[package]]
name = "ppv-lite86"
version = "0.2.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de"
[[package]]
name = "proc-macro-hack"
version = "0.5.20+deprecated"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dc375e1527247fe1a97d8b7156678dfe7c1af2fc075c9a4db3690ecd2a148068"
[[package]]
name = "proc-macro2"
version = "1.0.76"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "95fc56cda0b5c3325f5fbbd7ff9fda9e02bb00bb3dac51252d2f1bfa1cb8cc8c"
dependencies = [
"unicode-ident",
]
[[package]]
name = "pwdchan"
version = "0.1.0"
dependencies = [
"base64",
"cc",
"libc",
"openssl",
"paste",
"slapi_r_plugin",
"uuid",
]
[[package]]
name = "quote"
version = "1.0.35"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "291ec9ab5efd934aaf503a6466c5d5251535d108ee747472c3977cc5acc868ef"
dependencies = [
"proc-macro2",
]
[[package]]
name = "rand"
version = "0.8.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404"
dependencies = [
"libc",
"rand_chacha",
"rand_core",
]
[[package]]
name = "rand_chacha"
version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88"
dependencies = [
"ppv-lite86",
"rand_core",
]
[[package]]
name = "rand_core"
version = "0.6.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c"
dependencies = [
"getrandom",
]
[[package]]
name = "redox_syscall"
version = "0.2.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fb5a58c1855b4b6819d59012155603f0b22ad30cad752600aadfcb695265519a"
dependencies = [
"bitflags 1.3.2",
]
[[package]]
name = "redox_syscall"
version = "0.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4722d768eff46b75989dd134e5c353f0d6296e5aaa3132e776cbdb56be7731aa"
dependencies = [
"bitflags 1.3.2",
]
[[package]]
name = "rsds"
version = "0.1.0"
[[package]]
name = "rustc-demangle"
version = "0.1.23"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76"
[[package]]
name = "rustix"
version = "0.38.30"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "322394588aaf33c24007e8bb3238ee3e4c5c09c084ab32bc73890b99ff326bca"
dependencies = [
"bitflags 2.4.1",
"errno",
"libc",
"linux-raw-sys",
"windows-sys",
]
[[package]]
name = "ryu"
version = "1.0.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f98d2aa92eebf49b69786be48e4477826b256916e84a57ff2a4f21923b48eb4c"
[[package]]
name = "scopeguard"
version = "1.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49"
[[package]]
name = "serde"
version = "1.0.195"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "63261df402c67811e9ac6def069e4786148c4563f4b50fd4bf30aa370d626b02"
dependencies = [
"serde_derive",
]
[[package]]
name = "serde_derive"
version = "1.0.195"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "46fe8f8603d81ba86327b23a2e9cdf49e1255fb94a4c5f297f6ee0547178ea2c"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.48",
]
[[package]]
name = "serde_json"
version = "1.0.111"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "176e46fa42316f18edd598015a5166857fc835ec732f5215eac6b7bdbf0a84f4"
dependencies = [
"itoa",
"ryu",
"serde",
]
[[package]]
name = "slapd"
version = "0.1.0"
dependencies = [
"fernet",
]
[[package]]
name = "slapi_r_plugin"
version = "0.1.0"
dependencies = [
"libc",
"paste",
"uuid",
]
[[package]]
name = "smallvec"
version = "1.12.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2593d31f82ead8df961d8bd23a64c2ccf2eb5dd34b0a34bfb4dd54011c72009e"
[[package]]
name = "strsim"
version = "0.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a"
[[package]]
name = "syn"
version = "1.0.109"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237"
dependencies = [
"proc-macro2",
"quote",
"unicode-ident",
]
[[package]]
name = "syn"
version = "2.0.48"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0f3531638e407dfc0814761abb7c00a5b54992b849452a0646b7f65c9f770f3f"
dependencies = [
"proc-macro2",
"quote",
"unicode-ident",
]
[[package]]
name = "tempfile"
version = "3.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "01ce4141aa927a6d1bd34a041795abd0db1cccba5d5f24b009f694bdf3a1f3fa"
dependencies = [
"cfg-if",
"fastrand",
"redox_syscall 0.4.1",
"rustix",
"windows-sys",
]
[[package]]
name = "textwrap"
version = "0.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d326610f408c7a4eb6f51c37c330e496b08506c9457c9d34287ecc38809fb060"
dependencies = [
"unicode-width",
]
[[package]]
name = "tokio"
version = "1.35.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c89b4efa943be685f629b149f53829423f8f5531ea21249408e8e2f8671ec104"
dependencies = [
"backtrace",
"pin-project-lite",
"tokio-macros",
]
[[package]]
name = "tokio-macros"
version = "2.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5b8a1e28f2deaa14e508979454cb3a223b10b938b45af148bc0986de36f1923b"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.48",
]
[[package]]
name = "toml"
version = "0.5.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f4f7f0dd8d50a853a531c426359045b1998f04219d88799810762cd4ad314234"
dependencies = [
"serde",
]
[[package]]
name = "unicode-ident"
version = "1.0.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b"
[[package]]
name = "unicode-width"
version = "0.1.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e51733f11c9c4f72aa0c160008246859e340b00807569a0da0e7a1079b27ba85"
[[package]]
name = "uuid"
version = "0.8.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bc5cf98d8186244414c848017f0e2676b3fcb46807f6668a97dfe67359a3c4b7"
dependencies = [
"getrandom",
]
[[package]]
name = "vcpkg"
version = "0.2.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426"
[[package]]
name = "vec_map"
version = "0.8.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f1bddf1187be692e79c5ffeab891132dfb0f236ed36a43c7ed39f1165ee20191"
[[package]]
name = "version_check"
version = "0.9.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f"
[[package]]
name = "wasi"
version = "0.11.0+wasi-snapshot-preview1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
[[package]]
name = "winapi"
version = "0.3.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419"
dependencies = [
"winapi-i686-pc-windows-gnu",
"winapi-x86_64-pc-windows-gnu",
]
[[package]]
name = "winapi-i686-pc-windows-gnu"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
[[package]]
name = "winapi-x86_64-pc-windows-gnu"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
[[package]]
name = "windows-sys"
version = "0.52.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d"
dependencies = [
"windows-targets",
]
[[package]]
name = "windows-targets"
version = "0.52.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8a18201040b24831fbb9e4eb208f8892e1f50a37feb53cc7ff887feb8f50e7cd"
dependencies = [
"windows_aarch64_gnullvm",
"windows_aarch64_msvc",
"windows_i686_gnu",
"windows_i686_msvc",
"windows_x86_64_gnu",
"windows_x86_64_gnullvm",
"windows_x86_64_msvc",
]
[[package]]
name = "windows_aarch64_gnullvm"
version = "0.52.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cb7764e35d4db8a7921e09562a0304bf2f93e0a51bfccee0bd0bb0b666b015ea"
[[package]]
name = "windows_aarch64_msvc"
version = "0.52.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bbaa0368d4f1d2aaefc55b6fcfee13f41544ddf36801e793edbbfd7d7df075ef"
[[package]]
name = "windows_i686_gnu"
version = "0.52.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a28637cb1fa3560a16915793afb20081aba2c92ee8af57b4d5f28e4b3e7df313"
[[package]]
name = "windows_i686_msvc"
version = "0.52.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ffe5e8e31046ce6230cc7215707b816e339ff4d4d67c65dffa206fd0f7aa7b9a"
[[package]]
name = "windows_x86_64_gnu"
version = "0.52.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3d6fa32db2bc4a2f5abeacf2b69f7992cd09dca97498da74a151a3132c26befd"
[[package]]
name = "windows_x86_64_gnullvm"
version = "0.52.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1a657e1e9d3f514745a572a6846d3c7aa7dbe1658c056ed9c3344c4109a6949e"
[[package]]
name = "windows_x86_64_msvc"
version = "0.52.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dff9641d1cd4be8d1a070daf9e3773c5f67e78b4d9d42263020c057706765c04"
[[package]]
name = "zeroize"
version = "1.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "525b4ec142c6b68a2d10f01f7bbf6755599ca3f81ea53b8431b7dd348f5fdb2d"
dependencies = [
"zeroize_derive",
]
[[package]]
name = "zeroize_derive"
version = "1.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.48",
]

File diff suppressed because it is too large Load Diff
Loading…
Cancel
Save