Compare commits
3 Commits
c8-stream-
...
c10-beta
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
1b2fb8c72e | ||
| 2760ce3ca2 | |||
| 89b7694df5 |
@ -1,3 +0,0 @@
|
||||
bd9aab32d9cbf9231058d585479813f3420dc872 SOURCES/389-ds-base-1.4.3.39.tar.bz2
|
||||
1c8f2d0dfbf39fa8cd86363bf3314351ab21f8d4 SOURCES/jemalloc-5.3.0.tar.bz2
|
||||
978b7c5e4a9e5784fddb23ba1abe4dc5a071589f SOURCES/vendor-1.4.3.39-1.tar.gz
|
||||
7
.gitignore
vendored
7
.gitignore
vendored
@ -1,3 +1,4 @@
|
||||
SOURCES/389-ds-base-1.4.3.39.tar.bz2
|
||||
SOURCES/jemalloc-5.3.0.tar.bz2
|
||||
SOURCES/vendor-1.4.3.39-1.tar.gz
|
||||
389-ds-base-3.2.0.tar.bz2
|
||||
jemalloc-5.3.0.tar.bz2
|
||||
libdb-5.3.28-59.tar.bz2
|
||||
vendor-3.2.0-4.tar.gz
|
||||
|
||||
318
0001-Issue-7096-During-replication-online-total-init-the-.patch
Normal file
318
0001-Issue-7096-During-replication-online-total-init-the-.patch
Normal file
@ -0,0 +1,318 @@
|
||||
From 1c9c535888b9a850095794787d67900b04924a76 Mon Sep 17 00:00:00 2001
|
||||
From: tbordaz <tbordaz@redhat.com>
|
||||
Date: Wed, 7 Jan 2026 11:21:12 +0100
|
||||
Subject: [PATCH] Issue 7096 - During replication online total init the
|
||||
function idl_id_is_in_idlist is not scaling with large database (#7145)
|
||||
|
||||
Bug description:
|
||||
During a online total initialization, the supplier sorts
|
||||
the candidate list of entries so that the parents are sent before
|
||||
children entries.
|
||||
With large DB the ID array used for the sorting is not
|
||||
scaling. It takes so long to build the candidate list that
|
||||
the connection gets closed
|
||||
|
||||
Fix description:
|
||||
Instead of using an ID array, uses a list of ID ranges
|
||||
|
||||
fixes: #7096
|
||||
|
||||
Reviewed by: Mark Reynolds, Pierre Rogier (Thanks !!)
|
||||
---
|
||||
ldap/servers/slapd/back-ldbm/back-ldbm.h | 12 ++
|
||||
ldap/servers/slapd/back-ldbm/idl_common.c | 163 ++++++++++++++++++
|
||||
ldap/servers/slapd/back-ldbm/idl_new.c | 30 ++--
|
||||
.../servers/slapd/back-ldbm/proto-back-ldbm.h | 3 +
|
||||
4 files changed, 189 insertions(+), 19 deletions(-)
|
||||
|
||||
diff --git a/ldap/servers/slapd/back-ldbm/back-ldbm.h b/ldap/servers/slapd/back-ldbm/back-ldbm.h
|
||||
index 1bc36720d..b187c26bc 100644
|
||||
--- a/ldap/servers/slapd/back-ldbm/back-ldbm.h
|
||||
+++ b/ldap/servers/slapd/back-ldbm/back-ldbm.h
|
||||
@@ -282,6 +282,18 @@ typedef struct _idlist_set
|
||||
#define INDIRECT_BLOCK(idl) ((idl)->b_nids == INDBLOCK)
|
||||
#define IDL_NIDS(idl) (idl ? (idl)->b_nids : (NIDS)0)
|
||||
|
||||
+/*
|
||||
+ * used by the supplier during online total init
|
||||
+ * it stores the ranges of ID that are already present
|
||||
+ * in the candidate list ('parentid>=1')
|
||||
+ */
|
||||
+typedef struct IdRange {
|
||||
+ ID first;
|
||||
+ ID last;
|
||||
+ struct IdRange *next;
|
||||
+} IdRange_t;
|
||||
+
|
||||
+
|
||||
typedef size_t idl_iterator;
|
||||
|
||||
/* small hashtable implementation used in the entry cache -- the table
|
||||
diff --git a/ldap/servers/slapd/back-ldbm/idl_common.c b/ldap/servers/slapd/back-ldbm/idl_common.c
|
||||
index fcb0ece4b..fdc9b4e67 100644
|
||||
--- a/ldap/servers/slapd/back-ldbm/idl_common.c
|
||||
+++ b/ldap/servers/slapd/back-ldbm/idl_common.c
|
||||
@@ -172,6 +172,169 @@ idl_min(IDList *a, IDList *b)
|
||||
return (a->b_nids > b->b_nids ? b : a);
|
||||
}
|
||||
|
||||
+/*
|
||||
+ * This is a faster version of idl_id_is_in_idlist.
|
||||
+ * idl_id_is_in_idlist uses an array of ID so lookup is expensive
|
||||
+ * idl_id_is_in_idlist_ranges uses a list of ranges of ID lookup is faster
|
||||
+ * returns
|
||||
+ * 1: 'id' is present in idrange_list
|
||||
+ * 0: 'id' is not present in idrange_list
|
||||
+ */
|
||||
+int
|
||||
+idl_id_is_in_idlist_ranges(IDList *idl, IdRange_t *idrange_list, ID id)
|
||||
+{
|
||||
+ IdRange_t *range = idrange_list;
|
||||
+ int found = 0;
|
||||
+
|
||||
+ if (NULL == idl || NOID == id) {
|
||||
+ return 0; /* not in the list */
|
||||
+ }
|
||||
+ if (ALLIDS(idl)) {
|
||||
+ return 1; /* in the list */
|
||||
+ }
|
||||
+
|
||||
+ for(;range; range = range->next) {
|
||||
+ if (id > range->last) {
|
||||
+ /* check if it belongs to the next range */
|
||||
+ continue;
|
||||
+ }
|
||||
+ if (id >= range->first) {
|
||||
+ /* It belongs to that range [first..last ] */
|
||||
+ found = 1;
|
||||
+ break;
|
||||
+ } else {
|
||||
+ /* this range is after id */
|
||||
+ break;
|
||||
+ }
|
||||
+ }
|
||||
+ return found;
|
||||
+}
|
||||
+
|
||||
+/* This function is used during the online total initialisation
|
||||
+ * (see next function)
|
||||
+ * It frees all ranges of ID in the list
|
||||
+ */
|
||||
+void idrange_free(IdRange_t **head)
|
||||
+{
|
||||
+ IdRange_t *curr, *sav;
|
||||
+
|
||||
+ if ((head == NULL) || (*head == NULL)) {
|
||||
+ return;
|
||||
+ }
|
||||
+ curr = *head;
|
||||
+ sav = NULL;
|
||||
+ for (; curr;) {
|
||||
+ sav = curr;
|
||||
+ curr = curr->next;
|
||||
+ slapi_ch_free((void *) &sav);
|
||||
+ }
|
||||
+ if (sav) {
|
||||
+ slapi_ch_free((void *) &sav);
|
||||
+ }
|
||||
+ *head = NULL;
|
||||
+}
|
||||
+
|
||||
+/* This function is used during the online total initialisation
|
||||
+ * Because a MODRDN can move entries under a parent that
|
||||
+ * has a higher ID we need to sort the IDList so that parents
|
||||
+ * are sent, to the consumer, before the children are sent.
|
||||
+ * The sorting with a simple IDlist does not scale instead
|
||||
+ * a list of IDs ranges is much faster.
|
||||
+ * In that list we only ADD/lookup ID.
|
||||
+ */
|
||||
+IdRange_t *idrange_add_id(IdRange_t **head, ID id)
|
||||
+{
|
||||
+ if (head == NULL) {
|
||||
+ slapi_log_err(SLAPI_LOG_ERR, "idrange_add_id",
|
||||
+ "Can not add ID %d in non defined list\n", id);
|
||||
+ return NULL;
|
||||
+ }
|
||||
+
|
||||
+ if (*head == NULL) {
|
||||
+ /* This is the first range */
|
||||
+ IdRange_t *new_range = (IdRange_t *)slapi_ch_malloc(sizeof(IdRange_t));
|
||||
+ new_range->first = id;
|
||||
+ new_range->last = id;
|
||||
+ new_range->next = NULL;
|
||||
+ *head = new_range;
|
||||
+ return *head;
|
||||
+ }
|
||||
+
|
||||
+ IdRange_t *curr = *head, *prev = NULL;
|
||||
+
|
||||
+ /* First, find if id already falls within any existing range, or it is adjacent to any */
|
||||
+ while (curr) {
|
||||
+ if (id >= curr->first && id <= curr->last) {
|
||||
+ /* inside a range, nothing to do */
|
||||
+ return curr;
|
||||
+ }
|
||||
+
|
||||
+ if (id == curr->last + 1) {
|
||||
+ /* Extend this range upwards */
|
||||
+ curr->last = id;
|
||||
+
|
||||
+ /* Check for possible merge with next range */
|
||||
+ IdRange_t *next = curr->next;
|
||||
+ if (next && curr->last + 1 >= next->first) {
|
||||
+ slapi_log_err(SLAPI_LOG_REPL, "idrange_add_id",
|
||||
+ "(id=%d) merge current with next range [%d..%d]\n", id, curr->first, curr->last);
|
||||
+ curr->last = (next->last > curr->last) ? next->last : curr->last;
|
||||
+ curr->next = next->next;
|
||||
+ slapi_ch_free((void*) &next);
|
||||
+ } else {
|
||||
+ slapi_log_err(SLAPI_LOG_REPL, "idrange_add_id",
|
||||
+ "(id=%d) extend forward current range [%d..%d]\n", id, curr->first, curr->last);
|
||||
+ }
|
||||
+ return curr;
|
||||
+ }
|
||||
+
|
||||
+ if (id + 1 == curr->first) {
|
||||
+ /* Extend this range downwards */
|
||||
+ curr->first = id;
|
||||
+
|
||||
+ /* Check for possible merge with previous range */
|
||||
+ if (prev && prev->last + 1 >= curr->first) {
|
||||
+ prev->last = curr->last;
|
||||
+ prev->next = curr->next;
|
||||
+ slapi_ch_free((void *) &curr);
|
||||
+ slapi_log_err(SLAPI_LOG_REPL, "idrange_add_id",
|
||||
+ "(id=%d) merge current with previous range [%d..%d]\n", id, prev->first, prev->last);
|
||||
+ return prev;
|
||||
+ } else {
|
||||
+ slapi_log_err(SLAPI_LOG_REPL, "idrange_add_id",
|
||||
+ "(id=%d) extend backward current range [%d..%d]\n", id, curr->first, curr->last);
|
||||
+ return curr;
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ /* If id is before the current range, break so we can insert before */
|
||||
+ if (id < curr->first) {
|
||||
+ break;
|
||||
+ }
|
||||
+
|
||||
+ prev = curr;
|
||||
+ curr = curr->next;
|
||||
+ }
|
||||
+ /* Need to insert a new standalone IdRange */
|
||||
+ IdRange_t *new_range = (IdRange_t *)slapi_ch_malloc(sizeof(IdRange_t));
|
||||
+ new_range->first = id;
|
||||
+ new_range->last = id;
|
||||
+ new_range->next = curr;
|
||||
+
|
||||
+ if (prev) {
|
||||
+ slapi_log_err(SLAPI_LOG_REPL, "idrange_add_id",
|
||||
+ "(id=%d) add new range [%d..%d]\n", id, new_range->first, new_range->last);
|
||||
+ prev->next = new_range;
|
||||
+ } else {
|
||||
+ /* Insert at head */
|
||||
+ slapi_log_err(SLAPI_LOG_REPL, "idrange_add_id",
|
||||
+ "(id=%d) head range [%d..%d]\n", id, new_range->first, new_range->last);
|
||||
+ *head = new_range;
|
||||
+ }
|
||||
+ return *head;
|
||||
+}
|
||||
+
|
||||
+
|
||||
int
|
||||
idl_id_is_in_idlist(IDList *idl, ID id)
|
||||
{
|
||||
diff --git a/ldap/servers/slapd/back-ldbm/idl_new.c b/ldap/servers/slapd/back-ldbm/idl_new.c
|
||||
index 5fbcaff2e..2d978353f 100644
|
||||
--- a/ldap/servers/slapd/back-ldbm/idl_new.c
|
||||
+++ b/ldap/servers/slapd/back-ldbm/idl_new.c
|
||||
@@ -417,7 +417,6 @@ idl_new_range_fetch(
|
||||
{
|
||||
int ret = 0;
|
||||
int ret2 = 0;
|
||||
- int idl_rc = 0;
|
||||
dbi_cursor_t cursor = {0};
|
||||
IDList *idl = NULL;
|
||||
dbi_val_t cur_key = {0};
|
||||
@@ -436,6 +435,7 @@ idl_new_range_fetch(
|
||||
size_t leftoverlen = 32;
|
||||
size_t leftovercnt = 0;
|
||||
char *index_id = get_index_name(be, db, ai);
|
||||
+ IdRange_t *idrange_list = NULL;
|
||||
|
||||
|
||||
if (NULL == flag_err) {
|
||||
@@ -578,10 +578,12 @@ idl_new_range_fetch(
|
||||
* found entry is the one from the suffix
|
||||
*/
|
||||
suffix = key;
|
||||
- idl_rc = idl_append_extend(&idl, id);
|
||||
- } else if ((key == suffix) || idl_id_is_in_idlist(idl, key)) {
|
||||
+ idl_append_extend(&idl, id);
|
||||
+ idrange_add_id(&idrange_list, id);
|
||||
+ } else if ((key == suffix) || idl_id_is_in_idlist_ranges(idl, idrange_list, key)) {
|
||||
/* the parent is the suffix or already in idl. */
|
||||
- idl_rc = idl_append_extend(&idl, id);
|
||||
+ idl_append_extend(&idl, id);
|
||||
+ idrange_add_id(&idrange_list, id);
|
||||
} else {
|
||||
/* Otherwise, keep the {key,id} in leftover array */
|
||||
if (!leftover) {
|
||||
@@ -596,13 +598,7 @@ idl_new_range_fetch(
|
||||
leftovercnt++;
|
||||
}
|
||||
} else {
|
||||
- idl_rc = idl_append_extend(&idl, id);
|
||||
- }
|
||||
- if (idl_rc) {
|
||||
- slapi_log_err(SLAPI_LOG_ERR, "idl_new_range_fetch",
|
||||
- "Unable to extend id list (err=%d)\n", idl_rc);
|
||||
- idl_free(&idl);
|
||||
- goto error;
|
||||
+ idl_append_extend(&idl, id);
|
||||
}
|
||||
|
||||
count++;
|
||||
@@ -695,21 +691,17 @@ error:
|
||||
|
||||
while(remaining > 0) {
|
||||
for (size_t i = 0; i < leftovercnt; i++) {
|
||||
- if (leftover[i].key > 0 && idl_id_is_in_idlist(idl, leftover[i].key) != 0) {
|
||||
+ if (leftover[i].key > 0 && idl_id_is_in_idlist_ranges(idl, idrange_list, leftover[i].key) != 0) {
|
||||
/* if the leftover key has its parent in the idl */
|
||||
- idl_rc = idl_append_extend(&idl, leftover[i].id);
|
||||
- if (idl_rc) {
|
||||
- slapi_log_err(SLAPI_LOG_ERR, "idl_new_range_fetch",
|
||||
- "Unable to extend id list (err=%d)\n", idl_rc);
|
||||
- idl_free(&idl);
|
||||
- return NULL;
|
||||
- }
|
||||
+ idl_append_extend(&idl, leftover[i].id);
|
||||
+ idrange_add_id(&idrange_list, leftover[i].id);
|
||||
leftover[i].key = 0;
|
||||
remaining--;
|
||||
}
|
||||
}
|
||||
}
|
||||
slapi_ch_free((void **)&leftover);
|
||||
+ idrange_free(&idrange_list);
|
||||
}
|
||||
slapi_log_err(SLAPI_LOG_FILTER, "idl_new_range_fetch",
|
||||
"Found %d candidates; error code is: %d\n",
|
||||
diff --git a/ldap/servers/slapd/back-ldbm/proto-back-ldbm.h b/ldap/servers/slapd/back-ldbm/proto-back-ldbm.h
|
||||
index 91d61098a..30a7aa11f 100644
|
||||
--- a/ldap/servers/slapd/back-ldbm/proto-back-ldbm.h
|
||||
+++ b/ldap/servers/slapd/back-ldbm/proto-back-ldbm.h
|
||||
@@ -217,6 +217,9 @@ ID idl_firstid(IDList *idl);
|
||||
ID idl_nextid(IDList *idl, ID id);
|
||||
int idl_init_private(backend *be, struct attrinfo *a);
|
||||
int idl_release_private(struct attrinfo *a);
|
||||
+IdRange_t *idrange_add_id(IdRange_t **head, ID id);
|
||||
+void idrange_free(IdRange_t **head);
|
||||
+int idl_id_is_in_idlist_ranges(IDList *idl, IdRange_t *idrange_list, ID id);
|
||||
int idl_id_is_in_idlist(IDList *idl, ID id);
|
||||
|
||||
idl_iterator idl_iterator_init(const IDList *idl);
|
||||
--
|
||||
2.52.0
|
||||
|
||||
765
0002-Issue-Revise-paged-result-search-locking.patch
Normal file
765
0002-Issue-Revise-paged-result-search-locking.patch
Normal file
@ -0,0 +1,765 @@
|
||||
From 446bc42e7b64a8496c2c3fe486f86bba318bed5e Mon Sep 17 00:00:00 2001
|
||||
From: Mark Reynolds <mreynolds@redhat.com>
|
||||
Date: Wed, 7 Jan 2026 16:55:27 -0500
|
||||
Subject: [PATCH] Issue - Revise paged result search locking
|
||||
|
||||
Description:
|
||||
|
||||
Move to a single lock approach verses having two locks. This will impact
|
||||
concurrency when multiple async paged result searches are done on the same
|
||||
connection, but it simplifies the code and avoids race conditions and
|
||||
deadlocks.
|
||||
|
||||
Relates: https://github.com/389ds/389-ds-base/issues/7118
|
||||
|
||||
Reviewed by: progier & tbordaz (Thanks!!)
|
||||
---
|
||||
ldap/servers/slapd/abandon.c | 2 +-
|
||||
ldap/servers/slapd/opshared.c | 60 ++++----
|
||||
ldap/servers/slapd/pagedresults.c | 228 +++++++++++++++++++-----------
|
||||
ldap/servers/slapd/proto-slap.h | 26 ++--
|
||||
ldap/servers/slapd/slap.h | 5 +-
|
||||
5 files changed, 187 insertions(+), 134 deletions(-)
|
||||
|
||||
diff --git a/ldap/servers/slapd/abandon.c b/ldap/servers/slapd/abandon.c
|
||||
index 6024fcd31..1f47c531c 100644
|
||||
--- a/ldap/servers/slapd/abandon.c
|
||||
+++ b/ldap/servers/slapd/abandon.c
|
||||
@@ -179,7 +179,7 @@ do_abandon(Slapi_PBlock *pb)
|
||||
logpb.tv_sec = -1;
|
||||
logpb.tv_nsec = -1;
|
||||
|
||||
- if (0 == pagedresults_free_one_msgid(pb_conn, id, pageresult_lock_get_addr(pb_conn))) {
|
||||
+ if (0 == pagedresults_free_one_msgid(pb_conn, id, PR_NOT_LOCKED)) {
|
||||
if (log_format != LOG_FORMAT_DEFAULT) {
|
||||
/* JSON logging */
|
||||
logpb.target_op = "Simple Paged Results";
|
||||
diff --git a/ldap/servers/slapd/opshared.c b/ldap/servers/slapd/opshared.c
|
||||
index a5cddfd23..bf800f7dc 100644
|
||||
--- a/ldap/servers/slapd/opshared.c
|
||||
+++ b/ldap/servers/slapd/opshared.c
|
||||
@@ -572,8 +572,8 @@ op_shared_search(Slapi_PBlock *pb, int send_result)
|
||||
be = be_list[index];
|
||||
}
|
||||
}
|
||||
- pr_search_result = pagedresults_get_search_result(pb_conn, operation, 0 /*not locked*/, pr_idx);
|
||||
- estimate = pagedresults_get_search_result_set_size_estimate(pb_conn, operation, pr_idx);
|
||||
+ pr_search_result = pagedresults_get_search_result(pb_conn, operation, PR_NOT_LOCKED, pr_idx);
|
||||
+ estimate = pagedresults_get_search_result_set_size_estimate(pb_conn, operation, PR_NOT_LOCKED, pr_idx);
|
||||
/* Set operation note flags as required. */
|
||||
if (pagedresults_get_unindexed(pb_conn, operation, pr_idx)) {
|
||||
slapi_pblock_set_flag_operation_notes(pb, SLAPI_OP_NOTE_UNINDEXED);
|
||||
@@ -619,14 +619,7 @@ op_shared_search(Slapi_PBlock *pb, int send_result)
|
||||
int32_t tlimit;
|
||||
slapi_pblock_get(pb, SLAPI_SEARCH_TIMELIMIT, &tlimit);
|
||||
pagedresults_set_timelimit(pb_conn, operation, (time_t)tlimit, pr_idx);
|
||||
- /* When using this mutex in conjunction with the main paged
|
||||
- * result lock, you must do so in this order:
|
||||
- *
|
||||
- * --> pagedresults_lock()
|
||||
- * --> pagedresults_mutex
|
||||
- * <-- pagedresults_mutex
|
||||
- * <-- pagedresults_unlock()
|
||||
- */
|
||||
+ /* IMPORTANT: Never acquire pagedresults_mutex when holding c_mutex. */
|
||||
pagedresults_mutex = pageresult_lock_get_addr(pb_conn);
|
||||
}
|
||||
|
||||
@@ -743,17 +736,15 @@ op_shared_search(Slapi_PBlock *pb, int send_result)
|
||||
if (op_is_pagedresults(operation) && pr_search_result) {
|
||||
void *sr = NULL;
|
||||
/* PAGED RESULTS and already have the search results from the prev op */
|
||||
- pagedresults_lock(pb_conn, pr_idx);
|
||||
/*
|
||||
* In async paged result case, the search result might be released
|
||||
* by other theads. We need to double check it in the locked region.
|
||||
*/
|
||||
pthread_mutex_lock(pagedresults_mutex);
|
||||
- pr_search_result = pagedresults_get_search_result(pb_conn, operation, 1 /*locked*/, pr_idx);
|
||||
+ pr_search_result = pagedresults_get_search_result(pb_conn, operation, PR_LOCKED, pr_idx);
|
||||
if (pr_search_result) {
|
||||
- if (pagedresults_is_abandoned_or_notavailable(pb_conn, 1 /*locked*/, pr_idx)) {
|
||||
+ if (pagedresults_is_abandoned_or_notavailable(pb_conn, PR_LOCKED, pr_idx)) {
|
||||
pthread_mutex_unlock(pagedresults_mutex);
|
||||
- pagedresults_unlock(pb_conn, pr_idx);
|
||||
/* Previous operation was abandoned and the simplepaged object is not in use. */
|
||||
send_ldap_result(pb, 0, NULL, "Simple Paged Results Search abandoned", 0, NULL);
|
||||
rc = LDAP_SUCCESS;
|
||||
@@ -764,14 +755,13 @@ op_shared_search(Slapi_PBlock *pb, int send_result)
|
||||
|
||||
/* search result could be reset in the backend/dse */
|
||||
slapi_pblock_get(pb, SLAPI_SEARCH_RESULT_SET, &sr);
|
||||
- pagedresults_set_search_result(pb_conn, operation, sr, 1 /*locked*/, pr_idx);
|
||||
+ pagedresults_set_search_result(pb_conn, operation, sr, PR_LOCKED, pr_idx);
|
||||
}
|
||||
} else {
|
||||
pr_stat = PAGEDRESULTS_SEARCH_END;
|
||||
rc = LDAP_SUCCESS;
|
||||
}
|
||||
pthread_mutex_unlock(pagedresults_mutex);
|
||||
- pagedresults_unlock(pb_conn, pr_idx);
|
||||
|
||||
if ((PAGEDRESULTS_SEARCH_END == pr_stat) || (0 == pnentries)) {
|
||||
/* no more entries to send in the backend */
|
||||
@@ -789,22 +779,22 @@ op_shared_search(Slapi_PBlock *pb, int send_result)
|
||||
}
|
||||
pagedresults_set_response_control(pb, 0, estimate,
|
||||
curr_search_count, pr_idx);
|
||||
- if (pagedresults_get_with_sort(pb_conn, operation, pr_idx)) {
|
||||
+ if (pagedresults_get_with_sort(pb_conn, operation, PR_NOT_LOCKED, pr_idx)) {
|
||||
sort_make_sort_response_control(pb, CONN_GET_SORT_RESULT_CODE, NULL);
|
||||
}
|
||||
pagedresults_set_search_result_set_size_estimate(pb_conn,
|
||||
operation,
|
||||
- estimate, pr_idx);
|
||||
+ estimate, PR_NOT_LOCKED, pr_idx);
|
||||
if (PAGEDRESULTS_SEARCH_END == pr_stat) {
|
||||
- pagedresults_lock(pb_conn, pr_idx);
|
||||
+ pthread_mutex_lock(pagedresults_mutex);
|
||||
slapi_pblock_set(pb, SLAPI_SEARCH_RESULT_SET, NULL);
|
||||
- if (!pagedresults_is_abandoned_or_notavailable(pb_conn, 0 /*not locked*/, pr_idx)) {
|
||||
- pagedresults_free_one(pb_conn, operation, pr_idx);
|
||||
+ if (!pagedresults_is_abandoned_or_notavailable(pb_conn, PR_LOCKED, pr_idx)) {
|
||||
+ pagedresults_free_one(pb_conn, operation, PR_LOCKED, pr_idx);
|
||||
}
|
||||
- pagedresults_unlock(pb_conn, pr_idx);
|
||||
+ pthread_mutex_unlock(pagedresults_mutex);
|
||||
if (next_be) {
|
||||
/* no more entries, but at least another backend */
|
||||
- if (pagedresults_set_current_be(pb_conn, next_be, pr_idx, 0) < 0) {
|
||||
+ if (pagedresults_set_current_be(pb_conn, next_be, pr_idx, PR_NOT_LOCKED) < 0) {
|
||||
goto free_and_return;
|
||||
}
|
||||
}
|
||||
@@ -915,7 +905,7 @@ op_shared_search(Slapi_PBlock *pb, int send_result)
|
||||
}
|
||||
}
|
||||
pagedresults_set_search_result(pb_conn, operation, NULL, 1, pr_idx);
|
||||
- rc = pagedresults_set_current_be(pb_conn, NULL, pr_idx, 1);
|
||||
+ rc = pagedresults_set_current_be(pb_conn, NULL, pr_idx, PR_LOCKED);
|
||||
pthread_mutex_unlock(pagedresults_mutex);
|
||||
#pragma GCC diagnostic pop
|
||||
}
|
||||
@@ -954,7 +944,7 @@ op_shared_search(Slapi_PBlock *pb, int send_result)
|
||||
pthread_mutex_lock(pagedresults_mutex);
|
||||
pagedresults_set_search_result(pb_conn, operation, NULL, 1, pr_idx);
|
||||
be->be_search_results_release(&sr);
|
||||
- rc = pagedresults_set_current_be(pb_conn, next_be, pr_idx, 1);
|
||||
+ rc = pagedresults_set_current_be(pb_conn, next_be, pr_idx, PR_LOCKED);
|
||||
pthread_mutex_unlock(pagedresults_mutex);
|
||||
pr_stat = PAGEDRESULTS_SEARCH_END; /* make sure stat is SEARCH_END */
|
||||
if (NULL == next_be) {
|
||||
@@ -967,23 +957,23 @@ op_shared_search(Slapi_PBlock *pb, int send_result)
|
||||
} else {
|
||||
curr_search_count = pnentries;
|
||||
slapi_pblock_get(pb, SLAPI_SEARCH_RESULT_SET_SIZE_ESTIMATE, &estimate);
|
||||
- pagedresults_lock(pb_conn, pr_idx);
|
||||
- if ((pagedresults_set_current_be(pb_conn, be, pr_idx, 0) < 0) ||
|
||||
- (pagedresults_set_search_result(pb_conn, operation, sr, 0, pr_idx) < 0) ||
|
||||
- (pagedresults_set_search_result_count(pb_conn, operation, curr_search_count, pr_idx) < 0) ||
|
||||
- (pagedresults_set_search_result_set_size_estimate(pb_conn, operation, estimate, pr_idx) < 0) ||
|
||||
- (pagedresults_set_with_sort(pb_conn, operation, with_sort, pr_idx) < 0)) {
|
||||
- pagedresults_unlock(pb_conn, pr_idx);
|
||||
+ pthread_mutex_lock(pagedresults_mutex);
|
||||
+ if ((pagedresults_set_current_be(pb_conn, be, pr_idx, PR_LOCKED) < 0) ||
|
||||
+ (pagedresults_set_search_result(pb_conn, operation, sr, PR_LOCKED, pr_idx) < 0) ||
|
||||
+ (pagedresults_set_search_result_count(pb_conn, operation, curr_search_count, PR_LOCKED, pr_idx) < 0) ||
|
||||
+ (pagedresults_set_search_result_set_size_estimate(pb_conn, operation, estimate, PR_LOCKED, pr_idx) < 0) ||
|
||||
+ (pagedresults_set_with_sort(pb_conn, operation, with_sort, PR_LOCKED, pr_idx) < 0)) {
|
||||
+ pthread_mutex_unlock(pagedresults_mutex);
|
||||
cache_return_target_entry(pb, be, operation);
|
||||
goto free_and_return;
|
||||
}
|
||||
- pagedresults_unlock(pb_conn, pr_idx);
|
||||
+ pthread_mutex_unlock(pagedresults_mutex);
|
||||
}
|
||||
slapi_pblock_set(pb, SLAPI_SEARCH_RESULT_SET, NULL);
|
||||
next_be = NULL; /* to break the loop */
|
||||
if (operation->o_status & SLAPI_OP_STATUS_ABANDONED) {
|
||||
/* It turned out this search was abandoned. */
|
||||
- pagedresults_free_one_msgid(pb_conn, operation->o_msgid, pagedresults_mutex);
|
||||
+ pagedresults_free_one_msgid(pb_conn, operation->o_msgid, PR_NOT_LOCKED);
|
||||
/* paged-results-request was abandoned; making an empty cookie. */
|
||||
pagedresults_set_response_control(pb, 0, estimate, -1, pr_idx);
|
||||
send_ldap_result(pb, 0, NULL, "Simple Paged Results Search abandoned", 0, NULL);
|
||||
@@ -993,7 +983,7 @@ op_shared_search(Slapi_PBlock *pb, int send_result)
|
||||
}
|
||||
pagedresults_set_response_control(pb, 0, estimate, curr_search_count, pr_idx);
|
||||
if (curr_search_count == -1) {
|
||||
- pagedresults_free_one(pb_conn, operation, pr_idx);
|
||||
+ pagedresults_free_one(pb_conn, operation, PR_NOT_LOCKED, pr_idx);
|
||||
}
|
||||
}
|
||||
|
||||
diff --git a/ldap/servers/slapd/pagedresults.c b/ldap/servers/slapd/pagedresults.c
|
||||
index 941ab97e3..0d6c4a1aa 100644
|
||||
--- a/ldap/servers/slapd/pagedresults.c
|
||||
+++ b/ldap/servers/slapd/pagedresults.c
|
||||
@@ -34,9 +34,9 @@ pageresult_lock_cleanup()
|
||||
slapi_ch_free((void**)&lock_hash);
|
||||
}
|
||||
|
||||
-/* Beware to the lock order with c_mutex:
|
||||
- * c_mutex is sometime locked while holding pageresult_lock
|
||||
- * ==> Do not lock pageresult_lock when holing c_mutex
|
||||
+/* Lock ordering constraint with c_mutex:
|
||||
+ * c_mutex is sometimes locked while holding pageresult_lock.
|
||||
+ * Therefore: DO NOT acquire pageresult_lock when holding c_mutex.
|
||||
*/
|
||||
pthread_mutex_t *
|
||||
pageresult_lock_get_addr(Connection *conn)
|
||||
@@ -44,7 +44,11 @@ pageresult_lock_get_addr(Connection *conn)
|
||||
return &lock_hash[(((size_t)conn)/sizeof (Connection))%LOCK_HASH_SIZE];
|
||||
}
|
||||
|
||||
-/* helper function to clean up one prp slot */
|
||||
+/* helper function to clean up one prp slot
|
||||
+ *
|
||||
+ * NOTE: This function must be called while holding the pageresult_lock
|
||||
+ * (via pageresult_lock_get_addr(conn)) to ensure thread-safe cleanup.
|
||||
+ */
|
||||
static void
|
||||
_pr_cleanup_one_slot(PagedResults *prp)
|
||||
{
|
||||
@@ -56,7 +60,7 @@ _pr_cleanup_one_slot(PagedResults *prp)
|
||||
prp->pr_current_be->be_search_results_release(&(prp->pr_search_result_set));
|
||||
}
|
||||
|
||||
- /* clean up the slot except the mutex */
|
||||
+ /* clean up the slot */
|
||||
prp->pr_current_be = NULL;
|
||||
prp->pr_search_result_set = NULL;
|
||||
prp->pr_search_result_count = 0;
|
||||
@@ -136,6 +140,8 @@ pagedresults_parse_control_value(Slapi_PBlock *pb,
|
||||
return LDAP_UNWILLING_TO_PERFORM;
|
||||
}
|
||||
|
||||
+ /* Acquire hash-based lock for paged results list access
|
||||
+ * IMPORTANT: Never acquire this lock when holding c_mutex */
|
||||
pthread_mutex_lock(pageresult_lock_get_addr(conn));
|
||||
/* the ber encoding is no longer needed */
|
||||
ber_free(ber, 1);
|
||||
@@ -184,10 +190,6 @@ pagedresults_parse_control_value(Slapi_PBlock *pb,
|
||||
goto bail;
|
||||
}
|
||||
|
||||
- if ((*index > -1) && (*index < conn->c_pagedresults.prl_maxlen) &&
|
||||
- !conn->c_pagedresults.prl_list[*index].pr_mutex) {
|
||||
- conn->c_pagedresults.prl_list[*index].pr_mutex = PR_NewLock();
|
||||
- }
|
||||
conn->c_pagedresults.prl_count++;
|
||||
} else {
|
||||
/* Repeated paged results request.
|
||||
@@ -327,8 +329,14 @@ bailout:
|
||||
"<= idx=%d\n", index);
|
||||
}
|
||||
|
||||
+/*
|
||||
+ * Free one paged result entry by index.
|
||||
+ *
|
||||
+ * Locking: If locked=0, acquires pageresult_lock. If locked=1, assumes
|
||||
+ * caller already holds pageresult_lock. Never call when holding c_mutex.
|
||||
+ */
|
||||
int
|
||||
-pagedresults_free_one(Connection *conn, Operation *op, int index)
|
||||
+pagedresults_free_one(Connection *conn, Operation *op, bool locked, int index)
|
||||
{
|
||||
int rc = -1;
|
||||
|
||||
@@ -338,7 +346,9 @@ pagedresults_free_one(Connection *conn, Operation *op, int index)
|
||||
slapi_log_err(SLAPI_LOG_TRACE, "pagedresults_free_one",
|
||||
"=> idx=%d\n", index);
|
||||
if (conn && (index > -1)) {
|
||||
- pthread_mutex_lock(pageresult_lock_get_addr(conn));
|
||||
+ if (!locked) {
|
||||
+ pthread_mutex_lock(pageresult_lock_get_addr(conn));
|
||||
+ }
|
||||
if (conn->c_pagedresults.prl_count <= 0) {
|
||||
slapi_log_err(SLAPI_LOG_TRACE, "pagedresults_free_one",
|
||||
"conn=%" PRIu64 " paged requests list count is %d\n",
|
||||
@@ -349,7 +359,9 @@ pagedresults_free_one(Connection *conn, Operation *op, int index)
|
||||
conn->c_pagedresults.prl_count--;
|
||||
rc = 0;
|
||||
}
|
||||
- pthread_mutex_unlock(pageresult_lock_get_addr(conn));
|
||||
+ if (!locked) {
|
||||
+ pthread_mutex_unlock(pageresult_lock_get_addr(conn));
|
||||
+ }
|
||||
}
|
||||
|
||||
slapi_log_err(SLAPI_LOG_TRACE, "pagedresults_free_one", "<= %d\n", rc);
|
||||
@@ -357,21 +369,28 @@ pagedresults_free_one(Connection *conn, Operation *op, int index)
|
||||
}
|
||||
|
||||
/*
|
||||
- * Used for abandoning - pageresult_lock_get_addr(conn) is already locked in do_abandone.
|
||||
+ * Free one paged result entry by message ID.
|
||||
+ *
|
||||
+ * Locking: If locked=0, acquires pageresult_lock. If locked=1, assumes
|
||||
+ * caller already holds pageresult_lock. Never call when holding c_mutex.
|
||||
*/
|
||||
int
|
||||
-pagedresults_free_one_msgid(Connection *conn, ber_int_t msgid, pthread_mutex_t *mutex)
|
||||
+pagedresults_free_one_msgid(Connection *conn, ber_int_t msgid, bool locked)
|
||||
{
|
||||
int rc = -1;
|
||||
int i;
|
||||
+ pthread_mutex_t *lock = NULL;
|
||||
|
||||
if (conn && (msgid > -1)) {
|
||||
if (conn->c_pagedresults.prl_maxlen <= 0) {
|
||||
; /* Not a paged result. */
|
||||
} else {
|
||||
slapi_log_err(SLAPI_LOG_TRACE,
|
||||
- "pagedresults_free_one_msgid_nolock", "=> msgid=%d\n", msgid);
|
||||
- pthread_mutex_lock(mutex);
|
||||
+ "pagedresults_free_one_msgid", "=> msgid=%d\n", msgid);
|
||||
+ lock = pageresult_lock_get_addr(conn);
|
||||
+ if (!locked) {
|
||||
+ pthread_mutex_lock(lock);
|
||||
+ }
|
||||
for (i = 0; i < conn->c_pagedresults.prl_maxlen; i++) {
|
||||
if (conn->c_pagedresults.prl_list[i].pr_msgid == msgid) {
|
||||
PagedResults *prp = conn->c_pagedresults.prl_list + i;
|
||||
@@ -390,9 +409,11 @@ pagedresults_free_one_msgid(Connection *conn, ber_int_t msgid, pthread_mutex_t *
|
||||
break;
|
||||
}
|
||||
}
|
||||
- pthread_mutex_unlock(mutex);
|
||||
+ if (!locked) {
|
||||
+ pthread_mutex_unlock(lock);
|
||||
+ }
|
||||
slapi_log_err(SLAPI_LOG_TRACE,
|
||||
- "pagedresults_free_one_msgid_nolock", "<= %d\n", rc);
|
||||
+ "pagedresults_free_one_msgid", "<= %d\n", rc);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -418,29 +439,43 @@ pagedresults_get_current_be(Connection *conn, int index)
|
||||
return be;
|
||||
}
|
||||
|
||||
+/*
|
||||
+ * Set current backend for a paged result entry.
|
||||
+ *
|
||||
+ * Locking: If locked=false, acquires pageresult_lock. If locked=true, assumes
|
||||
+ * caller already holds pageresult_lock. Never call when holding c_mutex.
|
||||
+ */
|
||||
int
|
||||
-pagedresults_set_current_be(Connection *conn, Slapi_Backend *be, int index, int nolock)
|
||||
+pagedresults_set_current_be(Connection *conn, Slapi_Backend *be, int index, bool locked)
|
||||
{
|
||||
int rc = -1;
|
||||
slapi_log_err(SLAPI_LOG_TRACE,
|
||||
"pagedresults_set_current_be", "=> idx=%d\n", index);
|
||||
if (conn && (index > -1)) {
|
||||
- if (!nolock)
|
||||
+ if (!locked) {
|
||||
pthread_mutex_lock(pageresult_lock_get_addr(conn));
|
||||
+ }
|
||||
if (index < conn->c_pagedresults.prl_maxlen) {
|
||||
conn->c_pagedresults.prl_list[index].pr_current_be = be;
|
||||
}
|
||||
rc = 0;
|
||||
- if (!nolock)
|
||||
+ if (!locked) {
|
||||
pthread_mutex_unlock(pageresult_lock_get_addr(conn));
|
||||
+ }
|
||||
}
|
||||
slapi_log_err(SLAPI_LOG_TRACE,
|
||||
"pagedresults_set_current_be", "<= %d\n", rc);
|
||||
return rc;
|
||||
}
|
||||
|
||||
+/*
|
||||
+ * Get search result set for a paged result entry.
|
||||
+ *
|
||||
+ * Locking: If locked=0, acquires pageresult_lock. If locked=1, assumes
|
||||
+ * caller already holds pageresult_lock. Never call when holding c_mutex.
|
||||
+ */
|
||||
void *
|
||||
-pagedresults_get_search_result(Connection *conn, Operation *op, int locked, int index)
|
||||
+pagedresults_get_search_result(Connection *conn, Operation *op, bool locked, int index)
|
||||
{
|
||||
void *sr = NULL;
|
||||
if (!op_is_pagedresults(op)) {
|
||||
@@ -465,8 +500,14 @@ pagedresults_get_search_result(Connection *conn, Operation *op, int locked, int
|
||||
return sr;
|
||||
}
|
||||
|
||||
+/*
|
||||
+ * Set search result set for a paged result entry.
|
||||
+ *
|
||||
+ * Locking: If locked=0, acquires pageresult_lock. If locked=1, assumes
|
||||
+ * caller already holds pageresult_lock. Never call when holding c_mutex.
|
||||
+ */
|
||||
int
|
||||
-pagedresults_set_search_result(Connection *conn, Operation *op, void *sr, int locked, int index)
|
||||
+pagedresults_set_search_result(Connection *conn, Operation *op, void *sr, bool locked, int index)
|
||||
{
|
||||
int rc = -1;
|
||||
if (!op_is_pagedresults(op)) {
|
||||
@@ -494,8 +535,14 @@ pagedresults_set_search_result(Connection *conn, Operation *op, void *sr, int lo
|
||||
return rc;
|
||||
}
|
||||
|
||||
+/*
|
||||
+ * Get search result count for a paged result entry.
|
||||
+ *
|
||||
+ * Locking: If locked=0, acquires pageresult_lock. If locked=1, assumes
|
||||
+ * caller already holds pageresult_lock. Never call when holding c_mutex.
|
||||
+ */
|
||||
int
|
||||
-pagedresults_get_search_result_count(Connection *conn, Operation *op, int index)
|
||||
+pagedresults_get_search_result_count(Connection *conn, Operation *op, bool locked, int index)
|
||||
{
|
||||
int count = 0;
|
||||
if (!op_is_pagedresults(op)) {
|
||||
@@ -504,19 +551,29 @@ pagedresults_get_search_result_count(Connection *conn, Operation *op, int index)
|
||||
slapi_log_err(SLAPI_LOG_TRACE,
|
||||
"pagedresults_get_search_result_count", "=> idx=%d\n", index);
|
||||
if (conn && (index > -1)) {
|
||||
- pthread_mutex_lock(pageresult_lock_get_addr(conn));
|
||||
+ if (!locked) {
|
||||
+ pthread_mutex_lock(pageresult_lock_get_addr(conn));
|
||||
+ }
|
||||
if (index < conn->c_pagedresults.prl_maxlen) {
|
||||
count = conn->c_pagedresults.prl_list[index].pr_search_result_count;
|
||||
}
|
||||
- pthread_mutex_unlock(pageresult_lock_get_addr(conn));
|
||||
+ if (!locked) {
|
||||
+ pthread_mutex_unlock(pageresult_lock_get_addr(conn));
|
||||
+ }
|
||||
}
|
||||
slapi_log_err(SLAPI_LOG_TRACE,
|
||||
"pagedresults_get_search_result_count", "<= %d\n", count);
|
||||
return count;
|
||||
}
|
||||
|
||||
+/*
|
||||
+ * Set search result count for a paged result entry.
|
||||
+ *
|
||||
+ * Locking: If locked=0, acquires pageresult_lock. If locked=1, assumes
|
||||
+ * caller already holds pageresult_lock. Never call when holding c_mutex.
|
||||
+ */
|
||||
int
|
||||
-pagedresults_set_search_result_count(Connection *conn, Operation *op, int count, int index)
|
||||
+pagedresults_set_search_result_count(Connection *conn, Operation *op, int count, bool locked, int index)
|
||||
{
|
||||
int rc = -1;
|
||||
if (!op_is_pagedresults(op)) {
|
||||
@@ -525,11 +582,15 @@ pagedresults_set_search_result_count(Connection *conn, Operation *op, int count,
|
||||
slapi_log_err(SLAPI_LOG_TRACE,
|
||||
"pagedresults_set_search_result_count", "=> idx=%d\n", index);
|
||||
if (conn && (index > -1)) {
|
||||
- pthread_mutex_lock(pageresult_lock_get_addr(conn));
|
||||
+ if (!locked) {
|
||||
+ pthread_mutex_lock(pageresult_lock_get_addr(conn));
|
||||
+ }
|
||||
if (index < conn->c_pagedresults.prl_maxlen) {
|
||||
conn->c_pagedresults.prl_list[index].pr_search_result_count = count;
|
||||
}
|
||||
- pthread_mutex_unlock(pageresult_lock_get_addr(conn));
|
||||
+ if (!locked) {
|
||||
+ pthread_mutex_unlock(pageresult_lock_get_addr(conn));
|
||||
+ }
|
||||
rc = 0;
|
||||
}
|
||||
slapi_log_err(SLAPI_LOG_TRACE,
|
||||
@@ -537,9 +598,16 @@ pagedresults_set_search_result_count(Connection *conn, Operation *op, int count,
|
||||
return rc;
|
||||
}
|
||||
|
||||
+/*
|
||||
+ * Get search result set size estimate for a paged result entry.
|
||||
+ *
|
||||
+ * Locking: If locked=0, acquires pageresult_lock. If locked=1, assumes
|
||||
+ * caller already holds pageresult_lock. Never call when holding c_mutex.
|
||||
+ */
|
||||
int
|
||||
pagedresults_get_search_result_set_size_estimate(Connection *conn,
|
||||
Operation *op,
|
||||
+ bool locked,
|
||||
int index)
|
||||
{
|
||||
int count = 0;
|
||||
@@ -550,11 +618,15 @@ pagedresults_get_search_result_set_size_estimate(Connection *conn,
|
||||
"pagedresults_get_search_result_set_size_estimate",
|
||||
"=> idx=%d\n", index);
|
||||
if (conn && (index > -1)) {
|
||||
- pthread_mutex_lock(pageresult_lock_get_addr(conn));
|
||||
+ if (!locked) {
|
||||
+ pthread_mutex_lock(pageresult_lock_get_addr(conn));
|
||||
+ }
|
||||
if (index < conn->c_pagedresults.prl_maxlen) {
|
||||
count = conn->c_pagedresults.prl_list[index].pr_search_result_set_size_estimate;
|
||||
}
|
||||
- pthread_mutex_unlock(pageresult_lock_get_addr(conn));
|
||||
+ if (!locked) {
|
||||
+ pthread_mutex_unlock(pageresult_lock_get_addr(conn));
|
||||
+ }
|
||||
}
|
||||
slapi_log_err(SLAPI_LOG_TRACE,
|
||||
"pagedresults_get_search_result_set_size_estimate", "<= %d\n",
|
||||
@@ -562,10 +634,17 @@ pagedresults_get_search_result_set_size_estimate(Connection *conn,
|
||||
return count;
|
||||
}
|
||||
|
||||
+/*
|
||||
+ * Set search result set size estimate for a paged result entry.
|
||||
+ *
|
||||
+ * Locking: If locked=0, acquires pageresult_lock. If locked=1, assumes
|
||||
+ * caller already holds pageresult_lock. Never call when holding c_mutex.
|
||||
+ */
|
||||
int
|
||||
pagedresults_set_search_result_set_size_estimate(Connection *conn,
|
||||
Operation *op,
|
||||
int count,
|
||||
+ bool locked,
|
||||
int index)
|
||||
{
|
||||
int rc = -1;
|
||||
@@ -576,11 +655,15 @@ pagedresults_set_search_result_set_size_estimate(Connection *conn,
|
||||
"pagedresults_set_search_result_set_size_estimate",
|
||||
"=> idx=%d\n", index);
|
||||
if (conn && (index > -1)) {
|
||||
- pthread_mutex_lock(pageresult_lock_get_addr(conn));
|
||||
+ if (!locked) {
|
||||
+ pthread_mutex_lock(pageresult_lock_get_addr(conn));
|
||||
+ }
|
||||
if (index < conn->c_pagedresults.prl_maxlen) {
|
||||
conn->c_pagedresults.prl_list[index].pr_search_result_set_size_estimate = count;
|
||||
}
|
||||
- pthread_mutex_unlock(pageresult_lock_get_addr(conn));
|
||||
+ if (!locked) {
|
||||
+ pthread_mutex_unlock(pageresult_lock_get_addr(conn));
|
||||
+ }
|
||||
rc = 0;
|
||||
}
|
||||
slapi_log_err(SLAPI_LOG_TRACE,
|
||||
@@ -589,8 +672,14 @@ pagedresults_set_search_result_set_size_estimate(Connection *conn,
|
||||
return rc;
|
||||
}
|
||||
|
||||
+/*
|
||||
+ * Get with_sort flag for a paged result entry.
|
||||
+ *
|
||||
+ * Locking: If locked=0, acquires pageresult_lock. If locked=1, assumes
|
||||
+ * caller already holds pageresult_lock. Never call when holding c_mutex.
|
||||
+ */
|
||||
int
|
||||
-pagedresults_get_with_sort(Connection *conn, Operation *op, int index)
|
||||
+pagedresults_get_with_sort(Connection *conn, Operation *op, bool locked, int index)
|
||||
{
|
||||
int flags = 0;
|
||||
if (!op_is_pagedresults(op)) {
|
||||
@@ -599,19 +688,29 @@ pagedresults_get_with_sort(Connection *conn, Operation *op, int index)
|
||||
slapi_log_err(SLAPI_LOG_TRACE,
|
||||
"pagedresults_get_with_sort", "=> idx=%d\n", index);
|
||||
if (conn && (index > -1)) {
|
||||
- pthread_mutex_lock(pageresult_lock_get_addr(conn));
|
||||
+ if (!locked) {
|
||||
+ pthread_mutex_lock(pageresult_lock_get_addr(conn));
|
||||
+ }
|
||||
if (index < conn->c_pagedresults.prl_maxlen) {
|
||||
flags = conn->c_pagedresults.prl_list[index].pr_flags & CONN_FLAG_PAGEDRESULTS_WITH_SORT;
|
||||
}
|
||||
- pthread_mutex_unlock(pageresult_lock_get_addr(conn));
|
||||
+ if (!locked) {
|
||||
+ pthread_mutex_unlock(pageresult_lock_get_addr(conn));
|
||||
+ }
|
||||
}
|
||||
slapi_log_err(SLAPI_LOG_TRACE,
|
||||
"pagedresults_get_with_sort", "<= %d\n", flags);
|
||||
return flags;
|
||||
}
|
||||
|
||||
+/*
|
||||
+ * Set with_sort flag for a paged result entry.
|
||||
+ *
|
||||
+ * Locking: If locked=0, acquires pageresult_lock. If locked=1, assumes
|
||||
+ * caller already holds pageresult_lock. Never call when holding c_mutex.
|
||||
+ */
|
||||
int
|
||||
-pagedresults_set_with_sort(Connection *conn, Operation *op, int flags, int index)
|
||||
+pagedresults_set_with_sort(Connection *conn, Operation *op, int flags, bool locked, int index)
|
||||
{
|
||||
int rc = -1;
|
||||
if (!op_is_pagedresults(op)) {
|
||||
@@ -620,14 +719,18 @@ pagedresults_set_with_sort(Connection *conn, Operation *op, int flags, int index
|
||||
slapi_log_err(SLAPI_LOG_TRACE,
|
||||
"pagedresults_set_with_sort", "=> idx=%d\n", index);
|
||||
if (conn && (index > -1)) {
|
||||
- pthread_mutex_lock(pageresult_lock_get_addr(conn));
|
||||
+ if (!locked) {
|
||||
+ pthread_mutex_lock(pageresult_lock_get_addr(conn));
|
||||
+ }
|
||||
if (index < conn->c_pagedresults.prl_maxlen) {
|
||||
if (flags & OP_FLAG_SERVER_SIDE_SORTING) {
|
||||
conn->c_pagedresults.prl_list[index].pr_flags |=
|
||||
CONN_FLAG_PAGEDRESULTS_WITH_SORT;
|
||||
}
|
||||
}
|
||||
- pthread_mutex_unlock(pageresult_lock_get_addr(conn));
|
||||
+ if (!locked) {
|
||||
+ pthread_mutex_unlock(pageresult_lock_get_addr(conn));
|
||||
+ }
|
||||
rc = 0;
|
||||
}
|
||||
slapi_log_err(SLAPI_LOG_TRACE, "pagedresults_set_with_sort", "<= %d\n", rc);
|
||||
@@ -802,10 +905,6 @@ pagedresults_cleanup(Connection *conn, int needlock)
|
||||
rc = 1;
|
||||
}
|
||||
prp->pr_current_be = NULL;
|
||||
- if (prp->pr_mutex) {
|
||||
- PR_DestroyLock(prp->pr_mutex);
|
||||
- prp->pr_mutex = NULL;
|
||||
- }
|
||||
memset(prp, '\0', sizeof(PagedResults));
|
||||
}
|
||||
conn->c_pagedresults.prl_count = 0;
|
||||
@@ -840,10 +939,6 @@ pagedresults_cleanup_all(Connection *conn, int needlock)
|
||||
i < conn->c_pagedresults.prl_maxlen;
|
||||
i++) {
|
||||
prp = conn->c_pagedresults.prl_list + i;
|
||||
- if (prp->pr_mutex) {
|
||||
- PR_DestroyLock(prp->pr_mutex);
|
||||
- prp->pr_mutex = NULL;
|
||||
- }
|
||||
if (prp->pr_current_be && prp->pr_search_result_set &&
|
||||
prp->pr_current_be->be_search_results_release) {
|
||||
prp->pr_current_be->be_search_results_release(&(prp->pr_search_result_set));
|
||||
@@ -1010,43 +1105,8 @@ op_set_pagedresults(Operation *op)
|
||||
op->o_flags |= OP_FLAG_PAGED_RESULTS;
|
||||
}
|
||||
|
||||
-/*
|
||||
- * pagedresults_lock/unlock -- introduced to protect search results for the
|
||||
- * asynchronous searches. Do not call these functions while the PR conn lock
|
||||
- * is held (e.g. pageresult_lock_get_addr(conn))
|
||||
- */
|
||||
-void
|
||||
-pagedresults_lock(Connection *conn, int index)
|
||||
-{
|
||||
- PagedResults *prp;
|
||||
- if (!conn || (index < 0) || (index >= conn->c_pagedresults.prl_maxlen)) {
|
||||
- return;
|
||||
- }
|
||||
- pthread_mutex_lock(pageresult_lock_get_addr(conn));
|
||||
- prp = conn->c_pagedresults.prl_list + index;
|
||||
- if (prp->pr_mutex) {
|
||||
- PR_Lock(prp->pr_mutex);
|
||||
- }
|
||||
- pthread_mutex_unlock(pageresult_lock_get_addr(conn));
|
||||
-}
|
||||
-
|
||||
-void
|
||||
-pagedresults_unlock(Connection *conn, int index)
|
||||
-{
|
||||
- PagedResults *prp;
|
||||
- if (!conn || (index < 0) || (index >= conn->c_pagedresults.prl_maxlen)) {
|
||||
- return;
|
||||
- }
|
||||
- pthread_mutex_lock(pageresult_lock_get_addr(conn));
|
||||
- prp = conn->c_pagedresults.prl_list + index;
|
||||
- if (prp->pr_mutex) {
|
||||
- PR_Unlock(prp->pr_mutex);
|
||||
- }
|
||||
- pthread_mutex_unlock(pageresult_lock_get_addr(conn));
|
||||
-}
|
||||
-
|
||||
int
|
||||
-pagedresults_is_abandoned_or_notavailable(Connection *conn, int locked, int index)
|
||||
+pagedresults_is_abandoned_or_notavailable(Connection *conn, bool locked, int index)
|
||||
{
|
||||
PagedResults *prp;
|
||||
int32_t result;
|
||||
@@ -1066,7 +1126,7 @@ pagedresults_is_abandoned_or_notavailable(Connection *conn, int locked, int inde
|
||||
}
|
||||
|
||||
int
|
||||
-pagedresults_set_search_result_pb(Slapi_PBlock *pb, void *sr, int locked)
|
||||
+pagedresults_set_search_result_pb(Slapi_PBlock *pb, void *sr, bool locked)
|
||||
{
|
||||
int rc = -1;
|
||||
Connection *conn = NULL;
|
||||
diff --git a/ldap/servers/slapd/proto-slap.h b/ldap/servers/slapd/proto-slap.h
|
||||
index 765c12bf5..455d6d718 100644
|
||||
--- a/ldap/servers/slapd/proto-slap.h
|
||||
+++ b/ldap/servers/slapd/proto-slap.h
|
||||
@@ -1614,20 +1614,22 @@ pthread_mutex_t *pageresult_lock_get_addr(Connection *conn);
|
||||
int pagedresults_parse_control_value(Slapi_PBlock *pb, struct berval *psbvp, ber_int_t *pagesize, int *index, Slapi_Backend *be);
|
||||
void pagedresults_set_response_control(Slapi_PBlock *pb, int iscritical, ber_int_t estimate, int curr_search_count, int index);
|
||||
Slapi_Backend *pagedresults_get_current_be(Connection *conn, int index);
|
||||
-int pagedresults_set_current_be(Connection *conn, Slapi_Backend *be, int index, int nolock);
|
||||
-void *pagedresults_get_search_result(Connection *conn, Operation *op, int locked, int index);
|
||||
-int pagedresults_set_search_result(Connection *conn, Operation *op, void *sr, int locked, int index);
|
||||
-int pagedresults_get_search_result_count(Connection *conn, Operation *op, int index);
|
||||
-int pagedresults_set_search_result_count(Connection *conn, Operation *op, int cnt, int index);
|
||||
+int pagedresults_set_current_be(Connection *conn, Slapi_Backend *be, int index, bool locked);
|
||||
+void *pagedresults_get_search_result(Connection *conn, Operation *op, bool locked, int index);
|
||||
+int pagedresults_set_search_result(Connection *conn, Operation *op, void *sr, bool locked, int index);
|
||||
+int pagedresults_get_search_result_count(Connection *conn, Operation *op, bool locked, int index);
|
||||
+int pagedresults_set_search_result_count(Connection *conn, Operation *op, int cnt, bool locked, int index);
|
||||
int pagedresults_get_search_result_set_size_estimate(Connection *conn,
|
||||
Operation *op,
|
||||
+ bool locked,
|
||||
int index);
|
||||
int pagedresults_set_search_result_set_size_estimate(Connection *conn,
|
||||
Operation *op,
|
||||
int cnt,
|
||||
+ bool locked,
|
||||
int index);
|
||||
-int pagedresults_get_with_sort(Connection *conn, Operation *op, int index);
|
||||
-int pagedresults_set_with_sort(Connection *conn, Operation *op, int flags, int index);
|
||||
+int pagedresults_get_with_sort(Connection *conn, Operation *op, bool locked, int index);
|
||||
+int pagedresults_set_with_sort(Connection *conn, Operation *op, int flags, bool locked, int index);
|
||||
int pagedresults_get_unindexed(Connection *conn, Operation *op, int index);
|
||||
int pagedresults_set_unindexed(Connection *conn, Operation *op, int index);
|
||||
int pagedresults_get_sort_result_code(Connection *conn, Operation *op, int index);
|
||||
@@ -1639,15 +1641,13 @@ int pagedresults_cleanup(Connection *conn, int needlock);
|
||||
int pagedresults_is_timedout_nolock(Connection *conn);
|
||||
int pagedresults_reset_timedout_nolock(Connection *conn);
|
||||
int pagedresults_in_use_nolock(Connection *conn);
|
||||
-int pagedresults_free_one(Connection *conn, Operation *op, int index);
|
||||
-int pagedresults_free_one_msgid(Connection *conn, ber_int_t msgid, pthread_mutex_t *mutex);
|
||||
+int pagedresults_free_one(Connection *conn, Operation *op, bool locked, int index);
|
||||
+int pagedresults_free_one_msgid(Connection *conn, ber_int_t msgid, bool locked);
|
||||
int op_is_pagedresults(Operation *op);
|
||||
int pagedresults_cleanup_all(Connection *conn, int needlock);
|
||||
void op_set_pagedresults(Operation *op);
|
||||
-void pagedresults_lock(Connection *conn, int index);
|
||||
-void pagedresults_unlock(Connection *conn, int index);
|
||||
-int pagedresults_is_abandoned_or_notavailable(Connection *conn, int locked, int index);
|
||||
-int pagedresults_set_search_result_pb(Slapi_PBlock *pb, void *sr, int locked);
|
||||
+int pagedresults_is_abandoned_or_notavailable(Connection *conn, bool locked, int index);
|
||||
+int pagedresults_set_search_result_pb(Slapi_PBlock *pb, void *sr, bool locked);
|
||||
|
||||
/*
|
||||
* sort.c
|
||||
diff --git a/ldap/servers/slapd/slap.h b/ldap/servers/slapd/slap.h
|
||||
index 11c5602e3..d494931c2 100644
|
||||
--- a/ldap/servers/slapd/slap.h
|
||||
+++ b/ldap/servers/slapd/slap.h
|
||||
@@ -89,6 +89,10 @@ static char ptokPBE[34] = "Internal (Software) Token ";
|
||||
#include <stdbool.h>
|
||||
#include <time.h> /* For timespec definitions */
|
||||
|
||||
+/* Macros for paged results lock parameter */
|
||||
+#define PR_LOCKED true
|
||||
+#define PR_NOT_LOCKED false
|
||||
+
|
||||
/* Provides our int types and platform specific requirements. */
|
||||
#include <slapi_pal.h>
|
||||
|
||||
@@ -1669,7 +1673,6 @@ typedef struct _paged_results
|
||||
struct timespec pr_timelimit_hr; /* expiry time of this request rel to clock monotonic */
|
||||
int pr_flags;
|
||||
ber_int_t pr_msgid; /* msgid of the request; to abandon */
|
||||
- PRLock *pr_mutex; /* protect each conn structure */
|
||||
} PagedResults;
|
||||
|
||||
/* array of simple paged structure stashed in connection */
|
||||
--
|
||||
2.52.0
|
||||
|
||||
183
0003-Issue-7108-Fix-shutdown-crash-in-entry-cache-destruc.patch
Normal file
183
0003-Issue-7108-Fix-shutdown-crash-in-entry-cache-destruc.patch
Normal file
@ -0,0 +1,183 @@
|
||||
From 4936f953fa3b0726c2b178f135cd78dcac7463ba Mon Sep 17 00:00:00 2001
|
||||
From: Simon Pichugin <spichugi@redhat.com>
|
||||
Date: Thu, 8 Jan 2026 10:02:39 -0800
|
||||
Subject: [PATCH] Issue 7108 - Fix shutdown crash in entry cache destruction
|
||||
(#7163)
|
||||
|
||||
Description: The entry cache could experience LRU list corruption when
|
||||
using pinned entries, leading to crashes during cache flush operations.
|
||||
|
||||
In entrycache_add_int(), when returning an existing cached entry, the
|
||||
code checked the wrong entry's state before calling lru_delete(). It
|
||||
checked the new entry 'e' but operated on the existing entry 'my_alt',
|
||||
causing lru_delete() to be called on entries not in the LRU list. This
|
||||
is fixed by checking my_alt's refcnt and pinned state instead.
|
||||
|
||||
In flush_hash(), pinned_remove() and lru_delete() were both called on
|
||||
pinned entries. Since pinned entries are in the pinned list, calling
|
||||
lru_delete() afterwards corrupted the list. This is fixed by calling
|
||||
either pinned_remove() or lru_delete() based on the entry's state.
|
||||
|
||||
A NULL check is added in entrycache_flush() and dncache_flush() to
|
||||
gracefully handle corrupted LRU lists and prevent crashes when
|
||||
traversing backwards through the list encounters an unexpected NULL.
|
||||
|
||||
Entry pointers are now always cleared after lru_delete() removal to
|
||||
prevent stale pointer issues in non-debug builds.
|
||||
|
||||
Fixes: https://github.com/389ds/389-ds-base/issues/7108
|
||||
|
||||
Reviewed by: @progier389, @vashirov (Thanks!!)
|
||||
---
|
||||
ldap/servers/slapd/back-ldbm/cache.c | 48 +++++++++++++++++++++++++---
|
||||
1 file changed, 43 insertions(+), 5 deletions(-)
|
||||
|
||||
diff --git a/ldap/servers/slapd/back-ldbm/cache.c b/ldap/servers/slapd/back-ldbm/cache.c
|
||||
index 2e4126134..a87f30687 100644
|
||||
--- a/ldap/servers/slapd/back-ldbm/cache.c
|
||||
+++ b/ldap/servers/slapd/back-ldbm/cache.c
|
||||
@@ -458,11 +458,13 @@ static void
|
||||
lru_delete(struct cache *cache, void *ptr)
|
||||
{
|
||||
struct backcommon *e;
|
||||
+
|
||||
if (NULL == ptr) {
|
||||
LOG("=> lru_delete\n<= lru_delete (null entry)\n");
|
||||
return;
|
||||
}
|
||||
e = (struct backcommon *)ptr;
|
||||
+
|
||||
#ifdef LDAP_CACHE_DEBUG_LRU
|
||||
pinned_verify(cache, __LINE__);
|
||||
lru_verify(cache, e, 1);
|
||||
@@ -475,8 +477,9 @@ lru_delete(struct cache *cache, void *ptr)
|
||||
e->ep_lrunext->ep_lruprev = e->ep_lruprev;
|
||||
else
|
||||
cache->c_lrutail = e->ep_lruprev;
|
||||
-#ifdef LDAP_CACHE_DEBUG_LRU
|
||||
+ /* Always clear pointers after removal to prevent stale pointer issues */
|
||||
e->ep_lrunext = e->ep_lruprev = NULL;
|
||||
+#ifdef LDAP_CACHE_DEBUG_LRU
|
||||
lru_verify(cache, e, 0);
|
||||
#endif
|
||||
}
|
||||
@@ -633,9 +636,14 @@ flush_hash(struct cache *cache, struct timespec *start_time, int32_t type)
|
||||
if (entry->ep_refcnt == 0) {
|
||||
entry->ep_refcnt++;
|
||||
if (entry->ep_state & ENTRY_STATE_PINNED) {
|
||||
+ /* Entry is in pinned list, not LRU - remove from pinned only.
|
||||
+ * pinned_remove clears lru pointers and won't add to LRU since refcnt > 0.
|
||||
+ */
|
||||
pinned_remove(cache, laste);
|
||||
+ } else {
|
||||
+ /* Entry is in LRU list - remove from LRU */
|
||||
+ lru_delete(cache, laste);
|
||||
}
|
||||
- lru_delete(cache, laste);
|
||||
if (type == ENTRY_CACHE) {
|
||||
entrycache_remove_int(cache, laste);
|
||||
entrycache_return(cache, (struct backentry **)&laste, PR_TRUE);
|
||||
@@ -679,9 +687,14 @@ flush_hash(struct cache *cache, struct timespec *start_time, int32_t type)
|
||||
if (entry->ep_refcnt == 0) {
|
||||
entry->ep_refcnt++;
|
||||
if (entry->ep_state & ENTRY_STATE_PINNED) {
|
||||
+ /* Entry is in pinned list, not LRU - remove from pinned only.
|
||||
+ * pinned_remove clears lru pointers and won't add to LRU since refcnt > 0.
|
||||
+ */
|
||||
pinned_remove(cache, laste);
|
||||
+ } else {
|
||||
+ /* Entry is in LRU list - remove from LRU */
|
||||
+ lru_delete(cache, laste);
|
||||
}
|
||||
- lru_delete(cache, laste);
|
||||
entrycache_remove_int(cache, laste);
|
||||
entrycache_return(cache, (struct backentry **)&laste, PR_TRUE);
|
||||
} else {
|
||||
@@ -772,6 +785,11 @@ entrycache_flush(struct cache *cache)
|
||||
} else {
|
||||
e = BACK_LRU_PREV(e, struct backentry *);
|
||||
}
|
||||
+ if (e == NULL) {
|
||||
+ slapi_log_err(SLAPI_LOG_WARNING, "entrycache_flush",
|
||||
+ "Unexpected NULL entry while flushing cache - LRU list may be corrupted\n");
|
||||
+ break;
|
||||
+ }
|
||||
ASSERT(e->ep_refcnt == 0);
|
||||
e->ep_refcnt++;
|
||||
if (entrycache_remove_int(cache, e) < 0) {
|
||||
@@ -1160,6 +1178,7 @@ pinned_remove(struct cache *cache, void *ptr)
|
||||
{
|
||||
struct backentry *e = (struct backentry *)ptr;
|
||||
ASSERT(e->ep_state & ENTRY_STATE_PINNED);
|
||||
+
|
||||
cache->c_pinned_ctx->npinned--;
|
||||
cache->c_pinned_ctx->size -= e->ep_size;
|
||||
e->ep_state &= ~ENTRY_STATE_PINNED;
|
||||
@@ -1172,13 +1191,23 @@ pinned_remove(struct cache *cache, void *ptr)
|
||||
cache->c_pinned_ctx->head = cache->c_pinned_ctx->tail = NULL;
|
||||
} else {
|
||||
cache->c_pinned_ctx->head = BACK_LRU_NEXT(e, struct backentry *);
|
||||
+ /* Update new head's prev pointer to NULL */
|
||||
+ if (cache->c_pinned_ctx->head) {
|
||||
+ cache->c_pinned_ctx->head->ep_lruprev = NULL;
|
||||
+ }
|
||||
}
|
||||
} else if (cache->c_pinned_ctx->tail == e) {
|
||||
cache->c_pinned_ctx->tail = BACK_LRU_PREV(e, struct backentry *);
|
||||
+ /* Update new tail's next pointer to NULL */
|
||||
+ if (cache->c_pinned_ctx->tail) {
|
||||
+ cache->c_pinned_ctx->tail->ep_lrunext = NULL;
|
||||
+ }
|
||||
} else {
|
||||
+ /* Middle of list: update both neighbors to point to each other */
|
||||
BACK_LRU_PREV(e, struct backentry *)->ep_lrunext = BACK_LRU_NEXT(e, struct backcommon *);
|
||||
BACK_LRU_NEXT(e, struct backentry *)->ep_lruprev = BACK_LRU_PREV(e, struct backcommon *);
|
||||
}
|
||||
+ /* Clear the removed entry's pointers */
|
||||
e->ep_lrunext = e->ep_lruprev = NULL;
|
||||
if (e->ep_refcnt == 0) {
|
||||
lru_add(cache, ptr);
|
||||
@@ -1245,6 +1274,7 @@ pinned_add(struct cache *cache, void *ptr)
|
||||
return false;
|
||||
}
|
||||
/* Now it is time to insert the entry in the pinned list */
|
||||
+
|
||||
cache->c_pinned_ctx->npinned++;
|
||||
cache->c_pinned_ctx->size += e->ep_size;
|
||||
e->ep_state |= ENTRY_STATE_PINNED;
|
||||
@@ -1754,7 +1784,7 @@ entrycache_add_int(struct cache *cache, struct backentry *e, int state, struct b
|
||||
* 3) ep_state: 0 && state: 0
|
||||
* ==> increase the refcnt
|
||||
*/
|
||||
- if (e->ep_refcnt == 0)
|
||||
+ if (e->ep_refcnt == 0 && (e->ep_state & ENTRY_STATE_PINNED) == 0)
|
||||
lru_delete(cache, (void *)e);
|
||||
e->ep_refcnt++;
|
||||
e->ep_state &= ~ENTRY_STATE_UNAVAILABLE;
|
||||
@@ -1781,7 +1811,7 @@ entrycache_add_int(struct cache *cache, struct backentry *e, int state, struct b
|
||||
} else {
|
||||
if (alt) {
|
||||
*alt = my_alt;
|
||||
- if (e->ep_refcnt == 0 && (e->ep_state & ENTRY_STATE_PINNED) == 0)
|
||||
+ if (my_alt->ep_refcnt == 0 && (my_alt->ep_state & ENTRY_STATE_PINNED) == 0)
|
||||
lru_delete(cache, (void *)*alt);
|
||||
(*alt)->ep_refcnt++;
|
||||
LOG("the entry %s already exists. returning existing entry %s (state: 0x%x)\n",
|
||||
@@ -2379,6 +2409,14 @@ dncache_flush(struct cache *cache)
|
||||
} else {
|
||||
dn = BACK_LRU_PREV(dn, struct backdn *);
|
||||
}
|
||||
+ if (dn == NULL) {
|
||||
+ /* Safety check: we should normally exit via the CACHE_LRU_HEAD check.
|
||||
+ * If we get here, c_lruhead may be NULL or the LRU list is corrupted.
|
||||
+ */
|
||||
+ slapi_log_err(SLAPI_LOG_WARNING, "dncache_flush",
|
||||
+ "Unexpected NULL entry while flushing cache - LRU list may be corrupted\n");
|
||||
+ break;
|
||||
+ }
|
||||
ASSERT(dn->ep_refcnt == 0);
|
||||
dn->ep_refcnt++;
|
||||
if (dncache_remove_int(cache, dn) < 0) {
|
||||
--
|
||||
2.52.0
|
||||
|
||||
215
0004-Issue-7172-Index-ordering-mismatch-after-upgrade-717.patch
Normal file
215
0004-Issue-7172-Index-ordering-mismatch-after-upgrade-717.patch
Normal file
@ -0,0 +1,215 @@
|
||||
From 742c12e0247ab64e87da000a4de2f3e5c99044ab Mon Sep 17 00:00:00 2001
|
||||
From: Viktor Ashirov <vashirov@redhat.com>
|
||||
Date: Fri, 9 Jan 2026 11:39:50 +0100
|
||||
Subject: [PATCH] Issue 7172 - Index ordering mismatch after upgrade (#7173)
|
||||
|
||||
Bug Description:
|
||||
Commit daf731f55071d45eaf403a52b63d35f4e699ff28 introduced a regression.
|
||||
After upgrading to a version that adds `integerOrderingMatch` matching
|
||||
rule to `parentid` and `ancestorid` indexes, searches may return empty
|
||||
or incorrect results.
|
||||
|
||||
This happens because the existing index data was created with
|
||||
lexicographic ordering, but the new compare function expects integer
|
||||
ordering. Index lookups fail because the compare function doesn't match
|
||||
the data ordering.
|
||||
The root cause is that `ldbm_instance_create_default_indexes()` calls
|
||||
`attr_index_config()` unconditionally for `parentid` and `ancestorid`
|
||||
indexes, which triggers `ainfo_dup()` to overwrite `ai_key_cmp_fn` on
|
||||
existing indexes. This breaks indexes that were created without the
|
||||
`integerOrderingMatch` matching rule.
|
||||
|
||||
Fix Description:
|
||||
* Call `attr_index_config()` for `parentid` and `ancestorid` indexes
|
||||
only if index config doesn't exist.
|
||||
|
||||
* Add `upgrade_check_id_index_matching_rule()` that logs an error on
|
||||
server startup if `parentid` or `ancestorid` indexes are missing the
|
||||
integerOrderingMatch matching rule, advising administrators to reindex.
|
||||
|
||||
Fixes: https://github.com/389ds/389-ds-base/issues/7172
|
||||
|
||||
Reviewed by: @tbordaz, @progier389, @droideck (Thanks!)
|
||||
---
|
||||
ldap/servers/slapd/back-ldbm/instance.c | 25 ++++--
|
||||
ldap/servers/slapd/upgrade.c | 107 +++++++++++++++++++++++-
|
||||
2 files changed, 123 insertions(+), 9 deletions(-)
|
||||
|
||||
diff --git a/ldap/servers/slapd/back-ldbm/instance.c b/ldap/servers/slapd/back-ldbm/instance.c
|
||||
index cb002c379..71bf0f6fa 100644
|
||||
--- a/ldap/servers/slapd/back-ldbm/instance.c
|
||||
+++ b/ldap/servers/slapd/back-ldbm/instance.c
|
||||
@@ -190,6 +190,7 @@ ldbm_instance_create_default_indexes(backend *be)
|
||||
char *ancestorid_indexes_limit = NULL;
|
||||
char *parentid_indexes_limit = NULL;
|
||||
struct attrinfo *ai = NULL;
|
||||
+ struct attrinfo *index_already_configured = NULL;
|
||||
struct index_idlistsizeinfo *iter;
|
||||
int cookie;
|
||||
int limit;
|
||||
@@ -248,10 +249,14 @@ ldbm_instance_create_default_indexes(backend *be)
|
||||
ldbm_instance_config_add_index_entry(inst, e, flags);
|
||||
slapi_entry_free(e);
|
||||
|
||||
- e = ldbm_instance_init_config_entry(LDBM_PARENTID_STR, "eq", 0, 0, 0, "integerOrderingMatch", parentid_indexes_limit);
|
||||
- ldbm_instance_config_add_index_entry(inst, e, flags);
|
||||
- attr_index_config(be, "ldbm index init", 0, e, 1, 0, NULL);
|
||||
- slapi_entry_free(e);
|
||||
+ ainfo_get(be, (char *)LDBM_PARENTID_STR, &ai);
|
||||
+ index_already_configured = ai;
|
||||
+ if (!index_already_configured) {
|
||||
+ e = ldbm_instance_init_config_entry(LDBM_PARENTID_STR, "eq", 0, 0, 0, "integerOrderingMatch", parentid_indexes_limit);
|
||||
+ ldbm_instance_config_add_index_entry(inst, e, flags);
|
||||
+ attr_index_config(be, "ldbm index init", 0, e, 1, 0, NULL);
|
||||
+ slapi_entry_free(e);
|
||||
+ }
|
||||
|
||||
e = ldbm_instance_init_config_entry("objectclass", "eq", 0, 0, 0, 0, 0);
|
||||
ldbm_instance_config_add_index_entry(inst, e, flags);
|
||||
@@ -288,10 +293,14 @@ ldbm_instance_create_default_indexes(backend *be)
|
||||
* ancestorid is special, there is actually no such attr type
|
||||
* but we still want to use the attr index file APIs.
|
||||
*/
|
||||
- e = ldbm_instance_init_config_entry(LDBM_ANCESTORID_STR, "eq", 0, 0, 0, "integerOrderingMatch", ancestorid_indexes_limit);
|
||||
- ldbm_instance_config_add_index_entry(inst, e, flags);
|
||||
- attr_index_config(be, "ldbm index init", 0, e, 1, 0, NULL);
|
||||
- slapi_entry_free(e);
|
||||
+ ainfo_get(be, (char *)LDBM_ANCESTORID_STR, &ai);
|
||||
+ index_already_configured = ai;
|
||||
+ if (!index_already_configured) {
|
||||
+ e = ldbm_instance_init_config_entry(LDBM_ANCESTORID_STR, "eq", 0, 0, 0, "integerOrderingMatch", ancestorid_indexes_limit);
|
||||
+ ldbm_instance_config_add_index_entry(inst, e, flags);
|
||||
+ attr_index_config(be, "ldbm index init", 0, e, 1, 0, NULL);
|
||||
+ slapi_entry_free(e);
|
||||
+ }
|
||||
|
||||
slapi_ch_free_string(&ancestorid_indexes_limit);
|
||||
slapi_ch_free_string(&parentid_indexes_limit);
|
||||
diff --git a/ldap/servers/slapd/upgrade.c b/ldap/servers/slapd/upgrade.c
|
||||
index 858392564..b02e37ed6 100644
|
||||
--- a/ldap/servers/slapd/upgrade.c
|
||||
+++ b/ldap/servers/slapd/upgrade.c
|
||||
@@ -330,6 +330,107 @@ upgrade_remove_subtree_rename(void)
|
||||
return UPGRADE_SUCCESS;
|
||||
}
|
||||
|
||||
+/*
|
||||
+ * Check if parentid/ancestorid indexes are missing the integerOrderingMatch
|
||||
+ * matching rule.
|
||||
+ *
|
||||
+ * This function logs a warning if we detect this condition, advising
|
||||
+ * the administrator to reindex the affected attributes.
|
||||
+ */
|
||||
+static upgrade_status
|
||||
+upgrade_check_id_index_matching_rule(void)
|
||||
+{
|
||||
+ struct slapi_pblock *pb = slapi_pblock_new();
|
||||
+ Slapi_Entry **backends = NULL;
|
||||
+ const char *be_base_dn = "cn=ldbm database,cn=plugins,cn=config";
|
||||
+ const char *be_filter = "(objectclass=nsBackendInstance)";
|
||||
+ const char *attrs_to_check[] = {"parentid", "ancestorid", NULL};
|
||||
+ upgrade_status uresult = UPGRADE_SUCCESS;
|
||||
+
|
||||
+ /* Search for all backend instances */
|
||||
+ slapi_search_internal_set_pb(
|
||||
+ pb, be_base_dn,
|
||||
+ LDAP_SCOPE_ONELEVEL,
|
||||
+ be_filter, NULL, 0, NULL, NULL,
|
||||
+ plugin_get_default_component_id(), 0);
|
||||
+ slapi_search_internal_pb(pb);
|
||||
+ slapi_pblock_get(pb, SLAPI_PLUGIN_INTOP_SEARCH_ENTRIES, &backends);
|
||||
+
|
||||
+ if (backends) {
|
||||
+ for (size_t be_idx = 0; backends[be_idx] != NULL; be_idx++) {
|
||||
+ const char *be_name = slapi_entry_attr_get_ref(backends[be_idx], "cn");
|
||||
+ if (!be_name) {
|
||||
+ continue;
|
||||
+ }
|
||||
+
|
||||
+ /* Check each attribute that should have integerOrderingMatch */
|
||||
+ for (size_t attr_idx = 0; attrs_to_check[attr_idx] != NULL; attr_idx++) {
|
||||
+ const char *attr_name = attrs_to_check[attr_idx];
|
||||
+ struct slapi_pblock *idx_pb = slapi_pblock_new();
|
||||
+ Slapi_Entry **idx_entries = NULL;
|
||||
+ char *idx_dn = slapi_create_dn_string("cn=%s,cn=index,cn=%s,%s",
|
||||
+ attr_name, be_name, be_base_dn);
|
||||
+ char *idx_filter = "(objectclass=nsIndex)";
|
||||
+ PRBool has_matching_rule = PR_FALSE;
|
||||
+
|
||||
+ if (!idx_dn) {
|
||||
+ slapi_pblock_destroy(idx_pb);
|
||||
+ continue;
|
||||
+ }
|
||||
+
|
||||
+ slapi_search_internal_set_pb(
|
||||
+ idx_pb, idx_dn,
|
||||
+ LDAP_SCOPE_BASE,
|
||||
+ idx_filter, NULL, 0, NULL, NULL,
|
||||
+ plugin_get_default_component_id(), 0);
|
||||
+ slapi_search_internal_pb(idx_pb);
|
||||
+ slapi_pblock_get(idx_pb, SLAPI_PLUGIN_INTOP_SEARCH_ENTRIES, &idx_entries);
|
||||
+
|
||||
+ if (idx_entries && idx_entries[0]) {
|
||||
+ /* Index exists, check if it has integerOrderingMatch */
|
||||
+ Slapi_Attr *mr_attr = NULL;
|
||||
+ if (slapi_entry_attr_find(idx_entries[0], "nsMatchingRule", &mr_attr) == 0) {
|
||||
+ Slapi_Value *sval = NULL;
|
||||
+ int idx;
|
||||
+ for (idx = slapi_attr_first_value(mr_attr, &sval);
|
||||
+ idx != -1;
|
||||
+ idx = slapi_attr_next_value(mr_attr, idx, &sval)) {
|
||||
+ const struct berval *bval = slapi_value_get_berval(sval);
|
||||
+ if (bval && bval->bv_val &&
|
||||
+ strcasecmp(bval->bv_val, "integerOrderingMatch") == 0) {
|
||||
+ has_matching_rule = PR_TRUE;
|
||||
+ break;
|
||||
+ }
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ if (!has_matching_rule) {
|
||||
+ /* Index exists but doesn't have integerOrderingMatch, log a warning */
|
||||
+ slapi_log_err(SLAPI_LOG_ERR, "upgrade_check_id_index_matching_rule",
|
||||
+ "Index '%s' in backend '%s' is missing 'nsMatchingRule: integerOrderingMatch'. "
|
||||
+ "Incorrectly configured system indexes can lead to poor search performance, replication issues, and other operational problems. "
|
||||
+ "To fix this, add the matching rule and reindex: "
|
||||
+ "dsconf <instance> backend index set --add-mr integerOrderingMatch --attr %s %s && "
|
||||
+ "dsconf <instance> backend index reindex --attr %s %s. "
|
||||
+ "WARNING: Reindexing can be resource-intensive and may impact server performance on a live system. "
|
||||
+ "Consider scheduling reindexing during maintenance windows or periods of low activity.\n",
|
||||
+ attr_name, be_name, attr_name, be_name, attr_name, be_name);
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ slapi_ch_free_string(&idx_dn);
|
||||
+ slapi_free_search_results_internal(idx_pb);
|
||||
+ slapi_pblock_destroy(idx_pb);
|
||||
+ }
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ slapi_free_search_results_internal(pb);
|
||||
+ slapi_pblock_destroy(pb);
|
||||
+
|
||||
+ return uresult;
|
||||
+}
|
||||
+
|
||||
/*
|
||||
* Upgrade the base config of the PAM PTA plugin.
|
||||
*
|
||||
@@ -547,7 +648,11 @@ upgrade_server(void)
|
||||
if (upgrade_pam_pta_default_config() != UPGRADE_SUCCESS) {
|
||||
return UPGRADE_FAILURE;
|
||||
}
|
||||
-
|
||||
+
|
||||
+ if (upgrade_check_id_index_matching_rule() != UPGRADE_SUCCESS) {
|
||||
+ return UPGRADE_FAILURE;
|
||||
+ }
|
||||
+
|
||||
return UPGRADE_SUCCESS;
|
||||
}
|
||||
|
||||
--
|
||||
2.52.0
|
||||
|
||||
@ -0,0 +1,67 @@
|
||||
From f5de84e309d5a4435198c9cc9b31b5722979f1ff Mon Sep 17 00:00:00 2001
|
||||
From: Viktor Ashirov <vashirov@redhat.com>
|
||||
Date: Mon, 12 Jan 2026 10:58:02 +0100
|
||||
Subject: [PATCH] Issue 7172 - (2nd) Index ordering mismatch after upgrade
|
||||
(#7180)
|
||||
|
||||
Commit 742c12e0247ab64e87da000a4de2f3e5c99044ab introduced a regression
|
||||
where the check to skip creating parentid/ancestorid indexes if they
|
||||
already exist was incorrect.
|
||||
The `ainfo_get()` function falls back to returning
|
||||
LDBM_PSEUDO_ATTR_DEFAULT attrinfo when the requested attribute is not
|
||||
found.
|
||||
Since LDBM_PSEUDO_ATTR_DEFAULT is created before the ancestorid check,
|
||||
`ainfo_get()` returns LDBM_PSEUDO_ATTR_DEFAULT instead of NULL, causing
|
||||
the ancestorid index creation to be skipped entirely.
|
||||
|
||||
When operations later try to use the ancestorid index, they fall back to
|
||||
LDBM_PSEUDO_ATTR_DEFAULT, and attempting to open the .default dbi
|
||||
mid-transaction fails with MDB_NOTFOUND (-30798).
|
||||
|
||||
Fix Description:
|
||||
Instead of just checking if `ainfo_get()` returns non-NULL, verify that
|
||||
the returned attrinfo is actually for the requested attribute.
|
||||
|
||||
Fixes: https://github.com/389ds/389-ds-base/issues/7172
|
||||
|
||||
Reviewed by: @tbordaz (Thanks!)
|
||||
---
|
||||
ldap/servers/slapd/back-ldbm/instance.c | 8 +++++---
|
||||
1 file changed, 5 insertions(+), 3 deletions(-)
|
||||
|
||||
diff --git a/ldap/servers/slapd/back-ldbm/instance.c b/ldap/servers/slapd/back-ldbm/instance.c
|
||||
index 71bf0f6fa..2a6e8cbb8 100644
|
||||
--- a/ldap/servers/slapd/back-ldbm/instance.c
|
||||
+++ b/ldap/servers/slapd/back-ldbm/instance.c
|
||||
@@ -190,7 +190,7 @@ ldbm_instance_create_default_indexes(backend *be)
|
||||
char *ancestorid_indexes_limit = NULL;
|
||||
char *parentid_indexes_limit = NULL;
|
||||
struct attrinfo *ai = NULL;
|
||||
- struct attrinfo *index_already_configured = NULL;
|
||||
+ int index_already_configured = 0;
|
||||
struct index_idlistsizeinfo *iter;
|
||||
int cookie;
|
||||
int limit;
|
||||
@@ -250,7 +250,8 @@ ldbm_instance_create_default_indexes(backend *be)
|
||||
slapi_entry_free(e);
|
||||
|
||||
ainfo_get(be, (char *)LDBM_PARENTID_STR, &ai);
|
||||
- index_already_configured = ai;
|
||||
+ /* Check if the attrinfo is actually for parentid, not a fallback to .default */
|
||||
+ index_already_configured = (ai != NULL && strcmp(ai->ai_type, LDBM_PARENTID_STR) == 0);
|
||||
if (!index_already_configured) {
|
||||
e = ldbm_instance_init_config_entry(LDBM_PARENTID_STR, "eq", 0, 0, 0, "integerOrderingMatch", parentid_indexes_limit);
|
||||
ldbm_instance_config_add_index_entry(inst, e, flags);
|
||||
@@ -294,7 +295,8 @@ ldbm_instance_create_default_indexes(backend *be)
|
||||
* but we still want to use the attr index file APIs.
|
||||
*/
|
||||
ainfo_get(be, (char *)LDBM_ANCESTORID_STR, &ai);
|
||||
- index_already_configured = ai;
|
||||
+ /* Check if the attrinfo is actually for ancestorid, not a fallback to .default */
|
||||
+ index_already_configured = (ai != NULL && strcmp(ai->ai_type, LDBM_ANCESTORID_STR) == 0);
|
||||
if (!index_already_configured) {
|
||||
e = ldbm_instance_init_config_entry(LDBM_ANCESTORID_STR, "eq", 0, 0, 0, "integerOrderingMatch", ancestorid_indexes_limit);
|
||||
ldbm_instance_config_add_index_entry(inst, e, flags);
|
||||
--
|
||||
2.52.0
|
||||
|
||||
924
0006-Issue-6753-Port-ticket-548-test-7101.patch
Normal file
924
0006-Issue-6753-Port-ticket-548-test-7101.patch
Normal file
@ -0,0 +1,924 @@
|
||||
From fb23c9e366f5eafa6bdbb8cd71afd78e3edefde2 Mon Sep 17 00:00:00 2001
|
||||
From: Lenka Doudova <mirielka@users.noreply.github.com>
|
||||
Date: Tue, 13 Jan 2026 10:44:15 +0100
|
||||
Subject: [PATCH] Issue 6753 - Port ticket 548 test (#7101)
|
||||
|
||||
Description:
|
||||
Port ticket 548 test into
|
||||
dirsrvtests/tests/suites/password/pwdPolicy_attribute_test.py
|
||||
|
||||
Relates: #6753
|
||||
|
||||
Author: Lenka Doudova, aadhikar
|
||||
Assisted by: Cursor
|
||||
Reviewer: @droideck(Thanks!)
|
||||
---
|
||||
.../password/pwdPolicy_attribute_test.py | 464 +++++++++++++++++-
|
||||
dirsrvtests/tests/tickets/ticket548_test.py | 408 ---------------
|
||||
2 files changed, 463 insertions(+), 409 deletions(-)
|
||||
delete mode 100644 dirsrvtests/tests/tickets/ticket548_test.py
|
||||
|
||||
diff --git a/dirsrvtests/tests/suites/password/pwdPolicy_attribute_test.py b/dirsrvtests/tests/suites/password/pwdPolicy_attribute_test.py
|
||||
index d0c172f94..d021f4720 100644
|
||||
--- a/dirsrvtests/tests/suites/password/pwdPolicy_attribute_test.py
|
||||
+++ b/dirsrvtests/tests/suites/password/pwdPolicy_attribute_test.py
|
||||
@@ -9,12 +9,12 @@
|
||||
import pytest
|
||||
from lib389.tasks import *
|
||||
from lib389.utils import *
|
||||
-import pdb
|
||||
from lib389.topologies import topology_st
|
||||
from lib389.pwpolicy import PwPolicyManager
|
||||
from lib389.idm.user import UserAccount, UserAccounts, TEST_USER_PROPERTIES
|
||||
from lib389.idm.organizationalunit import OrganizationalUnits
|
||||
from lib389._constants import (DEFAULT_SUFFIX, DN_DM, PASSWORD)
|
||||
+from lib389.idm.directorymanager import DirectoryManager
|
||||
|
||||
pytestmark = pytest.mark.tier1
|
||||
|
||||
@@ -361,6 +361,468 @@ def test_pwdpolicysubentry(topology_st, password_policy):
|
||||
assert 'nsPwPolicyEntry_user' not in pwp_subentry
|
||||
|
||||
|
||||
+@pytest.fixture(scope="function")
|
||||
+def shadowUser(request, topology_st):
|
||||
+ """ Create a user with shadowAccount objectclass """
|
||||
+ users = UserAccounts(topology_st.standalone, DEFAULT_SUFFIX)
|
||||
+ shadowUser = users.create(properties={
|
||||
+ 'objectclass': ['top', 'person', 'organizationalPerson',
|
||||
+ 'inetOrgPerson', 'extensibleObject', 'shadowAccount'],
|
||||
+ 'sn': '1',
|
||||
+ 'cn': 'shadowUser',
|
||||
+ 'uid': 'shadowUser',
|
||||
+ 'uidNumber': '1',
|
||||
+ 'gidNumber': '11',
|
||||
+ 'homeDirectory': '/home/shadowUser',
|
||||
+ 'displayName': 'Shadow User',
|
||||
+ 'givenname': 'Shadow',
|
||||
+ 'mail':f'shadowuser@{DEFAULT_SUFFIX}',
|
||||
+ 'userpassword': 'password'
|
||||
+ })
|
||||
+
|
||||
+ def fin():
|
||||
+ if shadowUser.exists():
|
||||
+ shadowUser.delete()
|
||||
+
|
||||
+ request.addfinalizer(fin)
|
||||
+
|
||||
+ return shadowUser
|
||||
+
|
||||
+
|
||||
+def days_to_secs(days):
|
||||
+ """ Convert days to seconds """
|
||||
+ return days * 86400
|
||||
+
|
||||
+
|
||||
+def check_shadow_attr_value(inst, user_dn, attr_type, expected):
|
||||
+ """ Check that shadowAccount attribute has expected value """
|
||||
+ dm = DirectoryManager(inst)
|
||||
+ dm.rebind()
|
||||
+ user = UserAccount(inst, user_dn)
|
||||
+ assert user.present(attr_type), f'Entry {user_dn} does not have {attr_type} attribute'
|
||||
+ actual = int(user.get_attr_val_utf8(attr_type))
|
||||
+ assert actual == expected, f'{attr_type} of entry {user_dn} is {actual}, expected {expected}'
|
||||
+ log.info(f'{attr_type} of entry {user_dn} has expected value {actual}')
|
||||
+
|
||||
+
|
||||
+def setup_pwp(inst, pwp_mgr, policy, dn=None, policy_props=None):
|
||||
+ """ Setup password policy """
|
||||
+
|
||||
+ log.info(f'Setting up {policy} password policy for {dn}')
|
||||
+ dm = DirectoryManager(inst)
|
||||
+ dm.rebind()
|
||||
+
|
||||
+ log.info(f'Configuring {policy} password policy')
|
||||
+
|
||||
+ assert policy == 'global' or dn, 'dn is required for non-global policy'
|
||||
+
|
||||
+ if not policy_props:
|
||||
+ policy_props = {
|
||||
+ 'passwordMinAge': str(days_to_secs(1)),
|
||||
+ 'passwordExp': 'on',
|
||||
+ 'passwordMaxAge': str(days_to_secs(10)),
|
||||
+ 'passwordWarning': str(days_to_secs(3))
|
||||
+ }
|
||||
+
|
||||
+ if policy == 'global':
|
||||
+ pwp_mgr.set_global_policy(policy_props)
|
||||
+ elif policy == 'subtree':
|
||||
+ pwp_mgr.create_subtree_policy(dn, policy_props)
|
||||
+ elif policy == 'user':
|
||||
+ pwp_mgr.create_user_policy(dn, policy_props)
|
||||
+ else:
|
||||
+ raise ValueError(f'Invalid type of password policy: {policy}')
|
||||
+
|
||||
+
|
||||
+def modify_pwp(inst, pwp_mgr, policy, dn=None, policy_props=None):
|
||||
+ """ Modify password policy """
|
||||
+ dm = DirectoryManager(inst)
|
||||
+ dm.rebind()
|
||||
+
|
||||
+ assert policy == 'global' or dn, 'dn is required for non-global policy'
|
||||
+
|
||||
+ if not policy_props:
|
||||
+ policy_props = {
|
||||
+ 'passwordMinAge': str(days_to_secs(3)),
|
||||
+ 'passwordMaxAge': str(days_to_secs(30)),
|
||||
+ 'passwordWarning': str(days_to_secs(9))
|
||||
+ }
|
||||
+
|
||||
+ if policy == 'global':
|
||||
+ pwp_mgr.set_global_policy(properties=policy_props)
|
||||
+ elif policy in ['subtree', 'user']:
|
||||
+ policy_entry = pwp_mgr.get_pwpolicy_entry(dn)
|
||||
+ policy_entry.replace_many(*policy_props.items())
|
||||
+ else:
|
||||
+ raise ValueError(f'Invalid type of password policy: {policy}')
|
||||
+ log.info(f'Modified {policy} policy with {policy_props}.')
|
||||
+
|
||||
+@pytest.mark.skipif(ds_is_older('1.3.6'), reason="Not implemented")
|
||||
+def test_shadowaccount_no_policy(topology_st, shadowUser):
|
||||
+ """Check shadowAccount under no password policy
|
||||
+
|
||||
+ :id: a1b2c3d4-5e6f-7890-abcd-ef1234567890
|
||||
+ :setup: Standalone instance
|
||||
+ :steps:
|
||||
+ 1. Add a user with shadowAccount objectclass
|
||||
+ 2. Bind as the user
|
||||
+ 3. Check shadowLastChange attribute is set correctly
|
||||
+ :expectedresults:
|
||||
+ 1. User is added successfully
|
||||
+ 2. Bind is successful
|
||||
+ 3. shadowLastChange is set correctly (days since epoch)
|
||||
+ """
|
||||
+
|
||||
+ edate = int(time.time() / (60 * 60 * 24))
|
||||
+
|
||||
+ log.info(f"Bind as {shadowUser.dn}")
|
||||
+ shadowUser.rebind('password')
|
||||
+ check_shadow_attr_value(topology_st.standalone, shadowUser.dn,
|
||||
+ 'shadowLastChange', edate)
|
||||
+
|
||||
+
|
||||
+@pytest.mark.skipif(ds_is_older('1.3.6'), reason="Not implemented")
|
||||
+def test_shadowaccount_global_policy(topology_st, shadowUser, request):
|
||||
+ """Check shadowAccount with global password policy
|
||||
+
|
||||
+ :id: b2c3d4e5-6f7a-8901-bcde-f23456789012
|
||||
+ :setup: Standalone instance
|
||||
+ :steps:
|
||||
+ 1. Set global password policy
|
||||
+ 2. Add a second shadowAccount user
|
||||
+ 3. Bind as each user and check shadowAccount attributes
|
||||
+ 4. Modify global password policy
|
||||
+ 5. Change user password (as the user, not DM)
|
||||
+ 6. Re-bind with new password
|
||||
+ 7. Check shadowAccount attributes are updated
|
||||
+ 8. Clean up - delete second user and reset policy
|
||||
+ :expectedresults:
|
||||
+ 1. Global password policy is set successfully
|
||||
+ 2. Second user is added
|
||||
+ 3. shadowAccount attributes match policy values for both users
|
||||
+ 4. Password policy is modified successfully
|
||||
+ 5. Password is changed successfully
|
||||
+ 6. Re-bind with new password is successful
|
||||
+ 7. shadowAccount attributes are updated to match new policy values
|
||||
+ 8. Cleanup is successful
|
||||
+ """
|
||||
+ inst = topology_st.standalone
|
||||
+
|
||||
+ log.info('Create second shadowAccount user')
|
||||
+ users = UserAccounts(inst, DEFAULT_SUFFIX)
|
||||
+ shadowUser2 = users.create(properties={
|
||||
+ 'objectclass': ['top', 'person', 'organizationalPerson',
|
||||
+ 'inetOrgPerson', 'extensibleObject', 'shadowAccount'],
|
||||
+ 'sn': '2',
|
||||
+ 'cn': 'shadowUser2',
|
||||
+ 'uid': 'shadowUser2',
|
||||
+ 'uidNumber': '2',
|
||||
+ 'gidNumber': '22',
|
||||
+ 'homeDirectory': '/home/shadowUser2',
|
||||
+ 'displayName': 'Shadow User 2',
|
||||
+ 'givenname': 'Shadow2',
|
||||
+ 'mail': f'shadowuser2@{DEFAULT_SUFFIX}',
|
||||
+ 'userpassword': 'password'
|
||||
+ })
|
||||
+
|
||||
+ def fin():
|
||||
+ log.info('Clean up - delete second user and reset global policy')
|
||||
+ dm = DirectoryManager(inst)
|
||||
+ dm.rebind()
|
||||
+ try:
|
||||
+ shadowUser2.delete()
|
||||
+ except Exception:
|
||||
+ pass
|
||||
+ inst.config.replace('passwordMinAge', '0')
|
||||
+ inst.config.replace('passwordMaxAge', '8640000')
|
||||
+ inst.config.replace('passwordWarning', '86400')
|
||||
+ inst.config.replace('passwordExp', 'off')
|
||||
+ request.addfinalizer(fin)
|
||||
+
|
||||
+ log.info('Configure global password policy')
|
||||
+ pwp_mgr = PwPolicyManager(inst)
|
||||
+ setup_pwp(inst, pwp_mgr, 'global')
|
||||
+
|
||||
+ edate = int(time.time() / (60 * 60 * 24))
|
||||
+
|
||||
+ log.info('Verify attributes of shadowUser (user 1)')
|
||||
+ shadowUser.rebind('password')
|
||||
+ check_shadow_attr_value(inst, shadowUser.dn,
|
||||
+ 'shadowLastChange', edate)
|
||||
+ check_shadow_attr_value(inst, shadowUser.dn,
|
||||
+ 'shadowMin', 1)
|
||||
+ check_shadow_attr_value(inst, shadowUser.dn,
|
||||
+ 'shadowMax', 10)
|
||||
+ check_shadow_attr_value(inst, shadowUser.dn,
|
||||
+ 'shadowWarning', 3)
|
||||
+
|
||||
+ log.info('Verify attributes of shadowUser2 (user 2)')
|
||||
+ shadowUser2.rebind('password')
|
||||
+ check_shadow_attr_value(inst, shadowUser2.dn,
|
||||
+ 'shadowLastChange', edate)
|
||||
+ check_shadow_attr_value(inst, shadowUser2.dn,
|
||||
+ 'shadowMin', 1)
|
||||
+ check_shadow_attr_value(inst, shadowUser2.dn,
|
||||
+ 'shadowMax', 10)
|
||||
+ check_shadow_attr_value(inst, shadowUser2.dn,
|
||||
+ 'shadowWarning', 3)
|
||||
+
|
||||
+ log.info('Modify global password policy')
|
||||
+ modify_pwp(inst, pwp_mgr, 'global')
|
||||
+
|
||||
+ log.info('Change shadowUser2 password as the user')
|
||||
+ shadowUser2.rebind('password')
|
||||
+ shadowUser2.replace('userpassword', 'password2')
|
||||
+ time.sleep(1)
|
||||
+
|
||||
+ log.info('Re-bind as shadowUser2 with new password')
|
||||
+ shadowUser2.rebind('password2')
|
||||
+
|
||||
+ log.info('Verify modified shadowUser2 attributes')
|
||||
+ check_shadow_attr_value(inst, shadowUser2.dn,
|
||||
+ 'shadowMin', 3)
|
||||
+ check_shadow_attr_value(inst, shadowUser2.dn,
|
||||
+ 'shadowMax', 30)
|
||||
+ check_shadow_attr_value(inst, shadowUser2.dn,
|
||||
+ 'shadowWarning', 9)
|
||||
+
|
||||
+
|
||||
+@pytest.mark.skipif(ds_is_older('1.3.6'), reason="Not implemented")
|
||||
+def test_shadowaccount_subtree_policy(topology_st, request):
|
||||
+ """Check shadowAccount with subtree level password policy
|
||||
+
|
||||
+ :id: c3d4e5f6-7a8b-9012-cdef-345678901234
|
||||
+ :setup: Standalone instance
|
||||
+ :steps:
|
||||
+ 1. Create subtree password policy for DEFAULT_SUFFIX with passwordMustChange on
|
||||
+ 2. Add a new shadowAccount user under the subtree
|
||||
+ 3. Check shadowLastChange is 0 (since passwordMustChange is on)
|
||||
+ 4. Verify search as user fails with UNWILLING_TO_PERFORM
|
||||
+ 5. Change user password (as user)
|
||||
+ 6. Re-bind with new password
|
||||
+ 7. Check shadowAccount attributes are updated with correct values
|
||||
+ 8. Clean up subtree password policy
|
||||
+ :expectedresults:
|
||||
+ 1. Subtree password policy is created successfully
|
||||
+ 2. User is added successfully
|
||||
+ 3. shadowLastChange is 0 until password is changed
|
||||
+ 4. Search fails with UNWILLING_TO_PERFORM as expected
|
||||
+ 5. Password is changed successfully
|
||||
+ 6. Re-bind with new password is successful
|
||||
+ 7. shadowAccount attributes are updated to match policy values
|
||||
+ 8. Subtree password policy is deleted successfully
|
||||
+ """
|
||||
+ inst = topology_st.standalone
|
||||
+ subtree_dn = DEFAULT_SUFFIX
|
||||
+
|
||||
+ log.info('Configure subtree password policy with passwordMustChange on')
|
||||
+ properties = {
|
||||
+ 'passwordMustChange': 'on',
|
||||
+ 'passwordExp': 'on',
|
||||
+ 'passwordMinAge': str(days_to_secs(2)),
|
||||
+ 'passwordMaxAge': str(days_to_secs(20)),
|
||||
+ 'passwordWarning': str(days_to_secs(6)),
|
||||
+ 'passwordChange': 'on',
|
||||
+ 'passwordStorageScheme': 'clear'
|
||||
+ }
|
||||
+
|
||||
+ pwp_mgr = PwPolicyManager(inst)
|
||||
+ setup_pwp(inst, pwp_mgr, 'subtree', dn=subtree_dn, policy_props=properties)
|
||||
+
|
||||
+ def fin():
|
||||
+ log.info('Clean up: delete subtree password policy')
|
||||
+ dm = DirectoryManager(inst)
|
||||
+ dm.rebind()
|
||||
+ try:
|
||||
+ pwp_mgr.delete_local_policy(subtree_dn)
|
||||
+ except Exception:
|
||||
+ pass
|
||||
+ try:
|
||||
+ subtree_user.delete()
|
||||
+ except Exception:
|
||||
+ pass
|
||||
+ request.addfinalizer(fin)
|
||||
+
|
||||
+ log.info('Add a new shadowAccount user under the subtree')
|
||||
+ users = UserAccounts(inst, DEFAULT_SUFFIX)
|
||||
+ subtree_user = users.create(properties={
|
||||
+ 'objectclass': ['top', 'person', 'organizationalPerson',
|
||||
+ 'inetOrgPerson', 'extensibleObject', 'shadowAccount'],
|
||||
+ 'sn': '3',
|
||||
+ 'cn': 'subtreeUser',
|
||||
+ 'uid': 'subtreeUser',
|
||||
+ 'uidNumber': '3',
|
||||
+ 'gidNumber': '33',
|
||||
+ 'homeDirectory': '/home/subtreeUser',
|
||||
+ 'displayName': 'Subtree User',
|
||||
+ 'givenname': 'Subtree',
|
||||
+ 'mail': f'subtreeuser@{DEFAULT_SUFFIX}',
|
||||
+ 'userpassword': 'password'
|
||||
+ })
|
||||
+
|
||||
+ dm = DirectoryManager(inst)
|
||||
+ dm.rebind()
|
||||
+
|
||||
+ log.info('Verify shadowLastChange is 0 since passwordMustChange is on')
|
||||
+ check_shadow_attr_value(inst, subtree_user.dn,
|
||||
+ 'shadowLastChange', 0)
|
||||
+ check_shadow_attr_value(inst, subtree_user.dn,
|
||||
+ 'shadowMin', 2)
|
||||
+ check_shadow_attr_value(inst, subtree_user.dn,
|
||||
+ 'shadowMax', 20)
|
||||
+ check_shadow_attr_value(inst, subtree_user.dn,
|
||||
+ 'shadowWarning', 6)
|
||||
+
|
||||
+ log.info(f'Bind as {subtree_user.dn} and verify search fails with UNWILLING_TO_PERFORM')
|
||||
+ subtree_user.rebind('password')
|
||||
+ with pytest.raises(ldap.UNWILLING_TO_PERFORM):
|
||||
+ subtree_user.exists()
|
||||
+
|
||||
+ log.info('Modify subtree password policy')
|
||||
+ dm.rebind()
|
||||
+ modify_properties = {
|
||||
+ 'passwordMinAge': str(days_to_secs(4)),
|
||||
+ 'passwordMaxAge': str(days_to_secs(40)),
|
||||
+ 'passwordWarning': str(days_to_secs(12))
|
||||
+ }
|
||||
+ modify_pwp(inst, pwp_mgr, 'subtree', dn=subtree_dn, policy_props=modify_properties)
|
||||
+
|
||||
+ log.info(f'Change {subtree_user.dn} password as the user')
|
||||
+ subtree_user.rebind('password')
|
||||
+ subtree_user.replace('userpassword', 'password0')
|
||||
+ time.sleep(1)
|
||||
+
|
||||
+ log.info(f'Re-bind as {subtree_user.dn} with new password')
|
||||
+ subtree_user.rebind('password0')
|
||||
+
|
||||
+ edate = int(time.time() / (60 * 60 * 24))
|
||||
+
|
||||
+ log.info('Verify shadowLastChange is now set to today after password change')
|
||||
+ check_shadow_attr_value(inst, subtree_user.dn,
|
||||
+ 'shadowLastChange', edate)
|
||||
+ check_shadow_attr_value(inst, subtree_user.dn,
|
||||
+ 'shadowMin', 4)
|
||||
+ check_shadow_attr_value(inst, subtree_user.dn,
|
||||
+ 'shadowMax', 40)
|
||||
+ check_shadow_attr_value(inst, subtree_user.dn,
|
||||
+ 'shadowWarning', 12)
|
||||
+
|
||||
+
|
||||
+@pytest.mark.skipif(ds_is_older('1.3.6'), reason="Not implemented")
|
||||
+def test_shadowaccount_user_policy(topology_st, request):
|
||||
+ """Check shadowAccount with user level password policy
|
||||
+
|
||||
+ :id: d4e5f6a7-8b9c-0123-def0-456789012345
|
||||
+ :setup: Standalone instance
|
||||
+ :steps:
|
||||
+ 1. Create a new shadowAccount user
|
||||
+ 2. Create user password policy
|
||||
+ 3. Verify shadowAccount attributes match policy values
|
||||
+ 4. Modify user password policy
|
||||
+ 5. Change user password
|
||||
+ 6. Re-bind with new password
|
||||
+ 7. Check shadowAccount attributes are updated
|
||||
+ 8. Clean up user password policy
|
||||
+ :expectedresults:
|
||||
+ 1. User is created successfully
|
||||
+ 2. User password policy is created successfully
|
||||
+ 3. shadowAccount attributes match policy values
|
||||
+ 4. Password policy is modified successfully
|
||||
+ 5. Password is changed successfully
|
||||
+ 6. Re-bind with new password is successful
|
||||
+ 7. shadowAccount attributes are updated to match new policy values
|
||||
+ 8. User password policy is deleted successfully
|
||||
+ """
|
||||
+ inst = topology_st.standalone
|
||||
+
|
||||
+ log.info('Create a new shadowAccount user for user policy test')
|
||||
+ users = UserAccounts(inst, DEFAULT_SUFFIX)
|
||||
+ user_policy_user = users.create(properties={
|
||||
+ 'objectclass': ['top', 'person', 'organizationalPerson',
|
||||
+ 'inetOrgPerson', 'extensibleObject', 'shadowAccount'],
|
||||
+ 'sn': '4',
|
||||
+ 'cn': 'userPolicyUser',
|
||||
+ 'uid': 'userPolicyUser',
|
||||
+ 'uidNumber': '4',
|
||||
+ 'gidNumber': '44',
|
||||
+ 'homeDirectory': '/home/userPolicyUser',
|
||||
+ 'displayName': 'User Policy User',
|
||||
+ 'givenname': 'UserPolicy',
|
||||
+ 'mail': f'userpolicyuser@{DEFAULT_SUFFIX}',
|
||||
+ 'userpassword': 'password'
|
||||
+ })
|
||||
+
|
||||
+ pwp_mgr = PwPolicyManager(inst)
|
||||
+
|
||||
+ def fin():
|
||||
+ log.info('Clean up: delete user password policy and user')
|
||||
+ dm = DirectoryManager(inst)
|
||||
+ dm.rebind()
|
||||
+ try:
|
||||
+ pwp_mgr.delete_local_policy(user_policy_user.dn)
|
||||
+ except Exception:
|
||||
+ pass
|
||||
+ try:
|
||||
+ user_policy_user.delete()
|
||||
+ except Exception:
|
||||
+ pass
|
||||
+ request.addfinalizer(fin)
|
||||
+
|
||||
+ log.info('Configure user password policy')
|
||||
+ properties = {
|
||||
+ 'passwordExp': 'on',
|
||||
+ 'passwordMinAge': str(days_to_secs(2)),
|
||||
+ 'passwordMaxAge': str(days_to_secs(20)),
|
||||
+ 'passwordWarning': str(days_to_secs(6)),
|
||||
+ 'passwordChange': 'on',
|
||||
+ 'passwordStorageScheme': 'clear'
|
||||
+ }
|
||||
+ setup_pwp(inst, pwp_mgr, 'user', dn=user_policy_user.dn, policy_props=properties)
|
||||
+
|
||||
+ edate = int(time.time() / (60 * 60 * 24))
|
||||
+
|
||||
+ dm = DirectoryManager(inst)
|
||||
+ dm.rebind()
|
||||
+
|
||||
+ log.info('Verify shadowAccount attributes match user policy')
|
||||
+ check_shadow_attr_value(inst, user_policy_user.dn,
|
||||
+ 'shadowLastChange', edate)
|
||||
+ check_shadow_attr_value(inst, user_policy_user.dn,
|
||||
+ 'shadowMin', 2)
|
||||
+ check_shadow_attr_value(inst, user_policy_user.dn,
|
||||
+ 'shadowMax', 20)
|
||||
+ check_shadow_attr_value(inst, user_policy_user.dn,
|
||||
+ 'shadowWarning', 6)
|
||||
+
|
||||
+ log.info('Modify user password policy')
|
||||
+ modify_properties = {
|
||||
+ 'passwordMinAge': str(days_to_secs(4)),
|
||||
+ 'passwordMaxAge': str(days_to_secs(40)),
|
||||
+ 'passwordWarning': str(days_to_secs(12))
|
||||
+ }
|
||||
+ modify_pwp(inst, pwp_mgr, 'user', dn=user_policy_user.dn, policy_props=modify_properties)
|
||||
+
|
||||
+ log.info(f'Change {user_policy_user.dn} password as the user')
|
||||
+ user_policy_user.rebind('password')
|
||||
+ user_policy_user.replace('userpassword', 'password0')
|
||||
+ time.sleep(1)
|
||||
+
|
||||
+ log.info(f'Re-bind as {user_policy_user.dn} with new password')
|
||||
+ user_policy_user.rebind('password0')
|
||||
+
|
||||
+ edate = int(time.time() / (60 * 60 * 24))
|
||||
+
|
||||
+ log.info('Verify shadowAccount attributes are updated after password change')
|
||||
+ check_shadow_attr_value(inst, user_policy_user.dn,
|
||||
+ 'shadowLastChange', edate)
|
||||
+ check_shadow_attr_value(inst, user_policy_user.dn,
|
||||
+ 'shadowMin', 4)
|
||||
+ check_shadow_attr_value(inst, user_policy_user.dn,
|
||||
+ 'shadowMax', 40)
|
||||
+ check_shadow_attr_value(inst, user_policy_user.dn,
|
||||
+ 'shadowWarning', 12)
|
||||
+
|
||||
+
|
||||
if __name__ == '__main__':
|
||||
# Run isolated
|
||||
# -s for DEBUG mode
|
||||
diff --git a/dirsrvtests/tests/tickets/ticket548_test.py b/dirsrvtests/tests/tickets/ticket548_test.py
|
||||
deleted file mode 100644
|
||||
index cac3cc5f8..000000000
|
||||
--- a/dirsrvtests/tests/tickets/ticket548_test.py
|
||||
+++ /dev/null
|
||||
@@ -1,408 +0,0 @@
|
||||
-# --- BEGIN COPYRIGHT BLOCK ---
|
||||
-# Copyright (C) 2016 Red Hat, Inc.
|
||||
-# All rights reserved.
|
||||
-#
|
||||
-# License: GPL (version 3 or any later version).
|
||||
-# See LICENSE for details.
|
||||
-# --- END COPYRIGHT BLOCK ---
|
||||
-#
|
||||
-import pytest
|
||||
-from lib389.tasks import *
|
||||
-from lib389.utils import *
|
||||
-from lib389.topologies import topology_st
|
||||
-
|
||||
-from lib389._constants import DEFAULT_SUFFIX, DN_CONFIG, DN_DM, PASSWORD, DEFAULT_SUFFIX_ESCAPED
|
||||
-
|
||||
-# Skip on older versions
|
||||
-pytestmark = [pytest.mark.tier2,
|
||||
- pytest.mark.skipif(ds_is_older('1.3.6'), reason="Not implemented")]
|
||||
-
|
||||
-log = logging.getLogger(__name__)
|
||||
-
|
||||
-# Assuming DEFAULT_SUFFIX is "dc=example,dc=com", otherwise it does not work... :(
|
||||
-SUBTREE_CONTAINER = 'cn=nsPwPolicyContainer,' + DEFAULT_SUFFIX
|
||||
-SUBTREE_PWPDN = 'cn=nsPwPolicyEntry,' + DEFAULT_SUFFIX
|
||||
-SUBTREE_PWP = 'cn=cn\3DnsPwPolicyEntry\2C' + DEFAULT_SUFFIX_ESCAPED + ',' + SUBTREE_CONTAINER
|
||||
-SUBTREE_COS_TMPLDN = 'cn=nsPwTemplateEntry,' + DEFAULT_SUFFIX
|
||||
-SUBTREE_COS_TMPL = 'cn=cn\3DnsPwTemplateEntry\2C' + DEFAULT_SUFFIX_ESCAPED + ',' + SUBTREE_CONTAINER
|
||||
-SUBTREE_COS_DEF = 'cn=nsPwPolicy_CoS,' + DEFAULT_SUFFIX
|
||||
-
|
||||
-USER1_DN = 'uid=user1,' + DEFAULT_SUFFIX
|
||||
-USER2_DN = 'uid=user2,' + DEFAULT_SUFFIX
|
||||
-USER3_DN = 'uid=user3,' + DEFAULT_SUFFIX
|
||||
-USER_PW = 'password'
|
||||
-
|
||||
-
|
||||
-def days_to_secs(days):
|
||||
- # Value of 60 * 60 * 24
|
||||
- return days * 86400
|
||||
-
|
||||
-
|
||||
-# Values are in days
|
||||
-def set_global_pwpolicy(topology_st, min_=1, max_=10, warn=3):
|
||||
- log.info(" +++++ Enable global password policy +++++\n")
|
||||
- # Enable password policy
|
||||
- try:
|
||||
- topology_st.standalone.modify_s(DN_CONFIG, [(ldap.MOD_REPLACE, 'nsslapd-pwpolicy-local', b'on')])
|
||||
- except ldap.LDAPError as e:
|
||||
- log.error('Failed to set pwpolicy-local: error ' + e.message['desc'])
|
||||
- assert False
|
||||
-
|
||||
- # Convert our values to seconds
|
||||
- min_secs = days_to_secs(min_)
|
||||
- max_secs = days_to_secs(max_)
|
||||
- warn_secs = days_to_secs(warn)
|
||||
-
|
||||
- log.info(" Set global password Min Age -- %s day\n" % min_)
|
||||
- try:
|
||||
- topology_st.standalone.modify_s(DN_CONFIG, [(ldap.MOD_REPLACE, 'passwordMinAge', ('%s' % min_secs).encode())])
|
||||
- except ldap.LDAPError as e:
|
||||
- log.error('Failed to set passwordMinAge: error ' + e.message['desc'])
|
||||
- assert False
|
||||
-
|
||||
- log.info(" Set global password Expiration -- on\n")
|
||||
- try:
|
||||
- topology_st.standalone.modify_s(DN_CONFIG, [(ldap.MOD_REPLACE, 'passwordExp', b'on')])
|
||||
- except ldap.LDAPError as e:
|
||||
- log.error('Failed to set passwordExp: error ' + e.message['desc'])
|
||||
- assert False
|
||||
-
|
||||
- log.info(" Set global password Max Age -- %s days\n" % max_)
|
||||
- try:
|
||||
- topology_st.standalone.modify_s(DN_CONFIG, [(ldap.MOD_REPLACE, 'passwordMaxAge', ('%s' % max_secs).encode())])
|
||||
- except ldap.LDAPError as e:
|
||||
- log.error('Failed to set passwordMaxAge: error ' + e.message['desc'])
|
||||
- assert False
|
||||
-
|
||||
- log.info(" Set global password Warning -- %s days\n" % warn)
|
||||
- try:
|
||||
- topology_st.standalone.modify_s(DN_CONFIG, [(ldap.MOD_REPLACE, 'passwordWarning', ('%s' % warn_secs).encode())])
|
||||
- except ldap.LDAPError as e:
|
||||
- log.error('Failed to set passwordWarning: error ' + e.message['desc'])
|
||||
- assert False
|
||||
-
|
||||
-
|
||||
-def set_subtree_pwpolicy(topology_st, min_=2, max_=20, warn=6):
|
||||
- log.info(" +++++ Enable subtree level password policy +++++\n")
|
||||
-
|
||||
- # Convert our values to seconds
|
||||
- min_secs = days_to_secs(min_)
|
||||
- max_secs = days_to_secs(max_)
|
||||
- warn_secs = days_to_secs(warn)
|
||||
-
|
||||
- log.info(" Add the container")
|
||||
- try:
|
||||
- topology_st.standalone.add_s(Entry((SUBTREE_CONTAINER, {'objectclass': 'top nsContainer'.split(),
|
||||
- 'cn': 'nsPwPolicyContainer'})))
|
||||
- except ldap.ALREADY_EXISTS:
|
||||
- pass
|
||||
- except ldap.LDAPError as e:
|
||||
- log.error('Failed to add subtree container: error ' + e.message['desc'])
|
||||
- # assert False
|
||||
-
|
||||
- try:
|
||||
- # Purge the old policy
|
||||
- topology_st.standalone.delete_s(SUBTREE_PWP)
|
||||
- except:
|
||||
- pass
|
||||
-
|
||||
- log.info(
|
||||
- " Add the password policy subentry {passwordMustChange: on, passwordMinAge: %s, passwordMaxAge: %s, passwordWarning: %s}" % (
|
||||
- min_, max_, warn))
|
||||
- try:
|
||||
- topology_st.standalone.add_s(Entry((SUBTREE_PWP, {'objectclass': 'top ldapsubentry passwordpolicy'.split(),
|
||||
- 'cn': SUBTREE_PWPDN,
|
||||
- 'passwordMustChange': 'on',
|
||||
- 'passwordExp': 'on',
|
||||
- 'passwordMinAge': '%s' % min_secs,
|
||||
- 'passwordMaxAge': '%s' % max_secs,
|
||||
- 'passwordWarning': '%s' % warn_secs,
|
||||
- 'passwordChange': 'on',
|
||||
- 'passwordStorageScheme': 'clear'})))
|
||||
- except ldap.LDAPError as e:
|
||||
- log.error('Failed to add passwordpolicy: error ' + e.message['desc'])
|
||||
- assert False
|
||||
-
|
||||
- log.info(" Add the COS template")
|
||||
- try:
|
||||
- topology_st.standalone.add_s(
|
||||
- Entry((SUBTREE_COS_TMPL, {'objectclass': 'top ldapsubentry costemplate extensibleObject'.split(),
|
||||
- 'cn': SUBTREE_PWPDN,
|
||||
- 'cosPriority': '1',
|
||||
- 'cn': SUBTREE_COS_TMPLDN,
|
||||
- 'pwdpolicysubentry': SUBTREE_PWP})))
|
||||
- except ldap.ALREADY_EXISTS:
|
||||
- pass
|
||||
- except ldap.LDAPError as e:
|
||||
- log.error('Failed to add COS template: error ' + e.message['desc'])
|
||||
- # assert False
|
||||
-
|
||||
- log.info(" Add the COS definition")
|
||||
- try:
|
||||
- topology_st.standalone.add_s(
|
||||
- Entry((SUBTREE_COS_DEF, {'objectclass': 'top ldapsubentry cosSuperDefinition cosPointerDefinition'.split(),
|
||||
- 'cn': SUBTREE_PWPDN,
|
||||
- 'costemplatedn': SUBTREE_COS_TMPL,
|
||||
- 'cosAttribute': 'pwdpolicysubentry default operational-default'})))
|
||||
- except ldap.ALREADY_EXISTS:
|
||||
- pass
|
||||
- except ldap.LDAPError as e:
|
||||
- log.error('Failed to add COS def: error ' + e.message['desc'])
|
||||
- # assert False
|
||||
-
|
||||
- time.sleep(1)
|
||||
-
|
||||
-
|
||||
-def update_passwd(topology_st, user, passwd, newpasswd):
|
||||
- log.info(" Bind as {%s,%s}" % (user, passwd))
|
||||
- topology_st.standalone.simple_bind_s(user, passwd)
|
||||
- try:
|
||||
- topology_st.standalone.modify_s(user, [(ldap.MOD_REPLACE, 'userpassword', newpasswd.encode())])
|
||||
- except ldap.LDAPError as e:
|
||||
- log.fatal('test_ticket548: Failed to update the password ' + cpw + ' of user ' + user + ': error ' + e.message[
|
||||
- 'desc'])
|
||||
- assert False
|
||||
-
|
||||
- time.sleep(1)
|
||||
-
|
||||
-
|
||||
-def check_shadow_attr_value(entry, attr_type, expected, dn):
|
||||
- if entry.hasAttr(attr_type):
|
||||
- actual = entry.getValue(attr_type)
|
||||
- if int(actual) == expected:
|
||||
- log.info('%s of entry %s has expected value %s' % (attr_type, dn, actual))
|
||||
- assert True
|
||||
- else:
|
||||
- log.fatal('%s %s of entry %s does not have expected value %s' % (attr_type, actual, dn, expected))
|
||||
- assert False
|
||||
- else:
|
||||
- log.fatal('entry %s does not have %s attr' % (dn, attr_type))
|
||||
- assert False
|
||||
-
|
||||
-
|
||||
-def test_ticket548_test_with_no_policy(topology_st):
|
||||
- """
|
||||
- Check shadowAccount under no password policy
|
||||
- """
|
||||
- log.info("Case 1. No password policy")
|
||||
-
|
||||
- log.info("Bind as %s" % DN_DM)
|
||||
- topology_st.standalone.simple_bind_s(DN_DM, PASSWORD)
|
||||
-
|
||||
- log.info('Add an entry' + USER1_DN)
|
||||
- try:
|
||||
- topology_st.standalone.add_s(
|
||||
- Entry((USER1_DN, {'objectclass': "top person organizationalPerson inetOrgPerson shadowAccount".split(),
|
||||
- 'sn': '1',
|
||||
- 'cn': 'user 1',
|
||||
- 'uid': 'user1',
|
||||
- 'givenname': 'user',
|
||||
- 'mail': 'user1@' + DEFAULT_SUFFIX,
|
||||
- 'userpassword': USER_PW})))
|
||||
- except ldap.LDAPError as e:
|
||||
- log.fatal('test_ticket548: Failed to add user' + USER1_DN + ': error ' + e.message['desc'])
|
||||
- assert False
|
||||
-
|
||||
- edate = int(time.time() / (60 * 60 * 24))
|
||||
- log.info('Search entry %s' % USER1_DN)
|
||||
-
|
||||
- log.info("Bind as %s" % USER1_DN)
|
||||
- topology_st.standalone.simple_bind_s(USER1_DN, USER_PW)
|
||||
- entry = topology_st.standalone.getEntry(USER1_DN, ldap.SCOPE_BASE, "(objectclass=*)", ['shadowLastChange'])
|
||||
- check_shadow_attr_value(entry, 'shadowLastChange', edate, USER1_DN)
|
||||
-
|
||||
- log.info("Check shadowAccount with no policy was successfully verified.")
|
||||
-
|
||||
-
|
||||
-def test_ticket548_test_global_policy(topology_st):
|
||||
- """
|
||||
- Check shadowAccount with global password policy
|
||||
- """
|
||||
-
|
||||
- log.info("Case 2. Check shadowAccount with global password policy")
|
||||
-
|
||||
- log.info("Bind as %s" % DN_DM)
|
||||
- topology_st.standalone.simple_bind_s(DN_DM, PASSWORD)
|
||||
-
|
||||
- set_global_pwpolicy(topology_st)
|
||||
-
|
||||
- log.info('Add an entry' + USER2_DN)
|
||||
- try:
|
||||
- topology_st.standalone.add_s(
|
||||
- Entry((USER2_DN, {'objectclass': "top person organizationalPerson inetOrgPerson shadowAccount".split(),
|
||||
- 'sn': '2',
|
||||
- 'cn': 'user 2',
|
||||
- 'uid': 'user2',
|
||||
- 'givenname': 'user',
|
||||
- 'mail': 'user2@' + DEFAULT_SUFFIX,
|
||||
- 'userpassword': USER_PW})))
|
||||
- except ldap.LDAPError as e:
|
||||
- log.fatal('test_ticket548: Failed to add user' + USER2_DN + ': error ' + e.message['desc'])
|
||||
- assert False
|
||||
-
|
||||
- edate = int(time.time() / (60 * 60 * 24))
|
||||
-
|
||||
- log.info("Bind as %s" % USER1_DN)
|
||||
- topology_st.standalone.simple_bind_s(USER1_DN, USER_PW)
|
||||
-
|
||||
- log.info('Search entry %s' % USER1_DN)
|
||||
- entry = topology_st.standalone.getEntry(USER1_DN, ldap.SCOPE_BASE, "(objectclass=*)")
|
||||
- check_shadow_attr_value(entry, 'shadowLastChange', edate, USER1_DN)
|
||||
-
|
||||
- # passwordMinAge -- 1 day
|
||||
- check_shadow_attr_value(entry, 'shadowMin', 1, USER1_DN)
|
||||
-
|
||||
- # passwordMaxAge -- 10 days
|
||||
- check_shadow_attr_value(entry, 'shadowMax', 10, USER1_DN)
|
||||
-
|
||||
- # passwordWarning -- 3 days
|
||||
- check_shadow_attr_value(entry, 'shadowWarning', 3, USER1_DN)
|
||||
-
|
||||
- log.info("Bind as %s" % USER2_DN)
|
||||
- topology_st.standalone.simple_bind_s(USER2_DN, USER_PW)
|
||||
-
|
||||
- log.info('Search entry %s' % USER2_DN)
|
||||
- entry = topology_st.standalone.getEntry(USER2_DN, ldap.SCOPE_BASE, "(objectclass=*)")
|
||||
- check_shadow_attr_value(entry, 'shadowLastChange', edate, USER2_DN)
|
||||
-
|
||||
- # passwordMinAge -- 1 day
|
||||
- check_shadow_attr_value(entry, 'shadowMin', 1, USER2_DN)
|
||||
-
|
||||
- # passwordMaxAge -- 10 days
|
||||
- check_shadow_attr_value(entry, 'shadowMax', 10, USER2_DN)
|
||||
-
|
||||
- # passwordWarning -- 3 days
|
||||
- check_shadow_attr_value(entry, 'shadowWarning', 3, USER2_DN)
|
||||
-
|
||||
- # Bind as DM again, change policy
|
||||
- log.info("Bind as %s" % DN_DM)
|
||||
- topology_st.standalone.simple_bind_s(DN_DM, PASSWORD)
|
||||
- set_global_pwpolicy(topology_st, 3, 30, 9)
|
||||
-
|
||||
- # change the user password, then check again.
|
||||
- log.info("Bind as %s" % USER2_DN)
|
||||
- topology_st.standalone.simple_bind_s(USER2_DN, USER_PW)
|
||||
-
|
||||
- newpasswd = USER_PW + '2'
|
||||
- update_passwd(topology_st, USER2_DN, USER_PW, newpasswd)
|
||||
-
|
||||
- log.info("Re-bind as %s with new password" % USER2_DN)
|
||||
- topology_st.standalone.simple_bind_s(USER2_DN, newpasswd)
|
||||
-
|
||||
- ## This tests if we update the shadow values on password change.
|
||||
- log.info('Search entry %s' % USER2_DN)
|
||||
- entry = topology_st.standalone.getEntry(USER2_DN, ldap.SCOPE_BASE, "(objectclass=*)")
|
||||
-
|
||||
- # passwordMinAge -- 1 day
|
||||
- check_shadow_attr_value(entry, 'shadowMin', 3, USER2_DN)
|
||||
-
|
||||
- # passwordMaxAge -- 10 days
|
||||
- check_shadow_attr_value(entry, 'shadowMax', 30, USER2_DN)
|
||||
-
|
||||
- # passwordWarning -- 3 days
|
||||
- check_shadow_attr_value(entry, 'shadowWarning', 9, USER2_DN)
|
||||
-
|
||||
- log.info("Check shadowAccount with global policy was successfully verified.")
|
||||
-
|
||||
-
|
||||
-def test_ticket548_test_subtree_policy(topology_st):
|
||||
- """
|
||||
- Check shadowAccount with subtree level password policy
|
||||
- """
|
||||
-
|
||||
- log.info("Case 3. Check shadowAccount with subtree level password policy")
|
||||
-
|
||||
- log.info("Bind as %s" % DN_DM)
|
||||
- topology_st.standalone.simple_bind_s(DN_DM, PASSWORD)
|
||||
- # Check the global policy values
|
||||
-
|
||||
- set_subtree_pwpolicy(topology_st, 2, 20, 6)
|
||||
-
|
||||
- log.info('Add an entry' + USER3_DN)
|
||||
- try:
|
||||
- topology_st.standalone.add_s(
|
||||
- Entry((USER3_DN, {'objectclass': "top person organizationalPerson inetOrgPerson shadowAccount".split(),
|
||||
- 'sn': '3',
|
||||
- 'cn': 'user 3',
|
||||
- 'uid': 'user3',
|
||||
- 'givenname': 'user',
|
||||
- 'mail': 'user3@' + DEFAULT_SUFFIX,
|
||||
- 'userpassword': USER_PW})))
|
||||
- except ldap.LDAPError as e:
|
||||
- log.fatal('test_ticket548: Failed to add user' + USER3_DN + ': error ' + e.message['desc'])
|
||||
- assert False
|
||||
-
|
||||
- log.info('Search entry %s' % USER3_DN)
|
||||
- entry0 = topology_st.standalone.getEntry(USER3_DN, ldap.SCOPE_BASE, "(objectclass=*)")
|
||||
-
|
||||
- log.info('Expecting shadowLastChange 0 since passwordMustChange is on')
|
||||
- check_shadow_attr_value(entry0, 'shadowLastChange', 0, USER3_DN)
|
||||
-
|
||||
- # passwordMinAge -- 2 day
|
||||
- check_shadow_attr_value(entry0, 'shadowMin', 2, USER3_DN)
|
||||
-
|
||||
- # passwordMaxAge -- 20 days
|
||||
- check_shadow_attr_value(entry0, 'shadowMax', 20, USER3_DN)
|
||||
-
|
||||
- # passwordWarning -- 6 days
|
||||
- check_shadow_attr_value(entry0, 'shadowWarning', 6, USER3_DN)
|
||||
-
|
||||
- log.info("Bind as %s" % USER3_DN)
|
||||
- topology_st.standalone.simple_bind_s(USER3_DN, USER_PW)
|
||||
-
|
||||
- log.info('Search entry %s' % USER3_DN)
|
||||
- try:
|
||||
- entry1 = topology_st.standalone.getEntry(USER3_DN, ldap.SCOPE_BASE, "(objectclass=*)")
|
||||
- except ldap.UNWILLING_TO_PERFORM:
|
||||
- log.info('test_ticket548: Search by' + USER3_DN + ' failed by UNWILLING_TO_PERFORM as expected')
|
||||
- except ldap.LDAPError as e:
|
||||
- log.fatal('test_ticket548: Failed to serch user' + USER3_DN + ' by self: error ' + e.message['desc'])
|
||||
- assert False
|
||||
-
|
||||
- log.info("Bind as %s and updating the password with a new one" % USER3_DN)
|
||||
- topology_st.standalone.simple_bind_s(USER3_DN, USER_PW)
|
||||
-
|
||||
- # Bind as DM again, change policy
|
||||
- log.info("Bind as %s" % DN_DM)
|
||||
- topology_st.standalone.simple_bind_s(DN_DM, PASSWORD)
|
||||
-
|
||||
- set_subtree_pwpolicy(topology_st, 4, 40, 12)
|
||||
-
|
||||
- newpasswd = USER_PW + '0'
|
||||
- update_passwd(topology_st, USER3_DN, USER_PW, newpasswd)
|
||||
-
|
||||
- log.info("Re-bind as %s with new password" % USER3_DN)
|
||||
- topology_st.standalone.simple_bind_s(USER3_DN, newpasswd)
|
||||
-
|
||||
- try:
|
||||
- entry2 = topology_st.standalone.getEntry(USER3_DN, ldap.SCOPE_BASE, "(objectclass=*)")
|
||||
- except ldap.LDAPError as e:
|
||||
- log.fatal('test_ticket548: Failed to serch user' + USER3_DN + ' by self: error ' + e.message['desc'])
|
||||
- assert False
|
||||
-
|
||||
- edate = int(time.time() / (60 * 60 * 24))
|
||||
-
|
||||
- log.info('Expecting shadowLastChange %d once userPassword is updated', edate)
|
||||
- check_shadow_attr_value(entry2, 'shadowLastChange', edate, USER3_DN)
|
||||
-
|
||||
- log.info('Search entry %s' % USER3_DN)
|
||||
- entry = topology_st.standalone.getEntry(USER3_DN, ldap.SCOPE_BASE, "(objectclass=*)")
|
||||
- check_shadow_attr_value(entry, 'shadowLastChange', edate, USER3_DN)
|
||||
-
|
||||
- # passwordMinAge -- 1 day
|
||||
- check_shadow_attr_value(entry, 'shadowMin', 4, USER3_DN)
|
||||
-
|
||||
- # passwordMaxAge -- 10 days
|
||||
- check_shadow_attr_value(entry, 'shadowMax', 40, USER3_DN)
|
||||
-
|
||||
- # passwordWarning -- 3 days
|
||||
- check_shadow_attr_value(entry, 'shadowWarning', 12, USER3_DN)
|
||||
-
|
||||
- log.info("Check shadowAccount with subtree level policy was successfully verified.")
|
||||
-
|
||||
-
|
||||
-if __name__ == '__main__':
|
||||
- # Run isolated
|
||||
- # -s for DEBUG mode
|
||||
- CURRENT_FILE = os.path.realpath(__file__)
|
||||
- pytest.main("-s %s" % CURRENT_FILE)
|
||||
--
|
||||
2.52.0
|
||||
|
||||
@ -0,0 +1,82 @@
|
||||
From 7c8a16c6bed524fb54d18a5b7e93d4bd5bb19d49 Mon Sep 17 00:00:00 2001
|
||||
From: Viktor Ashirov <vashirov@redhat.com>
|
||||
Date: Wed, 14 Jan 2026 17:55:29 +0100
|
||||
Subject: [PATCH] Issue 7152 - ns-slapd fails to shutdown when deferred
|
||||
memberof update is in progress (#7187)
|
||||
|
||||
Bug Description:
|
||||
When a deferred memberof update is in progress during shutdown, the
|
||||
backend operations (add, modify, delete, modrdn) wait in a polling loop
|
||||
for the deferred task to complete. However, if the deferred thread exits
|
||||
before clearing the SLAPI_DEFERRED_MEMBEROF flag, the loop becomes
|
||||
infinite, causing the server to hang during shutdown.
|
||||
|
||||
Fix Description:
|
||||
Add additional check to the polling loops so they exit immediately when
|
||||
the server is shutting down.
|
||||
|
||||
Fixes: https://github.com/389ds/389-ds-base/issues/7152
|
||||
|
||||
Reviewed by: @tbordaz (Thanks!)
|
||||
---
|
||||
ldap/servers/slapd/back-ldbm/ldbm_add.c | 2 +-
|
||||
ldap/servers/slapd/back-ldbm/ldbm_delete.c | 2 +-
|
||||
ldap/servers/slapd/back-ldbm/ldbm_modify.c | 2 +-
|
||||
ldap/servers/slapd/back-ldbm/ldbm_modrdn.c | 2 +-
|
||||
4 files changed, 4 insertions(+), 4 deletions(-)
|
||||
|
||||
diff --git a/ldap/servers/slapd/back-ldbm/ldbm_add.c b/ldap/servers/slapd/back-ldbm/ldbm_add.c
|
||||
index db6024636..90d5abc3d 100644
|
||||
--- a/ldap/servers/slapd/back-ldbm/ldbm_add.c
|
||||
+++ b/ldap/servers/slapd/back-ldbm/ldbm_add.c
|
||||
@@ -1452,7 +1452,7 @@ common_return:
|
||||
slapi_pblock_get(pb, SLAPI_DEFERRED_MEMBEROF, &deferred);
|
||||
if (deferred) {
|
||||
PRIntervalTime delay = PR_MillisecondsToInterval(100);
|
||||
- while (deferred) {
|
||||
+ while (deferred && !g_get_shutdown()) {
|
||||
DS_Sleep(delay);
|
||||
slapi_pblock_get(pb, SLAPI_DEFERRED_MEMBEROF, &deferred);
|
||||
}
|
||||
diff --git a/ldap/servers/slapd/back-ldbm/ldbm_delete.c b/ldap/servers/slapd/back-ldbm/ldbm_delete.c
|
||||
index 498342f2d..cbfb5bca9 100644
|
||||
--- a/ldap/servers/slapd/back-ldbm/ldbm_delete.c
|
||||
+++ b/ldap/servers/slapd/back-ldbm/ldbm_delete.c
|
||||
@@ -1536,7 +1536,7 @@ diskfull_return:
|
||||
slapi_pblock_get(pb, SLAPI_DEFERRED_MEMBEROF, &deferred);
|
||||
if (deferred) {
|
||||
PRIntervalTime delay = PR_MillisecondsToInterval(100);
|
||||
- while (deferred) {
|
||||
+ while (deferred && !g_get_shutdown()) {
|
||||
DS_Sleep(delay);
|
||||
slapi_pblock_get(pb, SLAPI_DEFERRED_MEMBEROF, &deferred);
|
||||
}
|
||||
diff --git a/ldap/servers/slapd/back-ldbm/ldbm_modify.c b/ldap/servers/slapd/back-ldbm/ldbm_modify.c
|
||||
index ea49a4c56..c57ba43ae 100644
|
||||
--- a/ldap/servers/slapd/back-ldbm/ldbm_modify.c
|
||||
+++ b/ldap/servers/slapd/back-ldbm/ldbm_modify.c
|
||||
@@ -1179,7 +1179,7 @@ common_return:
|
||||
slapi_pblock_get(pb, SLAPI_DEFERRED_MEMBEROF, &deferred);
|
||||
if (deferred) {
|
||||
PRIntervalTime delay = PR_MillisecondsToInterval(100);
|
||||
- while (deferred) {
|
||||
+ while (deferred && !g_get_shutdown()) {
|
||||
DS_Sleep(delay);
|
||||
slapi_pblock_get(pb, SLAPI_DEFERRED_MEMBEROF, &deferred);
|
||||
}
|
||||
diff --git a/ldap/servers/slapd/back-ldbm/ldbm_modrdn.c b/ldap/servers/slapd/back-ldbm/ldbm_modrdn.c
|
||||
index 018ad9e49..759edb80d 100644
|
||||
--- a/ldap/servers/slapd/back-ldbm/ldbm_modrdn.c
|
||||
+++ b/ldap/servers/slapd/back-ldbm/ldbm_modrdn.c
|
||||
@@ -1469,7 +1469,7 @@ common_return:
|
||||
slapi_pblock_get(pb, SLAPI_DEFERRED_MEMBEROF, &deferred);
|
||||
if (deferred) {
|
||||
PRIntervalTime delay = PR_MillisecondsToInterval(100);
|
||||
- while (deferred) {
|
||||
+ while (deferred && !g_get_shutdown()) {
|
||||
DS_Sleep(delay);
|
||||
slapi_pblock_get(pb, SLAPI_DEFERRED_MEMBEROF, &deferred);
|
||||
}
|
||||
--
|
||||
2.52.0
|
||||
|
||||
@ -0,0 +1,61 @@
|
||||
From a84a6a7d8323316bfa4055729a3604a0fcfebaf9 Mon Sep 17 00:00:00 2001
|
||||
From: Akshay Adhikari <aadhikar@redhat.com>
|
||||
Date: Fri, 16 Jan 2026 19:35:42 +0530
|
||||
Subject: [PATCH] Issue 7169 - Fix automember_plugin CI test failures (#7181)
|
||||
|
||||
Description: Issue 7053 removed member cleanup from MemberOf plugin,
|
||||
transferring it to Referential Integrity plugin. Enable this plugin
|
||||
in automember tests and clean up groups before rebuild task tests.
|
||||
|
||||
Fixes: #7169
|
||||
|
||||
Reviewed by: @progier389
|
||||
---
|
||||
.../tests/suites/automember_plugin/basic_test.py | 16 ++++++++++++++--
|
||||
1 file changed, 14 insertions(+), 2 deletions(-)
|
||||
|
||||
diff --git a/dirsrvtests/tests/suites/automember_plugin/basic_test.py b/dirsrvtests/tests/suites/automember_plugin/basic_test.py
|
||||
index 6f2cf3326..f3629c811 100644
|
||||
--- a/dirsrvtests/tests/suites/automember_plugin/basic_test.py
|
||||
+++ b/dirsrvtests/tests/suites/automember_plugin/basic_test.py
|
||||
@@ -19,7 +19,8 @@ from lib389.idm.organizationalunit import OrganizationalUnits
|
||||
from lib389.idm.domain import Domain
|
||||
from lib389.idm.posixgroup import PosixGroups
|
||||
from lib389.plugins import AutoMembershipPlugin, AutoMembershipDefinitions, \
|
||||
- MemberOfPlugin, AutoMembershipRegexRules, AutoMembershipDefinition, RetroChangelogPlugin
|
||||
+ MemberOfPlugin, AutoMembershipRegexRules, AutoMembershipDefinition, RetroChangelogPlugin, \
|
||||
+ ReferentialIntegrityPlugin
|
||||
from lib389.backend import Backends
|
||||
from lib389.config import Config
|
||||
from lib389._constants import DEFAULT_SUFFIX
|
||||
@@ -196,6 +197,7 @@ def _create_all_entries(topo):
|
||||
auto = AutoMembershipPlugin(topo.ms["supplier1"])
|
||||
auto.add("nsslapd-pluginConfigArea", "cn=autoMembersPlugin,{}".format(BASE_REPL))
|
||||
MemberOfPlugin(topo.ms["supplier1"]).enable()
|
||||
+ ReferentialIntegrityPlugin(topo.ms["supplier1"]).enable()
|
||||
automembers_definitions = AutoMembershipDefinitions(topo.ms["supplier1"])
|
||||
automembers_definitions.create(properties={
|
||||
'cn': 'userGroups',
|
||||
@@ -950,8 +952,18 @@ def _startuptask(topo):
|
||||
|
||||
@pytest.fixture(scope="function")
|
||||
def _fixture_for_build_task(request, topo):
|
||||
+ supplier = topo.ms['supplier1']
|
||||
+ managers_grp = "cn=Managers,ou=userGroups,{}".format(BASE_SUFF)
|
||||
+ contract_grp = "cn=Contractors,ou=userGroups,{}".format(BASE_SUFF)
|
||||
+
|
||||
+ for grp in (managers_grp, contract_grp):
|
||||
+ group = Group(supplier, grp)
|
||||
+ try:
|
||||
+ group.remove_all('member')
|
||||
+ except ldap.NO_SUCH_ATTRIBUTE:
|
||||
+ pass
|
||||
+
|
||||
def finof():
|
||||
- supplier = topo.ms['supplier1']
|
||||
auto_mem_scope = "ou=TaskEmployees,{}".format(BASE_SUFF)
|
||||
for user in nsAdminGroups(supplier, auto_mem_scope, rdn=None).list():
|
||||
user.delete()
|
||||
--
|
||||
2.52.0
|
||||
|
||||
115
0009-Issue-6758-Use-OUIA-selectors-for-WebUI-plugin-tests.patch
Normal file
115
0009-Issue-6758-Use-OUIA-selectors-for-WebUI-plugin-tests.patch
Normal file
@ -0,0 +1,115 @@
|
||||
From 5b32479abcd68f8b37d2fb207c502113a5b4b16c Mon Sep 17 00:00:00 2001
|
||||
From: Akshay Adhikari <aadhikar@redhat.com>
|
||||
Date: Mon, 19 Jan 2026 19:45:29 +0530
|
||||
Subject: [PATCH] Issue 6758 - Use OUIA selectors for WebUI plugin tests
|
||||
(#7182)
|
||||
|
||||
Description:
|
||||
Add ouiaId to plugin NavItems and update tests to use OUIA selectors
|
||||
instead of text matching.
|
||||
|
||||
Relates: #6758
|
||||
|
||||
Reviewed by: @droideck
|
||||
---
|
||||
.../suites/webui/plugins/plugins_test.py | 28 +++++++++----------
|
||||
src/cockpit/389-console/src/plugins.jsx | 2 +-
|
||||
2 files changed, 15 insertions(+), 15 deletions(-)
|
||||
|
||||
diff --git a/dirsrvtests/tests/suites/webui/plugins/plugins_test.py b/dirsrvtests/tests/suites/webui/plugins/plugins_test.py
|
||||
index e4bd7f039..a849bfb91 100644
|
||||
--- a/dirsrvtests/tests/suites/webui/plugins/plugins_test.py
|
||||
+++ b/dirsrvtests/tests/suites/webui/plugins/plugins_test.py
|
||||
@@ -168,8 +168,8 @@ def test_linked_attributes_plugin_visibility(topology_st, page, browser_name):
|
||||
|
||||
log.info('Click on Plugins tab, click on Linked Attributes plugin and check if element is loaded.')
|
||||
frame.get_by_role('tab', name='Plugins', exact=True).click()
|
||||
- frame.get_by_text('Linked Attributes').wait_for()
|
||||
- frame.get_by_text('Linked Attributes').click()
|
||||
+ frame.locator('[data-ouia-component-id="linkedAttributes"]').wait_for()
|
||||
+ frame.locator('[data-ouia-component-id="linkedAttributes"]').click()
|
||||
frame.get_by_role('button', name='Add Config').wait_for()
|
||||
assert frame.get_by_role('button', name='Add Config').is_visible()
|
||||
|
||||
@@ -254,8 +254,8 @@ def test_ldap_pass_through_auth_plugin_visibility(topology_st, page, browser_nam
|
||||
|
||||
log.info('Click on Plugins tab, click on LDAP Pass Through Auth plugin and check if element is loaded.')
|
||||
frame.get_by_role('tab', name='Plugins', exact=True).click()
|
||||
- frame.get_by_text('LDAP Pass Through Auth').wait_for()
|
||||
- frame.get_by_text('LDAP Pass Through Auth').click()
|
||||
+ frame.locator('[data-ouia-component-id="passthroughAuthentication"]').wait_for()
|
||||
+ frame.locator('[data-ouia-component-id="passthroughAuthentication"]').click()
|
||||
frame.get_by_role('button', name='Add URL').wait_for()
|
||||
assert frame.get_by_role('button', name='Add URL').is_visible()
|
||||
|
||||
@@ -280,8 +280,8 @@ def test_pam_pass_through_auth_plugin_visibility(topology_st, page, browser_name
|
||||
|
||||
log.info('Click on Plugins tab, click on PAM Pass Through Auth plugin and check if element is loaded.')
|
||||
frame.get_by_role('tab', name='Plugins', exact=True).click()
|
||||
- frame.get_by_text('PAM Pass Through Auth').wait_for()
|
||||
- frame.get_by_text('PAM Pass Through Auth').click()
|
||||
+ frame.locator('[data-ouia-component-id="pamPassthroughAuthentication"]').wait_for()
|
||||
+ frame.locator('[data-ouia-component-id="pamPassthroughAuthentication"]').click()
|
||||
frame.get_by_role('button', name='Add Config').wait_for()
|
||||
assert frame.get_by_role('button', name='Add Config').is_visible()
|
||||
|
||||
@@ -306,8 +306,8 @@ def test_posix_winsync_plugin_visibility(topology_st, page, browser_name):
|
||||
|
||||
log.info('Click on Plugins tab, click on Posix Winsync plugin and check if element is loaded.')
|
||||
frame.get_by_role('tab', name='Plugins', exact=True).click()
|
||||
- frame.get_by_text('Posix Winsync').wait_for()
|
||||
- frame.get_by_text('Posix Winsync').click()
|
||||
+ frame.locator('[data-ouia-component-id="winsync"]').wait_for()
|
||||
+ frame.locator('[data-ouia-component-id="winsync"]').click()
|
||||
frame.locator('#posixWinsyncCreateMemberOfTask').wait_for()
|
||||
assert frame.locator('#posixWinsyncCreateMemberOfTask').is_visible()
|
||||
|
||||
@@ -332,8 +332,8 @@ def test_referential_integrity_plugin_visibility(topology_st, page, browser_name
|
||||
|
||||
log.info('Click on Plugins tab, click on Referential Integrity plugin and check if element is loaded.')
|
||||
frame.get_by_role('tab', name='Plugins', exact=True).click()
|
||||
- frame.get_by_text('Referential Integrity').wait_for()
|
||||
- frame.get_by_text('Referential Integrity').click()
|
||||
+ frame.locator('[data-ouia-component-id="referentialIntegrity"]').wait_for()
|
||||
+ frame.locator('[data-ouia-component-id="referentialIntegrity"]').click()
|
||||
frame.locator('#entryScope').wait_for()
|
||||
assert frame.locator('#entryScope').is_visible()
|
||||
|
||||
@@ -358,8 +358,8 @@ def test_retro_changelog_plugin_visibility(topology_st, page, browser_name):
|
||||
|
||||
log.info('Click on Plugins tab, click on Retro Changelog plugin and check if element is loaded.')
|
||||
frame.get_by_role('tab', name='Plugins', exact=True).click()
|
||||
- frame.get_by_text('Retro Changelog').wait_for()
|
||||
- frame.get_by_text('Retro Changelog').click()
|
||||
+ frame.locator('[data-ouia-component-id="retroChangelog"]').wait_for()
|
||||
+ frame.locator('[data-ouia-component-id="retroChangelog"]').click()
|
||||
frame.locator('#isReplicated').wait_for()
|
||||
assert frame.locator('#isReplicated').is_visible()
|
||||
|
||||
@@ -384,8 +384,8 @@ def test_rootdn_access_control_plugin_visibility(topology_st, page, browser_name
|
||||
|
||||
log.info('Click on Plugins tab, click on RootDN Access Control plugin and check if element is loaded.')
|
||||
frame.get_by_role('tab', name='Plugins', exact=True).click()
|
||||
- frame.get_by_text('RootDN Access Control').wait_for()
|
||||
- frame.get_by_text('RootDN Access Control').click()
|
||||
+ frame.locator('[data-ouia-component-id="rootDnaAccessControl"]').wait_for()
|
||||
+ frame.locator('[data-ouia-component-id="rootDnaAccessControl"]').click()
|
||||
frame.locator('#allowMon').wait_for()
|
||||
assert frame.locator('#allowMon').is_visible()
|
||||
|
||||
diff --git a/src/cockpit/389-console/src/plugins.jsx b/src/cockpit/389-console/src/plugins.jsx
|
||||
index 4124a77ef..e24255c76 100644
|
||||
--- a/src/cockpit/389-console/src/plugins.jsx
|
||||
+++ b/src/cockpit/389-console/src/plugins.jsx
|
||||
@@ -680,7 +680,7 @@ export class Plugins extends React.Component {
|
||||
<Nav key={this.state.pluginTableKey} theme="light" onSelect={(event, item) => this.handleSelect(event, item)}>
|
||||
<NavList>
|
||||
{Object.entries(selectPlugins).map(([id, item]) => (
|
||||
- <NavItem key={item.name} itemId={item.name} isActive={this.state.activePlugin === item.name}>
|
||||
+ <NavItem key={item.name} itemId={item.name} ouiaId={id} isActive={this.state.activePlugin === item.name}>
|
||||
{item.icon}
|
||||
</NavItem>
|
||||
))}
|
||||
--
|
||||
2.52.0
|
||||
|
||||
@ -0,0 +1,32 @@
|
||||
From 9bd93dc618c261d52222e713c56500abbce4113e Mon Sep 17 00:00:00 2001
|
||||
From: progier389 <progier@redhat.com>
|
||||
Date: Mon, 19 Jan 2026 17:24:40 +0100
|
||||
Subject: [PATCH] Issue 7196 - DynamicCertificates returns empty DER (#7197)
|
||||
|
||||
Fixing a mistake done while fixing memory leaks.
|
||||
Value was freed before being added in the entry rather than after ...
|
||||
|
||||
Issue: #7196
|
||||
|
||||
Reviewed by: @jchapma (thanks!)
|
||||
---
|
||||
ldap/servers/slapd/dyncerts.c | 2 +-
|
||||
1 file changed, 1 insertion(+), 1 deletion(-)
|
||||
|
||||
diff --git a/ldap/servers/slapd/dyncerts.c b/ldap/servers/slapd/dyncerts.c
|
||||
index 50b92aa5f..efeaa6eb6 100644
|
||||
--- a/ldap/servers/slapd/dyncerts.c
|
||||
+++ b/ldap/servers/slapd/dyncerts.c
|
||||
@@ -786,8 +786,8 @@ dyncerts_cert2entry(CERTCertificate *cert)
|
||||
COND_STR(e, DYCATTR_TYPE, "OBJECT SIGNING CA", cert->nsCertType & NS_CERT_TYPE_OBJECT_SIGNING_CA);
|
||||
slapi_entry_add_string(e, DYCATTR_TOKEN, PK11_GetTokenName(cert->slot));
|
||||
secitemv(&cert->derCert, &tmpv);
|
||||
- value_done(&tmpv);
|
||||
slapi_entry_add_value(e, DYCATTR_CERTDER, &tmpv);
|
||||
+ value_done(&tmpv);
|
||||
tmpstr = secitem2hex(&cert->serialNumber);
|
||||
slapi_entry_add_string(e, DYCATTR_SN, tmpstr);
|
||||
slapi_ch_free_string(&tmpstr);
|
||||
--
|
||||
2.52.0
|
||||
|
||||
235
0011-Issue-7189-DSBLE0007-generates-incorrect-remediation.patch
Normal file
235
0011-Issue-7189-DSBLE0007-generates-incorrect-remediation.patch
Normal file
@ -0,0 +1,235 @@
|
||||
From c6f458b421598b18a545582441472b910b5ba56e Mon Sep 17 00:00:00 2001
|
||||
From: Viktor Ashirov <vashirov@redhat.com>
|
||||
Date: Tue, 20 Jan 2026 09:52:47 +0100
|
||||
Subject: [PATCH] Issue 7189 - DSBLE0007 generates incorrect remediation
|
||||
commands for scan limits
|
||||
|
||||
Bug Description:
|
||||
|
||||
The generated dsconf commands for fixing missing system indexes had two issues:
|
||||
|
||||
1. The --add-scanlimit value was not quoted, causing the shell to interpret
|
||||
"limit=5000 type=eq flags=AND" as multiple arguments instead of a single
|
||||
value, resulting in "unrecognized arguments: type=eq flags=AND" error.
|
||||
|
||||
2. When both matching rule and scanlimit were missing, two separate commands
|
||||
were generated where the second would fail because the matching rule was
|
||||
already added by the first command.
|
||||
|
||||
Fix Description:
|
||||
|
||||
1. Quote the scanlimit value in all remediation commands
|
||||
|
||||
2. Combine matching rule and scanlimit fixes into a single command when
|
||||
both are missing for the same index instead of expected_scanlimit)
|
||||
|
||||
Fixes: https://github.com/389ds/389-ds-base/issues/7189
|
||||
|
||||
Reviewed by: @progier389, @droideck (Thanks!)
|
||||
---
|
||||
.../healthcheck/health_system_indexes_test.py | 126 ++++++++++++++++++
|
||||
src/lib389/lib389/backend.py | 39 +++---
|
||||
2 files changed, 147 insertions(+), 18 deletions(-)
|
||||
|
||||
diff --git a/dirsrvtests/tests/suites/healthcheck/health_system_indexes_test.py b/dirsrvtests/tests/suites/healthcheck/health_system_indexes_test.py
|
||||
index a977b71d1..486fad44b 100644
|
||||
--- a/dirsrvtests/tests/suites/healthcheck/health_system_indexes_test.py
|
||||
+++ b/dirsrvtests/tests/suites/healthcheck/health_system_indexes_test.py
|
||||
@@ -408,6 +408,132 @@ def test_retrocl_plugin_missing_matching_rule(topology_st, retrocl_plugin_enable
|
||||
run_healthcheck_and_flush_log(topology_st, standalone, json=True, searched_code=JSON_OUTPUT)
|
||||
|
||||
|
||||
+def test_missing_scanlimit(topology_st, log_buffering_enabled):
|
||||
+ """Check if healthcheck returns DSBLE0007 code when parentId index is missing scanlimit
|
||||
+
|
||||
+ :id: 40e1bf6a-2397-459b-bdf3-f787ca118b86
|
||||
+ :setup: Standalone instance
|
||||
+ :steps:
|
||||
+ 1. Create DS instance
|
||||
+ 2. Remove nsIndexIDListScanLimit from parentId index
|
||||
+ 3. Use healthcheck without --json option
|
||||
+ 4. Use healthcheck with --json option
|
||||
+ 5. Verify the remediation command has properly quoted scanlimit
|
||||
+ 6. Re-add the scanlimit
|
||||
+ 7. Use healthcheck without --json option
|
||||
+ 8. Use healthcheck with --json option
|
||||
+ :expectedresults:
|
||||
+ 1. Success
|
||||
+ 2. Success
|
||||
+ 3. healthcheck reports DSBLE0007 code and related details
|
||||
+ 4. healthcheck reports DSBLE0007 code and related details
|
||||
+ 5. The scanlimit value is quoted in the remediation command
|
||||
+ 6. Success
|
||||
+ 7. healthcheck reports no issues found
|
||||
+ 8. healthcheck reports no issues found
|
||||
+ """
|
||||
+
|
||||
+ RET_CODE = "DSBLE0007"
|
||||
+ PARENTID_DN = "cn=parentid,cn=index,cn=userroot,cn=ldbm database,cn=plugins,cn=config"
|
||||
+ SCANLIMIT_VALUE = "limit=5000 type=eq flags=AND"
|
||||
+
|
||||
+ standalone = topology_st.standalone
|
||||
+
|
||||
+ log.info("Remove nsIndexIDListScanLimit from parentId index")
|
||||
+ parentid_index = Index(standalone, PARENTID_DN)
|
||||
+ parentid_index.remove("nsIndexIDListScanLimit", SCANLIMIT_VALUE)
|
||||
+
|
||||
+ run_healthcheck_and_flush_log(topology_st, standalone, json=False, searched_code=RET_CODE)
|
||||
+
|
||||
+ # Verify the remediation command has properly quoted scanlimit
|
||||
+ args = FakeArgs()
|
||||
+ args.instance = standalone.serverid
|
||||
+ args.verbose = standalone.verbose
|
||||
+ args.list_errors = False
|
||||
+ args.list_checks = False
|
||||
+ args.exclude_check = []
|
||||
+ args.check = ["backends"]
|
||||
+ args.dry_run = False
|
||||
+ args.json = False
|
||||
+ health_check_run(standalone, topology_st.logcap.log, args)
|
||||
+ # Check that the scanlimit is quoted in the output
|
||||
+ assert topology_st.logcap.contains('--add-scanlimit "limit=5000 type=eq flags=AND"')
|
||||
+ log.info("Verified scanlimit is properly quoted in remediation command")
|
||||
+ topology_st.logcap.flush()
|
||||
+
|
||||
+ run_healthcheck_and_flush_log(topology_st, standalone, json=True, searched_code=RET_CODE)
|
||||
+
|
||||
+ log.info("Re-add the nsIndexIDListScanLimit")
|
||||
+ parentid_index = Index(standalone, PARENTID_DN)
|
||||
+ parentid_index.add("nsIndexIDListScanLimit", SCANLIMIT_VALUE)
|
||||
+
|
||||
+ run_healthcheck_and_flush_log(topology_st, standalone, json=False, searched_code=CMD_OUTPUT)
|
||||
+ run_healthcheck_and_flush_log(topology_st, standalone, json=True, searched_code=JSON_OUTPUT)
|
||||
+
|
||||
+
|
||||
+def test_missing_matching_rule_and_scanlimit(topology_st, log_buffering_enabled):
|
||||
+ """Check if healthcheck generates a single combined command when both matching rule and scanlimit are missing
|
||||
+
|
||||
+ :id: af8214ad-5e4c-422a-8f74-3e99227551df
|
||||
+ :setup: Standalone instance
|
||||
+ :steps:
|
||||
+ 1. Create DS instance
|
||||
+ 2. Remove both integerOrderingMatch and nsIndexIDListScanLimit from parentId index
|
||||
+ 3. Use healthcheck and verify a single combined command is generated
|
||||
+ 4. Re-add the matching rule and scanlimit
|
||||
+ 5. Use healthcheck without --json option
|
||||
+ 6. Use healthcheck with --json option
|
||||
+ :expectedresults:
|
||||
+ 1. Success
|
||||
+ 2. Success
|
||||
+ 3. healthcheck reports DSBLE0007 and generates a single command with both --add-mr and --add-scanlimit
|
||||
+ 4. Success
|
||||
+ 5. healthcheck reports no issues found
|
||||
+ 6. healthcheck reports no issues found
|
||||
+ """
|
||||
+
|
||||
+ RET_CODE = "DSBLE0007"
|
||||
+ PARENTID_DN = "cn=parentid,cn=index,cn=userroot,cn=ldbm database,cn=plugins,cn=config"
|
||||
+ SCANLIMIT_VALUE = "limit=5000 type=eq flags=AND"
|
||||
+
|
||||
+ standalone = topology_st.standalone
|
||||
+
|
||||
+ log.info("Remove both integerOrderingMatch and nsIndexIDListScanLimit from parentId index")
|
||||
+ parentid_index = Index(standalone, PARENTID_DN)
|
||||
+ parentid_index.remove("nsMatchingRule", "integerOrderingMatch")
|
||||
+ parentid_index.remove("nsIndexIDListScanLimit", SCANLIMIT_VALUE)
|
||||
+
|
||||
+ # Run healthcheck and verify combined command
|
||||
+ args = FakeArgs()
|
||||
+ args.instance = standalone.serverid
|
||||
+ args.verbose = standalone.verbose
|
||||
+ args.list_errors = False
|
||||
+ args.list_checks = False
|
||||
+ args.exclude_check = []
|
||||
+ args.check = ["backends"]
|
||||
+ args.dry_run = False
|
||||
+ args.json = False
|
||||
+ health_check_run(standalone, topology_st.logcap.log, args)
|
||||
+
|
||||
+ # Verify DSBLE0007 is reported
|
||||
+ assert topology_st.logcap.contains(RET_CODE)
|
||||
+ log.info("healthcheck returned code: %s" % RET_CODE)
|
||||
+
|
||||
+ # Verify a single combined command is generated with both --add-mr and --add-scanlimit
|
||||
+ assert topology_st.logcap.contains('--add-mr integerOrderingMatch --add-scanlimit "limit=5000 type=eq flags=AND"')
|
||||
+ log.info("Verified combined command with both --add-mr and --add-scanlimit")
|
||||
+
|
||||
+ topology_st.logcap.flush()
|
||||
+
|
||||
+ log.info("Re-add the integerOrderingMatch matching rule and scanlimit")
|
||||
+ parentid_index = Index(standalone, PARENTID_DN)
|
||||
+ parentid_index.add("nsMatchingRule", "integerOrderingMatch")
|
||||
+ parentid_index.add("nsIndexIDListScanLimit", SCANLIMIT_VALUE)
|
||||
+
|
||||
+ run_healthcheck_and_flush_log(topology_st, standalone, json=False, searched_code=CMD_OUTPUT)
|
||||
+ run_healthcheck_and_flush_log(topology_st, standalone, json=True, searched_code=JSON_OUTPUT)
|
||||
+
|
||||
+
|
||||
def test_multiple_missing_indexes(topology_st, log_buffering_enabled):
|
||||
"""Check if healthcheck returns DSBLE0007 code when multiple system indexes are missing
|
||||
|
||||
diff --git a/src/lib389/lib389/backend.py b/src/lib389/lib389/backend.py
|
||||
index fba95987b..db464b43a 100644
|
||||
--- a/src/lib389/lib389/backend.py
|
||||
+++ b/src/lib389/lib389/backend.py
|
||||
@@ -678,7 +678,7 @@ class Backend(DSLdapObject):
|
||||
if expected_config.get('matching_rule'):
|
||||
cmd += f" --matching-rule {expected_config['matching_rule']}"
|
||||
if expected_config.get('scanlimit'):
|
||||
- cmd += f" --add-scanlimit {expected_config['scanlimit']}"
|
||||
+ cmd += f" --add-scanlimit \"{expected_config['scanlimit']}\""
|
||||
remediation_commands.append(cmd)
|
||||
reindex_attrs.add(attr_name) # New index needs reindexing
|
||||
else:
|
||||
@@ -700,28 +700,31 @@ class Backend(DSLdapObject):
|
||||
remediation_commands.append(cmd)
|
||||
reindex_attrs.add(attr_name)
|
||||
|
||||
- # Check matching rules
|
||||
+ # Check matching rules and scanlimit together to generate a single combined command
|
||||
expected_mr = expected_config.get('matching_rule')
|
||||
+ expected_scanlimit = expected_config.get('scanlimit')
|
||||
+
|
||||
+ missing_mr = False
|
||||
if expected_mr:
|
||||
actual_mrs_lower = [mr.lower() for mr in actual_mrs]
|
||||
if expected_mr.lower() not in actual_mrs_lower:
|
||||
discrepancies.append(f"Index {attr_name} missing matching rule: {expected_mr}")
|
||||
- # Add the missing matching rule
|
||||
- cmd = f"dsconf YOUR_INSTANCE backend index set {bename} --attr {attr_name} --add-mr {expected_mr}"
|
||||
- remediation_commands.append(cmd)
|
||||
- reindex_attrs.add(attr_name)
|
||||
-
|
||||
- # Check fine grain definitions for parentid ONLY
|
||||
- expected_scanlimit = expected_config.get('scanlimit')
|
||||
- if (attr_name.lower() == "parentid") and expected_scanlimit and (len(actual_scanlimit) == 0):
|
||||
- discrepancies.append(f"Index {attr_name} missing fine grain definition of IDs limit: {expected_mr}")
|
||||
- # Add the missing scanlimit
|
||||
- if expected_mr:
|
||||
- cmd = f"dsconf YOUR_INSTANCE backend index set {bename} --attr {attr_name} --add-mr {expected_mr} --add-scanlimit {expected_scanlimit}"
|
||||
- else:
|
||||
- cmd = f"dsconf YOUR_INSTANCE backend index set {bename} --attr {attr_name} --add-scanlimit {expected_scanlimit}"
|
||||
- remediation_commands.append(cmd)
|
||||
- reindex_attrs.add(attr_name)
|
||||
+ missing_mr = True
|
||||
+
|
||||
+ missing_scanlimit = False
|
||||
+ if expected_scanlimit and (len(actual_scanlimit) == 0):
|
||||
+ discrepancies.append(f"Index {attr_name} missing fine grain definition of IDs limit: {expected_scanlimit}")
|
||||
+ missing_scanlimit = True
|
||||
+
|
||||
+ # Generate a single combined command for all missing items
|
||||
+ if missing_mr or missing_scanlimit:
|
||||
+ cmd = f"dsconf YOUR_INSTANCE backend index set {bename} --attr {attr_name}"
|
||||
+ if missing_mr:
|
||||
+ cmd += f" --add-mr {expected_mr}"
|
||||
+ if missing_scanlimit:
|
||||
+ cmd += f" --add-scanlimit \"{expected_scanlimit}\""
|
||||
+ remediation_commands.append(cmd)
|
||||
+ reindex_attrs.add(attr_name)
|
||||
|
||||
except Exception as e:
|
||||
self._log.debug(f"_lint_system_indexes - Error checking index {attr_name}: {e}")
|
||||
--
|
||||
2.52.0
|
||||
|
||||
504
0012-Issue-7170-Support-of-PQC-keys-7188.patch
Normal file
504
0012-Issue-7170-Support-of-PQC-keys-7188.patch
Normal file
@ -0,0 +1,504 @@
|
||||
From 6ce33b1bedd2cd13d7e6544692354715f6e613b8 Mon Sep 17 00:00:00 2001
|
||||
From: progier389 <progier@redhat.com>
|
||||
Date: Tue, 20 Jan 2026 19:41:05 +0100
|
||||
Subject: [PATCH] Issue 7170 - Support of PQC keys (#7188)
|
||||
|
||||
Support of Post Quantum Cryptography Keys in certificates:
|
||||
Added support of a new key type: ML_DSA
|
||||
Enable the following policies (that are not enabled by defaut): ML-DSA-44, ML-DSA-65- ML-DSA-87
|
||||
Replaced deprecated function SSL_ConfigSecureServer by SSL_ConfigServerCert
|
||||
Added test case for ML-DSA certificate. That test case rely of openssl command because python cryptography module does not yet support ML-DSA keys
|
||||
|
||||
Issue: #7170
|
||||
|
||||
Reviewed by: @tbordaz, @droideck and @vashirov (Thanks!)
|
||||
---
|
||||
dirsrvtests/tests/suites/tls/mldsa_test.py | 322 +++++++++++++++++++++
|
||||
ldap/servers/slapd/ssl.c | 95 +++++-
|
||||
2 files changed, 407 insertions(+), 10 deletions(-)
|
||||
create mode 100644 dirsrvtests/tests/suites/tls/mldsa_test.py
|
||||
|
||||
diff --git a/dirsrvtests/tests/suites/tls/mldsa_test.py b/dirsrvtests/tests/suites/tls/mldsa_test.py
|
||||
new file mode 100644
|
||||
index 000000000..2c815088b
|
||||
--- /dev/null
|
||||
+++ b/dirsrvtests/tests/suites/tls/mldsa_test.py
|
||||
@@ -0,0 +1,322 @@
|
||||
+# --- BEGIN COPYRIGHT BLOCK ---
|
||||
+# Copyright (C) 2026 Red Hat, Inc.
|
||||
+# All rights reserved.
|
||||
+#
|
||||
+# License: GPL (version 3 or any later version).
|
||||
+# See LICENSE for details.
|
||||
+# --- END COPYRIGHT BLOCK ---
|
||||
+#
|
||||
+import logging
|
||||
+import pytest
|
||||
+import os
|
||||
+import sys
|
||||
+import itertools
|
||||
+import rpm
|
||||
+import socket
|
||||
+import subprocess
|
||||
+from lib389.utils import ds_is_older
|
||||
+from lib389._constants import DN_DM, PW_DM, DEFAULT_SUFFIX
|
||||
+from lib389.config import Encryption, CertmapLegacy
|
||||
+from lib389.idm.user import UserAccount
|
||||
+from lib389.topologies import topology_st as topo
|
||||
+from tempfile import TemporaryDirectory
|
||||
+
|
||||
+pytestmark = pytest.mark.tier1
|
||||
+
|
||||
+DEBUGGING = os.getenv("DEBUGGING", default=False)
|
||||
+if DEBUGGING:
|
||||
+ logging.getLogger(__name__).setLevel(logging.DEBUG)
|
||||
+else:
|
||||
+ logging.getLogger(__name__).setLevel(logging.INFO)
|
||||
+log = logging.getLogger(__name__)
|
||||
+
|
||||
+
|
||||
+def rpm_is_older(pkg, version):
|
||||
+ ts = rpm.TransactionSet()
|
||||
+ mi = ts.dbMatch('name', pkg)
|
||||
+ for h in mi:
|
||||
+ print(f"{pkg} {h['version']} {version}")
|
||||
+ for n1,n2 in itertools.zip_longest(h['version'].split('.'), version.split('.'), fillvalue=""):
|
||||
+ try:
|
||||
+ if int(n1) < int(n2):
|
||||
+ return True
|
||||
+ except ValueError:
|
||||
+ if n1 < n2:
|
||||
+ return True
|
||||
+ return False
|
||||
+
|
||||
+
|
||||
+script_content="""
|
||||
+#!/bin/bash
|
||||
+set -e # Exit if a command fails
|
||||
+set -x # Log the commands
|
||||
+
|
||||
+cd {dir}
|
||||
+inst={instname}
|
||||
+url={url}
|
||||
+rootdn="{rootdn}"
|
||||
+rootpw="{rootpw}"
|
||||
+
|
||||
+################################
|
||||
+###### GENERATE CA CERT ########
|
||||
+################################
|
||||
+
|
||||
+echo "
|
||||
+[ req ]
|
||||
+distinguished_name = req_distinguished_name
|
||||
+policy = policy_match
|
||||
+x509_extensions = v3_ca
|
||||
+
|
||||
+# For the CA policy
|
||||
+[ policy_match ]
|
||||
+countryName = optional
|
||||
+stateOrProvinceName = optional
|
||||
+organizationName = optional
|
||||
+organizationalUnitName = optional
|
||||
+commonName = supplied
|
||||
+emailAddress = optional
|
||||
+
|
||||
+[ req_distinguished_name ]
|
||||
+countryName = Country Name (2 letter code)
|
||||
+countryName_default = FR
|
||||
+countryName_min = 2
|
||||
+countryName_max = 2
|
||||
+
|
||||
+stateOrProvinceName = State or Province Name (full name)
|
||||
+stateOrProvinceName_default = test
|
||||
+
|
||||
+localityName = Locality Name (eg, city)
|
||||
+
|
||||
+0.organizationName = Organization Name (eg, company)
|
||||
+0.organizationName_default = test-ML-DSA-CA
|
||||
+
|
||||
+organizationalUnitName = Organizational Unit Name (eg, section)
|
||||
+#organizationalUnitName_default =
|
||||
+
|
||||
+commonName = Common Name (e.g. server FQDN or YOUR name)
|
||||
+commonName_max = 64
|
||||
+
|
||||
+
|
||||
+[ v3_ca ]
|
||||
+subjectKeyIdentifier = hash
|
||||
+authorityKeyIdentifier = keyid:always,issuer
|
||||
+basicConstraints = critical,CA:true
|
||||
+#nsComment = "OpenSSL Generated Certificate"
|
||||
+keyUsage=critical, keyCertSign
|
||||
+" >ca.conf
|
||||
+
|
||||
+
|
||||
+openssl genpkey -algorithm ML-DSA-87 -out ca.key
|
||||
+openssl req -x509 -new -sha256 -key ca.key -nodes -days 3650 -config ca.conf -subj "/CN=`hostname`/O=test-ML-DSA-CA/C=FR" -out ca.pem -keyout ca.key
|
||||
+openssl x509 -outform der -in ca.pem -out ca.crt
|
||||
+
|
||||
+openssl x509 -text -in ca.pem
|
||||
+
|
||||
+####################################
|
||||
+###### GENERATE SERVER CERT ########
|
||||
+####################################
|
||||
+
|
||||
+echo "
|
||||
+[ req ]
|
||||
+distinguished_name = req_distinguished_name
|
||||
+policy = policy_match
|
||||
+x509_extensions = v3_cert
|
||||
+
|
||||
+# For the cert policy
|
||||
+[ policy_match ]
|
||||
+countryName = optional
|
||||
+stateOrProvinceName = optional
|
||||
+organizationName = optional
|
||||
+organizationalUnitName = optional
|
||||
+commonName = supplied
|
||||
+emailAddress = optional
|
||||
+
|
||||
+[ req_distinguished_name ]
|
||||
+countryName = Country Name (2 letter code)
|
||||
+countryName_default = FR
|
||||
+countryName_min = 2
|
||||
+countryName_max = 2
|
||||
+
|
||||
+stateOrProvinceName = State or Province Name (full name)
|
||||
+
|
||||
+localityName = Locality Name (eg, city)
|
||||
+
|
||||
+0.organizationName = Organization Name (eg, company)
|
||||
+0.organizationName_default = test-ML-DSA
|
||||
+
|
||||
+organizationalUnitName = Organizational Unit Name (eg, section)
|
||||
+#organizationalUnitName_default =
|
||||
+
|
||||
+commonName = Common Name (e.g. server FQDN or YOUR name)
|
||||
+commonName_max = 64
|
||||
+
|
||||
+
|
||||
+[ v3_cert ]
|
||||
+basicConstraints = critical,CA:false
|
||||
+subjectAltName=DNS:`hostname`
|
||||
+keyUsage=digitalSignature, nonRepudiation, keyEncipherment, dataEncipherment
|
||||
+#nsComment = "OpenSSL Generated Certificate"
|
||||
+extendedKeyUsage=clientAuth, serverAuth
|
||||
+nsCertType=client, server
|
||||
+" >cert.conf
|
||||
+
|
||||
+openssl genpkey -algorithm ML-DSA-65 -out cert.key
|
||||
+openssl req -new -sha256 -key cert.key -nodes -config cert.conf -subj "/CN=`hostname`/O=test-ML-DSA/C=FR" -out cert.csr
|
||||
+openssl x509 -req -sha256 -days 3650 -extensions v3_cert -extfile cert.conf -in cert.csr -CA ca.pem -CAkey ca.key -CAcreateserial -out cert.pem
|
||||
+openssl pkcs12 -export -inkey cert.key -in cert.pem -name mldsacert -out cert.p12 -passout pass:secret12
|
||||
+
|
||||
+openssl x509 -text -in cert.pem
|
||||
+
|
||||
+
|
||||
+
|
||||
+####################################
|
||||
+###### GENERATE CLIENT CERT ########
|
||||
+####################################
|
||||
+
|
||||
+echo "
|
||||
+[ req ]
|
||||
+distinguished_name = req_distinguished_name
|
||||
+policy = policy_match
|
||||
+x509_extensions = v3_cert
|
||||
+
|
||||
+# For the cert policy
|
||||
+[ policy_match ]
|
||||
+countryName = optional
|
||||
+stateOrProvinceName = optional
|
||||
+organizationName = optional
|
||||
+organizationalUnitName = optional
|
||||
+commonName = supplied
|
||||
+emailAddress = optional
|
||||
+
|
||||
+[ req_distinguished_name ]
|
||||
+countryName = Country Name (2 letter code)
|
||||
+countryName_default = FR
|
||||
+countryName_min = 2
|
||||
+countryName_max = 2
|
||||
+
|
||||
+stateOrProvinceName = State or Province Name (full name)
|
||||
+
|
||||
+localityName = Locality Name (eg, city)
|
||||
+
|
||||
+0.organizationName = Organization Name (eg, company)
|
||||
+0.organizationName_default = test-ML-DSA
|
||||
+
|
||||
+organizationalUnitName = Organizational Unit Name (eg, section)
|
||||
+#organizationalUnitName_default =
|
||||
+
|
||||
+commonName = Common Name (e.g. server FQDN or YOUR name)
|
||||
+commonName_max = 64
|
||||
+
|
||||
+
|
||||
+[ v3_cert ]
|
||||
+basicConstraints = critical,CA:false
|
||||
+subjectAltName=DNS:`hostname`
|
||||
+keyUsage=digitalSignature, nonRepudiation, keyEncipherment, dataEncipherment
|
||||
+#nsComment = "OpenSSL Generated Certificate"
|
||||
+extendedKeyUsage=clientAuth
|
||||
+nsCertType=client, server
|
||||
+" >client.conf
|
||||
+
|
||||
+openssl genpkey -algorithm ML-DSA-65 -out client.key
|
||||
+openssl req -new -sha256 -key client.key -nodes -config client.conf -subj "/CN=`hostname`/O=client-test-ML-DSA/C=FR" -out client.csr
|
||||
+openssl x509 -req -sha256 -days 3650 -extensions v3_cert -extfile client.conf -in client.csr -CA ca.pem -CAkey ca.key -CAcreateserial -out client.pem
|
||||
+openssl pkcs12 -export -inkey client.key -in client.pem -name mldsacert2 -out client.p12 -passout pass:secret12
|
||||
+
|
||||
+openssl x509 -text -in client.pem
|
||||
+
|
||||
+
|
||||
+#############################
|
||||
+###### INSTALL CERTS ########
|
||||
+#############################
|
||||
+
|
||||
+certdbdir=$PREFIX/etc/dirsrv/slapd-$inst
|
||||
+rm -f $certdbdir/cert9.db $certdbdir/key4.db
|
||||
+certutil -N -d $certdbdir -f $certdbdir/pwdfile.txt
|
||||
+
|
||||
+certutil -A -n Self-Signed-CA -t CT,, -f $certdbdir/pwdfile.txt -d $certdbdir -a -i ca.pem
|
||||
+certutil -A -n Client-Cert -t u,, -f $certdbdir/pwdfile.txt -d $certdbdir -a -i client.pem
|
||||
+
|
||||
+dsctl $inst tls import-server-key-cert cert.pem cert.key
|
||||
+
|
||||
+dsctl $inst restart
|
||||
+
|
||||
+
|
||||
+#########################
|
||||
+###### TEST CERT ########
|
||||
+#########################
|
||||
+export LDAPTLS_CACERT=$PWD/ca.pem
|
||||
+export LDAPTLS_CERT=$PWD/client.pem
|
||||
+export LDAPTLS_KEY=$PWD/client.key
|
||||
+
|
||||
+ldapsearch -x -H $url -D "$rootdn" -w "$rootpw" -b "" -s base
|
||||
+ldapsearch -Y external -H $url -b "" -s base
|
||||
+"""
|
||||
+
|
||||
+@pytest.mark.skipif(rpm_is_older("openssl", "3.5"), reason="OpenSSL too old to support PQC")
|
||||
+@pytest.mark.skipif(rpm_is_older("nss", "3.119.1"), reason="NSS too old to support PQC")
|
||||
+def test_mldsa(topo):
|
||||
+ """Test using ML-DSA Certificate - (PQC)
|
||||
+
|
||||
+ :id: 87fb19ef-672d-4fa7-934d-dfb4397f2312
|
||||
+ :setup: Standalone Instance
|
||||
+ :steps:
|
||||
+ 1. Configure the certmap
|
||||
+ 2. Create user mapped with theclient certificate
|
||||
+ 3. Generate the test script
|
||||
+ 4. Run the test script
|
||||
+ 5. Check that ldapsearch returned the namingcontext
|
||||
+ :expectedresults:
|
||||
+ 1. No error
|
||||
+ 2. No error
|
||||
+ 3. No error
|
||||
+ 4. No error and exit code should be 0
|
||||
+ 5. namingcontext should be in the script output
|
||||
+ """
|
||||
+
|
||||
+ inst = topo.standalone
|
||||
+ inst.enable_tls()
|
||||
+
|
||||
+ cm = CertmapLegacy(inst)
|
||||
+ certmaps = cm.list()
|
||||
+ certmaps['default'].update({'DNComps': None, 'CmapLdapAttr': 'description'})
|
||||
+ cm.set(certmaps)
|
||||
+
|
||||
+ cert_dn = f'C=FR,O=client-test-ML-DSA,CN={socket.gethostname()}'
|
||||
+ dn = f'uid=test_user,ou=people,{DEFAULT_SUFFIX}'
|
||||
+ UserAccount(inst, dn=dn).create( properties= {
|
||||
+ 'uid': 'test_user',
|
||||
+ 'cn': 'Test user',
|
||||
+ 'sn': 'Test user',
|
||||
+ 'uidNumber': '99998',
|
||||
+ 'gidNumber': '99998',
|
||||
+ 'homeDirectory': '/var/empty',
|
||||
+ 'loginShell': '/bin/false',
|
||||
+ 'description': cert_dn })
|
||||
+
|
||||
+ tmpdir_kwargs = {}
|
||||
+ if sys.version_info >= (3, 12):
|
||||
+ tmpdir_kwargs['delete'] = not DEBUGGING
|
||||
+ with TemporaryDirectory(**tmpdir_kwargs) as dir:
|
||||
+ scriptname = f"{dir}/doit"
|
||||
+ d = {
|
||||
+ 'dir': dir,
|
||||
+ 'instname': inst.serverid,
|
||||
+ 'url': f"ldaps://localhost:{inst.sslport}",
|
||||
+ 'rootdn': DN_DM,
|
||||
+ 'rootpw': PW_DM,
|
||||
+ }
|
||||
+ with open(scriptname, 'w') as f:
|
||||
+ f.write(script_content.format(**d))
|
||||
+ res = subprocess.run(('/bin/bash', scriptname), stdout=subprocess.PIPE, stderr=subprocess.STDOUT, encoding='utf-8')
|
||||
+ assert res
|
||||
+ log.info(res.stdout)
|
||||
+ res.check_returncode()
|
||||
+ # If ldapsearch is successful then defaultnamingcontext should be in res.stdout
|
||||
+ assert "defaultnamingcontext" in res.stdout
|
||||
+
|
||||
+
|
||||
+if __name__ == '__main__':
|
||||
+ # Run isolated
|
||||
+ # -s for DEBUG mode
|
||||
+ CURRENT_FILE = os.path.realpath(__file__)
|
||||
+ pytest.main(["-s", CURRENT_FILE])
|
||||
diff --git a/ldap/servers/slapd/ssl.c b/ldap/servers/slapd/ssl.c
|
||||
index a9c17ef42..053db5424 100644
|
||||
--- a/ldap/servers/slapd/ssl.c
|
||||
+++ b/ldap/servers/slapd/ssl.c
|
||||
@@ -169,6 +169,20 @@ PRBool enableTLS1 = PR_TRUE;
|
||||
/* CA cert pem file */
|
||||
static char *CACertPemFile = NULL;
|
||||
|
||||
+static const struct {
|
||||
+ KeyType kt;
|
||||
+ const char *shortname;
|
||||
+ const char *fullname;
|
||||
+} supported_key_types[] = {
|
||||
+ { rsaKey, "RSA", "Rivest–Shamir–Adleman" },
|
||||
+ { ecKey, "EC", "Elliptic Curve" },
|
||||
+#ifdef MAX_ML_DSA_PRIVATE_KEY_LEN
|
||||
+ { mldsaKey, "ML-DSA", "Module-Lattice-Based Digital Signature Algorithm (post-quantum)" },
|
||||
+#endif
|
||||
+ { 0 }
|
||||
+};
|
||||
+
|
||||
+
|
||||
/* helper functions for openldap update. */
|
||||
static int slapd_extract_cert(Slapi_Entry *entry, int isCA);
|
||||
static int slapd_extract_key(Slapi_Entry *entry, char *token, PK11SlotInfo *slot);
|
||||
@@ -718,9 +732,31 @@ SSLPLCY_Install(void)
|
||||
{
|
||||
|
||||
SECStatus s = 0;
|
||||
+#ifdef MAX_ML_DSA_PRIVATE_KEY_LEN
|
||||
+ int flags = NSS_USE_ALG_IN_SIGNATURE | NSS_USE_ALG_IN_SSL;
|
||||
+ static const SECOidTag oids[] = {
|
||||
+ SEC_OID_ML_DSA_44,
|
||||
+ SEC_OID_ML_DSA_65,
|
||||
+ SEC_OID_ML_DSA_87,
|
||||
+ };
|
||||
+#endif
|
||||
|
||||
s = NSS_SetDomesticPolicy();
|
||||
|
||||
+#ifdef MAX_ML_DSA_PRIVATE_KEY_LEN
|
||||
+ /* Should rely on the crypto module policy in FIPS mode */
|
||||
+ if (!slapd_pk11_isFIPS()) {
|
||||
+ /* Set explicitly PQC algorithm policy if it is not set by default */
|
||||
+ for (size_t i=0; s == SECSuccess && i < PR_ARRAY_SIZE(oids); i++) {
|
||||
+ int oflags = 0;
|
||||
+ (void) NSS_GetAlgorithmPolicy(oids[i], &oflags);
|
||||
+ if ((oflags & flags) != flags) {
|
||||
+ s = NSS_SetAlgorithmPolicy(oids[i], flags, 0);
|
||||
+ }
|
||||
+ }
|
||||
+ }
|
||||
+#endif
|
||||
+
|
||||
return s ? PR_FAILURE : PR_SUCCESS;
|
||||
}
|
||||
|
||||
@@ -1640,7 +1676,7 @@ slapd_ssl_init2(PRFileDesc **fd, int startTLS)
|
||||
/*
|
||||
* Now, get the complete list of cipher families. Each family
|
||||
* has a token name and personality name which we'll use to find
|
||||
- * appropriate keys and certs, and call SSL_ConfigSecureServer
|
||||
+ * appropriate keys and certs, and call SSL_ConfigServerCert
|
||||
* with.
|
||||
*/
|
||||
|
||||
@@ -1759,8 +1795,6 @@ slapd_ssl_init2(PRFileDesc **fd, int startTLS)
|
||||
}
|
||||
|
||||
if (SECSuccess == rv) {
|
||||
- SSLKEAType certKEA;
|
||||
-
|
||||
/* If we want weak dh params, flag it on the socket now! */
|
||||
rv = SSL_OptionSet(*fd, SSL_ENABLE_SERVER_DHE, PR_TRUE);
|
||||
if (rv != SECSuccess) {
|
||||
@@ -1774,11 +1808,10 @@ slapd_ssl_init2(PRFileDesc **fd, int startTLS)
|
||||
}
|
||||
}
|
||||
|
||||
- certKEA = NSS_FindCertKEAType(cert);
|
||||
- rv = SSL_ConfigSecureServer(*fd, cert, key, certKEA);
|
||||
+ rv = SSL_ConfigServerCert(*fd, cert, key, NULL, 0);
|
||||
if (SECSuccess != rv) {
|
||||
errorCode = PR_GetError();
|
||||
- slapd_SSL_warn("ConfigSecureServer: "
|
||||
+ slapd_SSL_warn("SSL_ConfigServerCert: "
|
||||
"Server key/certificate is "
|
||||
"bad for cert %s of family %s (" SLAPI_COMPONENT_NAME_NSPR " error %d - %s)",
|
||||
cert_name, *family, errorCode,
|
||||
@@ -2663,6 +2696,38 @@ bail:
|
||||
return rv;
|
||||
}
|
||||
|
||||
+/* Helper for get_supported_key_type */
|
||||
+static char *
|
||||
+buf_add_str(char *buf, char *bufend, const char *str)
|
||||
+{
|
||||
+ /* bufend is sizeof(buf)-4 (to avoid overflow with ...) */
|
||||
+ char *ret = buf+strlen(str);
|
||||
+ if (ret > bufend) {
|
||||
+ ret = bufend;
|
||||
+ strcpy(buf, "...");
|
||||
+ } else {
|
||||
+ strcpy(buf, str);
|
||||
+ }
|
||||
+ return ret;
|
||||
+}
|
||||
+
|
||||
+static void
|
||||
+get_supported_key_type_names(char *buf, size_t bufsize)
|
||||
+{
|
||||
+ char *bufend = buf + bufsize - 4;
|
||||
+ for (size_t i=0; supported_key_types[i].kt; i++) {
|
||||
+ if (i>0) {
|
||||
+ if (supported_key_types[i+1].kt == 0) {
|
||||
+ /* Last supported key type */
|
||||
+ buf = buf_add_str(buf, bufend, " or ");
|
||||
+ } else {
|
||||
+ buf = buf_add_str(buf, bufend, ", ");
|
||||
+ }
|
||||
+ }
|
||||
+ buf = buf_add_str(buf, bufend, supported_key_types[i].shortname);
|
||||
+ }
|
||||
+}
|
||||
+
|
||||
/*
|
||||
* Borrowed from keyutil.c (crypto-util)
|
||||
*
|
||||
@@ -2723,10 +2788,20 @@ extractKeysAndSubject(
|
||||
}
|
||||
|
||||
keytype = (*privkey)->keyType;
|
||||
- if (keytype != rsaKey && keytype != ecKey) {
|
||||
- slapi_log_err(SLAPI_LOG_ERR, "extractKeysAndSubject",
|
||||
- "Unexpected key algorythm in certificate: %s. Only rsa and ec keys are supported.\n", nickname);
|
||||
- goto bail;
|
||||
+ for (size_t i=0; ;i++) {
|
||||
+ KeyType kt = supported_key_types[i].kt;
|
||||
+ if (kt == keytype && keytype != 0) {
|
||||
+ /* Stop looping if the key type is supported */
|
||||
+ break;
|
||||
+ }
|
||||
+ if (kt == 0) {
|
||||
+ /* No supported key type have been found. */
|
||||
+ char sktnames[100] = "";
|
||||
+ get_supported_key_type_names(sktnames, sizeof sktnames);
|
||||
+ slapi_log_err(SLAPI_LOG_ERR, "extractKeysAndSubject",
|
||||
+ "Unexpected key algorithm in certificate: %s. Only %s are supported.\n", nickname, sktnames);
|
||||
+ goto bail;
|
||||
+ }
|
||||
}
|
||||
|
||||
*subject = CERT_AsciiToName(cert->subjectName);
|
||||
--
|
||||
2.52.0
|
||||
|
||||
@ -0,0 +1,56 @@
|
||||
From 4068f68bea77f466f9b3d87c766ea14d2f175b17 Mon Sep 17 00:00:00 2001
|
||||
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
|
||||
Date: Wed, 21 Jan 2026 19:58:46 -0800
|
||||
Subject: [PATCH] Bump lodash from 4.17.21 to 4.17.23 in
|
||||
/src/cockpit/389-console (#7203)
|
||||
|
||||
Bumps [lodash](https://github.com/lodash/lodash) from 4.17.21 to 4.17.23.
|
||||
- [Release notes](https://github.com/lodash/lodash/releases)
|
||||
- [Commits](https://github.com/lodash/lodash/compare/4.17.21...4.17.23)
|
||||
|
||||
---
|
||||
updated-dependencies:
|
||||
- dependency-name: lodash
|
||||
dependency-version: 4.17.23
|
||||
dependency-type: indirect
|
||||
...
|
||||
|
||||
Signed-off-by: dependabot[bot] <support@github.com>
|
||||
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
|
||||
---
|
||||
src/cockpit/389-console/package-lock.json | 12 ++++++------
|
||||
1 file changed, 6 insertions(+), 6 deletions(-)
|
||||
|
||||
diff --git a/src/cockpit/389-console/package-lock.json b/src/cockpit/389-console/package-lock.json
|
||||
index 0aa5bbbb9..23faef62f 100644
|
||||
--- a/src/cockpit/389-console/package-lock.json
|
||||
+++ b/src/cockpit/389-console/package-lock.json
|
||||
@@ -4833,9 +4833,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/lodash": {
|
||||
- "version": "4.17.21",
|
||||
- "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz",
|
||||
- "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg=="
|
||||
+ "version": "4.17.23",
|
||||
+ "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.23.tgz",
|
||||
+ "integrity": "sha512-LgVTMpQtIopCi79SJeDiP0TfWi5CNEc/L/aRdTh3yIvmZXTnheWpKjSZhnvMl8iXbC1tFg9gdHHDMLoV7CnG+w=="
|
||||
},
|
||||
"node_modules/lodash.merge": {
|
||||
"version": "4.6.2",
|
||||
@@ -11087,9 +11087,9 @@
|
||||
}
|
||||
},
|
||||
"lodash": {
|
||||
- "version": "4.17.21",
|
||||
- "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz",
|
||||
- "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg=="
|
||||
+ "version": "4.17.23",
|
||||
+ "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.23.tgz",
|
||||
+ "integrity": "sha512-LgVTMpQtIopCi79SJeDiP0TfWi5CNEc/L/aRdTh3yIvmZXTnheWpKjSZhnvMl8iXbC1tFg9gdHHDMLoV7CnG+w=="
|
||||
},
|
||||
"lodash.merge": {
|
||||
"version": "4.6.2",
|
||||
--
|
||||
2.52.0
|
||||
|
||||
517
0014-Issue-7198-Web-console-doesn-t-show-sub-suffix-when-.patch
Normal file
517
0014-Issue-7198-Web-console-doesn-t-show-sub-suffix-when-.patch
Normal file
@ -0,0 +1,517 @@
|
||||
From 3ff253af76df07fe0519481795b7ed155fefaa9e Mon Sep 17 00:00:00 2001
|
||||
From: Simon Pichugin <spichugi@redhat.com>
|
||||
Date: Fri, 23 Jan 2026 17:35:45 -0800
|
||||
Subject: [PATCH] Issue 7198 - Web console doesn't show sub-suffix when
|
||||
parent-suffix points to an entry (#7202)
|
||||
|
||||
Description: The web console doesn't show sub-suffixes when the
|
||||
nsslapd-parent-suffix attribute points to an entry rather than a backend
|
||||
suffix.
|
||||
For example, creating a sub-suffix ou=foo,ou=people,dc=example,dc=com
|
||||
with parent-suffix ou=people,dc=example,dc=com (where ou=people is just an
|
||||
entry, not a suffix) would not appear in the web console tree.
|
||||
|
||||
Fix: In backend_build_tree() and get_sub_suffixes(), the code only matched
|
||||
when nsslapd-parent-suffix exactly equaled an existing backend suffix.
|
||||
Now it also checks if the parent-suffix is an entry under the current
|
||||
suffix (ends with ,suffix) and is not itself a backend suffix. This
|
||||
correctly attaches sub-suffixes to their containing suffix when the
|
||||
parent-suffix points to an intermediate entry.
|
||||
|
||||
Fixes: https://github.com/389ds/389-ds-base/issues/7198
|
||||
|
||||
Reviewed by: @progier389 (Thanks!)
|
||||
---
|
||||
.../suites/lib389/subsuffix_tree_test.py | 313 ++++++++++++++++++
|
||||
src/lib389/lib389/backend.py | 47 ++-
|
||||
src/lib389/lib389/cli_conf/backend.py | 34 +-
|
||||
3 files changed, 370 insertions(+), 24 deletions(-)
|
||||
create mode 100644 dirsrvtests/tests/suites/lib389/subsuffix_tree_test.py
|
||||
|
||||
diff --git a/dirsrvtests/tests/suites/lib389/subsuffix_tree_test.py b/dirsrvtests/tests/suites/lib389/subsuffix_tree_test.py
|
||||
new file mode 100644
|
||||
index 000000000..fa10ba530
|
||||
--- /dev/null
|
||||
+++ b/dirsrvtests/tests/suites/lib389/subsuffix_tree_test.py
|
||||
@@ -0,0 +1,313 @@
|
||||
+# --- BEGIN COPYRIGHT BLOCK ---
|
||||
+# Copyright (C) 2026 Red Hat, Inc.
|
||||
+# All rights reserved.
|
||||
+#
|
||||
+# License: GPL (version 3 or any later version).
|
||||
+# See LICENSE for details.
|
||||
+# --- END COPYRIGHT BLOCK ---
|
||||
+#
|
||||
+import logging
|
||||
+import os
|
||||
+import pytest
|
||||
+from lib389.topologies import topology_st as topo
|
||||
+from lib389.backend import Backends
|
||||
+from lib389.idm.organizationalunit import OrganizationalUnits
|
||||
+from lib389._constants import DEFAULT_SUFFIX
|
||||
+
|
||||
+pytestmark = pytest.mark.tier1
|
||||
+
|
||||
+logging.getLogger(__name__).setLevel(logging.INFO)
|
||||
+log = logging.getLogger(__name__)
|
||||
+
|
||||
+
|
||||
+@pytest.fixture(scope="function")
|
||||
+def setup_subsuffix_with_entry_parent(topo, request):
|
||||
+ """Setup a sub-suffix whose parent-suffix points to an entry, not a suffix."""
|
||||
+ inst = topo.standalone
|
||||
+
|
||||
+ # Create ou=people entry under the root suffix
|
||||
+ log.info("Creating ou=people,dc=example,dc=com entry")
|
||||
+ ous = OrganizationalUnits(inst, DEFAULT_SUFFIX)
|
||||
+ ou_people = ous.get('people')
|
||||
+
|
||||
+ # Create sub-suffix with parent-suffix pointing to the entry
|
||||
+ log.info("Creating sub-suffix ou=foo,ou=people,dc=example,dc=com")
|
||||
+ backends = Backends(inst)
|
||||
+ subsuffix_dn = 'ou=foo,ou=people,dc=example,dc=com'
|
||||
+ parent_suffix_dn = 'ou=people,dc=example,dc=com'
|
||||
+
|
||||
+ foo_backend = backends.create(properties={
|
||||
+ 'cn': 'foo',
|
||||
+ 'nsslapd-suffix': subsuffix_dn,
|
||||
+ 'parent': parent_suffix_dn,
|
||||
+ })
|
||||
+
|
||||
+ # Create the suffix entry
|
||||
+ foo_ous = OrganizationalUnits(inst, parent_suffix_dn)
|
||||
+ foo_ou = foo_ous.create(properties={'ou': 'foo'})
|
||||
+
|
||||
+ def cleanup():
|
||||
+ log.info("Cleaning up test backends and entries")
|
||||
+ try:
|
||||
+ foo_ou.delete()
|
||||
+ except Exception as e:
|
||||
+ log.warning(f"Failed to delete foo_ou: {e}")
|
||||
+ try:
|
||||
+ foo_backend.delete()
|
||||
+ except Exception as e:
|
||||
+ log.warning(f"Failed to delete foo_backend: {e}")
|
||||
+
|
||||
+ request.addfinalizer(cleanup)
|
||||
+
|
||||
+ return {
|
||||
+ 'instance': inst,
|
||||
+ 'backends': backends,
|
||||
+ 'foo_backend': foo_backend,
|
||||
+ 'ou_people': ou_people,
|
||||
+ 'subsuffix_dn': subsuffix_dn,
|
||||
+ 'parent_suffix_dn': parent_suffix_dn,
|
||||
+ }
|
||||
+
|
||||
+
|
||||
+def test_subsuffix_with_entry_parent_in_tree(topo, setup_subsuffix_with_entry_parent):
|
||||
+ """Test that a sub-suffix with parent pointing to an entry is visible in the tree.
|
||||
+
|
||||
+ :id: 256f36f5-76ad-4043-ad8d-1f9e2afc4e1d
|
||||
+ :setup: Standalone instance with sub-suffix whose parent is an entry
|
||||
+ :steps:
|
||||
+ 1. Verify the sub-suffix backend exists
|
||||
+ 2. Get sub-suffixes of the root backend
|
||||
+ 3. Verify the sub-suffix appears in the list
|
||||
+ :expectedresults:
|
||||
+ 1. Backend should exist
|
||||
+ 2. Sub-suffixes should be retrievable
|
||||
+ 3. Sub-suffix should be visible (this is where the bug manifested)
|
||||
+ """
|
||||
+ backends = setup_subsuffix_with_entry_parent['backends']
|
||||
+ foo_backend = setup_subsuffix_with_entry_parent['foo_backend']
|
||||
+ subsuffix_dn = setup_subsuffix_with_entry_parent['subsuffix_dn']
|
||||
+
|
||||
+ # Step 1: Verify the sub-suffix backend exists
|
||||
+ assert foo_backend.exists(), "The foo backend should exist"
|
||||
+
|
||||
+ # Step 2: Get sub-suffixes of the root backend
|
||||
+ root_backend = backends.get(DEFAULT_SUFFIX)
|
||||
+ sub_suffixes = root_backend.get_sub_suffixes()
|
||||
+ log.info(f"Sub-suffixes found: {[s.get_attr_val_utf8('nsslapd-suffix') for s in sub_suffixes]}")
|
||||
+
|
||||
+ # Step 3: Verify sub-suffix is in the list
|
||||
+ sub_suffix_found = any(
|
||||
+ s.get_attr_val_utf8_l('nsslapd-suffix') == subsuffix_dn.lower()
|
||||
+ for s in sub_suffixes
|
||||
+ )
|
||||
+
|
||||
+ assert sub_suffix_found, (
|
||||
+ f"Sub-suffix {subsuffix_dn} should be visible in get_sub_suffixes(). "
|
||||
+ "The parent-suffix points to an entry, not a backend suffix."
|
||||
+ )
|
||||
+
|
||||
+
|
||||
+def test_subsuffix_in_backend_list(topo, setup_subsuffix_with_entry_parent):
|
||||
+ """Test that the sub-suffix appears in the backend list.
|
||||
+
|
||||
+ :id: 0ccc49af-91bb-4e8f-b0e1-1bd0b75c041b
|
||||
+ :setup: Standalone instance with sub-suffix configuration
|
||||
+ :steps:
|
||||
+ 1. Get all backends
|
||||
+ 2. Verify both root suffix and sub-suffix are present
|
||||
+ :expectedresults:
|
||||
+ 1. Should retrieve all backends
|
||||
+ 2. Both suffixes should be listed
|
||||
+ """
|
||||
+ backends = setup_subsuffix_with_entry_parent['backends']
|
||||
+ subsuffix_dn = setup_subsuffix_with_entry_parent['subsuffix_dn']
|
||||
+
|
||||
+ be_list = backends.list()
|
||||
+ suffixes = [be.get_attr_val_utf8_l('nsslapd-suffix') for be in be_list]
|
||||
+
|
||||
+ assert DEFAULT_SUFFIX.lower() in suffixes, \
|
||||
+ f"Root suffix {DEFAULT_SUFFIX} should be in the list"
|
||||
+ assert subsuffix_dn.lower() in suffixes, \
|
||||
+ f"Sub-suffix {subsuffix_dn} should be in the list"
|
||||
+
|
||||
+
|
||||
+def test_subsuffix_dn_boundary_matching():
|
||||
+ """Test that suffix matching respects DN component boundaries.
|
||||
+
|
||||
+ :id: 0b856e26-c394-4c36-b9ba-d7894aa2ed11
|
||||
+ :setup: None (unit test)
|
||||
+ :steps:
|
||||
+ 1. Test exact suffix match
|
||||
+ 2. Test proper DN ancestor match (ends with ,suffix)
|
||||
+ 3. Test that partial string matches are rejected
|
||||
+ :expectedresults:
|
||||
+ 1. Exact match should return True
|
||||
+ 2. Proper ancestor should return True
|
||||
+ 3. Partial string match should return False
|
||||
+ """
|
||||
+ from lib389.backend import is_subsuffix_of
|
||||
+
|
||||
+ all_suffixes = {'dc=com', 'dc=example,dc=com', 'ou=dept,dc=example,dc=com'}
|
||||
+
|
||||
+ # Test 1: Exact match
|
||||
+ assert is_subsuffix_of('dc=example,dc=com', 'dc=example,dc=com', all_suffixes), \
|
||||
+ "Exact match should return True"
|
||||
+
|
||||
+ # Test 2: Parent is an entry under the suffix (not itself a suffix)
|
||||
+ assert is_subsuffix_of('ou=people,dc=example,dc=com', 'dc=example,dc=com', all_suffixes), \
|
||||
+ "Parent entry under suffix should return True"
|
||||
+
|
||||
+ # Test 3: Parent IS a suffix - should return False (handled separately)
|
||||
+ assert not is_subsuffix_of('ou=dept,dc=example,dc=com', 'dc=example,dc=com', all_suffixes), \
|
||||
+ "Parent that is itself a suffix should return False"
|
||||
+
|
||||
+ # Test 4: Edge case - wrong DN boundary (string ends with suffix but wrong boundary)
|
||||
+ edge_suffixes = {'dc=com', 'st,dc=com'}
|
||||
+ assert is_subsuffix_of('dc=test,dc=com', 'dc=com', edge_suffixes), \
|
||||
+ "dc=test,dc=com should match dc=com"
|
||||
+ assert not is_subsuffix_of('dc=test,dc=com', 'st,dc=com', edge_suffixes), \
|
||||
+ "dc=test,dc=com should NOT match st,dc=com (wrong DN boundary)"
|
||||
+
|
||||
+ # Test 5: None input
|
||||
+ assert not is_subsuffix_of(None, 'dc=com', all_suffixes), \
|
||||
+ "None parent should return False"
|
||||
+
|
||||
+ # Test 6: Closest ancestor - should only match the nearest suffix
|
||||
+ # Hierarchy: dc=com -> dc=example,dc=com -> ou=branch,dc=example,dc=com (suffix)
|
||||
+ # -> ou=dept,ou=branch,dc=example,dc=com (entry) -> subsuffix
|
||||
+ # The subsuffix should only appear under ou=branch, not under dc=example,dc=com
|
||||
+ nested_suffixes = {'dc=com', 'dc=example,dc=com', 'ou=branch,dc=example,dc=com'}
|
||||
+ entry_parent = 'ou=dept,ou=branch,dc=example,dc=com'
|
||||
+ # Should match ou=branch (closest)
|
||||
+ assert is_subsuffix_of(entry_parent, 'ou=branch,dc=example,dc=com', nested_suffixes), \
|
||||
+ "Should match closest ancestor suffix (ou=branch)"
|
||||
+ # Should NOT match dc=example,dc=com (not closest)
|
||||
+ assert not is_subsuffix_of(entry_parent, 'dc=example,dc=com', nested_suffixes), \
|
||||
+ "Should NOT match distant ancestor (dc=example) - ou=branch is closer"
|
||||
+ # Should NOT match dc=com (not closest)
|
||||
+ assert not is_subsuffix_of(entry_parent, 'dc=com', nested_suffixes), \
|
||||
+ "Should NOT match distant ancestor (dc=com) - ou=branch is closer"
|
||||
+
|
||||
+ log.info("All DN boundary edge cases passed")
|
||||
+
|
||||
+
|
||||
+def test_deep_suffix_hierarchy(topo, request):
|
||||
+ """Test complex hierarchy: suffix -> suffix -> entry -> suffix -> suffix.
|
||||
+
|
||||
+ :id: fd06491a-defa-4780-8472-78c077febdfb
|
||||
+ :setup: Standalone instance
|
||||
+ :steps:
|
||||
+ 1. Create sub-suffix ou=branch (parent=dc=example,dc=com - a suffix)
|
||||
+ 2. Create entry ou=dept,ou=branch (not a suffix)
|
||||
+ 3. Create sub-suffix ou=team,ou=dept,ou=branch (parent=ou=dept - an entry)
|
||||
+ 4. Create sub-suffix ou=sub,ou=team,ou=dept,ou=branch (parent=ou=team - a suffix)
|
||||
+ 5. Verify all sub-suffixes are correctly placed in the tree
|
||||
+ :expectedresults:
|
||||
+ 1. Sub-suffix created successfully
|
||||
+ 2. Entry created successfully
|
||||
+ 3. Sub-suffix with entry parent created successfully
|
||||
+ 4. Sub-suffix with suffix parent created successfully
|
||||
+ 5. Tree hierarchy is correct
|
||||
+ """
|
||||
+ inst = topo.standalone
|
||||
+ backends = Backends(inst)
|
||||
+
|
||||
+ # Define the hierarchy
|
||||
+ branch_suffix = f'ou=branch,{DEFAULT_SUFFIX}'
|
||||
+ dept_entry = f'ou=dept,{branch_suffix}' # This is an ENTRY, not a suffix
|
||||
+ team_suffix = f'ou=team,{dept_entry}'
|
||||
+ sub_suffix = f'ou=sub,{team_suffix}'
|
||||
+
|
||||
+ created_backends = []
|
||||
+ created_entries = []
|
||||
+
|
||||
+ def cleanup():
|
||||
+ log.info("Cleaning up deep hierarchy test")
|
||||
+ for entry in reversed(created_entries):
|
||||
+ try:
|
||||
+ entry.delete()
|
||||
+ except Exception as e:
|
||||
+ log.warning(f"Failed to delete entry: {e}")
|
||||
+ for be in reversed(created_backends):
|
||||
+ try:
|
||||
+ be.delete()
|
||||
+ except Exception as e:
|
||||
+ log.warning(f"Failed to delete backend: {e}")
|
||||
+
|
||||
+ request.addfinalizer(cleanup)
|
||||
+
|
||||
+ # Step 1: Create ou=branch sub-suffix (parent is root suffix)
|
||||
+ log.info(f"Creating sub-suffix {branch_suffix}")
|
||||
+ branch_be = backends.create(properties={
|
||||
+ 'cn': 'branch',
|
||||
+ 'nsslapd-suffix': branch_suffix,
|
||||
+ 'parent': DEFAULT_SUFFIX,
|
||||
+ })
|
||||
+ created_backends.append(branch_be)
|
||||
+ branch_ous = OrganizationalUnits(inst, DEFAULT_SUFFIX)
|
||||
+ branch_ou = branch_ous.create(properties={'ou': 'branch'})
|
||||
+ created_entries.append(branch_ou)
|
||||
+
|
||||
+ # Step 2: Create ou=dept entry under branch (NOT a suffix)
|
||||
+ log.info(f"Creating entry {dept_entry}")
|
||||
+ dept_ous = OrganizationalUnits(inst, branch_suffix)
|
||||
+ dept_ou = dept_ous.create(properties={'ou': 'dept'})
|
||||
+ created_entries.append(dept_ou)
|
||||
+
|
||||
+ # Step 3: Create ou=team sub-suffix (parent is dept ENTRY, not a suffix)
|
||||
+ log.info(f"Creating sub-suffix {team_suffix} with entry parent {dept_entry}")
|
||||
+ team_be = backends.create(properties={
|
||||
+ 'cn': 'team',
|
||||
+ 'nsslapd-suffix': team_suffix,
|
||||
+ 'parent': dept_entry, # Parent is an ENTRY!
|
||||
+ })
|
||||
+ created_backends.append(team_be)
|
||||
+ team_ous = OrganizationalUnits(inst, dept_entry)
|
||||
+ team_ou = team_ous.create(properties={'ou': 'team'})
|
||||
+ created_entries.append(team_ou)
|
||||
+
|
||||
+ # Step 4: Create ou=sub sub-suffix (parent is team suffix)
|
||||
+ log.info(f"Creating sub-suffix {sub_suffix} with suffix parent {team_suffix}")
|
||||
+ sub_be = backends.create(properties={
|
||||
+ 'cn': 'sub',
|
||||
+ 'nsslapd-suffix': sub_suffix,
|
||||
+ 'parent': team_suffix, # Parent is a SUFFIX
|
||||
+ })
|
||||
+ created_backends.append(sub_be)
|
||||
+ sub_ous = OrganizationalUnits(inst, team_suffix)
|
||||
+ sub_ou = sub_ous.create(properties={'ou': 'sub'})
|
||||
+ created_entries.append(sub_ou)
|
||||
+
|
||||
+ # Step 5: Verify the tree hierarchy
|
||||
+ log.info("Verifying tree hierarchy...")
|
||||
+
|
||||
+ # Root should have branch as sub-suffix
|
||||
+ root_be = backends.get(DEFAULT_SUFFIX)
|
||||
+ root_subs = root_be.get_sub_suffixes()
|
||||
+ root_sub_suffixes = [s.get_attr_val_utf8_l('nsslapd-suffix') for s in root_subs]
|
||||
+ log.info(f"Root sub-suffixes: {root_sub_suffixes}")
|
||||
+ assert branch_suffix.lower() in root_sub_suffixes, \
|
||||
+ f"branch should be under root suffix"
|
||||
+
|
||||
+ # Branch should have team as sub-suffix (even though team's parent is an entry)
|
||||
+ branch_be_obj = backends.get(branch_suffix)
|
||||
+ branch_subs = branch_be_obj.get_sub_suffixes()
|
||||
+ branch_sub_suffixes = [s.get_attr_val_utf8_l('nsslapd-suffix') for s in branch_subs]
|
||||
+ log.info(f"Branch sub-suffixes: {branch_sub_suffixes}")
|
||||
+ assert team_suffix.lower() in branch_sub_suffixes, \
|
||||
+ f"team should be under branch suffix (parent is entry under branch)"
|
||||
+
|
||||
+ # Team should have sub as sub-suffix
|
||||
+ team_be_obj = backends.get(team_suffix)
|
||||
+ team_subs = team_be_obj.get_sub_suffixes()
|
||||
+ team_sub_suffixes = [s.get_attr_val_utf8_l('nsslapd-suffix') for s in team_subs]
|
||||
+ log.info(f"Team sub-suffixes: {team_sub_suffixes}")
|
||||
+ assert sub_suffix.lower() in team_sub_suffixes, \
|
||||
+ f"sub should be under team suffix"
|
||||
+
|
||||
+ log.info("Deep hierarchy test passed!")
|
||||
+
|
||||
+
|
||||
+if __name__ == '__main__':
|
||||
+ CURRENT_FILE = os.path.realpath(__file__)
|
||||
+ pytest.main(["-s", CURRENT_FILE])
|
||||
diff --git a/src/lib389/lib389/backend.py b/src/lib389/lib389/backend.py
|
||||
index db464b43a..274d45abe 100644
|
||||
--- a/src/lib389/lib389/backend.py
|
||||
+++ b/src/lib389/lib389/backend.py
|
||||
@@ -38,6 +38,36 @@ from lib389.lint import DSBLE0001, DSBLE0002, DSBLE0003, DSBLE0004, DSBLE0005, D
|
||||
from lib389.plugins import USNPlugin
|
||||
|
||||
|
||||
+def is_subsuffix_of(sub_parent, be_suffix, all_suffixes):
|
||||
+ """Check if sub_parent indicates this is a sub-suffix of be_suffix.
|
||||
+
|
||||
+ Returns True only if be_suffix is the CLOSEST ancestor suffix of sub_parent.
|
||||
+ This prevents a sub-suffix from appearing under multiple ancestors.
|
||||
+
|
||||
+ :param sub_parent: The nsslapd-parent-suffix value (lowercase)
|
||||
+ :param be_suffix: The suffix to check against (lowercase)
|
||||
+ :param all_suffixes: Set of all backend suffixes (lowercase)
|
||||
+ :returns: True if be_suffix is the closest ancestor suffix
|
||||
+ """
|
||||
+ if not sub_parent:
|
||||
+ return False
|
||||
+ if sub_parent == be_suffix:
|
||||
+ return True
|
||||
+ if sub_parent in all_suffixes:
|
||||
+ # sub_parent is itself a suffix, will be handled separately
|
||||
+ return False
|
||||
+ if not sub_parent.endswith(',' + be_suffix):
|
||||
+ return False
|
||||
+ # Find the closest (longest) matching suffix for this parent
|
||||
+ best_match = None
|
||||
+ for sfx in all_suffixes:
|
||||
+ if sub_parent == sfx or sub_parent.endswith(',' + sfx):
|
||||
+ if best_match is None or len(sfx) > len(best_match):
|
||||
+ best_match = sfx
|
||||
+ # Only return True if be_suffix is the closest match
|
||||
+ return best_match == be_suffix
|
||||
+
|
||||
+
|
||||
class BackendLegacy(object):
|
||||
proxied_methods = 'search_s getEntry'.split()
|
||||
|
||||
@@ -1104,22 +1134,27 @@ class Backend(DSLdapObject):
|
||||
vlv.create(rdn="cn=" + vlvname, properties=props, basedn=basedn)
|
||||
|
||||
def get_sub_suffixes(self):
|
||||
- """Return a list of Backend's
|
||||
- returns: a List of subsuffix entries
|
||||
+ """Return a list of Backend's that are sub-suffixes of this backend.
|
||||
+ :returns: A list of Backend instances that are sub-suffixes
|
||||
"""
|
||||
subsuffixes = []
|
||||
top_be_suffix = self.get_attr_val_utf8_l('nsslapd-suffix')
|
||||
+ if not top_be_suffix:
|
||||
+ return subsuffixes
|
||||
+
|
||||
mts = self._mts.list()
|
||||
+ be_insts = Backends(self._instance).list()
|
||||
+ all_suffixes = {be.get_attr_val_utf8_l('nsslapd-suffix') for be in be_insts}
|
||||
+
|
||||
for mt in mts:
|
||||
parent_suffix = mt.get_attr_val_utf8_l('nsslapd-parent-suffix')
|
||||
if parent_suffix is None:
|
||||
continue
|
||||
- if parent_suffix == top_be_suffix:
|
||||
+
|
||||
+ if is_subsuffix_of(parent_suffix, top_be_suffix, all_suffixes):
|
||||
child_suffix = mt.get_attr_val_utf8_l('cn')
|
||||
- be_insts = Backends(self._instance).list()
|
||||
for be in be_insts:
|
||||
- be_suffix = be.get_attr_val_utf8_l('nsslapd-suffix')
|
||||
- if child_suffix == be_suffix:
|
||||
+ if child_suffix == be.get_attr_val_utf8_l('nsslapd-suffix'):
|
||||
subsuffixes.append(be)
|
||||
break
|
||||
return subsuffixes
|
||||
diff --git a/src/lib389/lib389/cli_conf/backend.py b/src/lib389/lib389/cli_conf/backend.py
|
||||
index d0ec4bd9e..9772e39d4 100644
|
||||
--- a/src/lib389/lib389/cli_conf/backend.py
|
||||
+++ b/src/lib389/lib389/cli_conf/backend.py
|
||||
@@ -7,7 +7,7 @@
|
||||
# See LICENSE for details.
|
||||
# --- END COPYRIGHT BLOCK ---
|
||||
|
||||
-from lib389.backend import Backend, Backends, DatabaseConfig, BackendSuffixView
|
||||
+from lib389.backend import Backend, Backends, DatabaseConfig, BackendSuffixView, is_subsuffix_of
|
||||
from lib389.configurations.sample import (
|
||||
create_base_domain,
|
||||
create_base_org,
|
||||
@@ -338,6 +338,7 @@ def is_db_replicated(inst, suffix):
|
||||
def backend_get_subsuffixes(inst, basedn, log, args):
|
||||
subsuffixes = []
|
||||
be_insts = MANY(inst).list()
|
||||
+ all_suffixes = {be.get_attr_val_utf8_l('nsslapd-suffix') for be in be_insts}
|
||||
for be in be_insts:
|
||||
be_suffix = be.get_attr_val_utf8_l('nsslapd-suffix')
|
||||
if be_suffix == args.be_name.lower():
|
||||
@@ -347,7 +348,7 @@ def backend_get_subsuffixes(inst, basedn, log, args):
|
||||
db_type = "suffix"
|
||||
sub = mt.get_attr_val_utf8_l('nsslapd-parent-suffix')
|
||||
sub_be = mt.get_attr_val_utf8_l('nsslapd-backend')
|
||||
- if sub == be_suffix:
|
||||
+ if is_subsuffix_of(sub, be_suffix, all_suffixes):
|
||||
# We have a subsuffix (maybe a db link?)
|
||||
if is_db_link(inst, sub_be):
|
||||
db_type = "link"
|
||||
@@ -399,38 +400,34 @@ def build_node(suffix, be_name, subsuf=False, link=False, replicated=False):
|
||||
}
|
||||
|
||||
|
||||
-def backend_build_tree(inst, be_insts, nodes):
|
||||
- """Recursively build the tree
|
||||
- """
|
||||
- if len(nodes) == 0:
|
||||
- # Done
|
||||
+def backend_build_tree(inst, be_insts, nodes, all_suffixes):
|
||||
+ """Recursively build the tree."""
|
||||
+ if not nodes:
|
||||
return
|
||||
|
||||
for node in nodes:
|
||||
- node_suffix = node['id']
|
||||
+ node_suffix = node['id'].lower()
|
||||
# Get sub suffixes and chaining of node
|
||||
for be in be_insts:
|
||||
be_suffix = be.get_attr_val_utf8_l('nsslapd-suffix')
|
||||
- if be_suffix == node_suffix.lower():
|
||||
+ if be_suffix == node_suffix:
|
||||
# We have our parent, now find the children
|
||||
mts = be._mts.list()
|
||||
-
|
||||
for mt in mts:
|
||||
sub_parent = mt.get_attr_val_utf8_l('nsslapd-parent-suffix')
|
||||
sub_be = mt.get_attr_val_utf8_l('nsslapd-backend')
|
||||
sub_suffix = mt.get_attr_val_utf8_l('cn')
|
||||
- if sub_parent == be_suffix:
|
||||
+ if is_subsuffix_of(sub_parent, be_suffix, all_suffixes):
|
||||
# We have a subsuffix (maybe a db link?)
|
||||
link = is_db_link(inst, sub_be)
|
||||
replicated = is_db_replicated(inst, sub_suffix)
|
||||
node['children'].append(build_node(sub_suffix,
|
||||
- sub_be,
|
||||
- subsuf=True,
|
||||
- link=link,
|
||||
- replicated=replicated))
|
||||
-
|
||||
+ sub_be,
|
||||
+ subsuf=True,
|
||||
+ link=link,
|
||||
+ replicated=replicated))
|
||||
# Recurse over the new subsuffixes
|
||||
- backend_build_tree(inst, be_insts, node['children'])
|
||||
+ backend_build_tree(inst, be_insts, node['children'], all_suffixes)
|
||||
break
|
||||
|
||||
|
||||
@@ -471,7 +468,8 @@ def backend_get_tree(inst, basedn, log, args):
|
||||
else:
|
||||
# Build the tree
|
||||
be_insts = Backends(inst).list()
|
||||
- backend_build_tree(inst, be_insts, nodes)
|
||||
+ all_suffixes = {be.get_attr_val_utf8_l('nsslapd-suffix') for be in be_insts}
|
||||
+ backend_build_tree(inst, be_insts, nodes, all_suffixes)
|
||||
|
||||
# Done
|
||||
if args.json:
|
||||
--
|
||||
2.52.0
|
||||
|
||||
@ -0,0 +1,38 @@
|
||||
From aa24c00d9ed1ea9730c0e8c5dccc1bbb5b61e312 Mon Sep 17 00:00:00 2001
|
||||
From: Lenka Doudova <lryznaro@redhat.com>
|
||||
Date: Mon, 26 Jan 2026 16:21:23 +0100
|
||||
Subject: [PATCH] Issue 7014 - memberOf - ignored deferred updates with LMDB
|
||||
|
||||
Description:
|
||||
Fix typo in pytest marker reason.
|
||||
|
||||
Relates: #7014
|
||||
|
||||
Author: Lenka Doudova
|
||||
Reviewed by: ???
|
||||
---
|
||||
.../suites/memberof_plugin/memberof_deferred_lmdb_test.py | 3 ++-
|
||||
1 file changed, 2 insertions(+), 1 deletion(-)
|
||||
|
||||
diff --git a/dirsrvtests/tests/suites/memberof_plugin/memberof_deferred_lmdb_test.py b/dirsrvtests/tests/suites/memberof_plugin/memberof_deferred_lmdb_test.py
|
||||
index 0d9f793c1..12fcfa3ec 100644
|
||||
--- a/dirsrvtests/tests/suites/memberof_plugin/memberof_deferred_lmdb_test.py
|
||||
+++ b/dirsrvtests/tests/suites/memberof_plugin/memberof_deferred_lmdb_test.py
|
||||
@@ -1,4 +1,5 @@
|
||||
# --- BEGIN COPYRIGHT BLOCK ---
|
||||
+
|
||||
# Copyright (C) 2025 Red Hat, Inc.
|
||||
# All rights reserved.
|
||||
#
|
||||
@@ -28,7 +29,7 @@ else:
|
||||
logging.getLogger(__name__).setLevel(logging.INFO)
|
||||
|
||||
|
||||
-@pytest.mark.skipif(get_default_db_lib() != "mdb", reason="Not supported over mdb")
|
||||
+@pytest.mark.skipif(get_default_db_lib() != "mdb", reason="Not supported over bdb")
|
||||
def test_memberof_deferred_update_lmdb_rejection(topo):
|
||||
"""Test that memberOf plugin rejects deferred update configuration with LMDB backend
|
||||
|
||||
--
|
||||
2.52.0
|
||||
|
||||
@ -0,0 +1,48 @@
|
||||
From 4a73a31e6c91e507e5aa2cba1e5bd55d1d07894d Mon Sep 17 00:00:00 2001
|
||||
From: Mark Reynolds <mreynolds@redhat.com>
|
||||
Date: Mon, 12 Jan 2026 13:53:05 -0500
|
||||
Subject: [PATCH] Issue 7184 - argparse.HelpFormatter _format_actions_usage()
|
||||
is deprecated
|
||||
|
||||
Description:
|
||||
|
||||
_format_actions_usage() was removed in python 3.15. Instead we can use
|
||||
_get_actions_usage_parts() but it also behaves differently between
|
||||
python 3.14 and 3.15 so we need special handling.
|
||||
|
||||
Relates: https://github.com/389ds/389-ds-base/issues/7184
|
||||
|
||||
Reviewed by: spichugi(Thanks!)
|
||||
---
|
||||
src/lib389/lib389/cli_base/__init__.py | 15 ++++++++++++++-
|
||||
1 file changed, 14 insertions(+), 1 deletion(-)
|
||||
|
||||
diff --git a/src/lib389/lib389/cli_base/__init__.py b/src/lib389/lib389/cli_base/__init__.py
|
||||
index 06b8f9964..f1055aadc 100644
|
||||
--- a/src/lib389/lib389/cli_base/__init__.py
|
||||
+++ b/src/lib389/lib389/cli_base/__init__.py
|
||||
@@ -413,7 +413,20 @@ class CustomHelpFormatter(argparse.HelpFormatter):
|
||||
|
||||
def _format_usage(self, usage, actions, groups, prefix):
|
||||
usage = super(CustomHelpFormatter, self)._format_usage(usage, actions, groups, prefix)
|
||||
- formatted_options = self._format_actions_usage(parent_arguments, [])
|
||||
+
|
||||
+ if sys.version_info < (3, 13):
|
||||
+ # Use _format_actions_usage() for Python 3.12 and earlier
|
||||
+ formatted_options = self._format_actions_usage(parent_arguments, [])
|
||||
+ else:
|
||||
+ # Use _get_actions_usage_parts() for Python 3.13 and later
|
||||
+ action_parts = self._get_actions_usage_parts(parent_arguments, [])
|
||||
+ if sys.version_info >= (3, 15):
|
||||
+ # Python 3.15 returns a tuple (list of actions, count of actions)
|
||||
+ formatted_options = ' '.join(action_parts[0])
|
||||
+ else:
|
||||
+ # Python 3.13 and 3.14 return a list of actions
|
||||
+ formatted_options = ' '.join(action_parts)
|
||||
+
|
||||
# If formatted_options already in usage - remove them
|
||||
if formatted_options in usage:
|
||||
usage = usage.replace(f' {formatted_options}', '')
|
||||
--
|
||||
2.52.0
|
||||
|
||||
174
0017-Issue-6947-Revise-time-skew-check-in-healthcheck-too.patch
Normal file
174
0017-Issue-6947-Revise-time-skew-check-in-healthcheck-too.patch
Normal file
@ -0,0 +1,174 @@
|
||||
From bc28406778534a77064a26b4f0467dffecde33ea Mon Sep 17 00:00:00 2001
|
||||
From: Viktor Ashirov <vashirov@redhat.com>
|
||||
Date: Tue, 27 Jan 2026 09:40:12 +0100
|
||||
Subject: [PATCH] Issue 6947 - Revise time skew check in healthcheck tool - add
|
||||
tests (#7208)
|
||||
|
||||
Description:
|
||||
Add tests for DSSKEWLE0003 and DSSKEWLE0004 checks.
|
||||
|
||||
Relates: https://github.com/389ds/389-ds-base/issues/6947
|
||||
|
||||
Reviewed by: @droideck (Thanks!)
|
||||
---
|
||||
.../suites/healthcheck/health_skew_test.py | 148 ++++++++++++++++++
|
||||
1 file changed, 148 insertions(+)
|
||||
create mode 100644 dirsrvtests/tests/suites/healthcheck/health_skew_test.py
|
||||
|
||||
diff --git a/dirsrvtests/tests/suites/healthcheck/health_skew_test.py b/dirsrvtests/tests/suites/healthcheck/health_skew_test.py
|
||||
new file mode 100644
|
||||
index 000000000..9ee070364
|
||||
--- /dev/null
|
||||
+++ b/dirsrvtests/tests/suites/healthcheck/health_skew_test.py
|
||||
@@ -0,0 +1,148 @@
|
||||
+# --- BEGIN COPYRIGHT BLOCK ---
|
||||
+# Copyright (C) 2026 Red Hat, Inc.
|
||||
+# All rights reserved.
|
||||
+#
|
||||
+# License: GPL (version 3 or any later version).
|
||||
+# See LICENSE for details.
|
||||
+# --- END COPYRIGHT BLOCK ---
|
||||
+#
|
||||
+
|
||||
+import logging
|
||||
+import os
|
||||
+import pytest
|
||||
+import subprocess
|
||||
+from lib389._constants import DEFAULT_SUFFIX
|
||||
+from lib389.dseldif import DSEldif
|
||||
+from lib389.replica import ReplicationManager
|
||||
+from lib389.topologies import topology_m2
|
||||
+
|
||||
+pytestmark = pytest.mark.tier1
|
||||
+
|
||||
+CMD_OUTPUT = 'No issues found.'
|
||||
+JSON_OUTPUT = '[]'
|
||||
+
|
||||
+log = logging.getLogger(__name__)
|
||||
+
|
||||
+
|
||||
+def run_healthcheck_and_check_result(instance, searched_code, json=False, isnot=False):
|
||||
+ """Run healthcheck and verify the expected code is in the output"""
|
||||
+ cmd = ['dsctl']
|
||||
+ if json:
|
||||
+ cmd.append('--json')
|
||||
+ if searched_code == CMD_OUTPUT:
|
||||
+ searched_code = JSON_OUTPUT
|
||||
+ cmd.append(instance.serverid)
|
||||
+ cmd.extend(['healthcheck', '--check', 'dseldif:nsstate'])
|
||||
+
|
||||
+ result = subprocess.run(cmd, capture_output=True, universal_newlines=True)
|
||||
+ log.info(f'Running: {cmd}')
|
||||
+ log.info(f'Stdout: {result.stdout}')
|
||||
+ log.info(f'Stderr: {result.stderr}')
|
||||
+ log.info(f'Return code: {result.returncode}')
|
||||
+ stdout = result.stdout
|
||||
+
|
||||
+ # stdout should not be empty
|
||||
+ assert stdout is not None
|
||||
+ assert len(stdout) > 0
|
||||
+
|
||||
+ if isnot:
|
||||
+ assert searched_code not in stdout, \
|
||||
+ f'{searched_code} should NOT be in healthcheck output but was found'
|
||||
+ log.info(f'Verified {searched_code} is NOT in healthcheck output')
|
||||
+ else:
|
||||
+ assert searched_code in stdout, \
|
||||
+ f'{searched_code} should be in healthcheck output but was not found'
|
||||
+ log.info(f'Verified {searched_code} is in healthcheck output')
|
||||
+
|
||||
+
|
||||
+def test_healthcheck_time_skew_extensive(topology_m2):
|
||||
+ """Check if HealthCheck returns DSSKEWLE0003 and DSSKEWLE0004 codes for extensive time skew
|
||||
+
|
||||
+ :id: 7591cd2b-8d66-4d33-9f8d-babff0571086
|
||||
+ :setup: Two suppliers replication topology
|
||||
+ :steps:
|
||||
+ 1. Create a replicated topology
|
||||
+ 2. Stop supplier1
|
||||
+ 3. Increase time skew on supplier1 to over 24 hours
|
||||
+ 4. Start supplier1
|
||||
+ 5. Use HealthCheck and verify DSSKEWLE0003 is reported
|
||||
+ 6. Set nsslapd-ignore-time-skew to on
|
||||
+ 7. Use HealthCheck and verify DSSKEWLE0003 is NOT reported
|
||||
+ 8. Stop supplier1
|
||||
+ 9. Increase time skew on supplier1 to over 365 days
|
||||
+ 10. Start supplier1
|
||||
+ 11. Use HealthCheck and verify only DSSKEWLE0004 is reported
|
||||
+ :expectedresults:
|
||||
+ 1. Success
|
||||
+ 2. Success
|
||||
+ 3. Success
|
||||
+ 4. Success
|
||||
+ 5. Healthcheck reports DSSKEWLE0003 code
|
||||
+ 6. Success
|
||||
+ 7. Healthcheck does not report DSSKEWLE0003 code
|
||||
+ 8. Success
|
||||
+ 9. Success
|
||||
+ 10. Success
|
||||
+ 11. Healthcheck reports DSSKEWLE0004 code and not DSSKEWLE0003
|
||||
+ """
|
||||
+
|
||||
+ M1 = topology_m2.ms['supplier1']
|
||||
+ M2 = topology_m2.ms['supplier2']
|
||||
+
|
||||
+ # Ensure replication is working first
|
||||
+ repl = ReplicationManager(DEFAULT_SUFFIX)
|
||||
+ repl.wait_for_replication(M1, M2)
|
||||
+
|
||||
+ # Step 2-4: Stop supplier1, increase time skew to over 24 hours, start supplier1
|
||||
+ log.info('Stop supplier1 to modify dse.ldif')
|
||||
+ M1.stop()
|
||||
+
|
||||
+ # Set time skew to over 24 hours (86400 seconds)
|
||||
+ # Add a margin to be safely over the threshold
|
||||
+ time_skew_24h = 86400 + 3600 # 25 hours
|
||||
+ log.info(f'Increase time skew on supplier1 by {time_skew_24h} seconds')
|
||||
+ DSEldif(M1)._increaseTimeSkew(DEFAULT_SUFFIX, time_skew_24h)
|
||||
+
|
||||
+ log.info('Start supplier1')
|
||||
+ M1.start()
|
||||
+
|
||||
+ # Step 5: Verify DSSKEWLE0003 is reported
|
||||
+ log.info('Run healthcheck and verify DSSKEWLE0003 is reported')
|
||||
+ run_healthcheck_and_check_result(M1, 'DSSKEWLE0003', json=False)
|
||||
+ run_healthcheck_and_check_result(M1, 'DSSKEWLE0003', json=True)
|
||||
+
|
||||
+ # Step 6: Set nsslapd-ignore-time-skew to on
|
||||
+ log.info('Set nsslapd-ignore-time-skew to on')
|
||||
+ M1.config.set('nsslapd-ignore-time-skew', 'on')
|
||||
+
|
||||
+ # Step 7: Verify DSSKEWLE0003 is NOT reported when ignoring time skew
|
||||
+ log.info('Run healthcheck and verify DSSKEWLE0003 is NOT reported')
|
||||
+ run_healthcheck_and_check_result(M1, 'DSSKEWLE0003', json=False, isnot=True)
|
||||
+ run_healthcheck_and_check_result(M1, 'DSSKEWLE0003', json=True, isnot=True)
|
||||
+
|
||||
+ # Step 8-10: Stop supplier1, increase time skew to over 365 days, start supplier1
|
||||
+ log.info('Stop supplier1 to modify dse.ldif')
|
||||
+ M1.stop()
|
||||
+
|
||||
+ # Increase time skew to over 365 days (31536000 seconds)
|
||||
+ # We need to add enough to go from current ~25 hours to over 365 days
|
||||
+ time_skew_year = (86400 * 365) + 86400 # 366 days total additional
|
||||
+ log.info(f'Increase time skew on supplier1 by {time_skew_year} seconds')
|
||||
+ DSEldif(M1)._increaseTimeSkew(DEFAULT_SUFFIX, time_skew_year)
|
||||
+
|
||||
+ log.info('Start supplier1')
|
||||
+ M1.start()
|
||||
+
|
||||
+ # Step 11: Verify only DSSKEWLE0004 is reported (not DSSKEWLE0003)
|
||||
+ log.info('Run healthcheck and verify only DSSKEWLE0004 is reported')
|
||||
+ run_healthcheck_and_check_result(M1, 'DSSKEWLE0004', json=False)
|
||||
+ run_healthcheck_and_check_result(M1, 'DSSKEWLE0004', json=True)
|
||||
+ run_healthcheck_and_check_result(M1, 'DSSKEWLE0003', json=False, isnot=True)
|
||||
+ run_healthcheck_and_check_result(M1, 'DSSKEWLE0003', json=True, isnot=True)
|
||||
+
|
||||
+
|
||||
+if __name__ == '__main__':
|
||||
+ # Run isolated
|
||||
+ # -s for DEBUG mode
|
||||
+ CURRENT_FILE = os.path.realpath(__file__)
|
||||
+ pytest.main("-s %s" % CURRENT_FILE)
|
||||
--
|
||||
2.52.0
|
||||
|
||||
215
0018-Issue-7201-Syscall-overhead-in-LMDB-import-writer-th.patch
Normal file
215
0018-Issue-7201-Syscall-overhead-in-LMDB-import-writer-th.patch
Normal file
@ -0,0 +1,215 @@
|
||||
From a53c0e4dea5c35fef196500b2a36c92bc8f07a51 Mon Sep 17 00:00:00 2001
|
||||
From: Viktor Ashirov <vashirov@redhat.com>
|
||||
Date: Tue, 27 Jan 2026 09:49:16 +0100
|
||||
Subject: [PATCH] Issue 7201 - Syscall overhead in LMDB import writer thread
|
||||
(#7204)
|
||||
|
||||
Bug Description:
|
||||
ldif2db import is slower with LMDB than with BDB (~3500 vs ~4500
|
||||
entries/s) due to 2 issues:
|
||||
1. The MDB_STAT_STEP macro calls `clock_gettime()` to collect
|
||||
performance statistics. This was called on every single operation inside
|
||||
the writer loop, resulting in ~3 syscalls per write or ~6000 syscalls
|
||||
per transaction (with 2000 as the default batch size).
|
||||
|
||||
2. In `dbmdb_import_workerq_push()`, after copying work to a worker
|
||||
slot, the condition variable was never signaled. This caused workers to
|
||||
spend up to 100ms in `safe_cond_wait()` before checking for new work,
|
||||
severely limiting import throughput.
|
||||
|
||||
Fix Description:
|
||||
1. Add a new config parameter `nsslapd-mdb-import-stats` (default: off)
|
||||
to control whether performance statistics collection is enabled.
|
||||
2. Add `pthread_cond_broadcast()` immediately after copying data to wake
|
||||
the workers.
|
||||
|
||||
After applying these fixes ldif2db import rate is about ~10000 entries/s.
|
||||
|
||||
Fixes: https://github.com/389ds/389-ds-base/issues/7201
|
||||
|
||||
Reviewed by: @progier389 (Thanks!)
|
||||
---
|
||||
.../slapd/back-ldbm/db-mdb/mdb_config.c | 23 +++++++++++
|
||||
.../back-ldbm/db-mdb/mdb_import_threads.c | 40 ++++++++++---------
|
||||
.../slapd/back-ldbm/db-mdb/mdb_layer.h | 2 +
|
||||
3 files changed, 46 insertions(+), 19 deletions(-)
|
||||
|
||||
diff --git a/ldap/servers/slapd/back-ldbm/db-mdb/mdb_config.c b/ldap/servers/slapd/back-ldbm/db-mdb/mdb_config.c
|
||||
index 96295ed5b..ade42b1d7 100644
|
||||
--- a/ldap/servers/slapd/back-ldbm/db-mdb/mdb_config.c
|
||||
+++ b/ldap/servers/slapd/back-ldbm/db-mdb/mdb_config.c
|
||||
@@ -477,6 +477,28 @@ dbmdb_ctx_t_db_durable_transactions_set(void *arg, void *value, char *errorbuf _
|
||||
return retval;
|
||||
}
|
||||
|
||||
+static void *
|
||||
+dbmdb_ctx_t_db_import_stats_get(void *arg)
|
||||
+{
|
||||
+ struct ldbminfo *li = (struct ldbminfo *)arg;
|
||||
+
|
||||
+ return (void *)((uintptr_t)(MDB_CONFIG(li)->dsecfg.import_stats));
|
||||
+}
|
||||
+
|
||||
+static int
|
||||
+dbmdb_ctx_t_db_import_stats_set(void *arg, void *value, char *errorbuf __attribute__((unused)), int phase __attribute__((unused)), int apply)
|
||||
+{
|
||||
+ struct ldbminfo *li = (struct ldbminfo *)arg;
|
||||
+ int retval = LDAP_SUCCESS;
|
||||
+ int val = (int)((uintptr_t)value);
|
||||
+
|
||||
+ if (apply) {
|
||||
+ MDB_CONFIG(li)->dsecfg.import_stats = val;
|
||||
+ }
|
||||
+
|
||||
+ return retval;
|
||||
+}
|
||||
+
|
||||
static int
|
||||
dbmdb_ctx_t_set_bypass_filter_test(void *arg,
|
||||
void *value,
|
||||
@@ -589,6 +611,7 @@ static config_info dbmdb_ctx_t_param[] = {
|
||||
{CONFIG_MDB_MAX_DBS, CONFIG_TYPE_INT, "512", &dbmdb_ctx_t_db_max_dbs_get, &dbmdb_ctx_t_db_max_dbs_set, CONFIG_FLAG_ALWAYS_SHOW | CONFIG_FLAG_ALLOW_RUNNING_CHANGE},
|
||||
{CONFIG_MAXPASSBEFOREMERGE, CONFIG_TYPE_INT, "100", &dbmdb_ctx_t_maxpassbeforemerge_get, &dbmdb_ctx_t_maxpassbeforemerge_set, 0},
|
||||
{CONFIG_DB_DURABLE_TRANSACTIONS, CONFIG_TYPE_ONOFF, "on", &dbmdb_ctx_t_db_durable_transactions_get, &dbmdb_ctx_t_db_durable_transactions_set, CONFIG_FLAG_ALWAYS_SHOW},
|
||||
+ {CONFIG_MDB_IMPORT_STATS, CONFIG_TYPE_ONOFF, "off", &dbmdb_ctx_t_db_import_stats_get, &dbmdb_ctx_t_db_import_stats_set, CONFIG_FLAG_ALWAYS_SHOW | CONFIG_FLAG_ALLOW_RUNNING_CHANGE},
|
||||
{CONFIG_BYPASS_FILTER_TEST, CONFIG_TYPE_STRING, "on", &dbmdb_ctx_t_get_bypass_filter_test, &dbmdb_ctx_t_set_bypass_filter_test, CONFIG_FLAG_ALWAYS_SHOW | CONFIG_FLAG_ALLOW_RUNNING_CHANGE},
|
||||
{CONFIG_SERIAL_LOCK, CONFIG_TYPE_ONOFF, "on", &dbmdb_ctx_t_serial_lock_get, &dbmdb_ctx_t_serial_lock_set, CONFIG_FLAG_ALWAYS_SHOW | CONFIG_FLAG_ALLOW_RUNNING_CHANGE},
|
||||
{CONFIG_CACHE_AUTOSIZE, CONFIG_TYPE_INT, "25", &mdb_config_cache_autosize_get, &mdb_config_cache_autosize_set, CONFIG_FLAG_ALWAYS_SHOW | CONFIG_FLAG_ALLOW_RUNNING_CHANGE},
|
||||
diff --git a/ldap/servers/slapd/back-ldbm/db-mdb/mdb_import_threads.c b/ldap/servers/slapd/back-ldbm/db-mdb/mdb_import_threads.c
|
||||
index 2270abd69..65b29343e 100644
|
||||
--- a/ldap/servers/slapd/back-ldbm/db-mdb/mdb_import_threads.c
|
||||
+++ b/ldap/servers/slapd/back-ldbm/db-mdb/mdb_import_threads.c
|
||||
@@ -59,9 +59,9 @@
|
||||
|
||||
|
||||
/* import thread usage statistics */
|
||||
-#define MDB_STAT_INIT(stats) { mdb_stat_collect(&stats, MDB_STAT_RUN, 1); }
|
||||
-#define MDB_STAT_END(stats) { mdb_stat_collect(&stats, MDB_STAT_RUN, 0); }
|
||||
-#define MDB_STAT_STEP(stats, step) { mdb_stat_collect(&stats, (step), 0); }
|
||||
+#define MDB_STAT_INIT(stats, enabled) { if (enabled) mdb_stat_collect(&stats, MDB_STAT_RUN, 1); }
|
||||
+#define MDB_STAT_END(stats, enabled) { if (enabled) mdb_stat_collect(&stats, MDB_STAT_RUN, 0); }
|
||||
+#define MDB_STAT_STEP(stats, step, enabled) { if (enabled) mdb_stat_collect(&stats, (step), 0); }
|
||||
|
||||
typedef enum {
|
||||
MDB_STAT_RUN,
|
||||
@@ -424,6 +424,7 @@ dbmdb_import_workerq_push(ImportQueue_t *q, WorkerQueueData_t *data)
|
||||
return -1;
|
||||
}
|
||||
dbmdb_dup_worker_slot(q, data, slot);
|
||||
+ pthread_cond_broadcast(&q->cv);
|
||||
pthread_mutex_unlock(&q->mutex);
|
||||
return 0;
|
||||
}
|
||||
@@ -3914,14 +3915,15 @@ dbmdb_import_writer(void*param)
|
||||
int count = 0;
|
||||
int rc = 0;
|
||||
mdb_stat_info_t stats = {0};
|
||||
+ int stats_enabled = ctx->ctx->dsecfg.import_stats;
|
||||
|
||||
- MDB_STAT_INIT(stats);
|
||||
+ MDB_STAT_INIT(stats, stats_enabled);
|
||||
while (!rc && !info_is_finished(info)) {
|
||||
- MDB_STAT_STEP(stats, MDB_STAT_PAUSE);
|
||||
+ MDB_STAT_STEP(stats, MDB_STAT_PAUSE, stats_enabled);
|
||||
wait_for_starting(info);
|
||||
- MDB_STAT_STEP(stats, MDB_STAT_READ);
|
||||
+ MDB_STAT_STEP(stats, MDB_STAT_READ, stats_enabled);
|
||||
slot = dbmdb_import_q_getall(&ctx->writerq);
|
||||
- MDB_STAT_STEP(stats, MDB_STAT_RUN);
|
||||
+ MDB_STAT_STEP(stats, MDB_STAT_RUN, stats_enabled);
|
||||
if (info_is_finished(info)) {
|
||||
dbmdb_import_q_flush(&ctx->writerq);
|
||||
break;
|
||||
@@ -3932,14 +3934,14 @@ dbmdb_import_writer(void*param)
|
||||
|
||||
for (; slot; slot = nextslot) {
|
||||
if (!txn) {
|
||||
- MDB_STAT_STEP(stats, MDB_STAT_TXNSTART);
|
||||
+ MDB_STAT_STEP(stats, MDB_STAT_TXNSTART, stats_enabled);
|
||||
rc = TXN_BEGIN(ctx->ctx->env, NULL, 0, &txn);
|
||||
}
|
||||
if (!rc) {
|
||||
- MDB_STAT_STEP(stats, MDB_STAT_WRITE);
|
||||
+ MDB_STAT_STEP(stats, MDB_STAT_WRITE, stats_enabled);
|
||||
rc = MDB_PUT(txn, slot->dbi->dbi, &slot->key, &slot->data, 0);
|
||||
}
|
||||
- MDB_STAT_STEP(stats, MDB_STAT_RUN);
|
||||
+ MDB_STAT_STEP(stats, MDB_STAT_RUN, stats_enabled);
|
||||
nextslot = slot->next;
|
||||
slapi_ch_free((void**)&slot);
|
||||
}
|
||||
@@ -3947,9 +3949,9 @@ dbmdb_import_writer(void*param)
|
||||
break;
|
||||
}
|
||||
if (count++ >= WRITER_MAX_OPS_IN_TXN) {
|
||||
- MDB_STAT_STEP(stats, MDB_STAT_TXNSTOP);
|
||||
+ MDB_STAT_STEP(stats, MDB_STAT_TXNSTOP, stats_enabled);
|
||||
rc = TXN_COMMIT(txn);
|
||||
- MDB_STAT_STEP(stats, MDB_STAT_RUN);
|
||||
+ MDB_STAT_STEP(stats, MDB_STAT_RUN, stats_enabled);
|
||||
if (rc) {
|
||||
break;
|
||||
}
|
||||
@@ -3958,32 +3960,32 @@ dbmdb_import_writer(void*param)
|
||||
}
|
||||
}
|
||||
if (txn && !rc) {
|
||||
- MDB_STAT_STEP(stats, MDB_STAT_TXNSTOP);
|
||||
+ MDB_STAT_STEP(stats, MDB_STAT_TXNSTOP, stats_enabled);
|
||||
rc = TXN_COMMIT(txn);
|
||||
- MDB_STAT_STEP(stats, MDB_STAT_RUN);
|
||||
+ MDB_STAT_STEP(stats, MDB_STAT_RUN, stats_enabled);
|
||||
if (!rc) {
|
||||
txn = NULL;
|
||||
}
|
||||
}
|
||||
if (txn) {
|
||||
- MDB_STAT_STEP(stats, MDB_STAT_TXNSTOP);
|
||||
+ MDB_STAT_STEP(stats, MDB_STAT_TXNSTOP, stats_enabled);
|
||||
TXN_ABORT(txn);
|
||||
- MDB_STAT_STEP(stats, MDB_STAT_RUN);
|
||||
+ MDB_STAT_STEP(stats, MDB_STAT_RUN, stats_enabled);
|
||||
txn = NULL;
|
||||
}
|
||||
- MDB_STAT_STEP(stats, MDB_STAT_WRITE);
|
||||
+ MDB_STAT_STEP(stats, MDB_STAT_WRITE, stats_enabled);
|
||||
if (!rc) {
|
||||
/* Ensure that all data are written on disk */
|
||||
rc = mdb_env_sync(ctx->ctx->env, 1);
|
||||
}
|
||||
- MDB_STAT_END(stats);
|
||||
+ MDB_STAT_END(stats, stats_enabled);
|
||||
|
||||
if (rc) {
|
||||
slapi_log_err(SLAPI_LOG_ERR, "dbmdb_import_writer",
|
||||
"Failed to write in the database. Error is 0x%x: %s.\n",
|
||||
rc, mdb_strerror(rc));
|
||||
thread_abort(info);
|
||||
- } else {
|
||||
+ } else if (stats_enabled) {
|
||||
char buf[200];
|
||||
char *summary = mdb_stat_summarize(&stats, buf, sizeof buf);
|
||||
if (summary) {
|
||||
diff --git a/ldap/servers/slapd/back-ldbm/db-mdb/mdb_layer.h b/ldap/servers/slapd/back-ldbm/db-mdb/mdb_layer.h
|
||||
index 647d00db9..cf6d00dd4 100644
|
||||
--- a/ldap/servers/slapd/back-ldbm/db-mdb/mdb_layer.h
|
||||
+++ b/ldap/servers/slapd/back-ldbm/db-mdb/mdb_layer.h
|
||||
@@ -41,6 +41,7 @@
|
||||
#define CONFIG_MDB_MAX_SIZE "nsslapd-mdb-max-size"
|
||||
#define CONFIG_MDB_MAX_READERS "nsslapd-mdb-max-readers"
|
||||
#define CONFIG_MDB_MAX_DBS "nsslapd-mdb-max-dbs"
|
||||
+#define CONFIG_MDB_IMPORT_STATS "nsslapd-mdb-import-stats"
|
||||
|
||||
#define DBMDB_DB_MINSIZE ( 4LL * MEGABYTE )
|
||||
#define DBMDB_DISK_RESERVE(disksize) ((disksize)*2ULL/1000ULL)
|
||||
@@ -76,6 +77,7 @@ typedef struct
|
||||
int max_readers;
|
||||
int max_dbs;
|
||||
uint64_t max_size;
|
||||
+ int import_stats;
|
||||
} dbmdb_cfg_t;
|
||||
|
||||
/* config parameters limits */
|
||||
--
|
||||
2.52.0
|
||||
|
||||
135
0019-Issue-7096-2nd-During-replication-online-total-init-.patch
Normal file
135
0019-Issue-7096-2nd-During-replication-online-total-init-.patch
Normal file
@ -0,0 +1,135 @@
|
||||
From 40484cb0b5034bc3c1e23b2ae1f2d39eedce07e9 Mon Sep 17 00:00:00 2001
|
||||
From: Viktor Ashirov <vashirov@redhat.com>
|
||||
Date: Tue, 27 Jan 2026 14:26:29 +0100
|
||||
Subject: [PATCH] Issue 7096 - (2nd) During replication online total init the
|
||||
function idl_id_is_in_idlist is not scaling with large database (#7205)
|
||||
|
||||
Bug Description:
|
||||
The fix for #7096 optimized the BDB backend's `idl_new_range_fetch()`
|
||||
function to use ID ranges instead of checking the full ID list during
|
||||
online total initialization. However, the LMDB backend's
|
||||
`idl_lmdb_range_fetch()` function and its callback
|
||||
`idl_range_add_id_cb()` were not updated and still use the non-scaling
|
||||
`idl_id_is_in_idlist()` function.
|
||||
|
||||
Fix Description:
|
||||
Apply the same optimization to the LMDB backend.
|
||||
|
||||
Fixes: https://github.com/389ds/389-ds-base/issues/7096
|
||||
|
||||
Reviewed by: @tbordaz, @droideck (Thanks!)
|
||||
---
|
||||
ldap/servers/slapd/back-ldbm/idl_new.c | 39 ++++++++++----------------
|
||||
1 file changed, 15 insertions(+), 24 deletions(-)
|
||||
|
||||
diff --git a/ldap/servers/slapd/back-ldbm/idl_new.c b/ldap/servers/slapd/back-ldbm/idl_new.c
|
||||
index 2d978353f..613d53815 100644
|
||||
--- a/ldap/servers/slapd/back-ldbm/idl_new.c
|
||||
+++ b/ldap/servers/slapd/back-ldbm/idl_new.c
|
||||
@@ -66,6 +66,7 @@ typedef struct {
|
||||
size_t leftoverlen;
|
||||
size_t leftovercnt;
|
||||
IDList *idl;
|
||||
+ IdRange_t *idrange_list;
|
||||
int flag_err;
|
||||
ID lastid;
|
||||
ID suffix;
|
||||
@@ -700,9 +701,9 @@ error:
|
||||
}
|
||||
}
|
||||
}
|
||||
- slapi_ch_free((void **)&leftover);
|
||||
- idrange_free(&idrange_list);
|
||||
}
|
||||
+ slapi_ch_free((void **)&leftover);
|
||||
+ idrange_free(&idrange_list);
|
||||
slapi_log_err(SLAPI_LOG_FILTER, "idl_new_range_fetch",
|
||||
"Found %d candidates; error code is: %d\n",
|
||||
idl ? idl->b_nids : 0, *flag_err);
|
||||
@@ -716,7 +717,6 @@ static int
|
||||
idl_range_add_id_cb(dbi_val_t *key, dbi_val_t *data, void *ctx)
|
||||
{
|
||||
idl_range_ctx_t *rctx = ctx;
|
||||
- int idl_rc = 0;
|
||||
ID id = 0;
|
||||
|
||||
if (key->data == NULL) {
|
||||
@@ -779,10 +779,12 @@ idl_range_add_id_cb(dbi_val_t *key, dbi_val_t *data, void *ctx)
|
||||
* found entry is the one from the suffix
|
||||
*/
|
||||
rctx->suffix = keyval;
|
||||
- idl_rc = idl_append_extend(&rctx->idl, id);
|
||||
- } else if ((keyval == rctx->suffix) || idl_id_is_in_idlist(rctx->idl, keyval)) {
|
||||
+ idl_append_extend(&rctx->idl, id);
|
||||
+ idrange_add_id(&rctx->idrange_list, id);
|
||||
+ } else if ((keyval == rctx->suffix) || idl_id_is_in_idlist_ranges(rctx->idl, rctx->idrange_list, keyval)) {
|
||||
/* the parent is the suffix or already in idl. */
|
||||
- idl_rc = idl_append_extend(&rctx->idl, id);
|
||||
+ idl_append_extend(&rctx->idl, id);
|
||||
+ idrange_add_id(&rctx->idrange_list, id);
|
||||
} else {
|
||||
/* Otherwise, keep the {keyval,id} in leftover array */
|
||||
if (!rctx->leftover) {
|
||||
@@ -797,14 +799,7 @@ idl_range_add_id_cb(dbi_val_t *key, dbi_val_t *data, void *ctx)
|
||||
rctx->leftovercnt++;
|
||||
}
|
||||
} else {
|
||||
- idl_rc = idl_append_extend(&rctx->idl, id);
|
||||
- }
|
||||
- if (idl_rc) {
|
||||
- slapi_log_err(SLAPI_LOG_ERR, "idl_lmdb_range_fetch",
|
||||
- "Unable to extend id list (err=%d)\n", idl_rc);
|
||||
- idl_free(&rctx->idl);
|
||||
- rctx->flag_err = LDAP_UNWILLING_TO_PERFORM;
|
||||
- return DBI_RC_NOTFOUND;
|
||||
+ idl_append_extend(&rctx->idl, id);
|
||||
}
|
||||
#if defined(DB_ALLIDS_ON_READ)
|
||||
/* enforce the allids read limit */
|
||||
@@ -841,7 +836,6 @@ idl_lmdb_range_fetch(
|
||||
{
|
||||
int ret = 0;
|
||||
int ret2 = 0;
|
||||
- int idl_rc = 0;
|
||||
dbi_cursor_t cursor = {0};
|
||||
back_txn s_txn;
|
||||
struct ldbminfo *li = (struct ldbminfo *)be->be_database->plg_private;
|
||||
@@ -891,6 +885,7 @@ idl_lmdb_range_fetch(
|
||||
idl_range_ctx.lastid = 0;
|
||||
idl_range_ctx.count = 0;
|
||||
idl_range_ctx.index_id = index_id;
|
||||
+ idl_range_ctx.idrange_list = NULL;
|
||||
if (operator & SLAPI_OP_RANGE_NO_IDL_SORT) {
|
||||
struct _back_info_index_key bck_info;
|
||||
/* We are doing a bulk import
|
||||
@@ -966,22 +961,18 @@ error:
|
||||
while(remaining > 0) {
|
||||
for (size_t i = 0; i < idl_range_ctx.leftovercnt; i++) {
|
||||
if (idl_range_ctx.leftover[i].key > 0 &&
|
||||
- idl_id_is_in_idlist(idl_range_ctx.idl, idl_range_ctx.leftover[i].key) != 0) {
|
||||
+ idl_id_is_in_idlist_ranges(idl_range_ctx.idl, idl_range_ctx.idrange_list, idl_range_ctx.leftover[i].key) != 0) {
|
||||
/* if the leftover key has its parent in the idl */
|
||||
- idl_rc = idl_append_extend(&idl_range_ctx.idl, idl_range_ctx.leftover[i].id);
|
||||
- if (idl_rc) {
|
||||
- slapi_log_err(SLAPI_LOG_ERR, "idl_lmdb_range_fetch",
|
||||
- "Unable to extend id list (err=%d)\n", idl_rc);
|
||||
- idl_free(&idl_range_ctx.idl);
|
||||
- break;
|
||||
- }
|
||||
+ idl_append_extend(&idl_range_ctx.idl, idl_range_ctx.leftover[i].id);
|
||||
+ idrange_add_id(&idl_range_ctx.idrange_list, idl_range_ctx.leftover[i].id);
|
||||
idl_range_ctx.leftover[i].key = 0;
|
||||
remaining--;
|
||||
}
|
||||
}
|
||||
}
|
||||
- slapi_ch_free((void **)&idl_range_ctx.leftover);
|
||||
}
|
||||
+ slapi_ch_free((void **)&idl_range_ctx.leftover);
|
||||
+ idrange_free(&idl_range_ctx.idrange_list);
|
||||
*flag_err = idl_range_ctx.flag_err;
|
||||
slapi_log_err(SLAPI_LOG_FILTER, "idl_lmdb_range_fetch",
|
||||
"Found %d candidates; error code is: %d\n",
|
||||
--
|
||||
2.52.0
|
||||
|
||||
149
0020-Issue-7206-Should-log-whether-TLS-key-is-PQC-or-not-.patch
Normal file
149
0020-Issue-7206-Should-log-whether-TLS-key-is-PQC-or-not-.patch
Normal file
@ -0,0 +1,149 @@
|
||||
From 8c6d1cfca22f87b20b79a419ceabdb182846ff4a Mon Sep 17 00:00:00 2001
|
||||
From: progier389 <progier@redhat.com>
|
||||
Date: Tue, 27 Jan 2026 15:39:39 +0100
|
||||
Subject: [PATCH] Issue 7206 - Should log whether TLS key is PQC or not (#7207)
|
||||
|
||||
Append [PQC] to the cipher name when logging SSL/TLS if the Key Exchange is one of the KEM group
|
||||
If connection debug level is enabled, logs in error log the keaType and keaGroup (as integer)
|
||||
|
||||
Issue: #7206
|
||||
|
||||
Reviewed by: @tbordaz, @droideck (Thanks!)
|
||||
|
||||
* Issue 7206 - Should log whether TLS key is PQC or not
|
||||
|
||||
* Fix Sourcery A/I comments
|
||||
|
||||
* Check PQC in test case
|
||||
|
||||
* Update ldap/servers/slapd/auth.c
|
||||
|
||||
Co-authored-by: Simon Pichugin <spichugi@redhat.com>
|
||||
|
||||
---------
|
||||
|
||||
Co-authored-by: Simon Pichugin <spichugi@redhat.com>
|
||||
---
|
||||
dirsrvtests/tests/suites/tls/mldsa_test.py | 7 ++++
|
||||
ldap/servers/slapd/auth.c | 45 +++++++++++++++++++++-
|
||||
ldap/servers/slapd/ssl.c | 2 +-
|
||||
3 files changed, 52 insertions(+), 2 deletions(-)
|
||||
|
||||
diff --git a/dirsrvtests/tests/suites/tls/mldsa_test.py b/dirsrvtests/tests/suites/tls/mldsa_test.py
|
||||
index 2c815088b..d6cbaf662 100644
|
||||
--- a/dirsrvtests/tests/suites/tls/mldsa_test.py
|
||||
+++ b/dirsrvtests/tests/suites/tls/mldsa_test.py
|
||||
@@ -274,6 +274,7 @@ def test_mldsa(topo):
|
||||
"""
|
||||
|
||||
inst = topo.standalone
|
||||
+ inst.config.set('nsslapd-errorlog-level', str(16384 + 8))
|
||||
inst.enable_tls()
|
||||
|
||||
cm = CertmapLegacy(inst)
|
||||
@@ -293,6 +294,9 @@ def test_mldsa(topo):
|
||||
'loginShell': '/bin/false',
|
||||
'description': cert_dn })
|
||||
|
||||
+ inst.config.set("nsslapd-accesslog-logbuffering", "off")
|
||||
+ inst.config.set("nsslapd-errorlog-logbuffering", "off")
|
||||
+
|
||||
tmpdir_kwargs = {}
|
||||
if sys.version_info >= (3, 12):
|
||||
tmpdir_kwargs['delete'] = not DEBUGGING
|
||||
@@ -313,6 +317,9 @@ def test_mldsa(topo):
|
||||
res.check_returncode()
|
||||
# If ldapsearch is successful then defaultnamingcontext should be in res.stdout
|
||||
assert "defaultnamingcontext" in res.stdout
|
||||
+ assert inst.ds_access_log.match('.*RESULT.*dn="uid=test_user,ou=people,dc=example,dc=com".*')
|
||||
+ assert inst.ds_access_log.match('.*TLS.*[PQC].*')
|
||||
+ assert inst.ds_error_log.match('.*check_pqc.*')
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
diff --git a/ldap/servers/slapd/auth.c b/ldap/servers/slapd/auth.c
|
||||
index 48e4b7129..9e83408d4 100644
|
||||
--- a/ldap/servers/slapd/auth.c
|
||||
+++ b/ldap/servers/slapd/auth.c
|
||||
@@ -395,6 +395,43 @@ handle_bad_certificate(void *clientData, PRFileDesc *prfd)
|
||||
}
|
||||
|
||||
|
||||
+/*
|
||||
+ * Determine if the connection key exchange is Post Quantum Cryptography aware.
|
||||
+ * This function may need to evolve with NSS as more PQC methods get supported.
|
||||
+ */
|
||||
+static bool
|
||||
+check_pqc(uint64_t connid, SSLChannelInfo *sci)
|
||||
+{
|
||||
+ /*
|
||||
+ * FYI: To interpret the values:
|
||||
+ * KeaType and KeaGroup values are defined in
|
||||
+ * https://github.com/nss-dev/nss/blob/master/lib/ssl/sslt.h
|
||||
+ * Respectively in SSLKEAType and SSLNamedGroup enums
|
||||
+ */
|
||||
+ slapi_log_err(SLAPI_LOG_CONNS, "check_pqc", "conn=%" PRIu64 " TLS keaType=%d keaGroup=%d\n",
|
||||
+ connid, sci->keaType, sci->keaGroup);
|
||||
+#ifdef MAX_ML_DSA_PRIVATE_KEY_LEN
|
||||
+ /* NSS supports PQC (because of the ifdef). Now lets check if the connection uses it */
|
||||
+ /* Check that PQC KeaType is hybrid */
|
||||
+ switch (sci->keaType) {
|
||||
+ case ssl_kea_ecdh_hybrid:
|
||||
+ case ssl_kea_ecdh_hybrid_psk:
|
||||
+ break;
|
||||
+ default:
|
||||
+ return false;
|
||||
+ }
|
||||
+ /* Check that PQC keaGroup is KEM */
|
||||
+ switch (sci->keaGroup) {
|
||||
+ case ssl_grp_kem_secp256r1mlkem768:
|
||||
+ case ssl_grp_kem_secp384r1mlkem1024:
|
||||
+ case ssl_grp_kem_mlkem768x25519:
|
||||
+ case ssl_grp_kem_xyber768d00:
|
||||
+ return true;
|
||||
+ }
|
||||
+#endif
|
||||
+ return false;
|
||||
+}
|
||||
+
|
||||
/*
|
||||
* Get an identity from the client's certificate (if any was sent).
|
||||
*
|
||||
@@ -417,6 +454,7 @@ handle_handshake_done(PRFileDesc *prfd, void *clientData)
|
||||
SSLCipherSuiteInfo cipherInfo;
|
||||
char *subject = NULL;
|
||||
char sslversion[64];
|
||||
+ bool pqc = false;
|
||||
int err = 0;
|
||||
|
||||
if ((slapd_ssl_getChannelInfo(prfd, &channelInfo, sizeof(channelInfo))) != SECSuccess) {
|
||||
@@ -454,7 +492,12 @@ handle_handshake_done(PRFileDesc *prfd, void *clientData)
|
||||
}
|
||||
|
||||
keySize = cipherInfo.effectiveKeyBits;
|
||||
- cipher = slapi_ch_strdup(cipherInfo.symCipherName);
|
||||
+ pqc = check_pqc(conn->c_connid, &channelInfo);
|
||||
+ if (pqc) {
|
||||
+ cipher = slapi_ch_smprintf("%s[PQC]", cipherInfo.symCipherName);
|
||||
+ } else {
|
||||
+ cipher = slapi_ch_strdup(cipherInfo.symCipherName);
|
||||
+ }
|
||||
|
||||
/* If inside an Start TLS operation, perform the privacy level discovery
|
||||
* and if the security degree achieved after the handshake is not reckoned
|
||||
diff --git a/ldap/servers/slapd/ssl.c b/ldap/servers/slapd/ssl.c
|
||||
index 053db5424..7d5db2cdd 100644
|
||||
--- a/ldap/servers/slapd/ssl.c
|
||||
+++ b/ldap/servers/slapd/ssl.c
|
||||
@@ -748,7 +748,7 @@ SSLPLCY_Install(void)
|
||||
if (!slapd_pk11_isFIPS()) {
|
||||
/* Set explicitly PQC algorithm policy if it is not set by default */
|
||||
for (size_t i=0; s == SECSuccess && i < PR_ARRAY_SIZE(oids); i++) {
|
||||
- int oflags = 0;
|
||||
+ PRUint32 oflags = 0;
|
||||
(void) NSS_GetAlgorithmPolicy(oids[i], &oflags);
|
||||
if ((oflags & flags) != flags) {
|
||||
s = NSS_SetAlgorithmPolicy(oids[i], flags, 0);
|
||||
--
|
||||
2.52.0
|
||||
|
||||
@ -0,0 +1,53 @@
|
||||
From 27d3ea211847fa7ae674c5e4dcf485706e4ac591 Mon Sep 17 00:00:00 2001
|
||||
From: Viktor Ashirov <vashirov@redhat.com>
|
||||
Date: Fri, 30 Jan 2026 12:00:13 +0100
|
||||
Subject: [PATCH] Issue 7027 - (2nd) 389-ds-base OpenScanHub Leaks Detected
|
||||
(#7211)
|
||||
|
||||
Fix Description:
|
||||
Update coverity annotations.
|
||||
|
||||
Relates: https://github.com/389ds/389-ds-base/issues/7027
|
||||
|
||||
Reviewed by: @aadhikar (Thanks!)
|
||||
---
|
||||
ldap/servers/slapd/log.c | 6 +++---
|
||||
1 file changed, 3 insertions(+), 3 deletions(-)
|
||||
|
||||
diff --git a/ldap/servers/slapd/log.c b/ldap/servers/slapd/log.c
|
||||
index ea744ac1e..80c07382a 100644
|
||||
--- a/ldap/servers/slapd/log.c
|
||||
+++ b/ldap/servers/slapd/log.c
|
||||
@@ -206,8 +206,8 @@ compress_log_file(char *log_name, int32_t mode)
|
||||
|
||||
if ((source = fopen(log_name, "r")) == NULL) {
|
||||
/* Failed to open log file */
|
||||
- /* coverity[leaked_storage] gzclose does close FD */
|
||||
gzclose(outfile);
|
||||
+ /* coverity[leaked_handle] gzclose does close FD */
|
||||
return -1;
|
||||
}
|
||||
|
||||
@@ -217,17 +217,17 @@ compress_log_file(char *log_name, int32_t mode)
|
||||
if (bytes_written == 0)
|
||||
{
|
||||
fclose(source);
|
||||
- /* coverity[leaked_storage] gzclose does close FD */
|
||||
gzclose(outfile);
|
||||
+ /* coverity[leaked_handle] gzclose does close FD */
|
||||
return -1;
|
||||
}
|
||||
bytes_read = fread(buf, 1, LOG_CHUNK, source);
|
||||
}
|
||||
- /* coverity[leaked_storage] gzclose does close FD */
|
||||
gzclose(outfile);
|
||||
fclose(source);
|
||||
PR_Delete(log_name); /* remove the old uncompressed log */
|
||||
|
||||
+ /* coverity[leaked_handle] gzclose does close FD */
|
||||
return 0;
|
||||
}
|
||||
|
||||
--
|
||||
2.52.0
|
||||
|
||||
195
0022-Issue-7213-MDB_BAD_VALSIZE-error-while-handling-VLV-.patch
Normal file
195
0022-Issue-7213-MDB_BAD_VALSIZE-error-while-handling-VLV-.patch
Normal file
@ -0,0 +1,195 @@
|
||||
From 5ebce22d4214bec5ed94ad84c4448164be99389a Mon Sep 17 00:00:00 2001
|
||||
From: progier389 <progier@redhat.com>
|
||||
Date: Mon, 2 Feb 2026 15:39:18 +0100
|
||||
Subject: [PATCH] Issue 7213 - MDB_BAD_VALSIZE error while handling VLV (#7214)
|
||||
|
||||
* Issue 7213 - MDB_BAD_VALSIZE error while handling VLV
|
||||
Avoid failing lmdb operation when handling VLV index by truncating the key so that key+data is small enough.
|
||||
|
||||
Issue: #7213
|
||||
|
||||
Reviewed by: @mreynolds389 , @vashirov (Thanks!)
|
||||
|
||||
Assisted by: Claude A/I
|
||||
---
|
||||
.../tests/suites/vlv/regression_test.py | 110 ++++++++++++++++++
|
||||
.../slapd/back-ldbm/db-mdb/mdb_layer.c | 5 +
|
||||
ldap/servers/slapd/back-ldbm/vlv.c | 7 +-
|
||||
3 files changed, 121 insertions(+), 1 deletion(-)
|
||||
|
||||
diff --git a/dirsrvtests/tests/suites/vlv/regression_test.py b/dirsrvtests/tests/suites/vlv/regression_test.py
|
||||
index f7847ac74..7cdf16a84 100644
|
||||
--- a/dirsrvtests/tests/suites/vlv/regression_test.py
|
||||
+++ b/dirsrvtests/tests/suites/vlv/regression_test.py
|
||||
@@ -1175,6 +1175,116 @@ def test_vlv_with_mr(vlv_setup_with_uid_mr):
|
||||
|
||||
|
||||
|
||||
+def test_vlv_long_attribute_value(topology_st, request):
|
||||
+ """
|
||||
+ Test VLV with an entry containing a very long attribute value (2K).
|
||||
+
|
||||
+ :id: 99126fa4-003e-11f1-b7d6-c85309d5c3e3
|
||||
+ :setup: Standalone instance.
|
||||
+ :steps:
|
||||
+ 1. Cleanup leftover from previous tests
|
||||
+ 2. Create VLV search and index on cn attribute
|
||||
+ 3. Reindex VLV
|
||||
+ 4. Add an entry with a cn attribute having 2K character value
|
||||
+ 5. Verify the entry was added successfully
|
||||
+ 6. Perform a VLV search to ensure it still works
|
||||
+ 7. Add another entry with a cn attribute having 2K character value
|
||||
+ 8. Verify the entry was added successfully
|
||||
+ 9. Perform a VLV search to ensure it still works
|
||||
+ :expectedresults:
|
||||
+ 1. Should Success.
|
||||
+ 2. Should Success.
|
||||
+ 3. Should Success.
|
||||
+ 4. Should Success.
|
||||
+ 5. Should Success.
|
||||
+ 6. Should Success.
|
||||
+ 7. Should Success.
|
||||
+ 8. Should Success.
|
||||
+ 9. Should Success.
|
||||
+ """
|
||||
+ inst = topology_st.standalone
|
||||
+ reindex_task = Tasks(inst)
|
||||
+
|
||||
+ users_to_delete = []
|
||||
+
|
||||
+ def fin():
|
||||
+ cleanup(inst)
|
||||
+ # Clean the added users
|
||||
+ for user in users_to_delete:
|
||||
+ user.delete()
|
||||
+
|
||||
+ if not DEBUGGING:
|
||||
+ request.addfinalizer(fin)
|
||||
+
|
||||
+ # Clean previous tests leftover
|
||||
+ fin()
|
||||
+
|
||||
+ # Create VLV search and index
|
||||
+ vlv_search, vlv_index = create_vlv_search_and_index(inst)
|
||||
+ assert reindex_task.reindex(
|
||||
+ suffix=DEFAULT_SUFFIX,
|
||||
+ attrname=vlv_index.rdn,
|
||||
+ args={TASK_WAIT: True},
|
||||
+ vlv=True
|
||||
+ ) == 0
|
||||
+
|
||||
+ # Add a few regular users first
|
||||
+ add_users(inst, 10)
|
||||
+
|
||||
+ # Create a very long cn value (2K characters)
|
||||
+ long_cn_value = 'a' * 2048 + '1'
|
||||
+
|
||||
+ # Add an entry with the long cn attribute
|
||||
+ users = UserAccounts(inst, DEFAULT_SUFFIX)
|
||||
+ user_properties = {
|
||||
+ 'uid': 'longcnuser1',
|
||||
+ 'cn': long_cn_value,
|
||||
+ 'sn': 'user1',
|
||||
+ 'uidNumber': '99999',
|
||||
+ 'gidNumber': '99999',
|
||||
+ 'homeDirectory': '/home/longcnuser1'
|
||||
+ }
|
||||
+ user = users.create(properties=user_properties)
|
||||
+ users_to_delete.append(user);
|
||||
+
|
||||
+ # Verify the entry was created and has the long cn value
|
||||
+ entry = user.get_attr_vals_utf8('cn')
|
||||
+ assert entry[0] == long_cn_value
|
||||
+ log.info(f'Successfully created user with cn length: {len(entry[0])}')
|
||||
+
|
||||
+ # Perform VLV search to ensure VLV still works with long attribute values
|
||||
+ conn = open_new_ldapi_conn(inst.serverid)
|
||||
+ count = len(conn.search_s(DEFAULT_SUFFIX, ldap.SCOPE_SUBTREE, "(uid=*)"))
|
||||
+ assert count > 0
|
||||
+ log.info(f'VLV search successful with {count} entries including entry with 2K cn value')
|
||||
+
|
||||
+ # Add another entry with the long cn attribute
|
||||
+ long_cn_value = 'a' * 2048 + '2'
|
||||
+
|
||||
+ user_properties = {
|
||||
+ 'uid': 'longcnuser2',
|
||||
+ 'cn': long_cn_value,
|
||||
+ 'sn': 'user2',
|
||||
+ 'uidNumber': '99998',
|
||||
+ 'gidNumber': '99998',
|
||||
+ 'homeDirectory': '/home/longcnuser2'
|
||||
+ }
|
||||
+ user = users.create(properties=user_properties)
|
||||
+ users_to_delete.append(user);
|
||||
+
|
||||
+ # Verify the entry was created and has the long cn value
|
||||
+ entry = user.get_attr_vals_utf8('cn')
|
||||
+ assert entry[0] == long_cn_value
|
||||
+ log.info(f'Successfully created user with cn length: {len(entry[0])}')
|
||||
+
|
||||
+ # Perform VLV search to ensure VLV still works with long attribute values
|
||||
+ conn = open_new_ldapi_conn(inst.serverid)
|
||||
+ count = len(conn.search_s(DEFAULT_SUFFIX, ldap.SCOPE_SUBTREE, "(uid=*)"))
|
||||
+ assert count > 1
|
||||
+ log.info(f'VLV search successful with {count} entries including entry with 2K cn value')
|
||||
+
|
||||
+
|
||||
+
|
||||
if __name__ == "__main__":
|
||||
# Run isolated
|
||||
# -s for DEBUG mode
|
||||
diff --git a/ldap/servers/slapd/back-ldbm/db-mdb/mdb_layer.c b/ldap/servers/slapd/back-ldbm/db-mdb/mdb_layer.c
|
||||
index d320ecbeb..cd797621d 100644
|
||||
--- a/ldap/servers/slapd/back-ldbm/db-mdb/mdb_layer.c
|
||||
+++ b/ldap/servers/slapd/back-ldbm/db-mdb/mdb_layer.c
|
||||
@@ -2134,10 +2134,15 @@ void *dbmdb_recno_cache_build(void *arg)
|
||||
recno = 1;
|
||||
}
|
||||
while (rc == 0) {
|
||||
+ struct ldbminfo *li = (struct ldbminfo *)rcctx->cursor->be->be_database->plg_private;
|
||||
slapi_log_err(SLAPI_LOG_DEBUG, "dbmdb_recno_cache_build", "recno=%d\n", recno);
|
||||
if (recno % RECNO_CACHE_INTERVAL == 1) {
|
||||
/* Prepare the cache data */
|
||||
len = sizeof(*rce) + data.mv_size + key.mv_size;
|
||||
+ if (len > li->li_max_key_len) {
|
||||
+ key.mv_size = li->li_max_key_len - data.mv_size - sizeof(*rce);
|
||||
+ len = li->li_max_key_len;
|
||||
+ }
|
||||
rce = (dbmdb_recno_cache_elmt_t*)slapi_ch_malloc(len);
|
||||
rce->len = len;
|
||||
rce->recno = recno;
|
||||
diff --git a/ldap/servers/slapd/back-ldbm/vlv.c b/ldap/servers/slapd/back-ldbm/vlv.c
|
||||
index 8f9263f25..f2f882b5a 100644
|
||||
--- a/ldap/servers/slapd/back-ldbm/vlv.c
|
||||
+++ b/ldap/servers/slapd/back-ldbm/vlv.c
|
||||
@@ -866,6 +866,7 @@ do_vlv_update_index(back_txn *txn, struct ldbminfo *li, Slapi_PBlock *pb, struct
|
||||
struct vlv_key *key = NULL;
|
||||
dbi_val_t data = {0};
|
||||
dblayer_private *priv = NULL;
|
||||
+ size_t key_size_limit = li->li_max_key_len - sizeof(entry->ep_id);
|
||||
|
||||
slapi_pblock_get(pb, SLAPI_BACKEND, &be);
|
||||
priv = (dblayer_private *)li->li_dblayer_private;
|
||||
@@ -886,6 +887,10 @@ do_vlv_update_index(back_txn *txn, struct ldbminfo *li, Slapi_PBlock *pb, struct
|
||||
return rc;
|
||||
}
|
||||
|
||||
+ /* Truncate the key if it is too long */
|
||||
+ if (key->key.size > key_size_limit) {
|
||||
+ key->key.size = key_size_limit;
|
||||
+ }
|
||||
if (NULL != txn) {
|
||||
db_txn = txn->back_txn_txn;
|
||||
} else {
|
||||
@@ -930,7 +935,7 @@ do_vlv_update_index(back_txn *txn, struct ldbminfo *li, Slapi_PBlock *pb, struct
|
||||
if (txn && txn->back_special_handling_fn) {
|
||||
rc = txn->back_special_handling_fn(be, BTXNACT_VLV_DEL, db, &key->key, &data, txn);
|
||||
} else {
|
||||
- rc = dblayer_db_op(be, db, db_txn, DBI_OP_DEL, &key->key, NULL);
|
||||
+ rc = dblayer_db_op(be, db, db_txn, DBI_OP_DEL, &key->key, &data);
|
||||
}
|
||||
if (rc == 0) {
|
||||
if (txn && txn->back_special_handling_fn) {
|
||||
--
|
||||
2.52.0
|
||||
|
||||
285
0023-Issue-6753-Port-ticket-47781-test-7210.patch
Normal file
285
0023-Issue-6753-Port-ticket-47781-test-7210.patch
Normal file
@ -0,0 +1,285 @@
|
||||
From 56f1881b5fb0198af4810eeca9e98736c297a2a5 Mon Sep 17 00:00:00 2001
|
||||
From: Lenka Doudova <mirielka@users.noreply.github.com>
|
||||
Date: Tue, 3 Feb 2026 10:28:02 +0100
|
||||
Subject: [PATCH] Issue 6753 - Port ticket 47781 test (#7210)
|
||||
|
||||
Description:
|
||||
Port ticket 47781 test into dirsrvtests/tests/suites/replication/replication_deadlock_test.py
|
||||
|
||||
Relates: #6753
|
||||
Author: Lenka Doudova
|
||||
Assisted by: Cursor
|
||||
Reviewer: Mark Reynolds
|
||||
---
|
||||
.../replication/replication_deadlock_test.py | 129 +++++++++++++++++-
|
||||
dirsrvtests/tests/tickets/ticket47781_test.py | 104 --------------
|
||||
2 files changed, 128 insertions(+), 105 deletions(-)
|
||||
delete mode 100644 dirsrvtests/tests/tickets/ticket47781_test.py
|
||||
|
||||
diff --git a/dirsrvtests/tests/suites/replication/replication_deadlock_test.py b/dirsrvtests/tests/suites/replication/replication_deadlock_test.py
|
||||
index 8ddbb43bd..9c122add3 100644
|
||||
--- a/dirsrvtests/tests/suites/replication/replication_deadlock_test.py
|
||||
+++ b/dirsrvtests/tests/suites/replication/replication_deadlock_test.py
|
||||
@@ -15,14 +15,20 @@ with replication data when replication agreements are present.
|
||||
import logging
|
||||
import os
|
||||
import pytest
|
||||
+import ldap
|
||||
|
||||
+from lib389._mapped_object import DSLdapObjects
|
||||
from lib389.topologies import topology_m2 as topo
|
||||
+from lib389.topologies import topology_st
|
||||
from lib389.replica import Replicas, ReplicationManager
|
||||
from lib389.agreement import Agreements
|
||||
from lib389.tasks import ImportTask, ExportTask
|
||||
from lib389.idm.user import UserAccounts
|
||||
from lib389.tombstone import Tombstones
|
||||
-from lib389._constants import DEFAULT_SUFFIX
|
||||
+from lib389.backend import Backends
|
||||
+from lib389._constants import (DEFAULT_SUFFIX, DEFAULT_BENAME, defaultProperties,
|
||||
+ REPLICATION_BIND_DN, REPLICATION_BIND_PW,
|
||||
+ REPLICATION_BIND_METHOD, REPLICATION_TRANSPORT)
|
||||
|
||||
pytestmark = pytest.mark.tier2
|
||||
|
||||
@@ -200,6 +206,127 @@ def test_replication_deadlock_tombstone_search(topo):
|
||||
os.remove(export_ldif)
|
||||
|
||||
|
||||
+def test_replication_deadlock_tombstone_invalid_agreement(topology_st):
|
||||
+ """Test deadlock scenario after importing LDIF with replication data and invalid agreement
|
||||
+
|
||||
+ This test verifies that a deadlock does not occur while searching for tombstones after
|
||||
+ setting up an invalid replication agreement (pointing to a non-existent server)
|
||||
+
|
||||
+ :id: a1b2c3d4-e5f6-4781-9abc-def012345678
|
||||
+ :setup: Standalone instance with replication enabled
|
||||
+ :steps:
|
||||
+ 1. Create a supplier with an invalid replication agreement (port 5555, non-existent server)
|
||||
+ 2. Add two test user entries
|
||||
+ 3. Export LDIF with replication data
|
||||
+ 4. Restart the server
|
||||
+ 5. Import the LDIF back
|
||||
+ 6. Search for tombstone entries with timeout (should not hang/deadlock)
|
||||
+ 7. Cleanup: restore original timeout settings, delete invalid agreement, test entries, and export file
|
||||
+ :expectedresults:
|
||||
+ 1. Invalid replication agreement is created successfully
|
||||
+ 2. Test entries are created successfully
|
||||
+ 3. LDIF export completes successfully
|
||||
+ 4. Server restarts successfully
|
||||
+ 5. LDIF import completes successfully
|
||||
+ 6. Tombstone search completes without deadlock (returns at least one entry; no hang/timeout)
|
||||
+ 7. Cleanup completes successfully
|
||||
+ """
|
||||
+
|
||||
+ standalone = topology_st.standalone
|
||||
+ export_ldif = os.path.join(standalone.get_ldif_dir(), 'export.ldif')
|
||||
+
|
||||
+ # Step 1: Create supplier with invalid replication agreement
|
||||
+ log.info('Creating supplier with invalid replication agreement (port 5555 - non-existent server)')
|
||||
+ repl = ReplicationManager(DEFAULT_SUFFIX)
|
||||
+ repl.create_first_supplier(standalone)
|
||||
+
|
||||
+ replicas = Replicas(standalone)
|
||||
+ replica = replicas.get(DEFAULT_SUFFIX)
|
||||
+ agreements = Agreements(standalone, basedn=replica.dn)
|
||||
+
|
||||
+ # Create agreement with invalid port (non-existent server)
|
||||
+ invalid_agreement = agreements.create(properties={
|
||||
+ 'cn': r'meTo_$host:$port',
|
||||
+ 'nsDS5ReplicaRoot': DEFAULT_SUFFIX,
|
||||
+ 'nsDS5ReplicaHost': standalone.host,
|
||||
+ 'nsDS5ReplicaPort': '5555', # Invalid port - server does not exist
|
||||
+ 'nsDS5ReplicaBindDN': defaultProperties[REPLICATION_BIND_DN],
|
||||
+ 'nsDS5ReplicaBindMethod': defaultProperties[REPLICATION_BIND_METHOD],
|
||||
+ 'nsDS5ReplicaTransportInfo': defaultProperties[REPLICATION_TRANSPORT],
|
||||
+ 'nsDS5ReplicaCredentials': defaultProperties[REPLICATION_BIND_PW]
|
||||
+ })
|
||||
+
|
||||
+ # Step 2: Add two test entries
|
||||
+ log.info('Adding two test entries')
|
||||
+ users = UserAccounts(standalone, DEFAULT_SUFFIX)
|
||||
+
|
||||
+ entry1 = users.create(properties={
|
||||
+ 'uid': 'entry1',
|
||||
+ 'cn': 'entry1',
|
||||
+ 'sn': 'user',
|
||||
+ 'userpassword': 'password',
|
||||
+ 'uidNumber': '1001',
|
||||
+ 'gidNumber': '2001',
|
||||
+ 'homeDirectory': '/home/entry1'
|
||||
+ })
|
||||
+
|
||||
+ entry2 = users.create(properties={
|
||||
+ 'uid': 'entry2',
|
||||
+ 'cn': 'entry2',
|
||||
+ 'sn': 'user',
|
||||
+ 'userpassword': 'password',
|
||||
+ 'uidNumber': '1002',
|
||||
+ 'gidNumber': '2002',
|
||||
+ 'homeDirectory': '/home/entry2'
|
||||
+ })
|
||||
+
|
||||
+ # Step 3: Export LDIF with replication data
|
||||
+ log.info('Exporting LDIF with replication data')
|
||||
+ backends = Backends(standalone)
|
||||
+ export_task = backends.export_ldif(be_names=DEFAULT_BENAME, ldif=export_ldif, replication=True)
|
||||
+ export_task.wait()
|
||||
+
|
||||
+ # Step 4: Restart the server
|
||||
+ log.info('Restarting server')
|
||||
+ standalone.restart()
|
||||
+
|
||||
+ # Step 5: Import the LDIF
|
||||
+ log.info('Importing replication LDIF file')
|
||||
+ import_task = ImportTask(standalone)
|
||||
+ import_task.import_suffix_from_ldif(ldiffile=export_ldif, suffix=DEFAULT_SUFFIX)
|
||||
+ import_task.wait()
|
||||
+
|
||||
+ # Step 6: Search for tombstones with timeout - should not hang/deadlock
|
||||
+ log.info('Searching for tombstone entries (should find entries and not hang)')
|
||||
+ # Save original timeout settings so we can restore them in cleanup
|
||||
+ orig_network_timeout = standalone.get_option(ldap.OPT_NETWORK_TIMEOUT)
|
||||
+ orig_timeout = standalone.get_option(ldap.OPT_TIMEOUT)
|
||||
+ # Set explicit timeouts to detect deadlocks
|
||||
+ standalone.set_option(ldap.OPT_NETWORK_TIMEOUT, 5)
|
||||
+ standalone.set_option(ldap.OPT_TIMEOUT, 5)
|
||||
+
|
||||
+ # Verify at least one tombstone exists and the search does not hang/timeout
|
||||
+ try:
|
||||
+ results = DSLdapObjects(standalone, DEFAULT_SUFFIX).filter('(objectclass=nsTombstone)')
|
||||
+ log.info(f'Found {len(results)} tombstone entries')
|
||||
+ assert len(results) > 0, "Tombstone search should return at least one entry"
|
||||
+ except Exception as e:
|
||||
+ log.error(f"Tombstone search failed with error: {e}")
|
||||
+ pytest.fail(f"Tombstone search failed: {e}")
|
||||
+ finally:
|
||||
+ # Restore original timeout settings
|
||||
+ standalone.set_option(ldap.OPT_NETWORK_TIMEOUT, orig_network_timeout)
|
||||
+ standalone.set_option(ldap.OPT_TIMEOUT, orig_timeout)
|
||||
+ # Cleanup test entries and export file
|
||||
+ log.info('Cleaning up test entries')
|
||||
+ invalid_agreement.delete()
|
||||
+ entry1.delete()
|
||||
+ entry2.delete()
|
||||
+ if os.path.exists(export_ldif):
|
||||
+ os.remove(export_ldif)
|
||||
+
|
||||
+ log.info('Test completed successfully - no deadlock detected')
|
||||
+
|
||||
if __name__ == '__main__':
|
||||
CURRENT_FILE = os.path.realpath(__file__)
|
||||
pytest.main(["-s", CURRENT_FILE])
|
||||
diff --git a/dirsrvtests/tests/tickets/ticket47781_test.py b/dirsrvtests/tests/tickets/ticket47781_test.py
|
||||
deleted file mode 100644
|
||||
index ffb9a5e1a..000000000
|
||||
--- a/dirsrvtests/tests/tickets/ticket47781_test.py
|
||||
+++ /dev/null
|
||||
@@ -1,104 +0,0 @@
|
||||
-# --- BEGIN COPYRIGHT BLOCK ---
|
||||
-# Copyright (C) 2016 Red Hat, Inc.
|
||||
-# All rights reserved.
|
||||
-#
|
||||
-# License: GPL (version 3 or any later version).
|
||||
-# See LICENSE for details.
|
||||
-# --- END COPYRIGHT BLOCK ---
|
||||
-#
|
||||
-import logging
|
||||
-
|
||||
-import pytest
|
||||
-from lib389.tasks import *
|
||||
-from lib389.topologies import topology_st
|
||||
-from lib389.replica import ReplicationManager
|
||||
-
|
||||
-from lib389._constants import (defaultProperties, DEFAULT_SUFFIX, ReplicaRole,
|
||||
- REPLICAID_SUPPLIER_1, REPLICATION_BIND_DN, REPLICATION_BIND_PW,
|
||||
- REPLICATION_BIND_METHOD, REPLICATION_TRANSPORT, RA_NAME,
|
||||
- RA_BINDDN, RA_BINDPW, RA_METHOD, RA_TRANSPORT_PROT)
|
||||
-
|
||||
-pytestmark = pytest.mark.tier2
|
||||
-
|
||||
-log = logging.getLogger(__name__)
|
||||
-
|
||||
-
|
||||
-def test_ticket47781(topology_st):
|
||||
- """
|
||||
- Testing for a deadlock after doing an online import of an LDIF with
|
||||
- replication data. The replication agreement should be invalid.
|
||||
- """
|
||||
-
|
||||
- log.info('Testing Ticket 47781 - Testing for deadlock after importing LDIF with replication data')
|
||||
-
|
||||
- supplier = topology_st.standalone
|
||||
- repl = ReplicationManager(DEFAULT_SUFFIX)
|
||||
- repl.create_first_supplier(supplier)
|
||||
-
|
||||
- properties = {RA_NAME: r'meTo_$host:$port',
|
||||
- RA_BINDDN: defaultProperties[REPLICATION_BIND_DN],
|
||||
- RA_BINDPW: defaultProperties[REPLICATION_BIND_PW],
|
||||
- RA_METHOD: defaultProperties[REPLICATION_BIND_METHOD],
|
||||
- RA_TRANSPORT_PROT: defaultProperties[REPLICATION_TRANSPORT]}
|
||||
- # The agreement should point to a server that does NOT exist (invalid port)
|
||||
- repl_agreement = supplier.agreement.create(suffix=DEFAULT_SUFFIX,
|
||||
- host=supplier.host,
|
||||
- port=5555,
|
||||
- properties=properties)
|
||||
-
|
||||
- #
|
||||
- # add two entries
|
||||
- #
|
||||
- log.info('Adding two entries...')
|
||||
-
|
||||
- supplier.add_s(Entry(('cn=entry1,dc=example,dc=com', {
|
||||
- 'objectclass': 'top person'.split(),
|
||||
- 'sn': 'user',
|
||||
- 'cn': 'entry1'})))
|
||||
-
|
||||
- supplier.add_s(Entry(('cn=entry2,dc=example,dc=com', {
|
||||
- 'objectclass': 'top person'.split(),
|
||||
- 'sn': 'user',
|
||||
- 'cn': 'entry2'})))
|
||||
-
|
||||
- #
|
||||
- # export the replication ldif
|
||||
- #
|
||||
- log.info('Exporting replication ldif...')
|
||||
- args = {EXPORT_REPL_INFO: True}
|
||||
- exportTask = Tasks(supplier)
|
||||
- exportTask.exportLDIF(DEFAULT_SUFFIX, None, "/tmp/export.ldif", args)
|
||||
-
|
||||
- #
|
||||
- # Restart the server
|
||||
- #
|
||||
- log.info('Restarting server...')
|
||||
- supplier.stop()
|
||||
- supplier.start()
|
||||
-
|
||||
- #
|
||||
- # Import the ldif
|
||||
- #
|
||||
- log.info('Import replication LDIF file...')
|
||||
- importTask = Tasks(supplier)
|
||||
- args = {TASK_WAIT: True}
|
||||
- importTask.importLDIF(DEFAULT_SUFFIX, None, "/tmp/export.ldif", args)
|
||||
- os.remove("/tmp/export.ldif")
|
||||
-
|
||||
- #
|
||||
- # Search for tombstones - we should not hang/timeout
|
||||
- #
|
||||
- log.info('Search for tombstone entries(should find one and not hang)...')
|
||||
- supplier.set_option(ldap.OPT_NETWORK_TIMEOUT, 5)
|
||||
- supplier.set_option(ldap.OPT_TIMEOUT, 5)
|
||||
- entries = supplier.search_s(DEFAULT_SUFFIX, ldap.SCOPE_SUBTREE, 'objectclass=nsTombstone')
|
||||
- if not entries:
|
||||
- log.fatal('Search failed to find any entries.')
|
||||
- assert PR_False
|
||||
-
|
||||
-
|
||||
-if __name__ == '__main__':
|
||||
- # Run isolated
|
||||
- # -s for DEBUG mode
|
||||
- CURRENT_FILE = os.path.realpath(__file__)
|
||||
- pytest.main("-s %s" % CURRENT_FILE)
|
||||
--
|
||||
2.52.0
|
||||
|
||||
1645
0024-Issue-7194-Repl-Log-Analysis-Add-CSN-propagation-det.patch
Normal file
1645
0024-Issue-7194-Repl-Log-Analysis-Add-CSN-propagation-det.patch
Normal file
File diff suppressed because it is too large
Load Diff
265
0025-Issue-6753-Port-ticket-48896-test.patch
Normal file
265
0025-Issue-6753-Port-ticket-48896-test.patch
Normal file
@ -0,0 +1,265 @@
|
||||
From 603f4deb1e87c819c1830f58c7be4281d981fbbd Mon Sep 17 00:00:00 2001
|
||||
From: Lenka Doudova <lryznaro@redhat.com>
|
||||
Date: Mon, 2 Feb 2026 16:46:52 +0100
|
||||
Subject: [PATCH] Issue 6753 - Port ticket 48896 test
|
||||
|
||||
Description:
|
||||
Port ticket 48896 test into dirsrvtests/tests/suites/password/pwdPolicy_token_test.py
|
||||
|
||||
Relates: #6753
|
||||
Author: Lenka Doudova
|
||||
Assisted by: Cursor
|
||||
Reviewer: Mark Reynolds
|
||||
---
|
||||
.../suites/password/pwdPolicy_token_test.py | 68 ++++++---
|
||||
dirsrvtests/tests/tickets/ticket48896_test.py | 139 ------------------
|
||||
2 files changed, 44 insertions(+), 163 deletions(-)
|
||||
delete mode 100644 dirsrvtests/tests/tickets/ticket48896_test.py
|
||||
|
||||
diff --git a/dirsrvtests/tests/suites/password/pwdPolicy_token_test.py b/dirsrvtests/tests/suites/password/pwdPolicy_token_test.py
|
||||
index ae4eb300f..a3caaa230 100644
|
||||
--- a/dirsrvtests/tests/suites/password/pwdPolicy_token_test.py
|
||||
+++ b/dirsrvtests/tests/suites/password/pwdPolicy_token_test.py
|
||||
@@ -15,6 +15,7 @@ from lib389._constants import *
|
||||
from lib389.idm.user import UserAccounts
|
||||
from lib389.idm.organizationalunit import OrganizationalUnits
|
||||
from lib389.topologies import topology_st as topo
|
||||
+from lib389.idm.directorymanager import DirectoryManager
|
||||
|
||||
pytestmark = pytest.mark.tier1
|
||||
|
||||
@@ -25,14 +26,13 @@ else:
|
||||
logging.getLogger(__name__).setLevel(logging.INFO)
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
-USER_DN = 'uid=Test_user1,ou=People,dc=example,dc=com'
|
||||
USER_ACI = '(targetattr="userpassword")(version 3.0; acl "pwp test"; allow (all) userdn="ldap:///self";)'
|
||||
-TOKEN = 'test_user1'
|
||||
+TOKEN = 'test_user123'
|
||||
|
||||
user_properties = {
|
||||
- 'uid': 'Test_user1',
|
||||
- 'cn': 'test_user1',
|
||||
- 'sn': 'test_user1',
|
||||
+ 'uid': 'Test_user123',
|
||||
+ 'cn': 'test_user123',
|
||||
+ 'sn': 'test_user123',
|
||||
'uidNumber': '1001',
|
||||
'gidNumber': '2001',
|
||||
'userpassword': PASSWORD,
|
||||
@@ -59,28 +59,48 @@ def test_token_lengths(topo):
|
||||
:id: dae9d916-2a03-4707-b454-9e901d295b13
|
||||
:setup: Standalone instance
|
||||
:steps:
|
||||
- 1. Test token length rejects password of the same length as rdn value
|
||||
+ 1. Create user, setup global password policy
|
||||
+ 2. Bind as user, change password to 'Abcd012+'
|
||||
+ 3. Bind as user with 'Abcd012+', attempt changes to 'user', 'us123', 'Tuse!1234', 'Tuse!0987', 'Tabc!1234'
|
||||
+ 4. For each passwordMinTokenLength 4, 6, 10: change settings, rebind as user, attempt password with token of that length from TOKEN
|
||||
+ 5. Cleanup - delete user
|
||||
:expectedresults:
|
||||
- 1. Passwords are rejected
|
||||
+ 1. User created, password policy enabled and set
|
||||
+ 2. Success
|
||||
+ 3. All attempts fail with CONSTRAINT_VIOLATION
|
||||
+ 4. All attempts fail with CONSTRAINT_VIOLATION
|
||||
+ 5. User successfully deleted
|
||||
"""
|
||||
user = pwd_setup(topo)
|
||||
- for length in ['4', '6', '10']:
|
||||
- topo.standalone.simple_bind_s(DN_DM, PASSWORD)
|
||||
- topo.standalone.config.set('passwordMinTokenLength', length)
|
||||
- topo.standalone.simple_bind_s(USER_DN, PASSWORD)
|
||||
- time.sleep(1)
|
||||
-
|
||||
- try:
|
||||
- passwd = TOKEN[:int(length)]
|
||||
- log.info("Testing password len {} token ({})".format(length, passwd))
|
||||
- user.replace('userpassword', passwd)
|
||||
- log.fatal('Password incorrectly allowed!')
|
||||
- assert False
|
||||
- except ldap.CONSTRAINT_VIOLATION as e:
|
||||
- log.info('Password correctly rejected: ' + str(e))
|
||||
- except ldap.LDAPError as e:
|
||||
- log.fatal('Unexpected failure ' + str(e))
|
||||
- assert False
|
||||
+ dm = DirectoryManager(topo.standalone)
|
||||
+
|
||||
+ try:
|
||||
+ # Verify that the user can change their password
|
||||
+ user.rebind(PASSWORD)
|
||||
+ user.replace('userpassword', 'Abcd012+')
|
||||
+
|
||||
+ # Verify that the default password policy is enforced
|
||||
+ user.rebind('Abcd012+')
|
||||
+ for new_password in ['user', 'us123', 'Tuse!1234', 'Tuse!0987', 'Tabc!1234']:
|
||||
+ log.info(f"Testing password {new_password}")
|
||||
+ with pytest.raises(ldap.CONSTRAINT_VIOLATION):
|
||||
+ user.replace('userpassword', new_password)
|
||||
+
|
||||
+ # Verify that the password policy is enforced for different token lengths
|
||||
+ for length in ['4', '6', '10']:
|
||||
+ dm.rebind(PASSWORD)
|
||||
+ topo.standalone.config.set('passwordMinTokenLength', length)
|
||||
+ user.rebind('Abcd012+')
|
||||
+ time.sleep(1)
|
||||
+
|
||||
+ with pytest.raises(ldap.CONSTRAINT_VIOLATION):
|
||||
+ passwd = TOKEN[:int(length)]
|
||||
+ log.info("Testing password len {} token ({})".format(length, passwd))
|
||||
+ user.replace('userpassword', passwd)
|
||||
+
|
||||
+ finally:
|
||||
+ # Cleanup
|
||||
+ user.delete()
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
diff --git a/dirsrvtests/tests/tickets/ticket48896_test.py b/dirsrvtests/tests/tickets/ticket48896_test.py
|
||||
deleted file mode 100644
|
||||
index a1897589e..000000000
|
||||
--- a/dirsrvtests/tests/tickets/ticket48896_test.py
|
||||
+++ /dev/null
|
||||
@@ -1,139 +0,0 @@
|
||||
-# --- BEGIN COPYRIGHT BLOCK ---
|
||||
-# Copyright (C) 2016 Red Hat, Inc.
|
||||
-# All rights reserved.
|
||||
-#
|
||||
-# License: GPL (version 3 or any later version).
|
||||
-# See LICENSE for details.
|
||||
-# --- END COPYRIGHT BLOCK ---
|
||||
-#
|
||||
-import pytest
|
||||
-from lib389.tasks import *
|
||||
-from lib389.utils import *
|
||||
-from lib389.topologies import topology_st
|
||||
-
|
||||
-from lib389._constants import DEFAULT_SUFFIX, DN_DM, PASSWORD
|
||||
-
|
||||
-# Skip on older versions
|
||||
-pytestmark = [pytest.mark.tier2,
|
||||
- pytest.mark.skipif(ds_is_older('1.3.6'), reason="Not implemented")]
|
||||
-
|
||||
-logging.getLogger(__name__).setLevel(logging.DEBUG)
|
||||
-log = logging.getLogger(__name__)
|
||||
-
|
||||
-CONFIG_DN = 'cn=config'
|
||||
-UID = 'buser123'
|
||||
-TESTDN = 'uid=%s,' % UID + DEFAULT_SUFFIX
|
||||
-
|
||||
-
|
||||
-def check_attr_val(topology_st, dn, attr, expected):
|
||||
- try:
|
||||
- centry = topology_st.standalone.search_s(dn, ldap.SCOPE_BASE, 'cn=*')
|
||||
- if centry:
|
||||
- val = centry[0].getValue(attr)
|
||||
- if val == expected:
|
||||
- log.info('Default value of %s is %s' % (attr, expected))
|
||||
- else:
|
||||
- log.info('Default value of %s is not %s, but %s' % (attr, expected, val))
|
||||
- assert False
|
||||
- else:
|
||||
- log.fatal('Failed to get %s' % dn)
|
||||
- assert False
|
||||
- except ldap.LDAPError as e:
|
||||
- log.fatal('Failed to search ' + dn + ': ' + e.message['desc'])
|
||||
- assert False
|
||||
-
|
||||
-
|
||||
-def replace_pw(server, curpw, newpw, expstr, rc):
|
||||
- log.info('Binding as {%s, %s}' % (TESTDN, curpw))
|
||||
- server.simple_bind_s(TESTDN, curpw)
|
||||
-
|
||||
- hit = 0
|
||||
- log.info('Replacing password: %s -> %s, which should %s' % (curpw, newpw, expstr))
|
||||
- try:
|
||||
- server.modify_s(TESTDN, [(ldap.MOD_REPLACE, 'userPassword', ensure_bytes(newpw))])
|
||||
- except Exception as e:
|
||||
- log.info("Exception (expected): %s" % type(e).__name__)
|
||||
- hit = 1
|
||||
- assert isinstance(e, rc)
|
||||
-
|
||||
- if (0 != rc) and (0 == hit):
|
||||
- log.info('Expected to fail with %s, but passed' % rc.__name__)
|
||||
- assert False
|
||||
-
|
||||
- log.info('PASSED')
|
||||
-
|
||||
-
|
||||
-def test_ticket48896(topology_st):
|
||||
- """
|
||||
- """
|
||||
- log.info('Testing Ticket 48896 - Default Setting for passwordMinTokenLength does not work')
|
||||
-
|
||||
- log.info("Setting global password policy with password syntax.")
|
||||
- topology_st.standalone.simple_bind_s(DN_DM, PASSWORD)
|
||||
- topology_st.standalone.modify_s(CONFIG_DN, [(ldap.MOD_REPLACE, 'passwordCheckSyntax', b'on'),
|
||||
- (ldap.MOD_REPLACE, 'nsslapd-pwpolicy-local', b'on')])
|
||||
-
|
||||
- config = topology_st.standalone.search_s(CONFIG_DN, ldap.SCOPE_BASE, 'cn=*')
|
||||
- mintokenlen = config[0].getValue('passwordMinTokenLength')
|
||||
- history = config[0].getValue('passwordInHistory')
|
||||
-
|
||||
- log.info('Default passwordMinTokenLength == %s' % mintokenlen)
|
||||
- log.info('Default passwordInHistory == %s' % history)
|
||||
-
|
||||
- log.info('Adding a user.')
|
||||
- curpw = 'password'
|
||||
- topology_st.standalone.add_s(Entry((TESTDN,
|
||||
- {'objectclass': "top person organizationalPerson inetOrgPerson".split(),
|
||||
- 'cn': 'test user',
|
||||
- 'sn': 'user',
|
||||
- 'userPassword': curpw})))
|
||||
-
|
||||
- newpw = 'Abcd012+'
|
||||
- exp = 'be ok'
|
||||
- rc = 0
|
||||
- replace_pw(topology_st.standalone, curpw, newpw, exp, rc)
|
||||
-
|
||||
- curpw = 'Abcd012+'
|
||||
- newpw = 'user'
|
||||
- exp = 'fail'
|
||||
- rc = ldap.CONSTRAINT_VIOLATION
|
||||
- replace_pw(topology_st.standalone, curpw, newpw, exp, rc)
|
||||
-
|
||||
- curpw = 'Abcd012+'
|
||||
- newpw = UID
|
||||
- exp = 'fail'
|
||||
- rc = ldap.CONSTRAINT_VIOLATION
|
||||
- replace_pw(topology_st.standalone, curpw, newpw, exp, rc)
|
||||
-
|
||||
- curpw = 'Abcd012+'
|
||||
- newpw = 'Tuse!1234'
|
||||
- exp = 'fail'
|
||||
- rc = ldap.CONSTRAINT_VIOLATION
|
||||
- replace_pw(topology_st.standalone, curpw, newpw, exp, rc)
|
||||
-
|
||||
- curpw = 'Abcd012+'
|
||||
- newpw = 'Tuse!0987'
|
||||
- exp = 'fail'
|
||||
- rc = ldap.CONSTRAINT_VIOLATION
|
||||
- replace_pw(topology_st.standalone, curpw, newpw, exp, rc)
|
||||
-
|
||||
- curpw = 'Abcd012+'
|
||||
- newpw = 'Tabc!1234'
|
||||
- exp = 'fail'
|
||||
- rc = ldap.CONSTRAINT_VIOLATION
|
||||
- replace_pw(topology_st.standalone, curpw, newpw, exp, rc)
|
||||
-
|
||||
- curpw = 'Abcd012+'
|
||||
- newpw = 'Direc+ory389'
|
||||
- exp = 'be ok'
|
||||
- rc = 0
|
||||
- replace_pw(topology_st.standalone, curpw, newpw, exp, rc)
|
||||
-
|
||||
- log.info('SUCCESS')
|
||||
-
|
||||
-
|
||||
-if __name__ == '__main__':
|
||||
- # Run isolated
|
||||
- # -s for DEBUG mode
|
||||
- CURRENT_FILE = os.path.realpath(__file__)
|
||||
- pytest.main("-s %s" % CURRENT_FILE)
|
||||
--
|
||||
2.52.0
|
||||
|
||||
30
0026-Issue-6810-Fix-PAM-PTA-test-7219.patch
Normal file
30
0026-Issue-6810-Fix-PAM-PTA-test-7219.patch
Normal file
@ -0,0 +1,30 @@
|
||||
From ff44acffcc67c985148c4df280685a674fec010a Mon Sep 17 00:00:00 2001
|
||||
From: Akshay Adhikari <aadhikar@redhat.com>
|
||||
Date: Thu, 5 Feb 2026 15:19:58 +0530
|
||||
Subject: [PATCH] Issue 6810 - Fix PAM PTA test (#7219)
|
||||
|
||||
Description: Fix the PAM PTA test by add missing yield in fixture.
|
||||
|
||||
Relates: #6810
|
||||
|
||||
Reviewed by: @jchapma
|
||||
---
|
||||
dirsrvtests/tests/suites/plugins/pam_pta_test.py | 2 ++
|
||||
1 file changed, 2 insertions(+)
|
||||
|
||||
diff --git a/dirsrvtests/tests/suites/plugins/pam_pta_test.py b/dirsrvtests/tests/suites/plugins/pam_pta_test.py
|
||||
index 484c1bc80..e55a7f7ee 100644
|
||||
--- a/dirsrvtests/tests/suites/plugins/pam_pta_test.py
|
||||
+++ b/dirsrvtests/tests/suites/plugins/pam_pta_test.py
|
||||
@@ -104,6 +104,8 @@ def pam_service_ldapserver(migrated_child_config):
|
||||
f.write(line + "\n")
|
||||
os.chmod(pam_file, 0o644)
|
||||
|
||||
+ yield
|
||||
+
|
||||
except Exception as e:
|
||||
if os.path.exists(backup_file):
|
||||
# Restore backup on error
|
||||
--
|
||||
2.52.0
|
||||
|
||||
@ -0,0 +1,57 @@
|
||||
From 2a0ed9c267fc56a14e84ad53ffaeb0b822594367 Mon Sep 17 00:00:00 2001
|
||||
From: Akshay Adhikari <aadhikar@redhat.com>
|
||||
Date: Thu, 5 Feb 2026 15:41:09 +0530
|
||||
Subject: [PATCH] Issue 7076 - Fix revert_cache() never called in modrdn
|
||||
(#7220)
|
||||
|
||||
Description: The postentry check in PR #7077 was broken - postentry is always NULL
|
||||
at that point, fixed by removing the check.
|
||||
|
||||
Relates: #7076
|
||||
|
||||
Reviewed by: @vashirov, @mreynolds389, @droideck (Thanks!)
|
||||
---
|
||||
ldap/servers/slapd/back-ldbm/ldbm_modrdn.c | 12 +++++++-----
|
||||
1 file changed, 7 insertions(+), 5 deletions(-)
|
||||
|
||||
diff --git a/ldap/servers/slapd/back-ldbm/ldbm_modrdn.c b/ldap/servers/slapd/back-ldbm/ldbm_modrdn.c
|
||||
index 759edb80d..e859789b3 100644
|
||||
--- a/ldap/servers/slapd/back-ldbm/ldbm_modrdn.c
|
||||
+++ b/ldap/servers/slapd/back-ldbm/ldbm_modrdn.c
|
||||
@@ -102,6 +102,7 @@ ldbm_back_modrdn(Slapi_PBlock *pb)
|
||||
Connection *pb_conn = NULL;
|
||||
int32_t parent_op = 0;
|
||||
int32_t betxn_callback_fails = 0; /* if a BETXN fails we need to revert entry cache */
|
||||
+ int32_t cache_mod_phase = 0; /* set when we reach the cache modification phase */
|
||||
struct timespec parent_time;
|
||||
Slapi_Mods *smods_add_rdn = NULL;
|
||||
|
||||
@@ -1181,6 +1182,8 @@ ldbm_back_modrdn(Slapi_PBlock *pb)
|
||||
goto error_return;
|
||||
}
|
||||
|
||||
+ /* We're now past the BETXN PRE phase and entering the cache modification phase */
|
||||
+ cache_mod_phase = 1;
|
||||
postentry = slapi_entry_dup(ec->ep_entry);
|
||||
|
||||
if (parententry != NULL) {
|
||||
@@ -1363,12 +1366,11 @@ error_return:
|
||||
}
|
||||
}
|
||||
|
||||
- /* Revert the caches if this is the parent operation and cache modifications were made.
|
||||
- * Cache modifications (via modify_switch_entries) only happen after BETXN PRE plugins succeed,
|
||||
- * so we should only revert if we got past that point (i.e., BETXN POST plugin failures).
|
||||
- * For BETXN PRE failures, no cache modifications were made to parent/newparent entries.
|
||||
+ /* Revert the caches if this is the parent operation AND we reached the
|
||||
+ * cache modification phase. If BETXN PRE fails, cache_mod_phase is 0
|
||||
+ * and we don't need to revert since no cache modifications were made.
|
||||
*/
|
||||
- if (parent_op && betxn_callback_fails && postentry) {
|
||||
+ if (parent_op && betxn_callback_fails && cache_mod_phase) {
|
||||
revert_cache(inst, &parent_time);
|
||||
}
|
||||
|
||||
--
|
||||
2.52.0
|
||||
|
||||
1492
0028-Issue-6951-Dynamic-Certificate-refresh-phase-4-Updat.patch
Normal file
1492
0028-Issue-6951-Dynamic-Certificate-refresh-phase-4-Updat.patch
Normal file
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,93 @@
|
||||
From d5a83e8f2ccd0c9d11792f026947c4785996b4a6 Mon Sep 17 00:00:00 2001
|
||||
From: James Chapman <jachapma@redhat.com>
|
||||
Date: Thu, 5 Feb 2026 15:33:08 +0000
|
||||
Subject: [PATCH] Issue 7224 - CI Test - Simplify
|
||||
test_reserve_descriptor_validation (#7225)
|
||||
|
||||
Description:
|
||||
Previously, the test_reserve_descriptor_validation CItest calculated
|
||||
the expected number of file descriptors based on backends, indexes,
|
||||
SSL/FIPS mode, and compared it to the value returned by the server.
|
||||
This approach is fragile, especially in FIPS mode.
|
||||
|
||||
Fix:
|
||||
The test has been updated to simply verify that the server corrects
|
||||
the configured nsslapd-reservedescriptors value if it is set too low,
|
||||
instead of calculating the expected total.
|
||||
|
||||
Fixes: https://github.com/389ds/389-ds-base/issues/7224
|
||||
|
||||
Reviewed by: @bsimonova (Thank you)
|
||||
---
|
||||
.../suites/resource_limits/fdlimits_test.py | 36 +++++++------------
|
||||
1 file changed, 13 insertions(+), 23 deletions(-)
|
||||
|
||||
diff --git a/dirsrvtests/tests/suites/resource_limits/fdlimits_test.py b/dirsrvtests/tests/suites/resource_limits/fdlimits_test.py
|
||||
index c843a4b24..a49e378c3 100644
|
||||
--- a/dirsrvtests/tests/suites/resource_limits/fdlimits_test.py
|
||||
+++ b/dirsrvtests/tests/suites/resource_limits/fdlimits_test.py
|
||||
@@ -27,7 +27,7 @@ RESRV_FD_ATTR = "nsslapd-reservedescriptors"
|
||||
GLOBAL_LIMIT = resource.getrlimit(resource.RLIMIT_NOFILE)[1]
|
||||
SYSTEMD_LIMIT = ensure_str(check_output("systemctl show -p LimitNOFILE dirsrv@standalone1".split(" ")).strip()).split('=')[1]
|
||||
CUSTOM_VAL = str(int(SYSTEMD_LIMIT) - 10)
|
||||
-RESRV_DESC_VAL = str(10)
|
||||
+RESRV_DESC_VAL_LOW = 10
|
||||
TOO_HIGH_VAL = str(GLOBAL_LIMIT * 2)
|
||||
TOO_HIGH_VAL2 = str(int(SYSTEMD_LIMIT) * 2)
|
||||
TOO_LOW_VAL = "0"
|
||||
@@ -86,40 +86,30 @@ def test_reserve_descriptor_validation(topology_st):
|
||||
:id: 9bacdbcc-7754-4955-8a56-1d8c82bce274
|
||||
:setup: Standalone Instance
|
||||
:steps:
|
||||
- 1. Set attr nsslapd-reservedescriptors to a low value of RESRV_DESC_VAL (10)
|
||||
+ 1. Set attr nsslapd-reservedescriptors to a low value (10)
|
||||
2. Verify low value has been set
|
||||
3. Restart instance (On restart the reservedescriptor attr will be validated)
|
||||
- 4. Check updated value for nsslapd-reservedescriptors attr
|
||||
+ 4. Verify corrected value for nsslapd-reservedescriptors > low value
|
||||
:expectedresults:
|
||||
1. Success
|
||||
- 2. A value of RESRV_DESC_VAL (10) is returned
|
||||
+ 2. A value of RESRV_DESC_VAL_LOW (10) is returned
|
||||
3. Success
|
||||
- 4. A value of STANDALONE_INST_RESRV_DESCS (55) is returned
|
||||
+ 4. Corrected value for nsslapd-reservedescriptors > low value
|
||||
"""
|
||||
|
||||
- # Set nsslapd-reservedescriptors to a low value (RESRV_DESC_VAL:10)
|
||||
- topology_st.standalone.config.set(RESRV_FD_ATTR, RESRV_DESC_VAL)
|
||||
- resrv_fd = topology_st.standalone.config.get_attr_val_utf8(RESRV_FD_ATTR)
|
||||
- assert resrv_fd == RESRV_DESC_VAL
|
||||
+ # Set nsslapd-reservedescriptors to a low value (10)
|
||||
+ topology_st.standalone.config.set(RESRV_FD_ATTR, str(RESRV_DESC_VAL_LOW))
|
||||
+ resrv_fd = int(topology_st.standalone.config.get_attr_val_utf8(RESRV_FD_ATTR))
|
||||
+ assert resrv_fd == RESRV_DESC_VAL_LOW
|
||||
|
||||
# An instance restart triggers a validation of the configured nsslapd-reservedescriptors attribute
|
||||
topology_st.standalone.restart()
|
||||
|
||||
- """
|
||||
- A standalone instance contains a single backend with default indexes
|
||||
- so we only check these. TODO add tests for repl, chaining, PTA, SSL
|
||||
- """
|
||||
- STANDALONE_INST_RESRV_DESCS = 25 if is_fips() else 20 # Reserve descriptor constant (higher in FIPS mode)
|
||||
- backends = Backends(topology_st.standalone)
|
||||
- STANDALONE_INST_RESRV_DESCS += (len(backends.list()) * 4) # 4 = Backend descriptor constant
|
||||
- for be in backends.list() :
|
||||
- STANDALONE_INST_RESRV_DESCS += len(be.get_indexes().list())
|
||||
-
|
||||
- # Varify reservedescriptors has been updated
|
||||
- resrv_fd = topology_st.standalone.config.get_attr_val_utf8(RESRV_FD_ATTR)
|
||||
- assert resrv_fd == str(STANDALONE_INST_RESRV_DESCS)
|
||||
+ # Get the corrected value
|
||||
+ corrected_fd = int(topology_st.standalone.config.get_attr_val_utf8(RESRV_FD_ATTR))
|
||||
+ assert corrected_fd > RESRV_DESC_VAL_LOW
|
||||
|
||||
- log.info("test_reserve_descriptor_validation PASSED")
|
||||
+ log.info(f"test_reserve_descriptor_validation PASSED (corrected from {RESRV_DESC_VAL_LOW} to {corrected_fd})")
|
||||
|
||||
@pytest.mark.skipif(ds_is_older("1.4.1.2"), reason="Not implemented")
|
||||
def test_reserve_descriptors_high(topology_st):
|
||||
--
|
||||
2.52.0
|
||||
|
||||
@ -0,0 +1,94 @@
|
||||
From 9bfbec8c4aad1c698fd80b3086e11f06fd9df26d Mon Sep 17 00:00:00 2001
|
||||
From: Viktor Ashirov <vashirov@redhat.com>
|
||||
Date: Mon, 9 Feb 2026 13:15:44 +0100
|
||||
Subject: [PATCH] Issue 7178 - Bundled jemalloc fails to build with GCC 15
|
||||
(#7216)
|
||||
|
||||
Description:
|
||||
Update spec file to fix build failures on Fedora Rawhide
|
||||
|
||||
Fixes: https://github.com/389ds/389-ds-base/issues/7178
|
||||
|
||||
Reviewed by: @progier389, @droideck (Thanks!)
|
||||
---
|
||||
rpm.mk | 1 +
|
||||
rpm/389-ds-base.spec.in | 10 +++++-----
|
||||
rpm/jemalloc-5.3.0_throw_bad_alloc.patch | 12 ++++++++++++
|
||||
3 files changed, 18 insertions(+), 5 deletions(-)
|
||||
create mode 100644 rpm/jemalloc-5.3.0_throw_bad_alloc.patch
|
||||
|
||||
diff --git a/rpm.mk b/rpm.mk
|
||||
index f91011814..372cee5a6 100644
|
||||
--- a/rpm.mk
|
||||
+++ b/rpm.mk
|
||||
@@ -143,6 +143,7 @@ srpmdistdir:
|
||||
rpmbuildprep:
|
||||
cp dist/sources/$(TARBALL) $(RPMBUILD)/SOURCES/
|
||||
cp rpm/$(PACKAGE)-* $(RPMBUILD)/SOURCES/
|
||||
+ cp rpm/jemalloc-5.3.0_throw_bad_alloc.patch $(RPMBUILD)/SOURCES/
|
||||
if [ $(BUNDLE_JEMALLOC) -eq 1 ]; then \
|
||||
cp dist/sources/$(JEMALLOC_TARBALL) $(RPMBUILD)/SOURCES/ ; \
|
||||
fi
|
||||
diff --git a/rpm/389-ds-base.spec.in b/rpm/389-ds-base.spec.in
|
||||
index 51bfd7e77..dc8c75dac 100644
|
||||
--- a/rpm/389-ds-base.spec.in
|
||||
+++ b/rpm/389-ds-base.spec.in
|
||||
@@ -152,8 +152,8 @@ BuildRequires: python%{python3_pkgversion}-devel
|
||||
# For cockpit
|
||||
%if %{with cockpit}
|
||||
BuildRequires: rsync
|
||||
-BuildRequires: npm
|
||||
-BuildRequires: nodejs
|
||||
+BuildRequires: /usr/bin/npm
|
||||
+BuildRequires: /usr/bin/node
|
||||
%endif
|
||||
|
||||
# END BUILD REQUIRES
|
||||
@@ -188,9 +188,7 @@ Requires: %{name}-robdb-libs = %{version}-%{release}
|
||||
Requires: libdb
|
||||
%endif
|
||||
%endif
|
||||
-Requires: lmdb
|
||||
-# This picks up libperl.so as a Requires, so we add this versioned one
|
||||
-Requires: perl(:MODULE_COMPAT_%(eval "`%{__perl} -V:version`"; echo $version))
|
||||
+Requires: lmdb-libs
|
||||
# Needed by logconv.pl
|
||||
%if %{without libbdb_ro}
|
||||
%if %{without bundle_libdb}
|
||||
@@ -217,6 +215,7 @@ Source0: %{name}-%{version}.tar.bz2
|
||||
Source2: %{name}-devel.README
|
||||
%if %{with bundle_jemalloc}
|
||||
Source3: https://github.com/jemalloc/%{jemalloc_name}/releases/download/%{jemalloc_ver}/%{jemalloc_name}-%{jemalloc_ver}.tar.bz2
|
||||
+Source5: jemalloc-5.3.0_throw_bad_alloc.patch
|
||||
%endif
|
||||
%if %{with bundle_libdb}
|
||||
Source4: https://fedorapeople.org/groups/389ds/libdb-5.3.28-59.tar.bz2
|
||||
@@ -434,6 +433,7 @@ COCKPIT_FLAGS="--disable-cockpit"
|
||||
|
||||
# Build jemalloc
|
||||
pushd ../%{jemalloc_name}-%{jemalloc_ver}
|
||||
+patch -p1 -F3 < %{SOURCE5}
|
||||
%configure \
|
||||
--libdir=%{_libdir}/%{pkgname}/lib \
|
||||
--bindir=%{_libdir}/%{pkgname}/bin \
|
||||
diff --git a/rpm/jemalloc-5.3.0_throw_bad_alloc.patch b/rpm/jemalloc-5.3.0_throw_bad_alloc.patch
|
||||
new file mode 100644
|
||||
index 000000000..685e6968c
|
||||
--- /dev/null
|
||||
+++ b/rpm/jemalloc-5.3.0_throw_bad_alloc.patch
|
||||
@@ -0,0 +1,12 @@
|
||||
+diff --git a/src/jemalloc_cpp.cpp b/src/jemalloc_cpp.cpp
|
||||
+index fffd6aee..5a682991 100644
|
||||
+--- a/src/jemalloc_cpp.cpp
|
||||
++++ b/src/jemalloc_cpp.cpp
|
||||
+@@ -93,7 +93,7 @@ handleOOM(std::size_t size, bool nothrow) {
|
||||
+ }
|
||||
+
|
||||
+ if (ptr == nullptr && !nothrow)
|
||||
+- std::__throw_bad_alloc();
|
||||
++ throw std::bad_alloc();
|
||||
+ return ptr;
|
||||
+ }
|
||||
--
|
||||
2.52.0
|
||||
|
||||
@ -0,0 +1,71 @@
|
||||
From fb4254a97fe0da25064d2f6296705c9e1810ffda Mon Sep 17 00:00:00 2001
|
||||
From: Viktor Ashirov <vashirov@redhat.com>
|
||||
Date: Mon, 9 Feb 2026 13:18:09 +0100
|
||||
Subject: [PATCH] Issue 7121 - (2nd) LeakSanitizer: various leaks during
|
||||
replication (#7212)
|
||||
|
||||
Bug Description:
|
||||
With the previous fix 75e0e487545893a7b0d83f94f9264c10f8bb0353 applied,
|
||||
server can crash in ber_bvcpy.
|
||||
|
||||
```
|
||||
Program terminated with signal SIGSEGV, Segmentation fault.
|
||||
#0 ber_bvcpy (bvs=0x7f1d00000000, bvd=0x7f1da2cd73c0) at ldap/servers/slapd/value.c:47
|
||||
47 len = bvs->bv_len;
|
||||
[Current thread is 1 (Thread 0x7f1db47fe640 (LWP 36576))]
|
||||
(gdb) bt
|
||||
#0 ber_bvcpy (bvs=0x7f1d00000000, bvd=0x7f1da2cd73c0) at ldap/servers/slapd/value.c:47
|
||||
#1 ber_bvcpy (bvs=0x7f1d00000000, bvd=0x7f1da2cd73c0) at ldap/servers/slapd/value.c:40
|
||||
#2 slapi_value_set_berval (bval=0x7f1d00000000, value=0x7f1da2cd73c0) at ldap/servers/slapd/value.c:322
|
||||
#3 slapi_value_set_berval (value=value@entry=0x7f1da2cd73c0, bval=bval@entry=0x7f1d00000000) at ldap/servers/slapd/value.c:317
|
||||
#4 0x00007f1e48b7d787 in value_init (v=v@entry=0x7f1da2cd73c0, bval=bval@entry=0x7f1d00000000, t=t@entry=0 '\000', csn=csn@entry=0x0)
|
||||
at ldap/servers/slapd/value.c:179
|
||||
#5 0x00007f1e48b7d884 in value_new (bval=bval@entry=0x7f1d00000000, t=t@entry=0 '\000', csn=csn@entry=0x0) at ldap/servers/slapd/value.c:158
|
||||
#6 0x00007f1e48b7ddb7 in slapi_value_dup (v=0x7f1d00000000) at ldap/servers/slapd/value.c:147
|
||||
#7 0x00007f1e48b7e262 in valueset_set_valueset (vs2=0x7f1d502b5218, vs1=0x7f1da2c5b358) at ldap/servers/slapd/valueset.c:1244
|
||||
#8 valueset_set_valueset (vs1=0x7f1da2c5b358, vs2=0x7f1d502b5218) at ldap/servers/slapd/valueset.c:1220
|
||||
#9 0x00007f1e48add4af in slapi_attr_dup (attr=0x7f1d502b51e0) at ldap/servers/slapd/attr.c:396
|
||||
#10 0x00007f1e48af0f60 in slapi_entry_dup (e=0x7f1da2c19000) at ldap/servers/slapd/entry.c:2036
|
||||
#11 0x00007f1e442c734e in ldbm_back_modify (pb=0x7f1da2c00000) at ldap/servers/slapd/back-ldbm/ldbm_modify.c:741
|
||||
#12 0x00007f1e48b30076 in op_shared_modify (pb=pb@entry=0x7f1da2c00000, pw_change=pw_change@entry=0, old_pw=0x0)
|
||||
at ldap/servers/slapd/modify.c:1079
|
||||
#13 0x00007f1e48b30ced in do_modify (pb=pb@entry=0x7f1da2c00000) at ldap/servers/slapd/modify.c:377
|
||||
#14 0x000055e990e2fd1c in connection_dispatch_operation (pb=0x7f1da2c00000, op=<optimized out>, conn=<optimized out>)
|
||||
at ldap/servers/slapd/connection.c:672
|
||||
#15 connection_threadmain (arg=<optimized out>) at ldap/servers/slapd/connection.c:1955
|
||||
#16 0x00007f1e48839bd4 in _pt_root (arg=0x7f1e439d9500) at pthreads/../../../../nspr/pr/src/pthreads/ptthread.c:191
|
||||
#17 0x00007f1e4868a19a in start_thread (arg=<optimized out>) at pthread_create.c:443
|
||||
#18 0x00007f1e4870f100 in clone3 () at ../sysdeps/unix/sysv/linux/x86_64/clone3.S:81
|
||||
```
|
||||
|
||||
The fix changed from always setting `v_csnset = NULL` to only freeing it
|
||||
inside the if-block.
|
||||
|
||||
Fix Description:
|
||||
Keep `csnset_free()` outside the if-block to handle all values, not just
|
||||
those matching the condtion.
|
||||
|
||||
Related: https://github.com/389ds/389-ds-base/issues/7121
|
||||
|
||||
Reviewed by: @progier389, @droideck (Thanks!)
|
||||
---
|
||||
ldap/servers/slapd/entrywsi.c | 2 +-
|
||||
1 file changed, 1 insertion(+), 1 deletion(-)
|
||||
|
||||
diff --git a/ldap/servers/slapd/entrywsi.c b/ldap/servers/slapd/entrywsi.c
|
||||
index e1bdc1bab..0d044092d 100644
|
||||
--- a/ldap/servers/slapd/entrywsi.c
|
||||
+++ b/ldap/servers/slapd/entrywsi.c
|
||||
@@ -1185,8 +1185,8 @@ resolve_attribute_state_deleted_to_present(Slapi_Entry *e, Slapi_Attr *a, Slapi_
|
||||
if ((csn_compare(vucsn, deletedcsn) >= 0) ||
|
||||
value_distinguished_at_csn(e, a, valuestoupdate[i], deletedcsn)) {
|
||||
entry_deleted_value_to_present_value(a, valuestoupdate[i]);
|
||||
- csnset_free(&valuestoupdate[i]->v_csnset);
|
||||
}
|
||||
+ csnset_free(&valuestoupdate[i]->v_csnset);
|
||||
}
|
||||
}
|
||||
}
|
||||
--
|
||||
2.52.0
|
||||
|
||||
778
0032-Issue-7223-Revert-index-scan-limits-for-system-index.patch
Normal file
778
0032-Issue-7223-Revert-index-scan-limits-for-system-index.patch
Normal file
@ -0,0 +1,778 @@
|
||||
From 3938942a5418add83616b1413f3070d394c30a7f Mon Sep 17 00:00:00 2001
|
||||
From: Viktor Ashirov <vashirov@redhat.com>
|
||||
Date: Thu, 5 Feb 2026 12:17:06 +0100
|
||||
Subject: [PATCH] Issue 7223 - Revert index scan limits for system indexes
|
||||
|
||||
This reverts changes introduced by the following commits:
|
||||
c6f458b42 Issue 7189 - DSBLE0007 generates incorrect remediation commands for scan limits
|
||||
8b6b3a9f9 Issue 6966 - On large DB, unlimited IDL scan limit reduce the SRCH performance
|
||||
|
||||
Relates: https://github.com/389ds/389-ds-base/issues/7223
|
||||
|
||||
Reviewed by: @progier389, @tbordaz, @droideck (Thanks!)
|
||||
---
|
||||
.../tests/suites/config/config_test.py | 27 +---
|
||||
.../healthcheck/health_system_indexes_test.py | 136 +-----------------
|
||||
.../paged_results/paged_results_test.py | 25 +---
|
||||
ldap/servers/slapd/back-ldbm/back-ldbm.h | 1 -
|
||||
ldap/servers/slapd/back-ldbm/index.c | 2 -
|
||||
ldap/servers/slapd/back-ldbm/instance.c | 104 +++-----------
|
||||
ldap/servers/slapd/back-ldbm/ldbm_config.c | 30 ----
|
||||
ldap/servers/slapd/back-ldbm/ldbm_config.h | 1 -
|
||||
.../slapd/back-ldbm/ldbm_index_config.c | 8 --
|
||||
src/lib389/lib389/backend.py | 50 ++-----
|
||||
src/lib389/lib389/cli_conf/backend.py | 20 ---
|
||||
11 files changed, 40 insertions(+), 364 deletions(-)
|
||||
|
||||
diff --git a/dirsrvtests/tests/suites/config/config_test.py b/dirsrvtests/tests/suites/config/config_test.py
|
||||
index cbb8875fa..2c7d949d0 100644
|
||||
--- a/dirsrvtests/tests/suites/config/config_test.py
|
||||
+++ b/dirsrvtests/tests/suites/config/config_test.py
|
||||
@@ -706,19 +706,17 @@ def test_ndn_cache_size_enforcement(topo, request):
|
||||
|
||||
request.addfinalizer(fin)
|
||||
|
||||
-def test_require_index(topo, request):
|
||||
+def test_require_index(topo):
|
||||
"""Validate that unindexed searches are rejected
|
||||
|
||||
:id: fb6e31f2-acc2-4e75-a195-5c356faeb803
|
||||
:setup: Standalone instance
|
||||
:steps:
|
||||
1. Set "nsslapd-require-index" to "on"
|
||||
- 2. ancestorid/idlscanlimit to 100
|
||||
- 3. Test an unindexed search is rejected
|
||||
+ 2. Test an unindexed search is rejected
|
||||
:expectedresults:
|
||||
1. Success
|
||||
2. Success
|
||||
- 3. Success
|
||||
"""
|
||||
|
||||
# Set the config
|
||||
@@ -729,10 +727,6 @@ def test_require_index(topo, request):
|
||||
|
||||
db_cfg = DatabaseConfig(topo.standalone)
|
||||
db_cfg.set([('nsslapd-idlistscanlimit', '100')])
|
||||
- backend = Backends(topo.standalone).get_backend(DEFAULT_SUFFIX)
|
||||
- ancestorid_index = backend.get_index('ancestorid')
|
||||
- ancestorid_index.replace("nsIndexIDListScanLimit", ensure_bytes("limit=100 type=eq flags=AND"))
|
||||
- topo.standalone.restart()
|
||||
|
||||
users = UserAccounts(topo.standalone, DEFAULT_SUFFIX)
|
||||
for i in range(101):
|
||||
@@ -743,15 +737,10 @@ def test_require_index(topo, request):
|
||||
with pytest.raises(ldap.UNWILLING_TO_PERFORM):
|
||||
raw_objects.filter("(description=test*)")
|
||||
|
||||
- def fin():
|
||||
- ancestorid_index.replace("nsIndexIDListScanLimit", ensure_bytes("limit=5000 type=eq flags=AND"))
|
||||
-
|
||||
- request.addfinalizer(fin)
|
||||
-
|
||||
|
||||
|
||||
@pytest.mark.skipif(ds_is_older('1.4.2'), reason="The config setting only exists in 1.4.2 and higher")
|
||||
-def test_require_internal_index(topo, request):
|
||||
+def test_require_internal_index(topo):
|
||||
"""Ensure internal operations require indexed attributes
|
||||
|
||||
:id: 22b94f30-59e3-4f27-89a1-c4f4be036f7f
|
||||
@@ -782,10 +771,6 @@ def test_require_internal_index(topo, request):
|
||||
# Create a bunch of users
|
||||
db_cfg = DatabaseConfig(topo.standalone)
|
||||
db_cfg.set([('nsslapd-idlistscanlimit', '100')])
|
||||
- backend = Backends(topo.standalone).get_backend(DEFAULT_SUFFIX)
|
||||
- ancestorid_index = backend.get_index('ancestorid')
|
||||
- ancestorid_index.replace("nsIndexIDListScanLimit", ensure_bytes("limit=100 type=eq flags=AND"))
|
||||
- topo.standalone.restart()
|
||||
users = UserAccounts(topo.standalone, DEFAULT_SUFFIX)
|
||||
for i in range(102, 202):
|
||||
users.create_test_user(uid=i)
|
||||
@@ -810,12 +795,6 @@ def test_require_internal_index(topo, request):
|
||||
with pytest.raises(ldap.UNWILLING_TO_PERFORM):
|
||||
user.delete()
|
||||
|
||||
- def fin():
|
||||
- ancestorid_index.replace("nsIndexIDListScanLimit", ensure_bytes("limit=5000 type=eq flags=AND"))
|
||||
-
|
||||
- request.addfinalizer(fin)
|
||||
-
|
||||
-
|
||||
|
||||
def get_pstack(pid):
|
||||
"""Get a pstack of the pid."""
|
||||
diff --git a/dirsrvtests/tests/suites/healthcheck/health_system_indexes_test.py b/dirsrvtests/tests/suites/healthcheck/health_system_indexes_test.py
|
||||
index 486fad44b..140845a33 100644
|
||||
--- a/dirsrvtests/tests/suites/healthcheck/health_system_indexes_test.py
|
||||
+++ b/dirsrvtests/tests/suites/healthcheck/health_system_indexes_test.py
|
||||
@@ -172,9 +172,7 @@ def test_missing_parentid(topology_st, log_buffering_enabled):
|
||||
|
||||
log.info("Re-add the parentId index")
|
||||
backend = Backends(standalone).get("userRoot")
|
||||
- backend.add_index("parentid", ["eq"], matching_rules=["integerOrderingMatch"],
|
||||
- idlistscanlimit=['limit=5000 type=eq flags=AND'])
|
||||
- standalone.restart()
|
||||
+ backend.add_index("parentid", ["eq"], matching_rules=["integerOrderingMatch"])
|
||||
|
||||
run_healthcheck_and_flush_log(topology_st, standalone, json=False, searched_code=CMD_OUTPUT)
|
||||
run_healthcheck_and_flush_log(topology_st, standalone, json=True, searched_code=JSON_OUTPUT)
|
||||
@@ -263,8 +261,7 @@ def test_usn_plugin_missing_entryusn(topology_st, usn_plugin_enabled, log_buffer
|
||||
|
||||
log.info("Re-add the entryusn index")
|
||||
backend = Backends(standalone).get("userRoot")
|
||||
- backend.add_index("entryusn", ["eq"], matching_rules=["integerOrderingMatch"],
|
||||
- idlistscanlimit=['limit=5000 type=eq flags=AND'])
|
||||
+ backend.add_index("entryusn", ["eq"], matching_rules=["integerOrderingMatch"])
|
||||
|
||||
run_healthcheck_and_flush_log(topology_st, standalone, json=False, searched_code=CMD_OUTPUT)
|
||||
run_healthcheck_and_flush_log(topology_st, standalone, json=True, searched_code=JSON_OUTPUT)
|
||||
@@ -408,132 +405,6 @@ def test_retrocl_plugin_missing_matching_rule(topology_st, retrocl_plugin_enable
|
||||
run_healthcheck_and_flush_log(topology_st, standalone, json=True, searched_code=JSON_OUTPUT)
|
||||
|
||||
|
||||
-def test_missing_scanlimit(topology_st, log_buffering_enabled):
|
||||
- """Check if healthcheck returns DSBLE0007 code when parentId index is missing scanlimit
|
||||
-
|
||||
- :id: 40e1bf6a-2397-459b-bdf3-f787ca118b86
|
||||
- :setup: Standalone instance
|
||||
- :steps:
|
||||
- 1. Create DS instance
|
||||
- 2. Remove nsIndexIDListScanLimit from parentId index
|
||||
- 3. Use healthcheck without --json option
|
||||
- 4. Use healthcheck with --json option
|
||||
- 5. Verify the remediation command has properly quoted scanlimit
|
||||
- 6. Re-add the scanlimit
|
||||
- 7. Use healthcheck without --json option
|
||||
- 8. Use healthcheck with --json option
|
||||
- :expectedresults:
|
||||
- 1. Success
|
||||
- 2. Success
|
||||
- 3. healthcheck reports DSBLE0007 code and related details
|
||||
- 4. healthcheck reports DSBLE0007 code and related details
|
||||
- 5. The scanlimit value is quoted in the remediation command
|
||||
- 6. Success
|
||||
- 7. healthcheck reports no issues found
|
||||
- 8. healthcheck reports no issues found
|
||||
- """
|
||||
-
|
||||
- RET_CODE = "DSBLE0007"
|
||||
- PARENTID_DN = "cn=parentid,cn=index,cn=userroot,cn=ldbm database,cn=plugins,cn=config"
|
||||
- SCANLIMIT_VALUE = "limit=5000 type=eq flags=AND"
|
||||
-
|
||||
- standalone = topology_st.standalone
|
||||
-
|
||||
- log.info("Remove nsIndexIDListScanLimit from parentId index")
|
||||
- parentid_index = Index(standalone, PARENTID_DN)
|
||||
- parentid_index.remove("nsIndexIDListScanLimit", SCANLIMIT_VALUE)
|
||||
-
|
||||
- run_healthcheck_and_flush_log(topology_st, standalone, json=False, searched_code=RET_CODE)
|
||||
-
|
||||
- # Verify the remediation command has properly quoted scanlimit
|
||||
- args = FakeArgs()
|
||||
- args.instance = standalone.serverid
|
||||
- args.verbose = standalone.verbose
|
||||
- args.list_errors = False
|
||||
- args.list_checks = False
|
||||
- args.exclude_check = []
|
||||
- args.check = ["backends"]
|
||||
- args.dry_run = False
|
||||
- args.json = False
|
||||
- health_check_run(standalone, topology_st.logcap.log, args)
|
||||
- # Check that the scanlimit is quoted in the output
|
||||
- assert topology_st.logcap.contains('--add-scanlimit "limit=5000 type=eq flags=AND"')
|
||||
- log.info("Verified scanlimit is properly quoted in remediation command")
|
||||
- topology_st.logcap.flush()
|
||||
-
|
||||
- run_healthcheck_and_flush_log(topology_st, standalone, json=True, searched_code=RET_CODE)
|
||||
-
|
||||
- log.info("Re-add the nsIndexIDListScanLimit")
|
||||
- parentid_index = Index(standalone, PARENTID_DN)
|
||||
- parentid_index.add("nsIndexIDListScanLimit", SCANLIMIT_VALUE)
|
||||
-
|
||||
- run_healthcheck_and_flush_log(topology_st, standalone, json=False, searched_code=CMD_OUTPUT)
|
||||
- run_healthcheck_and_flush_log(topology_st, standalone, json=True, searched_code=JSON_OUTPUT)
|
||||
-
|
||||
-
|
||||
-def test_missing_matching_rule_and_scanlimit(topology_st, log_buffering_enabled):
|
||||
- """Check if healthcheck generates a single combined command when both matching rule and scanlimit are missing
|
||||
-
|
||||
- :id: af8214ad-5e4c-422a-8f74-3e99227551df
|
||||
- :setup: Standalone instance
|
||||
- :steps:
|
||||
- 1. Create DS instance
|
||||
- 2. Remove both integerOrderingMatch and nsIndexIDListScanLimit from parentId index
|
||||
- 3. Use healthcheck and verify a single combined command is generated
|
||||
- 4. Re-add the matching rule and scanlimit
|
||||
- 5. Use healthcheck without --json option
|
||||
- 6. Use healthcheck with --json option
|
||||
- :expectedresults:
|
||||
- 1. Success
|
||||
- 2. Success
|
||||
- 3. healthcheck reports DSBLE0007 and generates a single command with both --add-mr and --add-scanlimit
|
||||
- 4. Success
|
||||
- 5. healthcheck reports no issues found
|
||||
- 6. healthcheck reports no issues found
|
||||
- """
|
||||
-
|
||||
- RET_CODE = "DSBLE0007"
|
||||
- PARENTID_DN = "cn=parentid,cn=index,cn=userroot,cn=ldbm database,cn=plugins,cn=config"
|
||||
- SCANLIMIT_VALUE = "limit=5000 type=eq flags=AND"
|
||||
-
|
||||
- standalone = topology_st.standalone
|
||||
-
|
||||
- log.info("Remove both integerOrderingMatch and nsIndexIDListScanLimit from parentId index")
|
||||
- parentid_index = Index(standalone, PARENTID_DN)
|
||||
- parentid_index.remove("nsMatchingRule", "integerOrderingMatch")
|
||||
- parentid_index.remove("nsIndexIDListScanLimit", SCANLIMIT_VALUE)
|
||||
-
|
||||
- # Run healthcheck and verify combined command
|
||||
- args = FakeArgs()
|
||||
- args.instance = standalone.serverid
|
||||
- args.verbose = standalone.verbose
|
||||
- args.list_errors = False
|
||||
- args.list_checks = False
|
||||
- args.exclude_check = []
|
||||
- args.check = ["backends"]
|
||||
- args.dry_run = False
|
||||
- args.json = False
|
||||
- health_check_run(standalone, topology_st.logcap.log, args)
|
||||
-
|
||||
- # Verify DSBLE0007 is reported
|
||||
- assert topology_st.logcap.contains(RET_CODE)
|
||||
- log.info("healthcheck returned code: %s" % RET_CODE)
|
||||
-
|
||||
- # Verify a single combined command is generated with both --add-mr and --add-scanlimit
|
||||
- assert topology_st.logcap.contains('--add-mr integerOrderingMatch --add-scanlimit "limit=5000 type=eq flags=AND"')
|
||||
- log.info("Verified combined command with both --add-mr and --add-scanlimit")
|
||||
-
|
||||
- topology_st.logcap.flush()
|
||||
-
|
||||
- log.info("Re-add the integerOrderingMatch matching rule and scanlimit")
|
||||
- parentid_index = Index(standalone, PARENTID_DN)
|
||||
- parentid_index.add("nsMatchingRule", "integerOrderingMatch")
|
||||
- parentid_index.add("nsIndexIDListScanLimit", SCANLIMIT_VALUE)
|
||||
-
|
||||
- run_healthcheck_and_flush_log(topology_st, standalone, json=False, searched_code=CMD_OUTPUT)
|
||||
- run_healthcheck_and_flush_log(topology_st, standalone, json=True, searched_code=JSON_OUTPUT)
|
||||
-
|
||||
-
|
||||
def test_multiple_missing_indexes(topology_st, log_buffering_enabled):
|
||||
"""Check if healthcheck returns DSBLE0007 code when multiple system indexes are missing
|
||||
|
||||
@@ -574,8 +445,7 @@ def test_multiple_missing_indexes(topology_st, log_buffering_enabled):
|
||||
|
||||
log.info("Re-add the missing system indexes")
|
||||
backend = Backends(standalone).get("userRoot")
|
||||
- backend.add_index("parentid", ["eq"], matching_rules=["integerOrderingMatch"],
|
||||
- idlistscanlimit=['limit=5000 type=eq flags=AND'])
|
||||
+ backend.add_index("parentid", ["eq"], matching_rules=["integerOrderingMatch"])
|
||||
backend.add_index("nsuniqueid", ["eq"])
|
||||
standalone.restart()
|
||||
|
||||
diff --git a/dirsrvtests/tests/suites/paged_results/paged_results_test.py b/dirsrvtests/tests/suites/paged_results/paged_results_test.py
|
||||
index 61d6702da..1bb94b53a 100644
|
||||
--- a/dirsrvtests/tests/suites/paged_results/paged_results_test.py
|
||||
+++ b/dirsrvtests/tests/suites/paged_results/paged_results_test.py
|
||||
@@ -306,19 +306,19 @@ def test_search_success(topology_st, create_user, page_size, users_num):
|
||||
del_users(users_list)
|
||||
|
||||
|
||||
-@pytest.mark.parametrize("page_size,users_num,suffix,attr_name,attr_value,expected_err, restart", [
|
||||
+@pytest.mark.parametrize("page_size,users_num,suffix,attr_name,attr_value,expected_err", [
|
||||
(50, 200, 'cn=config,%s' % DN_LDBM, 'nsslapd-idlistscanlimit', '100',
|
||||
- ldap.UNWILLING_TO_PERFORM, True),
|
||||
+ ldap.UNWILLING_TO_PERFORM),
|
||||
(5, 15, DN_CONFIG, 'nsslapd-timelimit', '20',
|
||||
- ldap.UNAVAILABLE_CRITICAL_EXTENSION, False),
|
||||
+ ldap.UNAVAILABLE_CRITICAL_EXTENSION),
|
||||
(21, 50, DN_CONFIG, 'nsslapd-sizelimit', '20',
|
||||
- ldap.SIZELIMIT_EXCEEDED, False),
|
||||
+ ldap.SIZELIMIT_EXCEEDED),
|
||||
(21, 50, DN_CONFIG, 'nsslapd-pagedsizelimit', '5',
|
||||
- ldap.SIZELIMIT_EXCEEDED, False),
|
||||
+ ldap.SIZELIMIT_EXCEEDED),
|
||||
(5, 50, 'cn=config,%s' % DN_LDBM, 'nsslapd-lookthroughlimit', '20',
|
||||
- ldap.ADMINLIMIT_EXCEEDED, False)])
|
||||
+ ldap.ADMINLIMIT_EXCEEDED)])
|
||||
def test_search_limits_fail(topology_st, create_user, page_size, users_num,
|
||||
- suffix, attr_name, attr_value, expected_err, restart):
|
||||
+ suffix, attr_name, attr_value, expected_err):
|
||||
"""Verify that search with a simple paged results control
|
||||
throws expected exceptoins when corresponding limits are
|
||||
exceeded.
|
||||
@@ -341,15 +341,6 @@ def test_search_limits_fail(topology_st, create_user, page_size, users_num,
|
||||
|
||||
users_list = add_users(topology_st, users_num, DEFAULT_SUFFIX)
|
||||
attr_value_bck = change_conf_attr(topology_st, suffix, attr_name, attr_value)
|
||||
- ancestorid_index = None
|
||||
- if attr_name == 'nsslapd-idlistscanlimit':
|
||||
- backend = Backends(topology_st.standalone).get_backend(DEFAULT_SUFFIX)
|
||||
- ancestorid_index = backend.get_index('ancestorid')
|
||||
- ancestorid_index.replace("nsIndexIDListScanLimit", ensure_bytes("limit=100 type=eq flags=AND"))
|
||||
-
|
||||
- if (restart):
|
||||
- log.info('Instance restarted')
|
||||
- topology_st.standalone.restart()
|
||||
conf_param_dict = {attr_name: attr_value}
|
||||
search_flt = r'(uid=test*)'
|
||||
searchreq_attrlist = ['dn', 'sn']
|
||||
@@ -402,8 +393,6 @@ def test_search_limits_fail(topology_st, create_user, page_size, users_num,
|
||||
else:
|
||||
break
|
||||
finally:
|
||||
- if ancestorid_index:
|
||||
- ancestorid_index.replace("nsIndexIDListScanLimit", ensure_bytes("limit=5000 type=eq flags=AND"))
|
||||
del_users(users_list)
|
||||
change_conf_attr(topology_st, suffix, attr_name, attr_value_bck)
|
||||
|
||||
diff --git a/ldap/servers/slapd/back-ldbm/back-ldbm.h b/ldap/servers/slapd/back-ldbm/back-ldbm.h
|
||||
index b187c26bc..e23e7ff43 100644
|
||||
--- a/ldap/servers/slapd/back-ldbm/back-ldbm.h
|
||||
+++ b/ldap/servers/slapd/back-ldbm/back-ldbm.h
|
||||
@@ -583,7 +583,6 @@ struct ldbminfo
|
||||
int li_mode;
|
||||
int li_lookthroughlimit;
|
||||
int li_allidsthreshold;
|
||||
- int li_system_allidsthreshold;
|
||||
char *li_directory;
|
||||
int li_reslimit_lookthrough_handle;
|
||||
uint64_t li_dbcachesize;
|
||||
diff --git a/ldap/servers/slapd/back-ldbm/index.c b/ldap/servers/slapd/back-ldbm/index.c
|
||||
index 0ab82948c..a5004be19 100644
|
||||
--- a/ldap/servers/slapd/back-ldbm/index.c
|
||||
+++ b/ldap/servers/slapd/back-ldbm/index.c
|
||||
@@ -997,8 +997,6 @@ index_read_ext_allids(
|
||||
}
|
||||
if (pb) {
|
||||
slapi_pblock_get(pb, SLAPI_SEARCH_IS_AND, &is_and);
|
||||
- } else if (strcasecmp(type, LDBM_ANCESTORID_STR) == 0) {
|
||||
- is_and = 1;
|
||||
}
|
||||
ai_flags = is_and ? INDEX_ALLIDS_FLAG_AND : 0;
|
||||
/* the caller can pass in a value of 0 - just ignore those - but if the index
|
||||
diff --git a/ldap/servers/slapd/back-ldbm/instance.c b/ldap/servers/slapd/back-ldbm/instance.c
|
||||
index 2a6e8cbb8..2b71cd4f7 100644
|
||||
--- a/ldap/servers/slapd/back-ldbm/instance.c
|
||||
+++ b/ldap/servers/slapd/back-ldbm/instance.c
|
||||
@@ -16,7 +16,7 @@
|
||||
|
||||
/* Forward declarations */
|
||||
static void ldbm_instance_destructor(void **arg);
|
||||
-Slapi_Entry *ldbm_instance_init_config_entry(char *cn_val, char *v1, char *v2, char *v3, char *v4, char *mr, char *scanlimit);
|
||||
+Slapi_Entry *ldbm_instance_init_config_entry(char *cn_val, char *v1, char *v2, char *v3, char *v4, char *mr);
|
||||
|
||||
|
||||
/* Creates and initializes a new ldbm_instance structure.
|
||||
@@ -126,7 +126,7 @@ done:
|
||||
* Take a bunch of strings, and create a index config entry
|
||||
*/
|
||||
Slapi_Entry *
|
||||
-ldbm_instance_init_config_entry(char *cn_val, char *val1, char *val2, char *val3, char *val4, char *mr, char *scanlimit)
|
||||
+ldbm_instance_init_config_entry(char *cn_val, char *val1, char *val2, char *val3, char *val4, char *mr)
|
||||
{
|
||||
Slapi_Entry *e = slapi_entry_alloc();
|
||||
struct berval *vals[2];
|
||||
@@ -167,11 +167,6 @@ ldbm_instance_init_config_entry(char *cn_val, char *val1, char *val2, char *val3
|
||||
slapi_entry_add_values(e, "nsMatchingRule", vals);
|
||||
}
|
||||
|
||||
- if (scanlimit) {
|
||||
- val.bv_val = scanlimit;
|
||||
- val.bv_len = strlen(scanlimit);
|
||||
- slapi_entry_add_values(e, "nsIndexIDListScanLimit", vals);
|
||||
- }
|
||||
return e;
|
||||
}
|
||||
|
||||
@@ -184,60 +179,8 @@ ldbm_instance_create_default_indexes(backend *be)
|
||||
{
|
||||
Slapi_Entry *e;
|
||||
ldbm_instance *inst = (ldbm_instance *)be->be_instance_info;
|
||||
- struct ldbminfo *li = (struct ldbminfo *)be->be_database->plg_private;
|
||||
/* write the dse file only on the final index */
|
||||
int flags = LDBM_INSTANCE_CONFIG_DONT_WRITE;
|
||||
- char *ancestorid_indexes_limit = NULL;
|
||||
- char *parentid_indexes_limit = NULL;
|
||||
- struct attrinfo *ai = NULL;
|
||||
- int index_already_configured = 0;
|
||||
- struct index_idlistsizeinfo *iter;
|
||||
- int cookie;
|
||||
- int limit;
|
||||
-
|
||||
- ainfo_get(be, (char *)LDBM_ANCESTORID_STR, &ai);
|
||||
- if (ai && ai->ai_idlistinfo) {
|
||||
- iter = (struct index_idlistsizeinfo *)dl_get_first(ai->ai_idlistinfo, &cookie);
|
||||
- if (iter) {
|
||||
- limit = iter->ai_idlistsizelimit;
|
||||
- slapi_log_err(SLAPI_LOG_BACKLDBM, "ldbm_instance_create_default_indexes",
|
||||
- "set ancestorid limit to %d from attribute index\n",
|
||||
- limit);
|
||||
- } else {
|
||||
- limit = li->li_system_allidsthreshold;
|
||||
- slapi_log_err(SLAPI_LOG_BACKLDBM, "ldbm_instance_create_default_indexes",
|
||||
- "set ancestorid limit to %d from default (fail to read limit)\n",
|
||||
- limit);
|
||||
- }
|
||||
- ancestorid_indexes_limit = slapi_ch_smprintf("limit=%d type=eq flags=AND", limit);
|
||||
- } else {
|
||||
- ancestorid_indexes_limit = slapi_ch_smprintf("limit=%d type=eq flags=AND", li->li_system_allidsthreshold);
|
||||
- slapi_log_err(SLAPI_LOG_BACKLDBM, "ldbm_instance_create_default_indexes",
|
||||
- "set ancestorid limit to %d from default (no attribute or limit)\n",
|
||||
- li->li_system_allidsthreshold);
|
||||
- }
|
||||
-
|
||||
- ainfo_get(be, (char *)LDBM_PARENTID_STR, &ai);
|
||||
- if (ai && ai->ai_idlistinfo) {
|
||||
- iter = (struct index_idlistsizeinfo *)dl_get_first(ai->ai_idlistinfo, &cookie);
|
||||
- if (iter) {
|
||||
- limit = iter->ai_idlistsizelimit;
|
||||
- slapi_log_err(SLAPI_LOG_BACKLDBM, "ldbm_instance_create_default_indexes",
|
||||
- "set parentid limit to %d from attribute index\n",
|
||||
- limit);
|
||||
- } else {
|
||||
- limit = li->li_system_allidsthreshold;
|
||||
- slapi_log_err(SLAPI_LOG_BACKLDBM, "ldbm_instance_create_default_indexes",
|
||||
- "set parentid limit to %d from default (fail to read limit)\n",
|
||||
- limit);
|
||||
- }
|
||||
- parentid_indexes_limit = slapi_ch_smprintf("limit=%d type=eq flags=AND", limit);
|
||||
- } else {
|
||||
- parentid_indexes_limit = slapi_ch_smprintf("limit=%d type=eq flags=AND", li->li_system_allidsthreshold);
|
||||
- slapi_log_err(SLAPI_LOG_BACKLDBM, "ldbm_instance_create_default_indexes",
|
||||
- "set parentid limit to %d from default (no attribute or limit)\n",
|
||||
- li->li_system_allidsthreshold);
|
||||
- }
|
||||
|
||||
/*
|
||||
* Always index (entrydn or entryrdn), parentid, objectclass,
|
||||
@@ -245,48 +188,42 @@ ldbm_instance_create_default_indexes(backend *be)
|
||||
* since they are used by some searches, replication and the
|
||||
* ACL routines.
|
||||
*/
|
||||
- e = ldbm_instance_init_config_entry(LDBM_ENTRYRDN_STR, "subtree", 0, 0, 0, 0, 0);
|
||||
+ e = ldbm_instance_init_config_entry(LDBM_ENTRYRDN_STR, "subtree", 0, 0, 0, 0);
|
||||
ldbm_instance_config_add_index_entry(inst, e, flags);
|
||||
slapi_entry_free(e);
|
||||
|
||||
- ainfo_get(be, (char *)LDBM_PARENTID_STR, &ai);
|
||||
- /* Check if the attrinfo is actually for parentid, not a fallback to .default */
|
||||
- index_already_configured = (ai != NULL && strcmp(ai->ai_type, LDBM_PARENTID_STR) == 0);
|
||||
- if (!index_already_configured) {
|
||||
- e = ldbm_instance_init_config_entry(LDBM_PARENTID_STR, "eq", 0, 0, 0, "integerOrderingMatch", parentid_indexes_limit);
|
||||
- ldbm_instance_config_add_index_entry(inst, e, flags);
|
||||
- attr_index_config(be, "ldbm index init", 0, e, 1, 0, NULL);
|
||||
- slapi_entry_free(e);
|
||||
- }
|
||||
+ e = ldbm_instance_init_config_entry(LDBM_PARENTID_STR, "eq", 0, 0, 0, "integerOrderingMatch");
|
||||
+ ldbm_instance_config_add_index_entry(inst, e, flags);
|
||||
+ slapi_entry_free(e);
|
||||
|
||||
- e = ldbm_instance_init_config_entry("objectclass", "eq", 0, 0, 0, 0, 0);
|
||||
+ e = ldbm_instance_init_config_entry("objectclass", "eq", 0, 0, 0, 0);
|
||||
ldbm_instance_config_add_index_entry(inst, e, flags);
|
||||
slapi_entry_free(e);
|
||||
|
||||
- e = ldbm_instance_init_config_entry("aci", "pres", 0, 0, 0, 0, 0);
|
||||
+ e = ldbm_instance_init_config_entry("aci", "pres", 0, 0, 0, 0);
|
||||
ldbm_instance_config_add_index_entry(inst, e, flags);
|
||||
slapi_entry_free(e);
|
||||
|
||||
- e = ldbm_instance_init_config_entry(LDBM_NUMSUBORDINATES_STR, "pres", 0, 0, 0, 0, 0);
|
||||
+ e = ldbm_instance_init_config_entry(LDBM_NUMSUBORDINATES_STR, "pres", 0, 0, 0, 0);
|
||||
ldbm_instance_config_add_index_entry(inst, e, flags);
|
||||
slapi_entry_free(e);
|
||||
|
||||
- e = ldbm_instance_init_config_entry(SLAPI_ATTR_UNIQUEID, "eq", 0, 0, 0, 0, 0);
|
||||
+ e = ldbm_instance_init_config_entry(SLAPI_ATTR_UNIQUEID, "eq", 0, 0, 0, 0);
|
||||
ldbm_instance_config_add_index_entry(inst, e, flags);
|
||||
slapi_entry_free(e);
|
||||
|
||||
/* For MMR, we need this attribute (to replace use of dncomp in delete). */
|
||||
- e = ldbm_instance_init_config_entry(ATTR_NSDS5_REPLCONFLICT, "eq", "pres", 0, 0, 0, 0);
|
||||
+ e = ldbm_instance_init_config_entry(ATTR_NSDS5_REPLCONFLICT, "eq", "pres", 0, 0, 0);
|
||||
ldbm_instance_config_add_index_entry(inst, e, flags);
|
||||
slapi_entry_free(e);
|
||||
|
||||
/* write the dse file only on the final index */
|
||||
- e = ldbm_instance_init_config_entry(SLAPI_ATTR_NSCP_ENTRYDN, "eq", 0, 0, 0, 0, 0);
|
||||
+ e = ldbm_instance_init_config_entry(SLAPI_ATTR_NSCP_ENTRYDN, "eq", 0, 0, 0, 0);
|
||||
ldbm_instance_config_add_index_entry(inst, e, flags);
|
||||
slapi_entry_free(e);
|
||||
|
||||
/* ldbm_instance_config_add_index_entry(inst, 2, argv); */
|
||||
- e = ldbm_instance_init_config_entry(LDBM_PSEUDO_ATTR_DEFAULT, "none", 0, 0, 0, 0, 0);
|
||||
+ e = ldbm_instance_init_config_entry(LDBM_PSEUDO_ATTR_DEFAULT, "none", 0, 0, 0, 0);
|
||||
attr_index_config(be, "ldbm index init", 0, e, 1, 0, NULL);
|
||||
slapi_entry_free(e);
|
||||
|
||||
@@ -294,18 +231,9 @@ ldbm_instance_create_default_indexes(backend *be)
|
||||
* ancestorid is special, there is actually no such attr type
|
||||
* but we still want to use the attr index file APIs.
|
||||
*/
|
||||
- ainfo_get(be, (char *)LDBM_ANCESTORID_STR, &ai);
|
||||
- /* Check if the attrinfo is actually for ancestorid, not a fallback to .default */
|
||||
- index_already_configured = (ai != NULL && strcmp(ai->ai_type, LDBM_ANCESTORID_STR) == 0);
|
||||
- if (!index_already_configured) {
|
||||
- e = ldbm_instance_init_config_entry(LDBM_ANCESTORID_STR, "eq", 0, 0, 0, "integerOrderingMatch", ancestorid_indexes_limit);
|
||||
- ldbm_instance_config_add_index_entry(inst, e, flags);
|
||||
- attr_index_config(be, "ldbm index init", 0, e, 1, 0, NULL);
|
||||
- slapi_entry_free(e);
|
||||
- }
|
||||
-
|
||||
- slapi_ch_free_string(&ancestorid_indexes_limit);
|
||||
- slapi_ch_free_string(&parentid_indexes_limit);
|
||||
+ e = ldbm_instance_init_config_entry(LDBM_ANCESTORID_STR, "eq", 0, 0, 0, "integerOrderingMatch");
|
||||
+ attr_index_config(be, "ldbm index init", 0, e, 1, 0, NULL);
|
||||
+ slapi_entry_free(e);
|
||||
|
||||
return 0;
|
||||
}
|
||||
diff --git a/ldap/servers/slapd/back-ldbm/ldbm_config.c b/ldap/servers/slapd/back-ldbm/ldbm_config.c
|
||||
index c24e3d766..6a2ce4c27 100644
|
||||
--- a/ldap/servers/slapd/back-ldbm/ldbm_config.c
|
||||
+++ b/ldap/servers/slapd/back-ldbm/ldbm_config.c
|
||||
@@ -385,35 +385,6 @@ ldbm_config_allidsthreshold_set(void *arg, void *value, char *errorbuf __attribu
|
||||
return retval;
|
||||
}
|
||||
|
||||
-static void *
|
||||
-ldbm_config_system_allidsthreshold_get(void *arg)
|
||||
-{
|
||||
- struct ldbminfo *li = (struct ldbminfo *)arg;
|
||||
-
|
||||
- return (void *)((uintptr_t)(li->li_system_allidsthreshold));
|
||||
-}
|
||||
-
|
||||
-static int
|
||||
-ldbm_config_system_allidsthreshold_set(void *arg, void *value, char *errorbuf __attribute__((unused)), int phase __attribute__((unused)), int apply)
|
||||
-{
|
||||
- struct ldbminfo *li = (struct ldbminfo *)arg;
|
||||
- int retval = LDAP_SUCCESS;
|
||||
- int val = (int)((uintptr_t)value);
|
||||
-
|
||||
- /* Do whatever we can to make sure the data is ok. */
|
||||
-
|
||||
- /* Catch attempts to configure a stupidly low ancestorid allidsthreshold */
|
||||
- if ((val > -1) && (val < 5000)) {
|
||||
- val = 5000;
|
||||
- }
|
||||
-
|
||||
- if (apply) {
|
||||
- li->li_system_allidsthreshold = val;
|
||||
- }
|
||||
-
|
||||
- return retval;
|
||||
-}
|
||||
-
|
||||
static void *
|
||||
ldbm_config_pagedallidsthreshold_get(void *arg)
|
||||
{
|
||||
@@ -1094,7 +1065,6 @@ static config_info ldbm_config[] = {
|
||||
{CONFIG_LOOKTHROUGHLIMIT, CONFIG_TYPE_INT, "5000", &ldbm_config_lookthroughlimit_get, &ldbm_config_lookthroughlimit_set, CONFIG_FLAG_ALWAYS_SHOW | CONFIG_FLAG_ALLOW_RUNNING_CHANGE},
|
||||
{CONFIG_MODE, CONFIG_TYPE_INT_OCTAL, "0600", &ldbm_config_mode_get, &ldbm_config_mode_set, CONFIG_FLAG_ALWAYS_SHOW | CONFIG_FLAG_ALLOW_RUNNING_CHANGE},
|
||||
{CONFIG_IDLISTSCANLIMIT, CONFIG_TYPE_INT, "2147483646", &ldbm_config_allidsthreshold_get, &ldbm_config_allidsthreshold_set, CONFIG_FLAG_ALWAYS_SHOW | CONFIG_FLAG_ALLOW_RUNNING_CHANGE},
|
||||
- {CONFIG_SYSTEMIDLISTSCANLIMIT, CONFIG_TYPE_INT, "5000", &ldbm_config_system_allidsthreshold_get, &ldbm_config_system_allidsthreshold_set, CONFIG_FLAG_ALWAYS_SHOW | CONFIG_FLAG_ALLOW_RUNNING_CHANGE},
|
||||
{CONFIG_DIRECTORY, CONFIG_TYPE_STRING, "", &ldbm_config_directory_get, &ldbm_config_directory_set, CONFIG_FLAG_ALWAYS_SHOW | CONFIG_FLAG_ALLOW_RUNNING_CHANGE | CONFIG_FLAG_SKIP_DEFAULT_SETTING},
|
||||
{CONFIG_MAXPASSBEFOREMERGE, CONFIG_TYPE_INT, "100", &ldbm_config_maxpassbeforemerge_get, &ldbm_config_maxpassbeforemerge_set, 0},
|
||||
|
||||
diff --git a/ldap/servers/slapd/back-ldbm/ldbm_config.h b/ldap/servers/slapd/back-ldbm/ldbm_config.h
|
||||
index 29a3426ab..e69bfeedf 100644
|
||||
--- a/ldap/servers/slapd/back-ldbm/ldbm_config.h
|
||||
+++ b/ldap/servers/slapd/back-ldbm/ldbm_config.h
|
||||
@@ -60,7 +60,6 @@ struct config_info
|
||||
#define CONFIG_RANGELOOKTHROUGHLIMIT "nsslapd-rangelookthroughlimit"
|
||||
#define CONFIG_PAGEDLOOKTHROUGHLIMIT "nsslapd-pagedlookthroughlimit"
|
||||
#define CONFIG_IDLISTSCANLIMIT "nsslapd-idlistscanlimit"
|
||||
-#define CONFIG_SYSTEMIDLISTSCANLIMIT "nsslapd-systemidlistscanlimit"
|
||||
#define CONFIG_PAGEDIDLISTSCANLIMIT "nsslapd-pagedidlistscanlimit"
|
||||
#define CONFIG_DIRECTORY "nsslapd-directory"
|
||||
#define CONFIG_MODE "nsslapd-mode"
|
||||
diff --git a/ldap/servers/slapd/back-ldbm/ldbm_index_config.c b/ldap/servers/slapd/back-ldbm/ldbm_index_config.c
|
||||
index bae2a64b9..38e7368e1 100644
|
||||
--- a/ldap/servers/slapd/back-ldbm/ldbm_index_config.c
|
||||
+++ b/ldap/servers/slapd/back-ldbm/ldbm_index_config.c
|
||||
@@ -384,14 +384,6 @@ ldbm_instance_config_add_index_entry(
|
||||
}
|
||||
}
|
||||
|
||||
- /* get nsIndexIDListScanLimit and its values, and add them */
|
||||
- if (0 == slapi_entry_attr_find(e, "nsIndexIDListScanLimit", &attr)) {
|
||||
- for (j = slapi_attr_first_value(attr, &sval); j != -1; j = slapi_attr_next_value(attr, j, &sval)) {
|
||||
- attrValue = slapi_value_get_berval(sval);
|
||||
- eBuf = PR_sprintf_append(eBuf, "nsIndexIDListScanLimit: %s\n", attrValue->bv_val);
|
||||
- }
|
||||
- }
|
||||
-
|
||||
ldbm_config_add_dse_entry(li, eBuf, flags);
|
||||
if (eBuf) {
|
||||
PR_smprintf_free(eBuf);
|
||||
diff --git a/src/lib389/lib389/backend.py b/src/lib389/lib389/backend.py
|
||||
index 274d45abe..f3dbe7c92 100644
|
||||
--- a/src/lib389/lib389/backend.py
|
||||
+++ b/src/lib389/lib389/backend.py
|
||||
@@ -645,10 +645,11 @@ class Backend(DSLdapObject):
|
||||
indexes = self.get_indexes()
|
||||
|
||||
# Default system indexes taken from ldap/servers/slapd/back-ldbm/instance.c
|
||||
+ # Note: entryrdn and ancestorid are internal system indexes that are not
|
||||
+ # exposed in cn=config - they are managed internally by the server.
|
||||
+ # Only parentid has a DSE config entry (for the integerOrderingMatch rule).
|
||||
expected_system_indexes = {
|
||||
- 'entryrdn': {'types': ['subtree'], 'matching_rule': None},
|
||||
- 'parentid': {'types': ['eq'], 'matching_rule': 'integerOrderingMatch', 'scanlimit': 'limit=5000 type=eq flags=AND'},
|
||||
- 'ancestorid': {'types': ['eq'], 'matching_rule': 'integerOrderingMatch', 'scanlimit': 'limit=5000 type=eq flags=AND'},
|
||||
+ 'parentid': {'types': ['eq'], 'matching_rule': 'integerOrderingMatch'},
|
||||
'objectClass': {'types': ['eq'], 'matching_rule': None},
|
||||
'aci': {'types': ['pres'], 'matching_rule': None},
|
||||
'nscpEntryDN': {'types': ['eq'], 'matching_rule': None},
|
||||
@@ -705,17 +706,14 @@ class Backend(DSLdapObject):
|
||||
# Generate remediation command
|
||||
index_types = ' '.join([f"--index-type {t}" for t in expected_config['types']])
|
||||
cmd = f"dsconf YOUR_INSTANCE backend index add {bename} --attr {attr_name} {index_types}"
|
||||
- if expected_config.get('matching_rule'):
|
||||
+ if expected_config['matching_rule']:
|
||||
cmd += f" --matching-rule {expected_config['matching_rule']}"
|
||||
- if expected_config.get('scanlimit'):
|
||||
- cmd += f" --add-scanlimit \"{expected_config['scanlimit']}\""
|
||||
remediation_commands.append(cmd)
|
||||
reindex_attrs.add(attr_name) # New index needs reindexing
|
||||
else:
|
||||
# Index exists, check configuration
|
||||
actual_types = index.get_attr_vals_utf8('nsIndexType') or []
|
||||
actual_mrs = index.get_attr_vals_utf8('nsMatchingRule') or []
|
||||
- actual_scanlimit = index.get_attr_vals_utf8('nsIndexIDListScanLimit') or []
|
||||
|
||||
# Normalize to lowercase for comparison
|
||||
actual_types = [t.lower() for t in actual_types]
|
||||
@@ -730,31 +728,16 @@ class Backend(DSLdapObject):
|
||||
remediation_commands.append(cmd)
|
||||
reindex_attrs.add(attr_name)
|
||||
|
||||
- # Check matching rules and scanlimit together to generate a single combined command
|
||||
+ # Check matching rules
|
||||
expected_mr = expected_config.get('matching_rule')
|
||||
- expected_scanlimit = expected_config.get('scanlimit')
|
||||
-
|
||||
- missing_mr = False
|
||||
if expected_mr:
|
||||
actual_mrs_lower = [mr.lower() for mr in actual_mrs]
|
||||
if expected_mr.lower() not in actual_mrs_lower:
|
||||
discrepancies.append(f"Index {attr_name} missing matching rule: {expected_mr}")
|
||||
- missing_mr = True
|
||||
-
|
||||
- missing_scanlimit = False
|
||||
- if expected_scanlimit and (len(actual_scanlimit) == 0):
|
||||
- discrepancies.append(f"Index {attr_name} missing fine grain definition of IDs limit: {expected_scanlimit}")
|
||||
- missing_scanlimit = True
|
||||
-
|
||||
- # Generate a single combined command for all missing items
|
||||
- if missing_mr or missing_scanlimit:
|
||||
- cmd = f"dsconf YOUR_INSTANCE backend index set {bename} --attr {attr_name}"
|
||||
- if missing_mr:
|
||||
- cmd += f" --add-mr {expected_mr}"
|
||||
- if missing_scanlimit:
|
||||
- cmd += f" --add-scanlimit \"{expected_scanlimit}\""
|
||||
- remediation_commands.append(cmd)
|
||||
- reindex_attrs.add(attr_name)
|
||||
+ # Add the missing matching rule
|
||||
+ cmd = f"dsconf YOUR_INSTANCE backend index set {bename} --attr {attr_name} --add-mr {expected_mr}"
|
||||
+ remediation_commands.append(cmd)
|
||||
+ reindex_attrs.add(attr_name)
|
||||
|
||||
except Exception as e:
|
||||
self._log.debug(f"_lint_system_indexes - Error checking index {attr_name}: {e}")
|
||||
@@ -993,13 +976,12 @@ class Backend(DSLdapObject):
|
||||
return
|
||||
raise ValueError("Can not delete index because it does not exist")
|
||||
|
||||
- def add_index(self, attr_name, types, matching_rules=None, idlistscanlimit=None, reindex=False):
|
||||
+ def add_index(self, attr_name, types, matching_rules=None, reindex=False):
|
||||
""" Add an index.
|
||||
|
||||
:param attr_name - name of the attribute to index
|
||||
:param types - a List of index types(eq, pres, sub, approx)
|
||||
:param matching_rules - a List of matching rules for the index
|
||||
- :param idlistscanlimit - a List of fine grain definitions for scanning limit
|
||||
:param reindex - If set to True then index the attribute after creating it.
|
||||
"""
|
||||
|
||||
@@ -1029,15 +1011,6 @@ class Backend(DSLdapObject):
|
||||
# Only add if there are actually rules present in the list.
|
||||
if len(mrs) > 0:
|
||||
props['nsMatchingRule'] = mrs
|
||||
-
|
||||
- if idlistscanlimit is not None:
|
||||
- scanlimits = []
|
||||
- for scanlimit in idlistscanlimit:
|
||||
- scanlimits.append(scanlimit)
|
||||
- # Only add if there are actually limits in the list.
|
||||
- if len(scanlimits) > 0:
|
||||
- props['nsIndexIDListScanLimit'] = scanlimits
|
||||
-
|
||||
new_index.create(properties=props, basedn="cn=index," + self._dn)
|
||||
|
||||
if reindex:
|
||||
@@ -1349,7 +1322,6 @@ class DatabaseConfig(DSLdapObject):
|
||||
'nsslapd-lookthroughlimit',
|
||||
'nsslapd-mode',
|
||||
'nsslapd-idlistscanlimit',
|
||||
- 'nsslapd-systemidlistscanlimit',
|
||||
'nsslapd-directory',
|
||||
'nsslapd-import-cachesize',
|
||||
'nsslapd-idl-switch',
|
||||
diff --git a/src/lib389/lib389/cli_conf/backend.py b/src/lib389/lib389/cli_conf/backend.py
|
||||
index 9772e39d4..68efa795c 100644
|
||||
--- a/src/lib389/lib389/cli_conf/backend.py
|
||||
+++ b/src/lib389/lib389/cli_conf/backend.py
|
||||
@@ -39,7 +39,6 @@ arg_to_attr = {
|
||||
'mode': 'nsslapd-mode',
|
||||
'state': 'nsslapd-state',
|
||||
'idlistscanlimit': 'nsslapd-idlistscanlimit',
|
||||
- 'systemidlistscanlimit': 'nsslapd-systemidlistscanlimit',
|
||||
'directory': 'nsslapd-directory',
|
||||
'dbcachesize': 'nsslapd-dbcachesize',
|
||||
'logdirectory': 'nsslapd-db-logdirectory',
|
||||
@@ -626,21 +625,6 @@ def backend_set_index(inst, basedn, log, args):
|
||||
except ldap.NO_SUCH_ATTRIBUTE:
|
||||
raise ValueError('Can not delete matching rule type because it does not exist')
|
||||
|
||||
- if args.replace_scanlimit is not None:
|
||||
- for replace_scanlimit in args.replace_scanlimit:
|
||||
- index.replace('nsIndexIDListScanLimit', replace_scanlimit)
|
||||
-
|
||||
- if args.add_scanlimit is not None:
|
||||
- for add_scanlimit in args.add_scanlimit:
|
||||
- index.add('nsIndexIDListScanLimit', add_scanlimit)
|
||||
-
|
||||
- if args.del_scanlimit is not None:
|
||||
- for del_scanlimit in args.del_scanlimit:
|
||||
- try:
|
||||
- index.remove('nsIndexIDListScanLimit', del_scanlimit)
|
||||
- except ldap.NO_SUCH_ATTRIBUTE:
|
||||
- raise ValueError('Can not delete a fine grain limit definition because it does not exist')
|
||||
-
|
||||
if args.reindex:
|
||||
be.reindex(attrs=[args.attr])
|
||||
log.info("Index successfully updated")
|
||||
@@ -963,9 +947,6 @@ def create_parser(subparsers):
|
||||
edit_index_parser.add_argument('--del-type', action='append', help='Removes an index type from the index: (eq, sub, pres, or approx)')
|
||||
edit_index_parser.add_argument('--add-mr', action='append', help='Adds a matching-rule to the index')
|
||||
edit_index_parser.add_argument('--del-mr', action='append', help='Removes a matching-rule from the index')
|
||||
- edit_index_parser.add_argument('--add-scanlimit', action='append', help='Adds a fine grain limit definiton to the index')
|
||||
- edit_index_parser.add_argument('--replace-scanlimit', action='append', help='Replaces a fine grain limit definiton to the index')
|
||||
- edit_index_parser.add_argument('--del-scanlimit', action='append', help='Removes a fine grain limit definiton to the index')
|
||||
edit_index_parser.add_argument('--reindex', action='store_true', help='Re-indexes the database after editing the index')
|
||||
edit_index_parser.add_argument('be_name', help='The backend name or suffix')
|
||||
|
||||
@@ -1092,7 +1073,6 @@ def create_parser(subparsers):
|
||||
'will check when examining candidate entries in response to a search request')
|
||||
set_db_config_parser.add_argument('--mode', help='Specifies the permissions used for newly created index files')
|
||||
set_db_config_parser.add_argument('--idlistscanlimit', help='Specifies the number of entry IDs that are searched during a search operation')
|
||||
- set_db_config_parser.add_argument('--systemidlistscanlimit', help='Specifies the number of entry IDs that are fetch from ancestorid/parentid indexes')
|
||||
set_db_config_parser.add_argument('--directory', help='Specifies absolute path to database instance')
|
||||
set_db_config_parser.add_argument('--dbcachesize', help='Specifies the database index cache size in bytes')
|
||||
set_db_config_parser.add_argument('--logdirectory', help='Specifies the path to the directory that contains the database transaction logs')
|
||||
--
|
||||
2.52.0
|
||||
|
||||
212
0033-Issue-7223-Add-upgrade-function-to-remove-nsIndexIDL.patch
Normal file
212
0033-Issue-7223-Add-upgrade-function-to-remove-nsIndexIDL.patch
Normal file
@ -0,0 +1,212 @@
|
||||
From 4c44e4c522afc0f5401754f29a47645889e21aca Mon Sep 17 00:00:00 2001
|
||||
From: Viktor Ashirov <vashirov@redhat.com>
|
||||
Date: Thu, 5 Feb 2026 12:17:06 +0100
|
||||
Subject: [PATCH] Issue 7223 - Add upgrade function to remove
|
||||
nsIndexIDListScanLimit from parentid
|
||||
|
||||
Description:
|
||||
Add `upgrade_remove_index_scanlimit()` function that removes the
|
||||
nsIndexIDListScanLimit attribute from parentid index configuration
|
||||
if present.
|
||||
|
||||
This attribute was incorrectly added by a previous version and can
|
||||
cause issues with index configuration. The upgrade function runs
|
||||
automatically on server startup and removes the attribute if found.
|
||||
|
||||
Relates: https://github.com/389ds/389-ds-base/issues/7223
|
||||
|
||||
Reviewed by: @progier389, @tbordaz, @droideck (Thanks!)
|
||||
---
|
||||
.../healthcheck/health_system_indexes_test.py | 52 +++++++++
|
||||
ldap/servers/slapd/upgrade.c | 105 ++++++++++++++++++
|
||||
2 files changed, 157 insertions(+)
|
||||
|
||||
diff --git a/dirsrvtests/tests/suites/healthcheck/health_system_indexes_test.py b/dirsrvtests/tests/suites/healthcheck/health_system_indexes_test.py
|
||||
index 140845a33..aea88e0e2 100644
|
||||
--- a/dirsrvtests/tests/suites/healthcheck/health_system_indexes_test.py
|
||||
+++ b/dirsrvtests/tests/suites/healthcheck/health_system_indexes_test.py
|
||||
@@ -453,6 +453,58 @@ def test_multiple_missing_indexes(topology_st, log_buffering_enabled):
|
||||
run_healthcheck_and_flush_log(topology_st, standalone, json=True, searched_code=JSON_OUTPUT)
|
||||
|
||||
|
||||
+def test_upgrade_removes_parentid_scanlimit(topology_st):
|
||||
+ """Check if upgrade function removes nsIndexIDListScanLimit from parentid index
|
||||
+
|
||||
+ :id: 2808886e-c1c1-441d-b3a3-299c4ef1ab4a
|
||||
+ :setup: Standalone instance
|
||||
+ :steps:
|
||||
+ 1. Create DS instance
|
||||
+ 2. Stop the server
|
||||
+ 3. Use DSEldif to add nsIndexIDListScanLimit to parentid index
|
||||
+ 4. Start the server (triggers upgrade)
|
||||
+ 5. Verify nsIndexIDListScanLimit is removed from parentid index
|
||||
+ :expectedresults:
|
||||
+ 1. Success
|
||||
+ 2. Success
|
||||
+ 3. Success
|
||||
+ 4. Success
|
||||
+ 5. nsIndexIDListScanLimit is no longer present
|
||||
+ """
|
||||
+ from lib389.dseldif import DSEldif
|
||||
+
|
||||
+ standalone = topology_st.standalone
|
||||
+ PARENTID_DN = "cn=parentid,cn=index,cn=userroot,cn=ldbm database,cn=plugins,cn=config"
|
||||
+ SCANLIMIT_VALUE = "limit=5000 type=eq flags=AND"
|
||||
+
|
||||
+ log.info("Stop the server")
|
||||
+ standalone.stop()
|
||||
+
|
||||
+ log.info("Add nsIndexIDListScanLimit to parentid index using DSEldif")
|
||||
+ dse_ldif = DSEldif(standalone)
|
||||
+ dse_ldif.add(PARENTID_DN, "nsIndexIDListScanLimit", SCANLIMIT_VALUE)
|
||||
+
|
||||
+ # Verify it was added
|
||||
+ scanlimit = dse_ldif.get(PARENTID_DN, "nsIndexIDListScanLimit")
|
||||
+ assert scanlimit is not None, "Failed to add nsIndexIDListScanLimit"
|
||||
+ log.info(f"Added nsIndexIDListScanLimit: {scanlimit}")
|
||||
+
|
||||
+ log.info("Start the server (triggers upgrade)")
|
||||
+ standalone.start()
|
||||
+
|
||||
+ log.info("Verify nsIndexIDListScanLimit was removed by upgrade")
|
||||
+ # Check via LDAP - the upgrade should have removed it
|
||||
+ parentid_index = Index(standalone, PARENTID_DN)
|
||||
+ scanlimit_after = parentid_index.get_attr_vals_utf8("nsIndexIDListScanLimit")
|
||||
+ log.info(f"nsIndexIDListScanLimit after upgrade: {scanlimit_after}")
|
||||
+
|
||||
+ # The upgrade function should have removed nsIndexIDListScanLimit
|
||||
+ assert not scanlimit_after, \
|
||||
+ f"nsIndexIDListScanLimit should have been removed but found: {scanlimit_after}"
|
||||
+
|
||||
+ log.info("Upgrade successfully removed nsIndexIDListScanLimit from parentid index")
|
||||
+
|
||||
+
|
||||
if __name__ == "__main__":
|
||||
# Run isolated
|
||||
# -s for DEBUG mode
|
||||
diff --git a/ldap/servers/slapd/upgrade.c b/ldap/servers/slapd/upgrade.c
|
||||
index b02e37ed6..dcd16940b 100644
|
||||
--- a/ldap/servers/slapd/upgrade.c
|
||||
+++ b/ldap/servers/slapd/upgrade.c
|
||||
@@ -330,6 +330,107 @@ upgrade_remove_subtree_rename(void)
|
||||
return UPGRADE_SUCCESS;
|
||||
}
|
||||
|
||||
+/*
|
||||
+ * Remove nsIndexIDListScanLimit from parentid index configuration.
|
||||
+ *
|
||||
+ * This attribute was incorrectly added by a previous version and can
|
||||
+ * cause issues with index configuration. Remove it if present.
|
||||
+ */
|
||||
+static upgrade_status
|
||||
+upgrade_remove_index_scanlimit(void)
|
||||
+{
|
||||
+ struct slapi_pblock *pb = slapi_pblock_new();
|
||||
+ Slapi_Entry **backends = NULL;
|
||||
+ const char *be_base_dn = "cn=ldbm database,cn=plugins,cn=config";
|
||||
+ const char *be_filter = "(objectclass=nsBackendInstance)";
|
||||
+ const char *attrs_to_check[] = {"parentid", NULL};
|
||||
+ upgrade_status uresult = UPGRADE_SUCCESS;
|
||||
+
|
||||
+ /* Search for all backend instances */
|
||||
+ slapi_search_internal_set_pb(
|
||||
+ pb, be_base_dn,
|
||||
+ LDAP_SCOPE_ONELEVEL,
|
||||
+ be_filter, NULL, 0, NULL, NULL,
|
||||
+ plugin_get_default_component_id(), 0);
|
||||
+ slapi_search_internal_pb(pb);
|
||||
+ slapi_pblock_get(pb, SLAPI_PLUGIN_INTOP_SEARCH_ENTRIES, &backends);
|
||||
+
|
||||
+ if (backends) {
|
||||
+ for (size_t be_idx = 0; backends[be_idx] != NULL; be_idx++) {
|
||||
+ const char *be_dn = slapi_entry_get_dn_const(backends[be_idx]);
|
||||
+ const char *be_name = slapi_entry_attr_get_ref(backends[be_idx], "cn");
|
||||
+ if (!be_dn || !be_name) {
|
||||
+ continue;
|
||||
+ }
|
||||
+
|
||||
+ for (size_t attr_idx = 0; attrs_to_check[attr_idx] != NULL; attr_idx++) {
|
||||
+ const char *attr_name = attrs_to_check[attr_idx];
|
||||
+ struct slapi_pblock *idx_pb = slapi_pblock_new();
|
||||
+ Slapi_Entry **idx_entries = NULL;
|
||||
+ char *idx_dn = slapi_create_dn_string("cn=%s,cn=index,%s",
|
||||
+ attr_name, be_dn);
|
||||
+ char *idx_filter = "(objectclass=nsIndex)";
|
||||
+
|
||||
+ if (!idx_dn) {
|
||||
+ slapi_pblock_destroy(idx_pb);
|
||||
+ continue;
|
||||
+ }
|
||||
+
|
||||
+ slapi_search_internal_set_pb(
|
||||
+ idx_pb, idx_dn,
|
||||
+ LDAP_SCOPE_BASE,
|
||||
+ idx_filter, NULL, 0, NULL, NULL,
|
||||
+ plugin_get_default_component_id(), 0);
|
||||
+ slapi_search_internal_pb(idx_pb);
|
||||
+ slapi_pblock_get(idx_pb, SLAPI_PLUGIN_INTOP_SEARCH_ENTRIES, &idx_entries);
|
||||
+
|
||||
+ if (idx_entries && idx_entries[0]) {
|
||||
+ /* Check if nsIndexIDListScanLimit is present */
|
||||
+ if (slapi_entry_attr_get_ref(idx_entries[0], "nsIndexIDListScanLimit") != NULL) {
|
||||
+ /* Remove nsIndexIDListScanLimit */
|
||||
+ Slapi_PBlock *mod_pb = slapi_pblock_new();
|
||||
+ Slapi_Mods smods;
|
||||
+ int rc;
|
||||
+
|
||||
+ slapi_mods_init(&smods, 1);
|
||||
+ slapi_mods_add(&smods, LDAP_MOD_DELETE, "nsIndexIDListScanLimit", 0, NULL);
|
||||
+
|
||||
+ slapi_modify_internal_set_pb(
|
||||
+ mod_pb, idx_dn,
|
||||
+ slapi_mods_get_ldapmods_byref(&smods),
|
||||
+ NULL, NULL,
|
||||
+ plugin_get_default_component_id(), 0);
|
||||
+ slapi_modify_internal_pb(mod_pb);
|
||||
+ slapi_pblock_get(mod_pb, SLAPI_PLUGIN_INTOP_RESULT, &rc);
|
||||
+
|
||||
+ if (rc == LDAP_SUCCESS) {
|
||||
+ slapi_log_err(SLAPI_LOG_NOTICE, "upgrade_remove_index_scanlimit",
|
||||
+ "Removed 'nsIndexIDListScanLimit' from index '%s' in backend '%s'\n",
|
||||
+ attr_name, be_name);
|
||||
+ } else if (rc != LDAP_NO_SUCH_ATTRIBUTE) {
|
||||
+ slapi_log_err(SLAPI_LOG_ERR, "upgrade_remove_index_scanlimit",
|
||||
+ "Failed to remove 'nsIndexIDListScanLimit' from index '%s' in backend '%s': error %d\n",
|
||||
+ attr_name, be_name, rc);
|
||||
+ }
|
||||
+
|
||||
+ slapi_mods_done(&smods);
|
||||
+ slapi_pblock_destroy(mod_pb);
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ slapi_ch_free_string(&idx_dn);
|
||||
+ slapi_free_search_results_internal(idx_pb);
|
||||
+ slapi_pblock_destroy(idx_pb);
|
||||
+ }
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ slapi_free_search_results_internal(pb);
|
||||
+ slapi_pblock_destroy(pb);
|
||||
+
|
||||
+ return uresult;
|
||||
+}
|
||||
+
|
||||
/*
|
||||
* Check if parentid/ancestorid indexes are missing the integerOrderingMatch
|
||||
* matching rule.
|
||||
@@ -649,6 +750,10 @@ upgrade_server(void)
|
||||
return UPGRADE_FAILURE;
|
||||
}
|
||||
|
||||
+ if (upgrade_remove_index_scanlimit() != UPGRADE_SUCCESS) {
|
||||
+ return UPGRADE_FAILURE;
|
||||
+ }
|
||||
+
|
||||
if (upgrade_check_id_index_matching_rule() != UPGRADE_SUCCESS) {
|
||||
return UPGRADE_FAILURE;
|
||||
}
|
||||
--
|
||||
2.52.0
|
||||
|
||||
313
0034-Issue-7223-Add-upgrade-function-to-remove-ancestorid.patch
Normal file
313
0034-Issue-7223-Add-upgrade-function-to-remove-ancestorid.patch
Normal file
@ -0,0 +1,313 @@
|
||||
From 41670301ccad5558296a3380a4974f7c0d4baede Mon Sep 17 00:00:00 2001
|
||||
From: Viktor Ashirov <vashirov@redhat.com>
|
||||
Date: Thu, 5 Feb 2026 12:17:06 +0100
|
||||
Subject: [PATCH] Issue 7223 - Add upgrade function to remove ancestorid index
|
||||
config entry
|
||||
|
||||
Description:
|
||||
Add `upgrade_remove_ancestorid_index_config()` function that removes:
|
||||
* ancestorid from `cn=default indexes`
|
||||
* ancestorid index config entries from each backend's `cn=index`
|
||||
|
||||
Also remove ancestorid index configuration from template-dse.ldif.
|
||||
|
||||
Relates: https://github.com/389ds/389-ds-base/issues/7223
|
||||
|
||||
Reviewed by: @progier389, @tbordaz, @droideck (Thanks!)
|
||||
---
|
||||
.../healthcheck/health_system_indexes_test.py | 85 +++++++++++
|
||||
ldap/ldif/template-dse.ldif.in | 8 --
|
||||
ldap/servers/slapd/upgrade.c | 133 +++++++++++++++++-
|
||||
3 files changed, 214 insertions(+), 12 deletions(-)
|
||||
|
||||
diff --git a/dirsrvtests/tests/suites/healthcheck/health_system_indexes_test.py b/dirsrvtests/tests/suites/healthcheck/health_system_indexes_test.py
|
||||
index aea88e0e2..eb727b902 100644
|
||||
--- a/dirsrvtests/tests/suites/healthcheck/health_system_indexes_test.py
|
||||
+++ b/dirsrvtests/tests/suites/healthcheck/health_system_indexes_test.py
|
||||
@@ -504,6 +504,91 @@ def test_upgrade_removes_parentid_scanlimit(topology_st):
|
||||
|
||||
log.info("Upgrade successfully removed nsIndexIDListScanLimit from parentid index")
|
||||
|
||||
+ # Verify idempotency - restart again and ensure no errors
|
||||
+ log.info("Restart server again to verify idempotency (no errors on second run)")
|
||||
+ standalone.restart()
|
||||
+ # Verify the attribute is still absent
|
||||
+ scanlimit_after_second = parentid_index.get_attr_vals_utf8("nsIndexIDListScanLimit")
|
||||
+ assert not scanlimit_after_second, \
|
||||
+ f"nsIndexIDListScanLimit should still be absent after second restart but found: {scanlimit_after_second}"
|
||||
+ log.info("Idempotency verified - no issues on second restart")
|
||||
+
|
||||
+
|
||||
+def test_upgrade_removes_ancestorid_index_config(topology_st):
|
||||
+ """Check if upgrade function removes ancestorid index config entry
|
||||
+
|
||||
+ :id: 3f3d6e9b-75ac-4f0d-b2ce-7204e6eacd0a
|
||||
+ :setup: Standalone instance
|
||||
+ :steps:
|
||||
+ 1. Create DS instance
|
||||
+ 2. Stop the server
|
||||
+ 3. Use DSEldif to add an ancestorid index config entry
|
||||
+ 4. Start the server (triggers upgrade)
|
||||
+ 5. Verify ancestorid index config entry is removed
|
||||
+ :expectedresults:
|
||||
+ 1. Success
|
||||
+ 2. Success
|
||||
+ 3. Success
|
||||
+ 4. Success
|
||||
+ 5. ancestorid index config entry is no longer present
|
||||
+ """
|
||||
+ from lib389.dseldif import DSEldif
|
||||
+
|
||||
+ standalone = topology_st.standalone
|
||||
+ ANCESTORID_DN = "cn=ancestorid,cn=index,cn=userroot,cn=ldbm database,cn=plugins,cn=config"
|
||||
+
|
||||
+ log.info("Stop the server")
|
||||
+ standalone.stop()
|
||||
+
|
||||
+ log.info("Add ancestorid index config entry using DSEldif")
|
||||
+ dse_ldif = DSEldif(standalone)
|
||||
+
|
||||
+ # Create a fake ancestorid index entry
|
||||
+ ancestorid_entry = [
|
||||
+ "dn: {}\n".format(ANCESTORID_DN),
|
||||
+ "objectClass: top\n",
|
||||
+ "objectClass: nsIndex\n",
|
||||
+ "cn: ancestorid\n",
|
||||
+ "nsSystemIndex: true\n",
|
||||
+ "nsIndexType: eq\n",
|
||||
+ "nsMatchingRule: integerOrderingMatch\n",
|
||||
+ "\n"
|
||||
+ ]
|
||||
+ dse_ldif.add_entry(ancestorid_entry)
|
||||
+
|
||||
+ # Verify it was added by re-reading dse.ldif
|
||||
+ dse_ldif2 = DSEldif(standalone)
|
||||
+ cn_value = dse_ldif2.get(ANCESTORID_DN, "cn")
|
||||
+ assert cn_value is not None, "Failed to add ancestorid index config entry"
|
||||
+ log.info(f"Added ancestorid index entry with cn: {cn_value}")
|
||||
+
|
||||
+ log.info("Start the server (triggers upgrade)")
|
||||
+ standalone.start()
|
||||
+
|
||||
+ log.info("Verify ancestorid index config entry was removed by upgrade")
|
||||
+ # Check via LDAP - the upgrade should have removed the entry
|
||||
+ try:
|
||||
+ ancestorid_index = Index(standalone, ANCESTORID_DN)
|
||||
+ # If we can get the entry, it wasn't removed - this is a failure
|
||||
+ cn_after = ancestorid_index.get_attr_vals_utf8("cn")
|
||||
+ assert False, f"ancestorid index config entry should have been removed but still exists: {cn_after}"
|
||||
+ except Exception as e:
|
||||
+ # Entry should not exist - this is expected
|
||||
+ log.info(f"ancestorid index config entry correctly removed (got exception: {e})")
|
||||
+
|
||||
+ log.info("Upgrade successfully removed ancestorid index config entry")
|
||||
+
|
||||
+ # Verify idempotency - restart again and ensure no errors
|
||||
+ log.info("Restart server again to verify idempotency (no errors on second run)")
|
||||
+ standalone.restart()
|
||||
+ # Verify the entry is still absent
|
||||
+ try:
|
||||
+ ancestorid_index = Index(standalone, ANCESTORID_DN)
|
||||
+ cn_after_second = ancestorid_index.get_attr_vals_utf8("cn")
|
||||
+ assert False, f"ancestorid index config entry should still be absent after second restart but found: {cn_after_second}"
|
||||
+ except Exception as e:
|
||||
+ log.info(f"Idempotency verified - ancestorid still absent after second restart (got exception: {e})")
|
||||
+
|
||||
|
||||
if __name__ == "__main__":
|
||||
# Run isolated
|
||||
diff --git a/ldap/ldif/template-dse.ldif.in b/ldap/ldif/template-dse.ldif.in
|
||||
index bb8c71cd9..b6ab6f6c6 100644
|
||||
--- a/ldap/ldif/template-dse.ldif.in
|
||||
+++ b/ldap/ldif/template-dse.ldif.in
|
||||
@@ -998,14 +998,6 @@ cn: aci
|
||||
nssystemindex: true
|
||||
nsindextype: pres
|
||||
|
||||
-dn: cn=ancestorid,cn=default indexes, cn=config,cn=ldbm database,cn=plugins,cn=config
|
||||
-objectclass: top
|
||||
-objectclass: nsIndex
|
||||
-cn: ancestorid
|
||||
-nssystemindex: true
|
||||
-nsindextype: eq
|
||||
-nsmatchingrule: integerOrderingMatch
|
||||
-
|
||||
dn: cn=cn,cn=default indexes, cn=config,cn=ldbm database,cn=plugins,cn=config
|
||||
objectclass: top
|
||||
objectclass: nsIndex
|
||||
diff --git a/ldap/servers/slapd/upgrade.c b/ldap/servers/slapd/upgrade.c
|
||||
index dcd16940b..6b1b012da 100644
|
||||
--- a/ldap/servers/slapd/upgrade.c
|
||||
+++ b/ldap/servers/slapd/upgrade.c
|
||||
@@ -431,6 +431,126 @@ upgrade_remove_index_scanlimit(void)
|
||||
return uresult;
|
||||
}
|
||||
|
||||
+/*
|
||||
+ * Remove ancestorid index configuration entry if present.
|
||||
+ *
|
||||
+ * The ancestorid index is special - it has no corresponding attribute type
|
||||
+ * and should not have a DSE config entry. If an entry exists, remove it.
|
||||
+ *
|
||||
+ * This function removes:
|
||||
+ * 1. The ancestorid entry from cn=default indexes (to prevent re-creation on startup)
|
||||
+ * 2. The ancestorid entry from each backend's cn=index (if it exists)
|
||||
+ */
|
||||
+static upgrade_status
|
||||
+upgrade_remove_ancestorid_index_config(void)
|
||||
+{
|
||||
+ struct slapi_pblock *pb = slapi_pblock_new();
|
||||
+ Slapi_Entry **backends = NULL;
|
||||
+ const char *be_base_dn = "cn=ldbm database,cn=plugins,cn=config";
|
||||
+ const char *be_filter = "(objectclass=nsBackendInstance)";
|
||||
+ upgrade_status uresult = UPGRADE_SUCCESS;
|
||||
+ int rc;
|
||||
+
|
||||
+ /*
|
||||
+ * First, remove ancestorid from cn=default indexes to prevent
|
||||
+ * ldbm_instance_create_default_user_indexes() from re-creating it.
|
||||
+ */
|
||||
+ {
|
||||
+ Slapi_PBlock *def_pb = slapi_pblock_new();
|
||||
+ char *def_idx_dn = slapi_create_dn_string(
|
||||
+ "cn=ancestorid,cn=default indexes,cn=config,%s", be_base_dn);
|
||||
+
|
||||
+ if (def_idx_dn) {
|
||||
+ slapi_delete_internal_set_pb(
|
||||
+ def_pb, def_idx_dn, NULL, NULL,
|
||||
+ plugin_get_default_component_id(), 0);
|
||||
+ slapi_delete_internal_pb(def_pb);
|
||||
+ slapi_pblock_get(def_pb, SLAPI_PLUGIN_INTOP_RESULT, &rc);
|
||||
+
|
||||
+ if (rc == LDAP_SUCCESS) {
|
||||
+ slapi_log_err(SLAPI_LOG_NOTICE, "upgrade_remove_ancestorid_index_config",
|
||||
+ "Removed 'ancestorid' from default indexes.\n");
|
||||
+ } else if (rc != LDAP_NO_SUCH_OBJECT) {
|
||||
+ slapi_log_err(SLAPI_LOG_ERR, "upgrade_remove_ancestorid_index_config",
|
||||
+ "Failed to remove 'ancestorid' from default indexes: error %d\n", rc);
|
||||
+ }
|
||||
+
|
||||
+ slapi_ch_free_string(&def_idx_dn);
|
||||
+ }
|
||||
+ slapi_pblock_destroy(def_pb);
|
||||
+ }
|
||||
+
|
||||
+ /* Search for all backend instances */
|
||||
+ slapi_search_internal_set_pb(
|
||||
+ pb, be_base_dn,
|
||||
+ LDAP_SCOPE_ONELEVEL,
|
||||
+ be_filter, NULL, 0, NULL, NULL,
|
||||
+ plugin_get_default_component_id(), 0);
|
||||
+ slapi_search_internal_pb(pb);
|
||||
+ slapi_pblock_get(pb, SLAPI_PLUGIN_INTOP_SEARCH_ENTRIES, &backends);
|
||||
+
|
||||
+ if (backends) {
|
||||
+ for (size_t be_idx = 0; backends[be_idx] != NULL; be_idx++) {
|
||||
+ const char *be_dn = slapi_entry_get_dn_const(backends[be_idx]);
|
||||
+ const char *be_name = slapi_entry_attr_get_ref(backends[be_idx], "cn");
|
||||
+ if (!be_dn || !be_name) {
|
||||
+ continue;
|
||||
+ }
|
||||
+
|
||||
+ struct slapi_pblock *idx_pb = slapi_pblock_new();
|
||||
+ Slapi_Entry **idx_entries = NULL;
|
||||
+ char *idx_dn = slapi_create_dn_string("cn=ancestorid,cn=index,%s",
|
||||
+ be_dn);
|
||||
+ char *idx_filter = "(objectclass=nsIndex)";
|
||||
+
|
||||
+ if (!idx_dn) {
|
||||
+ slapi_pblock_destroy(idx_pb);
|
||||
+ continue;
|
||||
+ }
|
||||
+
|
||||
+ slapi_search_internal_set_pb(
|
||||
+ idx_pb, idx_dn,
|
||||
+ LDAP_SCOPE_BASE,
|
||||
+ idx_filter, NULL, 0, NULL, NULL,
|
||||
+ plugin_get_default_component_id(), 0);
|
||||
+ slapi_search_internal_pb(idx_pb);
|
||||
+ slapi_pblock_get(idx_pb, SLAPI_PLUGIN_INTOP_SEARCH_ENTRIES, &idx_entries);
|
||||
+
|
||||
+ if (idx_entries && idx_entries[0]) {
|
||||
+ /* ancestorid index entry exists - delete it */
|
||||
+ Slapi_PBlock *del_pb = slapi_pblock_new();
|
||||
+
|
||||
+ slapi_delete_internal_set_pb(
|
||||
+ del_pb, idx_dn, NULL, NULL,
|
||||
+ plugin_get_default_component_id(), 0);
|
||||
+ slapi_delete_internal_pb(del_pb);
|
||||
+ slapi_pblock_get(del_pb, SLAPI_PLUGIN_INTOP_RESULT, &rc);
|
||||
+
|
||||
+ if (rc == LDAP_SUCCESS) {
|
||||
+ slapi_log_err(SLAPI_LOG_NOTICE, "upgrade_remove_ancestorid_index_config",
|
||||
+ "Removed 'ancestorid' index config entry in backend '%s'.\n",
|
||||
+ be_name);
|
||||
+ } else if (rc != LDAP_NO_SUCH_OBJECT) {
|
||||
+ slapi_log_err(SLAPI_LOG_ERR, "upgrade_remove_ancestorid_index_config",
|
||||
+ "Failed to remove 'ancestorid' index config entry in backend '%s': error %d\n",
|
||||
+ be_name, rc);
|
||||
+ }
|
||||
+
|
||||
+ slapi_pblock_destroy(del_pb);
|
||||
+ }
|
||||
+
|
||||
+ slapi_ch_free_string(&idx_dn);
|
||||
+ slapi_free_search_results_internal(idx_pb);
|
||||
+ slapi_pblock_destroy(idx_pb);
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ slapi_free_search_results_internal(pb);
|
||||
+ slapi_pblock_destroy(pb);
|
||||
+
|
||||
+ return uresult;
|
||||
+}
|
||||
+
|
||||
/*
|
||||
* Check if parentid/ancestorid indexes are missing the integerOrderingMatch
|
||||
* matching rule.
|
||||
@@ -445,7 +565,7 @@ upgrade_check_id_index_matching_rule(void)
|
||||
Slapi_Entry **backends = NULL;
|
||||
const char *be_base_dn = "cn=ldbm database,cn=plugins,cn=config";
|
||||
const char *be_filter = "(objectclass=nsBackendInstance)";
|
||||
- const char *attrs_to_check[] = {"parentid", "ancestorid", NULL};
|
||||
+ const char *attrs_to_check[] = {"parentid", NULL};
|
||||
upgrade_status uresult = UPGRADE_SUCCESS;
|
||||
|
||||
/* Search for all backend instances */
|
||||
@@ -459,8 +579,9 @@ upgrade_check_id_index_matching_rule(void)
|
||||
|
||||
if (backends) {
|
||||
for (size_t be_idx = 0; backends[be_idx] != NULL; be_idx++) {
|
||||
+ const char *be_dn = slapi_entry_get_dn_const(backends[be_idx]);
|
||||
const char *be_name = slapi_entry_attr_get_ref(backends[be_idx], "cn");
|
||||
- if (!be_name) {
|
||||
+ if (!be_dn || !be_name) {
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -469,8 +590,8 @@ upgrade_check_id_index_matching_rule(void)
|
||||
const char *attr_name = attrs_to_check[attr_idx];
|
||||
struct slapi_pblock *idx_pb = slapi_pblock_new();
|
||||
Slapi_Entry **idx_entries = NULL;
|
||||
- char *idx_dn = slapi_create_dn_string("cn=%s,cn=index,cn=%s,%s",
|
||||
- attr_name, be_name, be_base_dn);
|
||||
+ char *idx_dn = slapi_create_dn_string("cn=%s,cn=index,%s",
|
||||
+ attr_name, be_dn);
|
||||
char *idx_filter = "(objectclass=nsIndex)";
|
||||
PRBool has_matching_rule = PR_FALSE;
|
||||
|
||||
@@ -754,6 +875,10 @@ upgrade_server(void)
|
||||
return UPGRADE_FAILURE;
|
||||
}
|
||||
|
||||
+ if (upgrade_remove_ancestorid_index_config() != UPGRADE_SUCCESS) {
|
||||
+ return UPGRADE_FAILURE;
|
||||
+ }
|
||||
+
|
||||
if (upgrade_check_id_index_matching_rule() != UPGRADE_SUCCESS) {
|
||||
return UPGRADE_FAILURE;
|
||||
}
|
||||
--
|
||||
2.52.0
|
||||
|
||||
300
0035-Issue-7223-Detect-and-log-index-ordering-mismatch-du.patch
Normal file
300
0035-Issue-7223-Detect-and-log-index-ordering-mismatch-du.patch
Normal file
@ -0,0 +1,300 @@
|
||||
From a260b50aa0c8e6c5b8b3fd0b164e9bbc4a15983f Mon Sep 17 00:00:00 2001
|
||||
From: Viktor Ashirov <vashirov@redhat.com>
|
||||
Date: Thu, 5 Feb 2026 12:17:06 +0100
|
||||
Subject: [PATCH] Issue 7223 - Detect and log index ordering mismatch during
|
||||
backend startup
|
||||
|
||||
Description:
|
||||
Add `ldbm_instance_check_index_config()` function that checks on-disk
|
||||
index data and logs a message in case of a mismatch with DSE config entry.
|
||||
|
||||
Relates: https://github.com/389ds/389-ds-base/issues/7223
|
||||
|
||||
Reviewed by: @progier389, @tbordaz, @droideck (Thanks!)
|
||||
---
|
||||
ldap/servers/slapd/back-ldbm/instance.c | 262 ++++++++++++++++++++++++
|
||||
1 file changed, 262 insertions(+)
|
||||
|
||||
diff --git a/ldap/servers/slapd/back-ldbm/instance.c b/ldap/servers/slapd/back-ldbm/instance.c
|
||||
index 2b71cd4f7..17bfc09a0 100644
|
||||
--- a/ldap/servers/slapd/back-ldbm/instance.c
|
||||
+++ b/ldap/servers/slapd/back-ldbm/instance.c
|
||||
@@ -239,6 +239,266 @@ ldbm_instance_create_default_indexes(backend *be)
|
||||
}
|
||||
|
||||
|
||||
+/*
|
||||
+ * Check if an index has integerOrderingMatch configured in DSE.
|
||||
+ *
|
||||
+ * This function performs an internal LDAP search to check if the index
|
||||
+ * configuration entry has nsMatchingRule: integerOrderingMatch.
|
||||
+ *
|
||||
+ * Parameters:
|
||||
+ * inst_name - backend instance name (e.g., "userRoot")
|
||||
+ * index_name - name of the index to check (e.g., "parentid", "ancestorid")
|
||||
+ *
|
||||
+ * Returns:
|
||||
+ * PR_TRUE if integerOrderingMatch is configured
|
||||
+ * PR_FALSE if not configured or index entry doesn't exist
|
||||
+ */
|
||||
+static PRBool
|
||||
+ldbm_instance_index_has_int_order_in_dse(const char *inst_name, const char *index_name)
|
||||
+{
|
||||
+ Slapi_PBlock *pb = NULL;
|
||||
+ Slapi_Entry **entries = NULL;
|
||||
+ char *idx_dn = NULL;
|
||||
+ PRBool has_int_order = PR_FALSE;
|
||||
+
|
||||
+ idx_dn = slapi_create_dn_string("cn=%s,cn=index,cn=%s,cn=ldbm database,cn=plugins,cn=config",
|
||||
+ index_name, inst_name);
|
||||
+ if (idx_dn == NULL) {
|
||||
+ return PR_FALSE;
|
||||
+ }
|
||||
+
|
||||
+ pb = slapi_pblock_new();
|
||||
+ slapi_search_internal_set_pb(pb, idx_dn, LDAP_SCOPE_BASE,
|
||||
+ "(objectclass=nsIndex)", NULL, 0, NULL, NULL,
|
||||
+ plugin_get_default_component_id(), 0);
|
||||
+ slapi_search_internal_pb(pb);
|
||||
+ slapi_pblock_get(pb, SLAPI_PLUGIN_INTOP_SEARCH_ENTRIES, &entries);
|
||||
+
|
||||
+ if (entries && entries[0]) {
|
||||
+ Slapi_Attr *mr_attr = NULL;
|
||||
+ if (slapi_entry_attr_find(entries[0], "nsMatchingRule", &mr_attr) == 0) {
|
||||
+ Slapi_Value *sval = NULL;
|
||||
+ int idx;
|
||||
+ for (idx = slapi_attr_first_value(mr_attr, &sval);
|
||||
+ idx != -1;
|
||||
+ idx = slapi_attr_next_value(mr_attr, idx, &sval)) {
|
||||
+ const struct berval *bval = slapi_value_get_berval(sval);
|
||||
+ if (bval && bval->bv_val &&
|
||||
+ strcasecmp(bval->bv_val, "integerOrderingMatch") == 0) {
|
||||
+ has_int_order = PR_TRUE;
|
||||
+ break;
|
||||
+ }
|
||||
+ }
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ slapi_ch_free_string(&idx_dn);
|
||||
+ slapi_free_search_results_internal(pb);
|
||||
+ slapi_pblock_destroy(pb);
|
||||
+
|
||||
+ return has_int_order;
|
||||
+}
|
||||
+
|
||||
+/*
|
||||
+ * Check a system index for ordering mismatch between config and on-disk data.
|
||||
+ *
|
||||
+ * This function compares what's configured in DSE (nsMatchingRule) with
|
||||
+ * what's actually on disk. A mismatch can occur in two scenarios:
|
||||
+ * 1. Ordering rule is configured but disk has lexicographic order
|
||||
+ * (rule was added after index was created)
|
||||
+ * 2. No ordering rule configured but disk has integer order
|
||||
+ * (rule was removed after index was created with it)
|
||||
+ *
|
||||
+ * This function reads the first keys from the specified index and checks
|
||||
+ * if they are stored in lexicographic order (string: "1" < "10" < "2") or
|
||||
+ * integer order (numeric: "1" < "2" < "10").
|
||||
+ *
|
||||
+ * Parameters:
|
||||
+ * be - backend
|
||||
+ * index_name - name of the index to check (e.g., "parentid", "ancestorid")
|
||||
+ *
|
||||
+ */
|
||||
+static void
|
||||
+ldbm_instance_check_index_config(backend *be, const char *index_name)
|
||||
+{
|
||||
+ ldbm_instance *inst = (ldbm_instance *)be->be_instance_info;
|
||||
+ struct attrinfo *ai = NULL;
|
||||
+ dbi_db_t *db = NULL;
|
||||
+ dbi_cursor_t dbc = {0};
|
||||
+ dbi_val_t key = {0};
|
||||
+ dbi_val_t data = {0};
|
||||
+ int ret = 0;
|
||||
+ PRBool config_has_int_order = PR_FALSE;
|
||||
+ PRBool disk_has_int_order = PR_TRUE; /* Assume integer order until proven otherwise */
|
||||
+ ID prev_id = 0;
|
||||
+ int key_count = 0;
|
||||
+ PRBool first_key = PR_TRUE;
|
||||
+ PRBool found_ordering_evidence = PR_FALSE;
|
||||
+
|
||||
+ slapi_log_err(SLAPI_LOG_DEBUG, "ldbm_instance_check_index_config",
|
||||
+ "Backend '%s': checking %s index ordering...\n",
|
||||
+ inst->inst_name, index_name);
|
||||
+
|
||||
+ /* Check if integerOrderingMatch is configured in DSE */
|
||||
+ config_has_int_order = ldbm_instance_index_has_int_order_in_dse(inst->inst_name, index_name);
|
||||
+
|
||||
+ /* Get attrinfo for the index */
|
||||
+ ainfo_get(be, (char *)index_name, &ai);
|
||||
+ if (ai == NULL || strcmp(ai->ai_type, index_name) != 0) {
|
||||
+ /* No index config found */
|
||||
+ slapi_log_err(SLAPI_LOG_DEBUG, "ldbm_instance_check_index_config",
|
||||
+ "Backend '%s': no %s attrinfo found, skipping check\n",
|
||||
+ inst->inst_name, index_name);
|
||||
+ return;
|
||||
+ }
|
||||
+
|
||||
+ /* Open the index file */
|
||||
+ ret = dblayer_get_index_file(be, ai, &db, 0);
|
||||
+ if (ret != 0 || db == NULL) {
|
||||
+ /* Index file doesn't exist or can't be opened - this is fine for new instances */
|
||||
+ slapi_log_err(SLAPI_LOG_DEBUG, "ldbm_instance_check_index_config",
|
||||
+ "Backend '%s': could not open %s index file (ret=%d), skipping order check\n",
|
||||
+ inst->inst_name, index_name, ret);
|
||||
+ return;
|
||||
+ }
|
||||
+
|
||||
+ /* Create a cursor to read keys */
|
||||
+ ret = dblayer_new_cursor(be, db, NULL, &dbc);
|
||||
+ if (ret != 0) {
|
||||
+ slapi_log_err(SLAPI_LOG_ERR, "ldbm_instance_check_index_config",
|
||||
+ "Backend '%s': could not create cursor on %s index (ret=%d)\n",
|
||||
+ inst->inst_name, index_name, ret);
|
||||
+ dblayer_release_index_file(be, ai, db);
|
||||
+ return;
|
||||
+ }
|
||||
+
|
||||
+ dblayer_value_init(be, &key);
|
||||
+ dblayer_value_init(be, &data);
|
||||
+
|
||||
+ /*
|
||||
+ * Read up to 100 unique keys and check their ordering.
|
||||
+ * With lexicographic ordering: "1" < "10" < "100" < "2" < "20" < "3"
|
||||
+ * With integer ordering: "1" < "2" < "3" < "10" < "20" < "100"
|
||||
+ *
|
||||
+ * If we find a case where prev_id > current_id (numerically), but the
|
||||
+ * keys are still in order (lexicographically), then the index uses
|
||||
+ * lexicographic ordering.
|
||||
+ */
|
||||
+ while (key_count < 100) {
|
||||
+ ID current_id;
|
||||
+
|
||||
+ ret = dblayer_cursor_op(&dbc, first_key ? DBI_OP_MOVE_TO_FIRST : DBI_OP_NEXT_KEY, &key, &data);
|
||||
+ first_key = PR_FALSE; /* Always advance cursor on next iteration */
|
||||
+ if (ret != 0) {
|
||||
+ break; /* No more keys or error */
|
||||
+ }
|
||||
+
|
||||
+ /* Skip non-equality keys */
|
||||
+ if (key.size < 2 || *(char *)key.data != EQ_PREFIX) {
|
||||
+ continue;
|
||||
+ }
|
||||
+
|
||||
+ /* Parse the ID from the key (format: "=<id>") */
|
||||
+ current_id = (ID)strtoul((char *)key.data + 1, NULL, 10);
|
||||
+ if (current_id == 0) {
|
||||
+ continue; /* Invalid ID, skip */
|
||||
+ }
|
||||
+
|
||||
+ key_count++;
|
||||
+
|
||||
+ if (prev_id != 0) {
|
||||
+ /*
|
||||
+ * Check ordering: if prev_id > current_id numerically,
|
||||
+ * but we got this key after prev in DB order, then
|
||||
+ * the index is using lexicographic ordering.
|
||||
+ *
|
||||
+ * Example: if we see "10" followed by "2", that's lexicographic
|
||||
+ * because "10" < "2" as strings, but 10 > 2 as integers.
|
||||
+ */
|
||||
+ if (prev_id > current_id) {
|
||||
+ /* Found evidence of lexicographic ordering */
|
||||
+ disk_has_int_order = PR_FALSE;
|
||||
+ found_ordering_evidence = PR_TRUE;
|
||||
+ break;
|
||||
+ } else if (prev_id < current_id) {
|
||||
+ /*
|
||||
+ * This is consistent with integer ordering, but we need
|
||||
+ * to find a case that proves lexicographic ordering.
|
||||
+ * For example, seeing "1" followed by "2" is ambiguous,
|
||||
+ * but seeing "1" followed by "10" (not "2") proves lexicographic.
|
||||
+ *
|
||||
+ * A definitive test: if we see an ID followed by a smaller
|
||||
+ * ID, that's lexicographic. If all IDs are strictly increasing,
|
||||
+ * it could be either (or the index only has sequential IDs).
|
||||
+ */
|
||||
+ found_ordering_evidence = PR_TRUE;
|
||||
+ }
|
||||
+ }
|
||||
+ prev_id = current_id;
|
||||
+ }
|
||||
+
|
||||
+ /* Close the cursor and free values */
|
||||
+ dblayer_cursor_op(&dbc, DBI_OP_CLOSE, NULL, NULL);
|
||||
+ dblayer_value_free(be, &key);
|
||||
+ dblayer_value_free(be, &data);
|
||||
+
|
||||
+ /* Release the index file */
|
||||
+ dblayer_release_index_file(be, ai, db);
|
||||
+
|
||||
+ /*
|
||||
+ * Report findings and check for config/disk mismatch.
|
||||
+ * Log an error if there's a discrepancy between what's configured
|
||||
+ * in DSE and what's actually on disk.
|
||||
+ */
|
||||
+ if (!found_ordering_evidence) {
|
||||
+ slapi_log_err(SLAPI_LOG_DEBUG, "ldbm_instance_check_index_config",
|
||||
+ "Backend '%s': %s index ordering check - "
|
||||
+ "could not determine on-disk ordering (index may be empty or have sequential IDs only). "
|
||||
+ "Config has integerOrderingMatch: %s\n",
|
||||
+ inst->inst_name, index_name, config_has_int_order ? "yes" : "no");
|
||||
+ } else if (config_has_int_order && !disk_has_int_order) {
|
||||
+ /* Config expects integer ordering, but disk has lexicographic - MISMATCH */
|
||||
+ slapi_log_err(SLAPI_LOG_ERR, "ldbm_instance_check_index_config",
|
||||
+ "Backend '%s': MISMATCH - %s index has integerOrderingMatch configured, "
|
||||
+ "but on-disk data uses lexicographic ordering. "
|
||||
+ "This will cause searches to return incorrect or incomplete results. "
|
||||
+ "Please reindex the %s attribute: "
|
||||
+ "dsconf <instance> backend index reindex --attr %s %s\n",
|
||||
+ inst->inst_name, index_name, index_name, index_name, inst->inst_name);
|
||||
+ } else if (!config_has_int_order && disk_has_int_order) {
|
||||
+ /* Config expects lexicographic ordering, but disk has integer - MISMATCH */
|
||||
+ slapi_log_err(SLAPI_LOG_ERR, "ldbm_instance_check_index_config",
|
||||
+ "Backend '%s': MISMATCH - %s index does not have integerOrderingMatch configured, "
|
||||
+ "but on-disk data uses integer ordering. "
|
||||
+ "This will cause searches to return incorrect or incomplete results. "
|
||||
+ "Please reindex the %s attribute: "
|
||||
+ "dsconf <instance> backend index reindex --attr %s %s\n",
|
||||
+ inst->inst_name, index_name, index_name, index_name, inst->inst_name);
|
||||
+ } else {
|
||||
+ /* Config and disk ordering match - no action needed */
|
||||
+ slapi_log_err(SLAPI_LOG_DEBUG, "ldbm_instance_check_index_config",
|
||||
+ "Backend '%s': %s index ordering check passed - "
|
||||
+ "config has integerOrderingMatch: %s, on-disk data matches.\n",
|
||||
+ inst->inst_name, index_name, config_has_int_order ? "yes" : "no");
|
||||
+ }
|
||||
+}
|
||||
+
|
||||
+/*
|
||||
+ * Check system indexes for ordering mismatches.
|
||||
+ * If a mismatch is detected, log an error advising the administrator
|
||||
+ * to reindex the affected attribute.
|
||||
+ *
|
||||
+ * Note: We only check parentid here. The ancestorid index is a special
|
||||
+ * system index that has no DSE config entry - its ordering is hardcoded
|
||||
+ * in ldbm_instance_init_config_entry() and cannot be changed by users.
|
||||
+ */
|
||||
+static void
|
||||
+ldbm_instance_check_indexes(backend *be)
|
||||
+{
|
||||
+ /* Check parentid index */
|
||||
+ ldbm_instance_check_index_config(be, LDBM_PARENTID_STR);
|
||||
+}
|
||||
+
|
||||
/* Starts a backend instance */
|
||||
int
|
||||
ldbm_instance_start(backend *be)
|
||||
@@ -308,6 +568,8 @@ ldbm_instance_startall(struct ldbminfo *li)
|
||||
ldbm_instance_register_modify_callback(inst);
|
||||
vlv_init(inst);
|
||||
slapi_mtn_be_started(inst->inst_be);
|
||||
+ /* Check index configuration for potential issues */
|
||||
+ ldbm_instance_check_indexes(inst->inst_be);
|
||||
}
|
||||
if (slapi_exist_referral(inst->inst_be)) {
|
||||
slapi_be_set_flag(inst->inst_be, SLAPI_BE_FLAG_CONTAINS_REFERRAL);
|
||||
--
|
||||
2.52.0
|
||||
|
||||
1233
0036-Issue-7223-Add-dsctl-index-check-command-for-offline.patch
Normal file
1233
0036-Issue-7223-Add-dsctl-index-check-command-for-offline.patch
Normal file
File diff suppressed because it is too large
Load Diff
62
0037-Issue-7230-Regression-in-healtcheck-NssCheck-7235.patch
Normal file
62
0037-Issue-7230-Regression-in-healtcheck-NssCheck-7235.patch
Normal file
@ -0,0 +1,62 @@
|
||||
From 3ebb30a65e2b40610301e5feedda0408ac9f3631 Mon Sep 17 00:00:00 2001
|
||||
From: James Chapman <jachapma@redhat.com>
|
||||
Date: Tue, 10 Feb 2026 10:35:48 +0000
|
||||
Subject: [PATCH] Issue 7230 - Regression in healtcheck NssCheck (#7235)
|
||||
|
||||
Description:
|
||||
Dynamic Certificate lib389 updadates modified get_cert_details() to
|
||||
return a dict instead of tuple format. _lint_certificate_expiration() and
|
||||
tls.list_cas() still assumes tuple style access.
|
||||
|
||||
Fix:
|
||||
Update method to use dict key.
|
||||
|
||||
Fixes: https://github.com/389ds/389-ds-base/issues/7230
|
||||
|
||||
Co-authored-by: @flo-renaud
|
||||
|
||||
Reviewed by: @droideck (Thank you)
|
||||
---
|
||||
src/lib389/lib389/cli_ctl/tls.py | 4 ++--
|
||||
src/lib389/lib389/nss_ssl.py | 4 ++--
|
||||
2 files changed, 4 insertions(+), 4 deletions(-)
|
||||
|
||||
diff --git a/src/lib389/lib389/cli_ctl/tls.py b/src/lib389/lib389/cli_ctl/tls.py
|
||||
index 8834f5758..91f1f3a59 100644
|
||||
--- a/src/lib389/lib389/cli_ctl/tls.py
|
||||
+++ b/src/lib389/lib389/cli_ctl/tls.py
|
||||
@@ -25,9 +25,9 @@ def list_client_cas(inst, log, args):
|
||||
|
||||
def list_cas(inst, log, args):
|
||||
tls = NssSsl(dirsrv=inst)
|
||||
- # This turns an array of [('CA', 'C,,')]
|
||||
+ # Returns a list of cert dicts, eg {'cn': 'nickname', etc}
|
||||
for c in tls.list_ca_certs():
|
||||
- log.info(c[0])
|
||||
+ log.info(c['cn'])
|
||||
|
||||
|
||||
def show_cert(inst, log, args):
|
||||
diff --git a/src/lib389/lib389/nss_ssl.py b/src/lib389/lib389/nss_ssl.py
|
||||
index 764434166..fae65d19c 100644
|
||||
--- a/src/lib389/lib389/nss_ssl.py
|
||||
+++ b/src/lib389/lib389/nss_ssl.py
|
||||
@@ -91,13 +91,13 @@ class NssSsl(DSLint):
|
||||
if diff_date < timedelta(days=0):
|
||||
# Expired
|
||||
report = copy.deepcopy(DSCERTLE0002)
|
||||
- report['detail'] = report['detail'].replace('CERT', cert[0])
|
||||
+ report['detail'] = report['detail'].replace('CERT', cert['cn'])
|
||||
report['check'] = f'tls:certificate_expiration'
|
||||
yield report
|
||||
elif diff_date < timedelta(days=30):
|
||||
# Expiring within 30 days
|
||||
report = copy.deepcopy(DSCERTLE0001)
|
||||
- report['detail'] = report['detail'].replace('CERT', cert[0])
|
||||
+ report['detail'] = report['detail'].replace('CERT', cert['cn'])
|
||||
report['check'] = f'tls:certificate_expiration'
|
||||
yield report
|
||||
|
||||
--
|
||||
2.52.0
|
||||
|
||||
1225
0038-Issue-3555-UI-Fix-audit-issue-with-npm-isaacs-brace-.patch
Normal file
1225
0038-Issue-3555-UI-Fix-audit-issue-with-npm-isaacs-brace-.patch
Normal file
File diff suppressed because it is too large
Load Diff
97
0039-Issue-7221-CI-tests-fix-some-flaky-tests.patch
Normal file
97
0039-Issue-7221-CI-tests-fix-some-flaky-tests.patch
Normal file
@ -0,0 +1,97 @@
|
||||
From ae4a39474df08d56064453f0fb6c2272e6c3dc8b Mon Sep 17 00:00:00 2001
|
||||
From: Mark Reynolds <mreynolds@redhat.com>
|
||||
Date: Fri, 6 Feb 2026 15:13:30 -0500
|
||||
Subject: [PATCH] Issue 7221 - CI tests - fix some flaky tests
|
||||
|
||||
Description:
|
||||
|
||||
Try to harden some of the flaky tests with sleeps and more relaxed contraints
|
||||
|
||||
Relates: https://github.com/389ds/389-ds-base/issues/7221
|
||||
|
||||
Reviewed by: spichugi(Thanks!)
|
||||
---
|
||||
dirsrvtests/tests/suites/acl/acivattr_test.py | 7 +++++--
|
||||
dirsrvtests/tests/suites/import/regression_test.py | 2 +-
|
||||
.../suites/replication/wait_for_async_feature_test.py | 2 +-
|
||||
dirsrvtests/tests/suites/retrocl/basic_test.py | 2 +-
|
||||
4 files changed, 8 insertions(+), 5 deletions(-)
|
||||
|
||||
diff --git a/dirsrvtests/tests/suites/acl/acivattr_test.py b/dirsrvtests/tests/suites/acl/acivattr_test.py
|
||||
index d55eea023..efe2d5ef1 100644
|
||||
--- a/dirsrvtests/tests/suites/acl/acivattr_test.py
|
||||
+++ b/dirsrvtests/tests/suites/acl/acivattr_test.py
|
||||
@@ -1,12 +1,12 @@
|
||||
# --- BEGIN COPYRIGHT BLOCK ---
|
||||
-# Copyright (C) 2019 Red Hat, Inc.
|
||||
+# Copyright (C) 2026 Red Hat, Inc.
|
||||
# All rights reserved.
|
||||
#
|
||||
# License: GPL (version 3 or any later version).
|
||||
# See LICENSE for details.
|
||||
# --- END COPYRIGHT BLOCK ---
|
||||
|
||||
-import pytest, os, ldap
|
||||
+import pytest, os, ldap, time
|
||||
from lib389._constants import DEFAULT_SUFFIX, PW_DM
|
||||
from lib389.idm.user import UserAccount
|
||||
from lib389.idm.organization import Organization
|
||||
@@ -190,6 +190,8 @@ def test_positive(topo, _add_user, aci_of_user, user, entry, aci):
|
||||
"""
|
||||
# set aci
|
||||
Domain(topo.standalone, DNBASE).set("aci", aci)
|
||||
+ time.sleep(.5)
|
||||
+
|
||||
# create connection
|
||||
conn = UserAccount(topo.standalone, user).bind(PW_DM)
|
||||
# according to the aci , user will be able to change description
|
||||
@@ -242,6 +244,7 @@ def test_negative(topo, _add_user, aci_of_user, user, entry, aci):
|
||||
"""
|
||||
# set aci
|
||||
Domain(topo.standalone, DNBASE).set("aci", aci)
|
||||
+ time.sleep(.5)
|
||||
# create connection
|
||||
conn = UserAccount(topo.standalone, user).bind(PW_DM)
|
||||
# according to the aci , user will not be able to change description
|
||||
diff --git a/dirsrvtests/tests/suites/import/regression_test.py b/dirsrvtests/tests/suites/import/regression_test.py
|
||||
index 0e3ba1930..25a7f359d 100644
|
||||
--- a/dirsrvtests/tests/suites/import/regression_test.py
|
||||
+++ b/dirsrvtests/tests/suites/import/regression_test.py
|
||||
@@ -687,7 +687,7 @@ def test_ldif2db_after_backend_create(topo, verify):
|
||||
import_time_2 = create_backend_and_import(instance, ldif_file_2, 'o=test_2', 'test_2')
|
||||
|
||||
log.info('Import times should be approximately the same')
|
||||
- assert abs(import_time_1 - import_time_2) < 15
|
||||
+ assert abs(import_time_1 - import_time_2) < 20
|
||||
|
||||
|
||||
def test_ldif_missing_suffix_entry(topo, request, verify):
|
||||
diff --git a/dirsrvtests/tests/suites/replication/wait_for_async_feature_test.py b/dirsrvtests/tests/suites/replication/wait_for_async_feature_test.py
|
||||
index c5ab585e4..84ce1ca2b 100644
|
||||
--- a/dirsrvtests/tests/suites/replication/wait_for_async_feature_test.py
|
||||
+++ b/dirsrvtests/tests/suites/replication/wait_for_async_feature_test.py
|
||||
@@ -26,7 +26,7 @@ log = logging.getLogger(__name__)
|
||||
installation1_prefix = None
|
||||
|
||||
# Expected minimum and maximum number of async result in usual cases
|
||||
-USUAL_MIN_AP = 3
|
||||
+USUAL_MIN_AP = 2
|
||||
USUAL_MAX_AP = 11
|
||||
|
||||
@pytest.fixture(params=[(None, (USUAL_MIN_AP, USUAL_MAX_AP)),
|
||||
diff --git a/dirsrvtests/tests/suites/retrocl/basic_test.py b/dirsrvtests/tests/suites/retrocl/basic_test.py
|
||||
index b53a60851..2fce72049 100644
|
||||
--- a/dirsrvtests/tests/suites/retrocl/basic_test.py
|
||||
+++ b/dirsrvtests/tests/suites/retrocl/basic_test.py
|
||||
@@ -492,7 +492,7 @@ def test_retrocl_trimming_entries(topology_st):
|
||||
if inst.searchErrorsLog("trim_changelog: removed "):
|
||||
log.info(f'Trimming detected after {attempt * 6} seconds')
|
||||
break
|
||||
-
|
||||
+
|
||||
log.info('Verify trimming occurred by checking error log')
|
||||
assert inst.searchErrorsLog("trim_changelog: removed ")
|
||||
|
||||
--
|
||||
2.52.0
|
||||
|
||||
@ -0,0 +1,56 @@
|
||||
From 58f5d129496cc8b4271daf5d0cd3ab31e8b926a8 Mon Sep 17 00:00:00 2001
|
||||
From: Akshay Adhikari <aadhikar@redhat.com>
|
||||
Date: Thu, 12 Feb 2026 12:47:23 +0530
|
||||
Subject: [PATCH] Issue 7233 - test_produce_division_by_zero fails with
|
||||
IsADirectoryError in conftest.py (#7234)
|
||||
|
||||
Description: glob('/*/*') matches directories causing open() to fail.
|
||||
|
||||
Fixes: #7233
|
||||
|
||||
Reviewed by: @droideck (Thanks!)
|
||||
---
|
||||
dirsrvtests/conftest.py | 2 ++
|
||||
.../suites/disk_monitoring/disk_monitoring_divide_test.py | 7 ++++---
|
||||
2 files changed, 6 insertions(+), 3 deletions(-)
|
||||
|
||||
diff --git a/dirsrvtests/conftest.py b/dirsrvtests/conftest.py
|
||||
index 0db6045f4..19b34c4a4 100644
|
||||
--- a/dirsrvtests/conftest.py
|
||||
+++ b/dirsrvtests/conftest.py
|
||||
@@ -126,6 +126,8 @@ def pytest_runtest_makereport(item, call):
|
||||
text = asan_report.read()
|
||||
extra.append(pytest_html.extras.text(text, name=os.path.basename(f)))
|
||||
for f in glob.glob(f'{p.log_dir.split("/slapd",1)[0]}/*/*'):
|
||||
+ if not os.path.isfile(f):
|
||||
+ continue
|
||||
if f.endswith('gz'):
|
||||
with gzip.open(f, 'rb') as dirsrv_log:
|
||||
text = dirsrv_log.read()
|
||||
diff --git a/dirsrvtests/tests/suites/disk_monitoring/disk_monitoring_divide_test.py b/dirsrvtests/tests/suites/disk_monitoring/disk_monitoring_divide_test.py
|
||||
index 9d952f93a..3e52e7c6b 100644
|
||||
--- a/dirsrvtests/tests/suites/disk_monitoring/disk_monitoring_divide_test.py
|
||||
+++ b/dirsrvtests/tests/suites/disk_monitoring/disk_monitoring_divide_test.py
|
||||
@@ -31,15 +31,16 @@ def create_dummy_mount(topology_st, request):
|
||||
log.info('Create dummy mount')
|
||||
for cmd in cmds:
|
||||
log.info('Command used : %s' % cmd)
|
||||
- subprocess.Popen(cmd, shell=True)
|
||||
+ subprocess.run(cmd, shell=True)
|
||||
|
||||
def fin():
|
||||
cmds = ['umount /var/log/dirsrv/slapd-{}/tmp'.format(topology_st.standalone.serverid),
|
||||
+ 'rmdir /var/log/dirsrv/slapd-{}/tmp'.format(topology_st.standalone.serverid),
|
||||
'setenforce 1']
|
||||
|
||||
for cmd in cmds:
|
||||
- log.info('Command used : %s' % cmds)
|
||||
- subprocess.Popen(cmd, shell=True)
|
||||
+ log.info('Command used : %s' % cmd)
|
||||
+ subprocess.run(cmd, shell=True)
|
||||
|
||||
request.addfinalizer(fin)
|
||||
|
||||
--
|
||||
2.52.0
|
||||
|
||||
226
0041-Issue-7241-Drop-dateutil-7242.patch
Normal file
226
0041-Issue-7241-Drop-dateutil-7242.patch
Normal file
@ -0,0 +1,226 @@
|
||||
From bbda49b86f3841ac5100894da426edc541b6226c Mon Sep 17 00:00:00 2001
|
||||
From: Viktor Ashirov <vashirov@redhat.com>
|
||||
Date: Thu, 12 Feb 2026 09:15:18 +0100
|
||||
Subject: [PATCH] Issue 7241 - Drop dateutil (#7242)
|
||||
|
||||
Bug Description:
|
||||
python-dateutil is unmaintained upstream and is marked for deprecation.
|
||||
|
||||
Fix Description:
|
||||
* Replace `dateutil.tz.tzoffset` with `datetime.timezone(datetime.timedelta())`.
|
||||
* Replace `dateutil.parser.parse` with standard `datetime` calls.
|
||||
* Import `datetime` as `dt` to avoid confusion between module and class.
|
||||
* Fix month lookup bug ('Oct': 9 / 'Sep': 10).
|
||||
|
||||
Fixes: https://github.com/389ds/389-ds-base/issues/7241
|
||||
|
||||
Reviewed by: jchapma, droideck (Thanks!)
|
||||
---
|
||||
.../suites/password/pwdPolicy_warning_test.py | 5 +-
|
||||
src/lib389/lib389/dirsrv_log.py | 48 ++++++++++++-------
|
||||
src/lib389/lib389/tests/dirsrv_log_test.py | 13 +++--
|
||||
src/lib389/pyproject.toml | 2 -
|
||||
src/lib389/requirements.txt | 1 -
|
||||
5 files changed, 38 insertions(+), 31 deletions(-)
|
||||
|
||||
diff --git a/dirsrvtests/tests/suites/password/pwdPolicy_warning_test.py b/dirsrvtests/tests/suites/password/pwdPolicy_warning_test.py
|
||||
index 154ec01f1..2341da6eb 100644
|
||||
--- a/dirsrvtests/tests/suites/password/pwdPolicy_warning_test.py
|
||||
+++ b/dirsrvtests/tests/suites/password/pwdPolicy_warning_test.py
|
||||
@@ -15,9 +15,8 @@ from lib389.topologies import topology_st
|
||||
from lib389.idm.user import UserAccounts
|
||||
from lib389.idm.organizationalunit import OrganizationalUnits
|
||||
from lib389._constants import (DEFAULT_SUFFIX, DN_CONFIG, PASSWORD, DN_DM)
|
||||
-from dateutil.parser import parse as dt_parse
|
||||
from lib389.config import Config
|
||||
-import datetime
|
||||
+import datetime as dt
|
||||
|
||||
pytestmark = pytest.mark.tier1
|
||||
|
||||
@@ -351,7 +350,7 @@ def test_with_different_password_states(topology_st, global_policy, add_user):
|
||||
old_ts = user.get_attr_val_utf8('passwordExpirationTime')
|
||||
log.info("Old passwordExpirationTime: {}".format(old_ts))
|
||||
|
||||
- new_ts = (dt_parse(old_ts) - datetime.timedelta(31)).strftime('%Y%m%d%H%M%SZ')
|
||||
+ new_ts = (dt.datetime.strptime(old_ts, '%Y%m%d%H%M%SZ') - dt.timedelta(31)).strftime('%Y%m%d%H%M%SZ')
|
||||
log.info("New passwordExpirationTime: {}".format(new_ts))
|
||||
user.replace('passwordExpirationTime', new_ts)
|
||||
|
||||
diff --git a/src/lib389/lib389/dirsrv_log.py b/src/lib389/lib389/dirsrv_log.py
|
||||
index e40105ad3..3d923a8bf 100644
|
||||
--- a/src/lib389/lib389/dirsrv_log.py
|
||||
+++ b/src/lib389/lib389/dirsrv_log.py
|
||||
@@ -10,11 +10,11 @@
|
||||
"""
|
||||
|
||||
import copy
|
||||
+import datetime as dt
|
||||
import json
|
||||
import glob
|
||||
import re
|
||||
import gzip
|
||||
-from dateutil.parser import parse as dt_parse
|
||||
from lib389.utils import ensure_bytes
|
||||
from lib389._mapped_object_lint import DSLint
|
||||
from lib389.lint import (
|
||||
@@ -34,8 +34,8 @@ MONTH_LOOKUP = {
|
||||
'Jun': 6,
|
||||
'Jul': 7,
|
||||
'Aug': 8,
|
||||
- 'Oct': 9,
|
||||
- 'Sep': 10,
|
||||
+ 'Sep': 9,
|
||||
+ 'Oct': 10,
|
||||
'Nov': 11,
|
||||
'Dec': 12,
|
||||
}
|
||||
@@ -50,9 +50,9 @@ class DirsrvLog(DSLint):
|
||||
"""
|
||||
self.dirsrv = dirsrv
|
||||
self.log = self.dirsrv.log
|
||||
- self.prog_timestamp = re.compile(r'\[(?P<day>\d*)\/(?P<month>\w*)\/(?P<year>\d*):(?P<hour>\d*):(?P<minute>\d*):(?P<second>\d*)(.(?P<nanosecond>\d*))+\s(?P<tz>[\+\-]\d*)') # noqa
|
||||
+ self.prog_timestamp = re.compile(r'\[(?P<day>\d*)\/(?P<month>\w*)\/(?P<year>\d*):(?P<hour>\d*):(?P<minute>\d*):(?P<second>\d*)(.(?P<nanosecond>\d*))+\s(?P<tz>[\+\-]\d{4})') # noqa
|
||||
# JSON timestamp uses strftime %FT%T --> 2025-02-12T17:00:47.663123181 -0500
|
||||
- self.prog_json_timestamp = re.compile(r'(?P<year>\d*)-(?P<month>\w*)-(?P<day>\d*)T(?P<hour>\d*):(?P<minute>\d*):(?P<second>\d*)(.(?P<nanosecond>\d*))+\s(?P<tz>[\+\-]\d*)') # noqa
|
||||
+ self.prog_json_timestamp = re.compile(r'(?P<year>\d*)-(?P<month>\w*)-(?P<day>\d*)T(?P<hour>\d*):(?P<minute>\d*):(?P<second>\d*)(.(?P<nanosecond>\d*))+\s(?P<tz>[\+\-]\d{4})') # noqa
|
||||
self.prog_datetime = re.compile(r'^(?P<timestamp>\[.*\])')
|
||||
self.jsonFormat = False
|
||||
|
||||
@@ -157,20 +157,32 @@ class DirsrvLog(DSLint):
|
||||
else:
|
||||
timedata = self.prog_timestamp.match(ts).groupdict()
|
||||
|
||||
- # Now, have to convert month to an int.
|
||||
- dt_str = '{YEAR}-{MONTH}-{DAY} {HOUR}-{MINUTE}-{SECOND} {TZ}'.format(
|
||||
- YEAR=timedata['year'],
|
||||
- MONTH=timedata['month'],
|
||||
- DAY=timedata['day'],
|
||||
- HOUR=timedata['hour'],
|
||||
- MINUTE=timedata['minute'],
|
||||
- SECOND=timedata['second'],
|
||||
- TZ=timedata['tz'],
|
||||
- )
|
||||
- dt = dt_parse(dt_str)
|
||||
+ # Convert month to an int.
|
||||
+ month = timedata['month']
|
||||
+ if not month.isdigit():
|
||||
+ month = MONTH_LOOKUP[month]
|
||||
+ else:
|
||||
+ month = int(month)
|
||||
+
|
||||
+ # Parse timezone offset string (e.g. "+1000" or "-0500") into a timezone
|
||||
+ tz_str = timedata['tz']
|
||||
+ tz_sign = 1 if tz_str[0] == '+' else -1
|
||||
+ tz_hours = int(tz_str[1:3])
|
||||
+ tz_minutes = int(tz_str[3:5])
|
||||
+ tz = dt.timezone(dt.timedelta(hours=tz_sign * tz_hours, minutes=tz_sign * tz_minutes))
|
||||
+
|
||||
+ parsed_dt = dt.datetime(
|
||||
+ int(timedata['year']),
|
||||
+ month,
|
||||
+ int(timedata['day']),
|
||||
+ int(timedata['hour']),
|
||||
+ int(timedata['minute']),
|
||||
+ int(timedata['second']),
|
||||
+ tzinfo=tz
|
||||
+ )
|
||||
if timedata['nanosecond']:
|
||||
- dt = dt.replace(microsecond=int(int(timedata['nanosecond']) / 1000))
|
||||
- return dt
|
||||
+ parsed_dt = parsed_dt.replace(microsecond=int(timedata['nanosecond']) // 1000)
|
||||
+ return parsed_dt
|
||||
|
||||
def get_time_in_secs(self, log_line):
|
||||
"""Take the timestamp (not the date) from a DS access log and convert
|
||||
diff --git a/src/lib389/lib389/tests/dirsrv_log_test.py b/src/lib389/lib389/tests/dirsrv_log_test.py
|
||||
index 920e67a01..d0259ced9 100644
|
||||
--- a/src/lib389/lib389/tests/dirsrv_log_test.py
|
||||
+++ b/src/lib389/lib389/tests/dirsrv_log_test.py
|
||||
@@ -12,8 +12,7 @@ from lib389 import DirSrv, Entry
|
||||
import pytest
|
||||
import time
|
||||
import shutil
|
||||
-import datetime
|
||||
-from dateutil.tz import tzoffset
|
||||
+import datetime as dt
|
||||
|
||||
INSTANCE_PORT = 54321
|
||||
INSTANCE_SERVERID = 'standalone'
|
||||
@@ -74,7 +73,7 @@ def test_access_log(topology):
|
||||
topology.standalone.ds_access_log.parse_line('[27/Apr/2016:12:49:49.726093186 +1000] conn=1 fd=64 slot=64 connection from ::1 to ::1') ==
|
||||
{
|
||||
'slot': '64', 'remote': '::1', 'action': 'CONNECT', 'timestamp': '[27/Apr/2016:12:49:49.726093186 +1000]', 'fd': '64', 'conn': '1', 'local': '::1',
|
||||
- 'datetime': datetime.datetime(2016, 4, 27, 12, 0, 0, 726093, tzinfo=tzoffset(None, 36000))
|
||||
+ 'datetime': dt.datetime(2016, 4, 27, 12, 49, 49, 726093, tzinfo=dt.timezone(dt.timedelta(seconds=36000)))
|
||||
}
|
||||
)
|
||||
assert(
|
||||
@@ -82,21 +81,21 @@ def test_access_log(topology):
|
||||
{
|
||||
'rem': 'base="cn=config" scope=0 filter="(objectClass=*)" attrs="nsslapd-instancedir nsslapd-errorlog nsslapd-accesslog nsslapd-auditlog nsslapd-certdir nsslapd-schemadir nsslapd-bakdir nsslapd-ldifdir"', # noqa
|
||||
'action': 'SRCH', 'timestamp': '[27/Apr/2016:12:49:49.727235997 +1000]', 'conn': '1', 'op': '2',
|
||||
- 'datetime': datetime.datetime(2016, 4, 27, 12, 0, 0, 727235, tzinfo=tzoffset(None, 36000))
|
||||
+ 'datetime': dt.datetime(2016, 4, 27, 12, 49, 49, 727235, tzinfo=dt.timezone(dt.timedelta(seconds=36000)))
|
||||
}
|
||||
)
|
||||
assert(
|
||||
topology.standalone.ds_access_log.parse_line('[27/Apr/2016:12:49:49.736297002 +1000] conn=1 op=4 fd=64 closed - U1') ==
|
||||
{
|
||||
'status': 'U1', 'fd': '64', 'action': 'DISCONNECT', 'timestamp': '[27/Apr/2016:12:49:49.736297002 +1000]', 'conn': '1', 'op': '4',
|
||||
- 'datetime': datetime.datetime(2016, 4, 27, 12, 0, 0, 736297, tzinfo=tzoffset(None, 36000))
|
||||
+ 'datetime': dt.datetime(2016, 4, 27, 12, 49, 49, 736297, tzinfo=dt.timezone(dt.timedelta(seconds=36000)))
|
||||
}
|
||||
)
|
||||
assert(
|
||||
topology.standalone.ds_access_log.parse_line('[27/Apr/2016:12:49:49.736297002 -1000] conn=1 op=4 fd=64 closed - U1') ==
|
||||
{
|
||||
'status': 'U1', 'fd': '64', 'action': 'DISCONNECT', 'timestamp': '[27/Apr/2016:12:49:49.736297002 -1000]', 'conn': '1', 'op': '4',
|
||||
- 'datetime': datetime.datetime(2016, 4, 27, 12, 0, 0, 736297, tzinfo=tzoffset(None, -36000))
|
||||
+ 'datetime': dt.datetime(2016, 4, 27, 12, 49, 49, 736297, tzinfo=dt.timezone(dt.timedelta(seconds=-36000)))
|
||||
}
|
||||
)
|
||||
|
||||
@@ -113,7 +112,7 @@ def test_error_log(topology):
|
||||
topology.standalone.ds_error_log.parse_line('[27/Apr/2016:13:46:35.775670167 +1000] slapd started. Listening on All Interfaces port 54321 for LDAP requests') == # noqa
|
||||
{
|
||||
'timestamp': '[27/Apr/2016:13:46:35.775670167 +1000]', 'message': 'slapd started. Listening on All Interfaces port 54321 for LDAP requests',
|
||||
- 'datetime': datetime.datetime(2016, 4, 27, 13, 0, 0, 775670, tzinfo=tzoffset(None, 36000))
|
||||
+ 'datetime': dt.datetime(2016, 4, 27, 13, 46, 35, 775670, tzinfo=dt.timezone(dt.timedelta(seconds=36000)))
|
||||
}
|
||||
)
|
||||
|
||||
diff --git a/src/lib389/pyproject.toml b/src/lib389/pyproject.toml
|
||||
index 63c7c9710..e067d1590 100644
|
||||
--- a/src/lib389/pyproject.toml
|
||||
+++ b/src/lib389/pyproject.toml
|
||||
@@ -5,7 +5,6 @@ requires = [
|
||||
"argparse-manpage[setuptools]",
|
||||
"pyasn1",
|
||||
"pyasn1-modules",
|
||||
- "python-dateutil",
|
||||
"argcomplete",
|
||||
"python-ldap",
|
||||
"distro",
|
||||
@@ -43,7 +42,6 @@ classifiers = [
|
||||
dependencies = [
|
||||
"pyasn1",
|
||||
"pyasn1-modules",
|
||||
- "python-dateutil",
|
||||
"argcomplete",
|
||||
"python-ldap",
|
||||
"distro",
|
||||
diff --git a/src/lib389/requirements.txt b/src/lib389/requirements.txt
|
||||
index 5e1b3dad7..94b10e3c2 100644
|
||||
--- a/src/lib389/requirements.txt
|
||||
+++ b/src/lib389/requirements.txt
|
||||
@@ -1,6 +1,5 @@
|
||||
pyasn1
|
||||
pyasn1-modules
|
||||
-python-dateutil
|
||||
argcomplete
|
||||
argparse-manpage
|
||||
python-ldap
|
||||
--
|
||||
2.52.0
|
||||
|
||||
@ -0,0 +1,94 @@
|
||||
From d19c50372d5c5d901f05ce6e7dd03313f41fc197 Mon Sep 17 00:00:00 2001
|
||||
From: James Chapman <jachapma@redhat.com>
|
||||
Date: Thu, 12 Feb 2026 10:42:09 +0000
|
||||
Subject: [PATCH] Issue 7231 - Sync repl tests fail in FIPS mode due to non
|
||||
FIPS compliant crypto (#7232)
|
||||
|
||||
Description:
|
||||
Several sync_repl tests fail when running on a FIPS enabled system. The failures
|
||||
are caused by the sync repl client (Sync_persist), using TLS options and ciphers
|
||||
that are not FIPS compatible.
|
||||
|
||||
Fix:
|
||||
Update the sync repl client to use FIPS approved TLS version.
|
||||
|
||||
Fixes: https://github.com/389ds/389-ds-base/issues/7231
|
||||
|
||||
Reviewed by: @progier389, @droideck (Thank you)
|
||||
---
|
||||
.../tests/suites/syncrepl_plugin/basic_test.py | 14 +++++++++++---
|
||||
1 file changed, 11 insertions(+), 3 deletions(-)
|
||||
|
||||
diff --git a/dirsrvtests/tests/suites/syncrepl_plugin/basic_test.py b/dirsrvtests/tests/suites/syncrepl_plugin/basic_test.py
|
||||
index 85b4ac078..d0e7e8a32 100644
|
||||
--- a/dirsrvtests/tests/suites/syncrepl_plugin/basic_test.py
|
||||
+++ b/dirsrvtests/tests/suites/syncrepl_plugin/basic_test.py
|
||||
@@ -21,7 +21,7 @@ from lib389.idm.group import Groups
|
||||
from lib389.topologies import topology_st as topology
|
||||
from lib389.topologies import topology_m2 as topo_m2
|
||||
from lib389.paths import Paths
|
||||
-from lib389.utils import ds_is_older
|
||||
+from lib389.utils import ds_is_older, is_fips
|
||||
from lib389.plugins import RetroChangelogPlugin, ContentSyncPlugin, AutoMembershipPlugin, MemberOfPlugin, MemberOfSharedConfig, AutoMembershipDefinitions, MEPTemplates, MEPConfigs, ManagedEntriesPlugin, MEPTemplate
|
||||
from lib389._constants import *
|
||||
|
||||
@@ -214,10 +214,13 @@ class Sync_persist(threading.Thread, ReconnectLDAPObject, SyncreplConsumer):
|
||||
|
||||
def run(self):
|
||||
"""Start a sync repl client"""
|
||||
- ldap.set_option(ldap.OPT_X_TLS_CACERTFILE, os.path.join(self.inst.get_config_dir(), "ca.crt"))
|
||||
ldap_connection = TestSyncer(self.inst.toLDAPURL())
|
||||
ldap_connection.set_option(ldap.OPT_X_TLS_REQUIRE_CERT, ldap.OPT_X_TLS_DEMAND)
|
||||
+ ldap_connection.set_option(ldap.OPT_X_TLS_CACERTFILE, os.path.join(self.inst.get_config_dir(), "ca.crt"))
|
||||
+ if is_fips():
|
||||
+ ldap_connection.set_option(ldap.OPT_X_TLS_PROTOCOL_MIN, ldap.OPT_X_TLS_PROTOCOL_TLS1_2)
|
||||
ldap_connection.set_option(ldap.OPT_X_TLS_NEWCTX, 0)
|
||||
+
|
||||
ldap_connection.simple_bind_s('cn=directory manager', 'password')
|
||||
ldap_search = ldap_connection.syncrepl_search(
|
||||
"dc=example,dc=com",
|
||||
@@ -257,6 +260,7 @@ def test_sync_repl_mep(topology, request):
|
||||
5. Success
|
||||
"""
|
||||
inst = topology[0]
|
||||
+ inst.enable_tls()
|
||||
|
||||
# Enable/configure retroCL
|
||||
plugin = RetroChangelogPlugin(inst)
|
||||
@@ -342,6 +346,7 @@ def test_sync_repl_cookie(topology, init_sync_repl_plugins, request):
|
||||
5.: succeeds
|
||||
"""
|
||||
inst = topology[0]
|
||||
+ inst.enable_tls()
|
||||
|
||||
# create a sync repl client and wait 5 seconds to be sure it is running
|
||||
sync_repl = Sync_persist(inst)
|
||||
@@ -408,6 +413,8 @@ def test_sync_repl_cookie_add_del(topology, init_sync_repl_plugins, request):
|
||||
6.: succeeds
|
||||
"""
|
||||
inst = topology[0]
|
||||
+ inst.enable_tls()
|
||||
+
|
||||
# create a sync repl client and wait 5 seconds to be sure it is running
|
||||
sync_repl = Sync_persist(inst)
|
||||
sync_repl.start()
|
||||
@@ -551,6 +558,7 @@ def test_sync_repl_cenotaph(topo_m2, request):
|
||||
5. Should succeeds
|
||||
"""
|
||||
m1 = topo_m2.ms["supplier1"]
|
||||
+ m1.enable_tls()
|
||||
# Enable/configure retroCL
|
||||
plugin = RetroChangelogPlugin(m1)
|
||||
plugin.disable()
|
||||
@@ -609,7 +617,7 @@ def test_sync_repl_dynamic_plugin(topology, request):
|
||||
3. Should succeeds
|
||||
4. Should succeeds
|
||||
"""
|
||||
-
|
||||
+ topology.standalone.enable_tls()
|
||||
# Reset the instance in a default config
|
||||
# Disable content sync plugin
|
||||
topology.standalone.plugins.disable(name=PLUGIN_REPL_SYNC)
|
||||
--
|
||||
2.52.0
|
||||
|
||||
@ -0,0 +1,33 @@
|
||||
From 1df3852cf0e073cfe006d661aecdd909862fc79a Mon Sep 17 00:00:00 2001
|
||||
From: Mark Reynolds <mreynolds@redhat.com>
|
||||
Date: Thu, 12 Feb 2026 09:58:54 -0500
|
||||
Subject: [PATCH] Issue 7248 - CLI - attribute uniqueness - fix usage for
|
||||
exclude subtree option
|
||||
|
||||
Description:
|
||||
|
||||
Fix typo in usage message for the exclude subtree option
|
||||
|
||||
relates: https://github.com/389ds/389-ds-base/issues/7248
|
||||
|
||||
Reviewed by: progier (Thanks!)
|
||||
---
|
||||
src/lib389/lib389/cli_conf/plugins/attruniq.py | 2 +-
|
||||
1 file changed, 1 insertion(+), 1 deletion(-)
|
||||
|
||||
diff --git a/src/lib389/lib389/cli_conf/plugins/attruniq.py b/src/lib389/lib389/cli_conf/plugins/attruniq.py
|
||||
index bc925eb1c..26ca5d819 100644
|
||||
--- a/src/lib389/lib389/cli_conf/plugins/attruniq.py
|
||||
+++ b/src/lib389/lib389/cli_conf/plugins/attruniq.py
|
||||
@@ -127,7 +127,7 @@ def _add_parser_args(parser):
|
||||
help='Sets the DN under which the plug-in checks for uniqueness of '
|
||||
'the attributes value. This attribute is multi-valued (uniqueness-subtrees)')
|
||||
parser.add_argument('--exclude-subtree', nargs='+',
|
||||
- help='Sets subtrees that should not excludedfrom attribute uniqueness. '
|
||||
+ help='Sets subtrees that should be excluded from attribute uniqueness checks. '
|
||||
'This attribute is multi-valued (uniqueness-exclude-subtrees)')
|
||||
parser.add_argument('--across-all-subtrees', choices=['on', 'off'], type=str.lower,
|
||||
help='If enabled (on), the plug-in checks that the attribute is unique across all subtrees '
|
||||
--
|
||||
2.52.0
|
||||
|
||||
201
0044-Issue-CLI-dsctl-db2index-needs-some-hardening-with-M.patch
Normal file
201
0044-Issue-CLI-dsctl-db2index-needs-some-hardening-with-M.patch
Normal file
@ -0,0 +1,201 @@
|
||||
From 63bf648f699b8fcd8f319254b0348d969ceea7a0 Mon Sep 17 00:00:00 2001
|
||||
From: Mark Reynolds <mreynolds@redhat.com>
|
||||
Date: Thu, 12 Feb 2026 11:13:45 -0500
|
||||
Subject: [PATCH] Issue - CLI - dsctl db2index needs some hardening with MBD
|
||||
|
||||
Description:
|
||||
|
||||
The usage for dsctl db2index was confusing. The way the attr options and
|
||||
backend name were displayed it looks like the backend name could come after
|
||||
the attributes, but instead the backend name was treated as an attribute.
|
||||
|
||||
Instead make the backend name required, and change the attribute naming to
|
||||
require individual options instead of a list of values.
|
||||
|
||||
Relates: https://github.com/389ds/389-ds-base/issues/7250
|
||||
|
||||
Reviewed by: progier(Thanks!)
|
||||
---
|
||||
.../tests/suites/import/import_test.py | 2 +-
|
||||
src/lib389/lib389/__init__.py | 40 ++++++-------------
|
||||
src/lib389/lib389/cli_ctl/dbtasks.py | 37 +++++++----------
|
||||
3 files changed, 27 insertions(+), 52 deletions(-)
|
||||
|
||||
diff --git a/dirsrvtests/tests/suites/import/import_test.py b/dirsrvtests/tests/suites/import/import_test.py
|
||||
index c7275e4cb..ad6d05a1e 100644
|
||||
--- a/dirsrvtests/tests/suites/import/import_test.py
|
||||
+++ b/dirsrvtests/tests/suites/import/import_test.py
|
||||
@@ -514,7 +514,7 @@ def test_entry_with_escaped_characters_fails_to_import_and_index(topo, _import_c
|
||||
count += 1
|
||||
# Now re-index the database
|
||||
topo.standalone.stop()
|
||||
- topo.standalone.db2index()
|
||||
+ topo.standalone.db2index(bename="userroot")
|
||||
topo.standalone.start()
|
||||
# Should not return error.
|
||||
assert not topo.standalone.searchErrorsLog('error')
|
||||
diff --git a/src/lib389/lib389/__init__.py b/src/lib389/lib389/__init__.py
|
||||
index d57a91929..01b3ea23c 100644
|
||||
--- a/src/lib389/lib389/__init__.py
|
||||
+++ b/src/lib389/lib389/__init__.py
|
||||
@@ -2953,7 +2953,7 @@ class DirSrv(SimpleLDAPObject, object):
|
||||
|
||||
return True
|
||||
|
||||
- def db2index(self, bename=None, suffixes=None, attrs=None, vlvTag=None):
|
||||
+ def db2index(self, bename, suffixes=None, attrs=None, vlvTag=None):
|
||||
"""
|
||||
@param bename - The backend name to reindex
|
||||
@param suffixes - List/tuple of suffixes to reindex, currently unused
|
||||
@@ -2966,34 +2966,18 @@ class DirSrv(SimpleLDAPObject, object):
|
||||
if self.status():
|
||||
self.log.error("db2index: Can not operate while directory server is running")
|
||||
return False
|
||||
- cmd = [prog, ]
|
||||
- # No backend specified, do an upgrade on all backends
|
||||
- # Backend and no attrs specified, reindex with all backend indexes
|
||||
- # Backend and attr/s specified, reindex backend with attr/s
|
||||
- if bename:
|
||||
- cmd.append('db2index')
|
||||
- cmd.append('-n')
|
||||
- cmd.append(bename)
|
||||
- if attrs:
|
||||
- for attr in attrs:
|
||||
- cmd.append('-t')
|
||||
- cmd.append(attr)
|
||||
- else:
|
||||
- dse_ldif = DSEldif(self)
|
||||
- indexes = dse_ldif.get_indexes(bename)
|
||||
- if indexes:
|
||||
- for idx in indexes:
|
||||
- cmd.append('-t')
|
||||
- cmd.append(idx)
|
||||
+ cmd = [prog, 'db2index', '-n', bename, '-D', self.get_config_dir()]
|
||||
+ if attrs:
|
||||
+ for attr in attrs:
|
||||
+ cmd.append('-t')
|
||||
+ cmd.append(attr)
|
||||
else:
|
||||
- cmd.append('upgradedb')
|
||||
- cmd.append('-a')
|
||||
- now = datetime.now().isoformat()
|
||||
- cmd.append(os.path.join(self.get_bak_dir(), 'reindex_%s' % now))
|
||||
- cmd.append('-f')
|
||||
-
|
||||
- cmd.append('-D')
|
||||
- cmd.append(self.get_config_dir())
|
||||
+ dse_ldif = DSEldif(self)
|
||||
+ indexes = dse_ldif.get_indexes(bename)
|
||||
+ if indexes:
|
||||
+ for idx in indexes:
|
||||
+ cmd.append('-t')
|
||||
+ cmd.append(idx)
|
||||
|
||||
try:
|
||||
result = subprocess.check_output(cmd, encoding='utf-8')
|
||||
diff --git a/src/lib389/lib389/cli_ctl/dbtasks.py b/src/lib389/lib389/cli_ctl/dbtasks.py
|
||||
index 16da966d1..cd96cdaf7 100644
|
||||
--- a/src/lib389/lib389/cli_ctl/dbtasks.py
|
||||
+++ b/src/lib389/lib389/cli_ctl/dbtasks.py
|
||||
@@ -26,32 +26,18 @@ class IndexOrdering(Enum):
|
||||
|
||||
|
||||
def dbtasks_db2index(inst, log, args):
|
||||
- rtn = False
|
||||
- if not args.backend:
|
||||
- if not inst.db2index():
|
||||
- rtn = False
|
||||
- else:
|
||||
- rtn = True
|
||||
- elif args.backend and not args.attr:
|
||||
- if not inst.db2index(bename=args.backend):
|
||||
- rtn = False
|
||||
- else:
|
||||
- rtn = True
|
||||
+ inst.log = log
|
||||
+ if not inst.db2index(bename=args.backend, attrs=args.attr):
|
||||
+ log.fatal("db2index failed")
|
||||
+ return False
|
||||
else:
|
||||
- if not inst.db2index(bename=args.backend, attrs=args.attr):
|
||||
- rtn = False
|
||||
- else:
|
||||
- rtn = True
|
||||
- if rtn:
|
||||
log.info("db2index successful")
|
||||
- return rtn
|
||||
- else:
|
||||
- log.fatal("db2index failed")
|
||||
- return rtn
|
||||
+ return True
|
||||
|
||||
|
||||
def dbtasks_db2bak(inst, log, args):
|
||||
# Needs an output name?
|
||||
+ inst.log = log
|
||||
if not inst.db2bak(args.archive):
|
||||
log.fatal("db2bak failed")
|
||||
return False
|
||||
@@ -61,6 +47,7 @@ def dbtasks_db2bak(inst, log, args):
|
||||
|
||||
def dbtasks_bak2db(inst, log, args):
|
||||
# Needs the archive to restore.
|
||||
+ inst.log = log
|
||||
if not inst.bak2db(args.archive):
|
||||
log.fatal("bak2db failed")
|
||||
return False
|
||||
@@ -70,6 +57,7 @@ def dbtasks_bak2db(inst, log, args):
|
||||
|
||||
def dbtasks_db2ldif(inst, log, args):
|
||||
# If export filename is provided, check if file path exists
|
||||
+ inst.log = log
|
||||
if args.ldif:
|
||||
path = Path(args.ldif)
|
||||
parent = path.parent.absolute()
|
||||
@@ -88,6 +76,7 @@ def dbtasks_db2ldif(inst, log, args):
|
||||
|
||||
def dbtasks_ldif2db(inst, log, args):
|
||||
# Check if ldif file exists
|
||||
+ inst.log = log
|
||||
if not os.path.exists(args.ldif):
|
||||
raise ValueError("The LDIF file does not exist: " + args.ldif)
|
||||
|
||||
@@ -103,6 +92,7 @@ def dbtasks_ldif2db(inst, log, args):
|
||||
|
||||
|
||||
def dbtasks_backups(inst, log, args):
|
||||
+ inst.log = log
|
||||
if args.delete:
|
||||
# Delete backup
|
||||
inst.del_backup(args.delete[0])
|
||||
@@ -117,6 +107,7 @@ def dbtasks_backups(inst, log, args):
|
||||
|
||||
|
||||
def dbtasks_ldifs(inst, log, args):
|
||||
+ inst.log = log
|
||||
if args.delete:
|
||||
# Delete LDIF file
|
||||
inst.del_ldif(args.delete[0])
|
||||
@@ -131,6 +122,7 @@ def dbtasks_ldifs(inst, log, args):
|
||||
|
||||
|
||||
def dbtasks_verify(inst, log, args):
|
||||
+ inst.log = log
|
||||
if not inst.dbverify(bename=args.backend):
|
||||
log.fatal("dbverify failed")
|
||||
return False
|
||||
@@ -521,9 +513,8 @@ def dbtasks_index_check(inst, log, args):
|
||||
|
||||
def create_parser(subcommands):
|
||||
db2index_parser = subcommands.add_parser('db2index', help="Initialise a reindex of the server database. The server must be stopped for this to proceed.", formatter_class=CustomHelpFormatter)
|
||||
- # db2index_parser.add_argument('suffix', help="The suffix to reindex. IE dc=example,dc=com.")
|
||||
- db2index_parser.add_argument('backend', nargs="?", help="The backend to reindex. IE userRoot", default=False)
|
||||
- db2index_parser.add_argument('--attr', nargs="*", help="The attribute's to reindex. IE --attr aci cn givenname", default=False)
|
||||
+ db2index_parser.add_argument('backend', help="The backend to reindex. IE userRoot")
|
||||
+ db2index_parser.add_argument('--attr', action='append', help="An attribute to reindex. IE: --attr member --attr cn ...")
|
||||
db2index_parser.set_defaults(func=dbtasks_db2index)
|
||||
|
||||
db2bak_parser = subcommands.add_parser('db2bak', help="Initialise a BDB backup of the database. The server must be stopped for this to proceed.", formatter_class=CustomHelpFormatter)
|
||||
--
|
||||
2.52.0
|
||||
|
||||
@ -0,0 +1,74 @@
|
||||
From d52901f69e9b7952b33b219ec197308a1a20bda9 Mon Sep 17 00:00:00 2001
|
||||
From: progier389 <progier@redhat.com>
|
||||
Date: Fri, 13 Feb 2026 15:13:05 +0100
|
||||
Subject: [PATCH] Issue 7252 - PQC - Need to iterate on SECOidTag instead of
|
||||
using OID (#7254)
|
||||
|
||||
* Issue 7252 - PQC - Need to iterate on SECOidTag instead of using OID
|
||||
|
||||
Need to dynamically iterate on SECOidTag instead of using SEC_OID_ML_DSA_* OIDs to avoid issue with upcoming nss versions and fix a RHEL build break with nss 3.112
|
||||
|
||||
Issue: #7252
|
||||
|
||||
Reviewed by: @mreynolds389, @droideck, @vashirov
|
||||
|
||||
|
||||
* Update ldap/servers/slapd/ssl.c
|
||||
|
||||
Co-authored-by: Simon Pichugin <spichugi@redhat.com>
|
||||
|
||||
---------
|
||||
|
||||
Co-authored-by: Simon Pichugin <spichugi@redhat.com>
|
||||
---
|
||||
ldap/servers/slapd/ssl.c | 24 +++++++++---------------
|
||||
1 file changed, 9 insertions(+), 15 deletions(-)
|
||||
|
||||
diff --git a/ldap/servers/slapd/ssl.c b/ldap/servers/slapd/ssl.c
|
||||
index 7d5db2cdd..d05c64fb1 100644
|
||||
--- a/ldap/servers/slapd/ssl.c
|
||||
+++ b/ldap/servers/slapd/ssl.c
|
||||
@@ -732,31 +732,25 @@ SSLPLCY_Install(void)
|
||||
{
|
||||
|
||||
SECStatus s = 0;
|
||||
-#ifdef MAX_ML_DSA_PRIVATE_KEY_LEN
|
||||
int flags = NSS_USE_ALG_IN_SIGNATURE | NSS_USE_ALG_IN_SSL;
|
||||
- static const SECOidTag oids[] = {
|
||||
- SEC_OID_ML_DSA_44,
|
||||
- SEC_OID_ML_DSA_65,
|
||||
- SEC_OID_ML_DSA_87,
|
||||
- };
|
||||
-#endif
|
||||
+ SECOidData *oid = NULL;
|
||||
|
||||
s = NSS_SetDomesticPolicy();
|
||||
|
||||
-#ifdef MAX_ML_DSA_PRIVATE_KEY_LEN
|
||||
/* Should rely on the crypto module policy in FIPS mode */
|
||||
if (!slapd_pk11_isFIPS()) {
|
||||
/* Set explicitly PQC algorithm policy if it is not set by default */
|
||||
- for (size_t i=0; s == SECSuccess && i < PR_ARRAY_SIZE(oids); i++) {
|
||||
- PRUint32 oflags = 0;
|
||||
- (void) NSS_GetAlgorithmPolicy(oids[i], &oflags);
|
||||
- if ((oflags & flags) != flags) {
|
||||
- s = NSS_SetAlgorithmPolicy(oids[i], flags, 0);
|
||||
+ for (SECOidTag tag = 1; s == SECSuccess && (oid = SECOID_FindOIDByTag(tag)) != NULL; tag++) {
|
||||
+ if (oid->mechanism != CKM_INVALID_MECHANISM &&
|
||||
+ PL_strncasecmp(oid->desc, "ML-DSA-", 7) == 0) {
|
||||
+ PRUint32 oflags = 0;
|
||||
+ (void) NSS_GetAlgorithmPolicy(tag, &oflags);
|
||||
+ if ((oflags & flags) != flags) {
|
||||
+ s = NSS_SetAlgorithmPolicy(tag, flags, 0);
|
||||
+ }
|
||||
}
|
||||
}
|
||||
}
|
||||
-#endif
|
||||
-
|
||||
return s ? PR_FAILURE : PR_SUCCESS;
|
||||
}
|
||||
|
||||
--
|
||||
2.52.0
|
||||
|
||||
3031
0046-Issue-6951-Dynamic-Certificas-Refresh-CI-tests-7238.patch
Normal file
3031
0046-Issue-6951-Dynamic-Certificas-Refresh-CI-tests-7238.patch
Normal file
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,41 @@
|
||||
From 48ad61231203d9ccb96d0fe542aae93dbb74a9bf Mon Sep 17 00:00:00 2001
|
||||
From: Viktor Ashirov <vashirov@redhat.com>
|
||||
Date: Fri, 13 Feb 2026 15:38:52 +0100
|
||||
Subject: [PATCH] Issue 7184 - (2nd) argparse.HelpFormatter
|
||||
_format_actions_usage() is deprecated (#7257)
|
||||
|
||||
Description:
|
||||
`_format_actions_usage()` was also removed in Python 3.14.3.
|
||||
Replace version check with `isinstance()` to handle the return type of
|
||||
`_get_actions_usage_parts()` more robustly across Python versions.
|
||||
|
||||
Relates: https://github.com/389ds/389-ds-base/issues/7184
|
||||
Fixes: https://github.com/389ds/389-ds-base/issues/7253
|
||||
|
||||
Reviewed by: @progier389 (Thanks!)
|
||||
---
|
||||
src/lib389/lib389/cli_base/__init__.py | 6 +++---
|
||||
1 file changed, 3 insertions(+), 3 deletions(-)
|
||||
|
||||
diff --git a/src/lib389/lib389/cli_base/__init__.py b/src/lib389/lib389/cli_base/__init__.py
|
||||
index f1055aadc..3af8a46e6 100644
|
||||
--- a/src/lib389/lib389/cli_base/__init__.py
|
||||
+++ b/src/lib389/lib389/cli_base/__init__.py
|
||||
@@ -420,11 +420,11 @@ class CustomHelpFormatter(argparse.HelpFormatter):
|
||||
else:
|
||||
# Use _get_actions_usage_parts() for Python 3.13 and later
|
||||
action_parts = self._get_actions_usage_parts(parent_arguments, [])
|
||||
- if sys.version_info >= (3, 15):
|
||||
- # Python 3.15 returns a tuple (list of actions, count of actions)
|
||||
+ if isinstance(action_parts, tuple):
|
||||
+ # Python 3.14.3+ and 3.15+ return a tuple (list of actions, count of actions)
|
||||
formatted_options = ' '.join(action_parts[0])
|
||||
else:
|
||||
- # Python 3.13 and 3.14 return a list of actions
|
||||
+ # Earlier versions return a list of actions
|
||||
formatted_options = ' '.join(action_parts)
|
||||
|
||||
# If formatted_options already in usage - remove them
|
||||
--
|
||||
2.52.0
|
||||
|
||||
@ -0,0 +1,32 @@
|
||||
From c7ef5b3073bbd94a5d2b544556368c830c165e0d Mon Sep 17 00:00:00 2001
|
||||
From: Viktor Ashirov <vashirov@redhat.com>
|
||||
Date: Fri, 13 Feb 2026 16:27:25 +0100
|
||||
Subject: [PATCH] Issue 7213 - (2nd) MDB_BAD_VALSIZE error while handling VLV
|
||||
(#7258)
|
||||
|
||||
Decription:
|
||||
Disable test_vlv_long_attribute_value on BDB as it hangs sometimes in
|
||||
CI, blocking other pipelines.
|
||||
|
||||
Relates: https://github.com/389ds/389-ds-base/issues/7213
|
||||
|
||||
Reviewed by: @progier389 (Thanks!)
|
||||
---
|
||||
dirsrvtests/tests/suites/vlv/regression_test.py | 1 +
|
||||
1 file changed, 1 insertion(+)
|
||||
|
||||
diff --git a/dirsrvtests/tests/suites/vlv/regression_test.py b/dirsrvtests/tests/suites/vlv/regression_test.py
|
||||
index 7cdf16a84..89a747199 100644
|
||||
--- a/dirsrvtests/tests/suites/vlv/regression_test.py
|
||||
+++ b/dirsrvtests/tests/suites/vlv/regression_test.py
|
||||
@@ -1175,6 +1175,7 @@ def test_vlv_with_mr(vlv_setup_with_uid_mr):
|
||||
|
||||
|
||||
|
||||
+@pytest.mark.skipif(get_default_db_lib() == "bdb", reason="Hangs on BDB")
|
||||
def test_vlv_long_attribute_value(topology_st, request):
|
||||
"""
|
||||
Test VLV with an entry containing a very long attribute value (2K).
|
||||
--
|
||||
2.52.0
|
||||
|
||||
@ -0,0 +1,34 @@
|
||||
From 7e575cc8cc6f1bf558f50ca0fc55145e469d60d2 Mon Sep 17 00:00:00 2001
|
||||
From: Viktor Ashirov <vashirov@redhat.com>
|
||||
Date: Fri, 13 Feb 2026 16:58:24 +0100
|
||||
Subject: [PATCH] Issue 7223 - Use lexicographical order for ancestorid (#7256)
|
||||
|
||||
Description:
|
||||
`ldbm_instance_create_default_indexes()` configured ancestorid with
|
||||
integerOrderingMatch in the in-memory attrinfo, but ancestorid on disk
|
||||
might be using lexicographic ordering (data before the upgrade or after
|
||||
ldif2db import).
|
||||
|
||||
Relates: https://github.com/389ds/389-ds-base/issues/7223
|
||||
|
||||
Reviewed by: @tbordaz (Thanks!)
|
||||
---
|
||||
ldap/servers/slapd/back-ldbm/instance.c | 2 +-
|
||||
1 file changed, 1 insertion(+), 1 deletion(-)
|
||||
|
||||
diff --git a/ldap/servers/slapd/back-ldbm/instance.c b/ldap/servers/slapd/back-ldbm/instance.c
|
||||
index 17bfc09a0..1569eb7ff 100644
|
||||
--- a/ldap/servers/slapd/back-ldbm/instance.c
|
||||
+++ b/ldap/servers/slapd/back-ldbm/instance.c
|
||||
@@ -231,7 +231,7 @@ ldbm_instance_create_default_indexes(backend *be)
|
||||
* ancestorid is special, there is actually no such attr type
|
||||
* but we still want to use the attr index file APIs.
|
||||
*/
|
||||
- e = ldbm_instance_init_config_entry(LDBM_ANCESTORID_STR, "eq", 0, 0, 0, "integerOrderingMatch");
|
||||
+ e = ldbm_instance_init_config_entry(LDBM_ANCESTORID_STR, "eq", 0, 0, 0, 0);
|
||||
attr_index_config(be, "ldbm index init", 0, e, 1, 0, NULL);
|
||||
slapi_entry_free(e);
|
||||
|
||||
--
|
||||
2.52.0
|
||||
|
||||
38
0050-Issue-3134-Fix-build-break-7260.patch
Normal file
38
0050-Issue-3134-Fix-build-break-7260.patch
Normal file
@ -0,0 +1,38 @@
|
||||
From 245bc3b53f385e12e4dc9d2cb765a55e10e0fdc5 Mon Sep 17 00:00:00 2001
|
||||
From: progier389 <progier@redhat.com>
|
||||
Date: Fri, 13 Feb 2026 17:51:12 +0100
|
||||
Subject: [PATCH] Issue 3134 - Fix build break (#7260)
|
||||
|
||||
Fix build break of PR #7238 related to import rpm
|
||||
|
||||
Issue: #3134
|
||||
|
||||
Reviewed by: @vashirov (Thanks!)
|
||||
---
|
||||
src/lib389/lib389/utils.py | 3 ++-
|
||||
1 file changed, 2 insertions(+), 1 deletion(-)
|
||||
|
||||
diff --git a/src/lib389/lib389/utils.py b/src/lib389/lib389/utils.py
|
||||
index 07cb34d93..4df36cf1f 100644
|
||||
--- a/src/lib389/lib389/utils.py
|
||||
+++ b/src/lib389/lib389/utils.py
|
||||
@@ -28,7 +28,6 @@ from datetime import (datetime, timedelta)
|
||||
import sys
|
||||
import filecmp
|
||||
import pwd
|
||||
-import rpm
|
||||
import shlex
|
||||
import operator
|
||||
import subprocess
|
||||
@@ -2135,6 +2134,8 @@ def get_timeout_scale():
|
||||
|
||||
def rpm_is_older(pkg, version):
|
||||
"""Check if an RPM package version is older than specified version"""
|
||||
+ # rpm module is not installed in build environment so let import it only when used.
|
||||
+ import rpm
|
||||
ts = rpm.TransactionSet()
|
||||
mi = ts.dbMatch('name', pkg)
|
||||
for h in mi:
|
||||
--
|
||||
2.52.0
|
||||
|
||||
@ -0,0 +1,87 @@
|
||||
From ac3d9253e0a7a4b5f0108506bcf25255b302fd16 Mon Sep 17 00:00:00 2001
|
||||
From: Mark Reynolds <mreynolds@redhat.com>
|
||||
Date: Wed, 11 Feb 2026 15:51:47 -0500
|
||||
Subject: [PATCH] Issue 7066/7052 - allow password history to be set to zero
|
||||
and remove history
|
||||
|
||||
Description:
|
||||
|
||||
For local password policies the server was incorrectly rejecting updates that
|
||||
set the value to zero. When password history is set to zero the old passwords
|
||||
in the entry history are not cleaned as expected.
|
||||
|
||||
relates: https://github.com/389ds/389-ds-base/issues/7052
|
||||
relates: https://github.com/389ds/389-ds-base/issues/7066
|
||||
|
||||
Reviewed by: progier(Thanks!)
|
||||
---
|
||||
.../tests/suites/password/pwp_history_test.py | 7 ++++---
|
||||
ldap/servers/slapd/modify.c | 2 +-
|
||||
ldap/servers/slapd/pw.c | 13 ++++++++++++-
|
||||
3 files changed, 17 insertions(+), 5 deletions(-)
|
||||
|
||||
diff --git a/dirsrvtests/tests/suites/password/pwp_history_test.py b/dirsrvtests/tests/suites/password/pwp_history_test.py
|
||||
index cf68d743c..78b448a87 100644
|
||||
--- a/dirsrvtests/tests/suites/password/pwp_history_test.py
|
||||
+++ b/dirsrvtests/tests/suites/password/pwp_history_test.py
|
||||
@@ -189,9 +189,9 @@ def test_history_is_not_overwritten(topology_st, user):
|
||||
|
||||
|
||||
@pytest.mark.parametrize('policy',
|
||||
- [(pytest.param('global', marks=pytest.mark.xfail(reason="DS7052"))),
|
||||
- (pytest.param('subtree', marks=pytest.mark.xfail(reason="DS7066, DS7052"))),
|
||||
- (pytest.param('user', marks=pytest.mark.xfail(reason="DS7066, DS7052")))])
|
||||
+ [(pytest.param('global')),
|
||||
+ (pytest.param('subtree')),
|
||||
+ (pytest.param('user'))])
|
||||
def test_basic(topology_st, user, policy):
|
||||
"""Test basic password policy history feature functionality with dynamic count reduction
|
||||
|
||||
@@ -282,6 +282,7 @@ def test_basic(topology_st, user, policy):
|
||||
# Password history [password3, password4], current password is "password1"
|
||||
|
||||
# Reset password by Directory Manager(admin reset)
|
||||
+ dm = DirectoryManager(topology_st.standalone)
|
||||
dm.rebind()
|
||||
time.sleep(.5)
|
||||
change_password(user, 'password-reset', success=True)
|
||||
diff --git a/ldap/servers/slapd/modify.c b/ldap/servers/slapd/modify.c
|
||||
index 9e5bce80b..0ecce9bc8 100644
|
||||
--- a/ldap/servers/slapd/modify.c
|
||||
+++ b/ldap/servers/slapd/modify.c
|
||||
@@ -87,7 +87,7 @@ static struct attr_value_check
|
||||
{CONFIG_PW_WARNING_ATTRIBUTE, check_pw_duration_value, 0, -1},
|
||||
{CONFIG_PW_MINLENGTH_ATTRIBUTE, attr_check_minmax, 2, 512},
|
||||
{CONFIG_PW_MAXFAILURE_ATTRIBUTE, attr_check_minmax, 1, 32767},
|
||||
- {CONFIG_PW_INHISTORY_ATTRIBUTE, attr_check_minmax, 1, 24},
|
||||
+ {CONFIG_PW_INHISTORY_ATTRIBUTE, attr_check_minmax, 0, 24},
|
||||
{CONFIG_PW_LOCKDURATION_ATTRIBUTE, check_pw_duration_value, -1, -1},
|
||||
{CONFIG_PW_RESETFAILURECOUNT_ATTRIBUTE, check_pw_resetfailurecount_value, -1, -1},
|
||||
{CONFIG_PW_GRACELIMIT_ATTRIBUTE, attr_check_minmax, 0, -1},
|
||||
diff --git a/ldap/servers/slapd/pw.c b/ldap/servers/slapd/pw.c
|
||||
index 055ec0d74..c53ecf23d 100644
|
||||
--- a/ldap/servers/slapd/pw.c
|
||||
+++ b/ldap/servers/slapd/pw.c
|
||||
@@ -1535,7 +1535,18 @@ update_pw_history(Slapi_PBlock *pb, const Slapi_DN *sdn, char *old_pw)
|
||||
pwpolicy = new_passwdPolicy(pb, dn);
|
||||
|
||||
if (pwpolicy->pw_inhistory == 0){
|
||||
- /* We are only enforcing the current password, just return */
|
||||
+ /* We are only enforcing the current password, just return but first
|
||||
+ * cleanup any old passwords in the history */
|
||||
+ attribute.mod_type = "passwordHistory";
|
||||
+ attribute.mod_op = LDAP_MOD_REPLACE;
|
||||
+ attribute.mod_values = NULL;
|
||||
+ list_of_mods[0] = &attribute;
|
||||
+ list_of_mods[1] = NULL;
|
||||
+ mod_pb = slapi_pblock_new();
|
||||
+ slapi_modify_internal_set_pb_ext(mod_pb, sdn, list_of_mods, NULL, NULL, pw_get_componentID(), 0);
|
||||
+ slapi_modify_internal_pb(mod_pb);
|
||||
+ slapi_pblock_destroy(mod_pb);
|
||||
+
|
||||
return res;
|
||||
}
|
||||
|
||||
--
|
||||
2.52.0
|
||||
|
||||
1379
0052-Issue-7243-UI-add-support-for-hot-certificates.patch
Normal file
1379
0052-Issue-7243-UI-add-support-for-hot-certificates.patch
Normal file
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,32 @@
|
||||
From b216b86c5607dc0421eb609c46f3004844fb37c0 Mon Sep 17 00:00:00 2001
|
||||
From: Akshay Adhikari <aadhikar@redhat.com>
|
||||
Date: Tue, 17 Feb 2026 17:40:44 +0530
|
||||
Subject: [PATCH] Issue 6758 - Fix Enable Replication dropdown not opening
|
||||
(#7262)
|
||||
|
||||
Description: Removed hardcoded isOpen={false} and empty onToggle handler that
|
||||
prevented dropdown from opening. Let component manage its own state.
|
||||
|
||||
Relates: #6758
|
||||
|
||||
Reviewed by: @vashirov
|
||||
---
|
||||
src/cockpit/389-console/src/lib/replication/replModals.jsx | 2 --
|
||||
1 file changed, 2 deletions(-)
|
||||
|
||||
diff --git a/src/cockpit/389-console/src/lib/replication/replModals.jsx b/src/cockpit/389-console/src/lib/replication/replModals.jsx
|
||||
index ba4859617..83a8b75a5 100644
|
||||
--- a/src/cockpit/389-console/src/lib/replication/replModals.jsx
|
||||
+++ b/src/cockpit/389-console/src/lib/replication/replModals.jsx
|
||||
@@ -1757,8 +1757,6 @@ export class EnableReplModal extends React.Component {
|
||||
handleChange(syntheticEvent);
|
||||
}}
|
||||
options={[_("Supplier"), _("Hub"), _("Consumer")]}
|
||||
- isOpen={false}
|
||||
- onToggle={() => {}}
|
||||
placeholder={_("Select role...")}
|
||||
ariaLabel="Replication role selection"
|
||||
isMulti={false}
|
||||
--
|
||||
2.52.0
|
||||
|
||||
538
0054-Issue-7223-Remove-integerOrderingMatch-requirement-f.patch
Normal file
538
0054-Issue-7223-Remove-integerOrderingMatch-requirement-f.patch
Normal file
@ -0,0 +1,538 @@
|
||||
From 6ce19a9a3e36213a5604144aa5eb3cba666e5ed4 Mon Sep 17 00:00:00 2001
|
||||
From: Viktor Ashirov <vashirov@redhat.com>
|
||||
Date: Wed, 18 Feb 2026 09:26:57 +0100
|
||||
Subject: [PATCH] Issue 7223 - Remove integerOrderingMatch requirement for
|
||||
parentid (#7264)
|
||||
|
||||
Description:
|
||||
integerOrderingMatch was introduced as a requirement for parentid and
|
||||
ancestorid indexes for performance reasons. But after #7096 the order
|
||||
for parentid doesn't make a lot of difference.
|
||||
|
||||
Fix Description:
|
||||
* Remove integerOrderingMatch requirement for parentid.
|
||||
* Read only first 100 keys from dbscan in index ordering check
|
||||
* Do not run dsctl index-check during RPM upgrade
|
||||
|
||||
Relates: https://github.com/389ds/389-ds-base/pull/7223
|
||||
|
||||
Reviewed by: @progier389, @tbordaz (Thanks!)
|
||||
---
|
||||
.../healthcheck/health_system_indexes_test.py | 83 ++++----------
|
||||
ldap/servers/slapd/upgrade.c | 105 ------------------
|
||||
rpm/389-ds-base.spec.in | 3 -
|
||||
src/lib389/lib389/backend.py | 5 +-
|
||||
src/lib389/lib389/cli_ctl/dbtasks.py | 99 ++++++++---------
|
||||
5 files changed, 73 insertions(+), 222 deletions(-)
|
||||
|
||||
diff --git a/dirsrvtests/tests/suites/healthcheck/health_system_indexes_test.py b/dirsrvtests/tests/suites/healthcheck/health_system_indexes_test.py
|
||||
index dd42cd197..8dc82c779 100644
|
||||
--- a/dirsrvtests/tests/suites/healthcheck/health_system_indexes_test.py
|
||||
+++ b/dirsrvtests/tests/suites/healthcheck/health_system_indexes_test.py
|
||||
@@ -179,7 +179,8 @@ def test_missing_parentid(topology_st, log_buffering_enabled):
|
||||
|
||||
|
||||
def test_missing_matching_rule(topology_st, log_buffering_enabled):
|
||||
- """Check if healthcheck returns DSBLE0007 code when parentId index is missing integerOrderingMatch
|
||||
+ """Check that healthcheck does NOT report DSBLE0007 when parentId index is missing integerOrderingMatch.
|
||||
+ Both lexicographic and integer orderings are valid for parentid.
|
||||
|
||||
:id: 7ffa71db-8995-430a-bed8-59bce944221c
|
||||
:setup: Standalone instance
|
||||
@@ -189,19 +190,14 @@ def test_missing_matching_rule(topology_st, log_buffering_enabled):
|
||||
3. Use healthcheck without --json option
|
||||
4. Use healthcheck with --json option
|
||||
5. Re-add the matching rule
|
||||
- 6. Use healthcheck without --json option
|
||||
- 7. Use healthcheck with --json option
|
||||
:expectedresults:
|
||||
1. Success
|
||||
2. Success
|
||||
- 3. healthcheck reports DSBLE0007 code and related details
|
||||
- 4. healthcheck reports DSBLE0007 code and related details
|
||||
+ 3. healthcheck reports no issues found
|
||||
+ 4. healthcheck reports no issues found
|
||||
5. Success
|
||||
- 6. healthcheck reports no issues found
|
||||
- 7. healthcheck reports no issues found
|
||||
"""
|
||||
|
||||
- RET_CODE = "DSBLE0007"
|
||||
PARENTID_DN = "cn=parentid,cn=index,cn=userroot,cn=ldbm database,cn=plugins,cn=config"
|
||||
|
||||
standalone = topology_st.standalone
|
||||
@@ -210,17 +206,14 @@ def test_missing_matching_rule(topology_st, log_buffering_enabled):
|
||||
parentid_index = Index(standalone, PARENTID_DN)
|
||||
parentid_index.remove("nsMatchingRule", "integerOrderingMatch")
|
||||
|
||||
- run_healthcheck_and_flush_log(topology_st, standalone, json=False, searched_code=RET_CODE)
|
||||
- run_healthcheck_and_flush_log(topology_st, standalone, json=True, searched_code=RET_CODE)
|
||||
+ run_healthcheck_and_flush_log(topology_st, standalone, json=False, searched_code=CMD_OUTPUT)
|
||||
+ run_healthcheck_and_flush_log(topology_st, standalone, json=True, searched_code=JSON_OUTPUT)
|
||||
|
||||
log.info("Re-add the integerOrderingMatch matching rule")
|
||||
parentid_index = Index(standalone, PARENTID_DN)
|
||||
parentid_index.add("nsMatchingRule", "integerOrderingMatch")
|
||||
standalone.restart()
|
||||
|
||||
- run_healthcheck_and_flush_log(topology_st, standalone, json=False, searched_code=CMD_OUTPUT)
|
||||
- run_healthcheck_and_flush_log(topology_st, standalone, json=True, searched_code=JSON_OUTPUT)
|
||||
-
|
||||
|
||||
def test_usn_plugin_missing_entryusn(topology_st, usn_plugin_enabled, log_buffering_enabled):
|
||||
"""Check if healthcheck returns DSBLE0007 code when USN plugin is enabled but entryusn index is missing
|
||||
@@ -910,7 +903,9 @@ def test_index_check_fixes_ancestorid_config(topology_st):
|
||||
|
||||
|
||||
def test_index_check_fixes_missing_matching_rule(topology_st):
|
||||
- """Check if dsctl index-check --fix adds missing integerOrderingMatch
|
||||
+ """Check that removing integerOrderingMatch from parentid config is not
|
||||
+ flagged as an issue when disk ordering cannot be determined.
|
||||
+ Both lexicographic and integer orderings are valid for parentid.
|
||||
|
||||
:id: 6c1d4e9f-0a3b-4d5c-1e7f-8a9b0c2d3e4f
|
||||
:setup: Standalone instance
|
||||
@@ -918,18 +913,14 @@ def test_index_check_fixes_missing_matching_rule(topology_st):
|
||||
1. Create DS instance
|
||||
2. Stop the server
|
||||
3. Remove integerOrderingMatch from parentid index using DSEldif
|
||||
- 4. Run dsctl index-check (should detect issue)
|
||||
- 5. Run dsctl index-check --fix
|
||||
- 6. Verify integerOrderingMatch was added back
|
||||
- 7. Start the server
|
||||
+ 4. Run dsctl index-check (should NOT detect issue since disk ordering is unknown)
|
||||
+ 5. Start the server
|
||||
:expectedresults:
|
||||
1. Success
|
||||
2. Success
|
||||
3. Success
|
||||
- 4. index-check returns False and detects missing matching rule
|
||||
- 5. index-check returns True after fix
|
||||
- 6. integerOrderingMatch is present
|
||||
- 7. Success
|
||||
+ 4. index-check returns True (no issues, disk ordering unknown)
|
||||
+ 5. Success
|
||||
"""
|
||||
from lib389.cli_ctl.dbtasks import dbtasks_index_check
|
||||
from lib389.dseldif import DSEldif
|
||||
@@ -963,34 +954,20 @@ def test_index_check_fixes_missing_matching_rule(topology_st):
|
||||
f"integerOrderingMatch should be removed, but found: {mr}"
|
||||
log.info("integerOrderingMatch removed from parentid index")
|
||||
|
||||
- log.info("Run index-check without --fix (should detect issue)")
|
||||
+ log.info("Run index-check (should NOT detect issue - disk ordering unknown)")
|
||||
args = FakeArgs()
|
||||
args.backend = "userRoot"
|
||||
args.fix = False
|
||||
|
||||
result = dbtasks_index_check(standalone, topology_st.logcap.log, args)
|
||||
- assert result is False, "index-check should detect missing matching rule"
|
||||
- assert topology_st.logcap.contains("missing integerOrderingMatch")
|
||||
+ assert result is True, \
|
||||
+ "index-check should not flag missing integerOrderingMatch when disk ordering is unknown"
|
||||
+ assert topology_st.logcap.contains("could not determine disk ordering")
|
||||
topology_st.logcap.flush()
|
||||
|
||||
- log.info("Run index-check with --fix")
|
||||
- args.fix = True
|
||||
- result = dbtasks_index_check(standalone, topology_st.logcap.log, args)
|
||||
- assert result is True, "index-check --fix should succeed"
|
||||
- assert topology_st.logcap.contains("integerOrderingMatch")
|
||||
- topology_st.logcap.flush()
|
||||
-
|
||||
- log.info("Verify integerOrderingMatch was added back")
|
||||
- dse_ldif = DSEldif(standalone) # Reload to get fresh data
|
||||
- matching_rules = dse_ldif.get(parentid_dn, "nsMatchingRule")
|
||||
- assert matching_rules is not None, "nsMatchingRule should be present"
|
||||
- found_int_order = False
|
||||
- for mr in matching_rules:
|
||||
- if "integerorderingmatch" in mr.lower():
|
||||
- found_int_order = True
|
||||
- break
|
||||
- assert found_int_order, f"integerOrderingMatch should be present, got: {matching_rules}"
|
||||
- log.info("integerOrderingMatch successfully added back")
|
||||
+ log.info("Restore integerOrderingMatch and start the server")
|
||||
+ dse_ldif = DSEldif(standalone)
|
||||
+ dse_ldif.add(parentid_dn, "nsMatchingRule", "integerOrderingMatch")
|
||||
|
||||
log.info("Start the server")
|
||||
standalone.start()
|
||||
@@ -1080,7 +1057,7 @@ def test_index_check_fixes_multiple_issues(topology_st):
|
||||
:steps:
|
||||
1. Create DS instance
|
||||
2. Stop the server
|
||||
- 3. Add multiple issues: scanlimit, ancestorid config, missing matching rule
|
||||
+ 3. Add multiple issues: scanlimit and ancestorid config
|
||||
4. Run dsctl index-check (should detect all issues)
|
||||
5. Run dsctl index-check --fix
|
||||
6. Verify all issues were fixed
|
||||
@@ -1122,14 +1099,6 @@ def test_index_check_fixes_multiple_issues(topology_st):
|
||||
]
|
||||
dse_ldif.add_entry(ancestorid_entry)
|
||||
|
||||
- log.info("Add issue 3: Remove integerOrderingMatch from parentid")
|
||||
- dse_ldif = DSEldif(standalone) # Reload
|
||||
- matching_rules = dse_ldif.get(parentid_dn, "nsMatchingRule")
|
||||
- if matching_rules:
|
||||
- for mr in matching_rules:
|
||||
- if "integerorderingmatch" in mr.lower():
|
||||
- dse_ldif.delete(parentid_dn, "nsMatchingRule", mr)
|
||||
-
|
||||
log.info("Run index-check without --fix (should detect all issues)")
|
||||
args = FakeArgs()
|
||||
args.backend = "userRoot"
|
||||
@@ -1160,16 +1129,6 @@ def test_index_check_fixes_multiple_issues(topology_st):
|
||||
cn_value = dse_ldif.get(ancestorid_dn, "cn", single=True)
|
||||
assert cn_value is None, f"ancestorid config should be removed, got: {cn_value}"
|
||||
|
||||
- # Check matching rule added back
|
||||
- matching_rules = dse_ldif.get(parentid_dn, "nsMatchingRule")
|
||||
- found_int_order = False
|
||||
- if matching_rules:
|
||||
- for mr in matching_rules:
|
||||
- if "integerorderingmatch" in mr.lower():
|
||||
- found_int_order = True
|
||||
- break
|
||||
- assert found_int_order, f"integerOrderingMatch should be present, got: {matching_rules}"
|
||||
-
|
||||
log.info("All issues verified as fixed")
|
||||
|
||||
log.info("Run index-check again to confirm all clear")
|
||||
diff --git a/ldap/servers/slapd/upgrade.c b/ldap/servers/slapd/upgrade.c
|
||||
index 6b1b012da..9557e9066 100644
|
||||
--- a/ldap/servers/slapd/upgrade.c
|
||||
+++ b/ldap/servers/slapd/upgrade.c
|
||||
@@ -551,107 +551,6 @@ upgrade_remove_ancestorid_index_config(void)
|
||||
return uresult;
|
||||
}
|
||||
|
||||
-/*
|
||||
- * Check if parentid/ancestorid indexes are missing the integerOrderingMatch
|
||||
- * matching rule.
|
||||
- *
|
||||
- * This function logs a warning if we detect this condition, advising
|
||||
- * the administrator to reindex the affected attributes.
|
||||
- */
|
||||
-static upgrade_status
|
||||
-upgrade_check_id_index_matching_rule(void)
|
||||
-{
|
||||
- struct slapi_pblock *pb = slapi_pblock_new();
|
||||
- Slapi_Entry **backends = NULL;
|
||||
- const char *be_base_dn = "cn=ldbm database,cn=plugins,cn=config";
|
||||
- const char *be_filter = "(objectclass=nsBackendInstance)";
|
||||
- const char *attrs_to_check[] = {"parentid", NULL};
|
||||
- upgrade_status uresult = UPGRADE_SUCCESS;
|
||||
-
|
||||
- /* Search for all backend instances */
|
||||
- slapi_search_internal_set_pb(
|
||||
- pb, be_base_dn,
|
||||
- LDAP_SCOPE_ONELEVEL,
|
||||
- be_filter, NULL, 0, NULL, NULL,
|
||||
- plugin_get_default_component_id(), 0);
|
||||
- slapi_search_internal_pb(pb);
|
||||
- slapi_pblock_get(pb, SLAPI_PLUGIN_INTOP_SEARCH_ENTRIES, &backends);
|
||||
-
|
||||
- if (backends) {
|
||||
- for (size_t be_idx = 0; backends[be_idx] != NULL; be_idx++) {
|
||||
- const char *be_dn = slapi_entry_get_dn_const(backends[be_idx]);
|
||||
- const char *be_name = slapi_entry_attr_get_ref(backends[be_idx], "cn");
|
||||
- if (!be_dn || !be_name) {
|
||||
- continue;
|
||||
- }
|
||||
-
|
||||
- /* Check each attribute that should have integerOrderingMatch */
|
||||
- for (size_t attr_idx = 0; attrs_to_check[attr_idx] != NULL; attr_idx++) {
|
||||
- const char *attr_name = attrs_to_check[attr_idx];
|
||||
- struct slapi_pblock *idx_pb = slapi_pblock_new();
|
||||
- Slapi_Entry **idx_entries = NULL;
|
||||
- char *idx_dn = slapi_create_dn_string("cn=%s,cn=index,%s",
|
||||
- attr_name, be_dn);
|
||||
- char *idx_filter = "(objectclass=nsIndex)";
|
||||
- PRBool has_matching_rule = PR_FALSE;
|
||||
-
|
||||
- if (!idx_dn) {
|
||||
- slapi_pblock_destroy(idx_pb);
|
||||
- continue;
|
||||
- }
|
||||
-
|
||||
- slapi_search_internal_set_pb(
|
||||
- idx_pb, idx_dn,
|
||||
- LDAP_SCOPE_BASE,
|
||||
- idx_filter, NULL, 0, NULL, NULL,
|
||||
- plugin_get_default_component_id(), 0);
|
||||
- slapi_search_internal_pb(idx_pb);
|
||||
- slapi_pblock_get(idx_pb, SLAPI_PLUGIN_INTOP_SEARCH_ENTRIES, &idx_entries);
|
||||
-
|
||||
- if (idx_entries && idx_entries[0]) {
|
||||
- /* Index exists, check if it has integerOrderingMatch */
|
||||
- Slapi_Attr *mr_attr = NULL;
|
||||
- if (slapi_entry_attr_find(idx_entries[0], "nsMatchingRule", &mr_attr) == 0) {
|
||||
- Slapi_Value *sval = NULL;
|
||||
- int idx;
|
||||
- for (idx = slapi_attr_first_value(mr_attr, &sval);
|
||||
- idx != -1;
|
||||
- idx = slapi_attr_next_value(mr_attr, idx, &sval)) {
|
||||
- const struct berval *bval = slapi_value_get_berval(sval);
|
||||
- if (bval && bval->bv_val &&
|
||||
- strcasecmp(bval->bv_val, "integerOrderingMatch") == 0) {
|
||||
- has_matching_rule = PR_TRUE;
|
||||
- break;
|
||||
- }
|
||||
- }
|
||||
- }
|
||||
-
|
||||
- if (!has_matching_rule) {
|
||||
- /* Index exists but doesn't have integerOrderingMatch, log a warning */
|
||||
- slapi_log_err(SLAPI_LOG_ERR, "upgrade_check_id_index_matching_rule",
|
||||
- "Index '%s' in backend '%s' is missing 'nsMatchingRule: integerOrderingMatch'. "
|
||||
- "Incorrectly configured system indexes can lead to poor search performance, replication issues, and other operational problems. "
|
||||
- "To fix this, add the matching rule and reindex: "
|
||||
- "dsconf <instance> backend index set --add-mr integerOrderingMatch --attr %s %s && "
|
||||
- "dsconf <instance> backend index reindex --attr %s %s. "
|
||||
- "WARNING: Reindexing can be resource-intensive and may impact server performance on a live system. "
|
||||
- "Consider scheduling reindexing during maintenance windows or periods of low activity.\n",
|
||||
- attr_name, be_name, attr_name, be_name, attr_name, be_name);
|
||||
- }
|
||||
- }
|
||||
-
|
||||
- slapi_ch_free_string(&idx_dn);
|
||||
- slapi_free_search_results_internal(idx_pb);
|
||||
- slapi_pblock_destroy(idx_pb);
|
||||
- }
|
||||
- }
|
||||
- }
|
||||
-
|
||||
- slapi_free_search_results_internal(pb);
|
||||
- slapi_pblock_destroy(pb);
|
||||
-
|
||||
- return uresult;
|
||||
-}
|
||||
|
||||
/*
|
||||
* Upgrade the base config of the PAM PTA plugin.
|
||||
@@ -879,10 +778,6 @@ upgrade_server(void)
|
||||
return UPGRADE_FAILURE;
|
||||
}
|
||||
|
||||
- if (upgrade_check_id_index_matching_rule() != UPGRADE_SUCCESS) {
|
||||
- return UPGRADE_FAILURE;
|
||||
- }
|
||||
-
|
||||
return UPGRADE_SUCCESS;
|
||||
}
|
||||
|
||||
diff --git a/rpm/389-ds-base.spec.in b/rpm/389-ds-base.spec.in
|
||||
index 0e0e28285..370e3abd4 100644
|
||||
--- a/rpm/389-ds-base.spec.in
|
||||
+++ b/rpm/389-ds-base.spec.in
|
||||
@@ -650,9 +650,6 @@ for dir in "$instbase"/slapd-* ; do
|
||||
else
|
||||
echo "instance $inst is not running" >> "$output" 2>&1 || :
|
||||
fi
|
||||
- # Run index-check on all instances (running or not)
|
||||
- # This fixes index ordering mismatches from older versions
|
||||
- dsctl "$inst_name" index-check --fix >> "$output2" 2>&1 || :
|
||||
ninst=$((ninst + 1))
|
||||
done
|
||||
|
||||
diff --git a/src/lib389/lib389/backend.py b/src/lib389/lib389/backend.py
|
||||
index f3dbe7c92..6c8cbc018 100644
|
||||
--- a/src/lib389/lib389/backend.py
|
||||
+++ b/src/lib389/lib389/backend.py
|
||||
@@ -647,9 +647,10 @@ class Backend(DSLdapObject):
|
||||
# Default system indexes taken from ldap/servers/slapd/back-ldbm/instance.c
|
||||
# Note: entryrdn and ancestorid are internal system indexes that are not
|
||||
# exposed in cn=config - they are managed internally by the server.
|
||||
- # Only parentid has a DSE config entry (for the integerOrderingMatch rule).
|
||||
+ # parentid works correctly with both lexicographic and integer ordering,
|
||||
+ # so integerOrderingMatch is not required.
|
||||
expected_system_indexes = {
|
||||
- 'parentid': {'types': ['eq'], 'matching_rule': 'integerOrderingMatch'},
|
||||
+ 'parentid': {'types': ['eq'], 'matching_rule': None},
|
||||
'objectClass': {'types': ['eq'], 'matching_rule': None},
|
||||
'aci': {'types': ['pres'], 'matching_rule': None},
|
||||
'nscpEntryDN': {'types': ['eq'], 'matching_rule': None},
|
||||
diff --git a/src/lib389/lib389/cli_ctl/dbtasks.py b/src/lib389/lib389/cli_ctl/dbtasks.py
|
||||
index cd96cdaf7..b02de203f 100644
|
||||
--- a/src/lib389/lib389/cli_ctl/dbtasks.py
|
||||
+++ b/src/lib389/lib389/cli_ctl/dbtasks.py
|
||||
@@ -10,6 +10,7 @@
|
||||
import glob
|
||||
import os
|
||||
import re
|
||||
+import signal
|
||||
import subprocess
|
||||
from enum import Enum
|
||||
from lib389._constants import TaskWarning
|
||||
@@ -263,45 +264,53 @@ def _check_disk_ordering(db_dir, backend, index_name, dbscan_path, is_mdb, log):
|
||||
if not index_file:
|
||||
return IndexOrdering.UNKNOWN
|
||||
|
||||
+ # Only read the first 100 lines from dbscan to avoid scanning the
|
||||
+ # entire index (which can take hours on large databases).
|
||||
try:
|
||||
- result = subprocess.run(
|
||||
+ proc = subprocess.Popen(
|
||||
[dbscan_path, "-f", index_file],
|
||||
stdout=subprocess.PIPE,
|
||||
stderr=subprocess.PIPE,
|
||||
universal_newlines=True,
|
||||
- timeout=60,
|
||||
)
|
||||
|
||||
- if result.returncode != 0:
|
||||
- log.warning(" dbscan returned non-zero exit code for %s", index_file)
|
||||
- return IndexOrdering.UNKNOWN
|
||||
-
|
||||
- # Parse keys from dbscan output
|
||||
keys = []
|
||||
- for line in result.stdout.split("\n"):
|
||||
+ line_count = 0
|
||||
+ assert proc.stdout is not None
|
||||
+ for line in proc.stdout:
|
||||
+ line_count += 1
|
||||
+ if line_count > 100:
|
||||
+ break
|
||||
line = line.strip()
|
||||
if line.startswith("="):
|
||||
match = re.match(r"^=(\d+)", line)
|
||||
if match:
|
||||
keys.append(int(match.group(1)))
|
||||
|
||||
+ proc.terminate()
|
||||
+ try:
|
||||
+ proc.wait(timeout=5)
|
||||
+ except subprocess.TimeoutExpired:
|
||||
+ proc.kill()
|
||||
+ proc.wait()
|
||||
+
|
||||
+ if proc.returncode not in (0, -signal.SIGTERM):
|
||||
+ log.warning(" dbscan returned non-zero exit code for %s", index_file)
|
||||
+ return IndexOrdering.UNKNOWN
|
||||
+
|
||||
if len(keys) < 2:
|
||||
return IndexOrdering.UNKNOWN
|
||||
|
||||
# Check if keys are in integer order by looking for decreasing numeric values
|
||||
# (which would indicate lexicographic ordering, e.g., "3" < "30" < "4")
|
||||
prev_id = keys[0]
|
||||
- for i in range(1, min(len(keys), 100)):
|
||||
- current_id = keys[i]
|
||||
+ for current_id in keys[1:]:
|
||||
if prev_id > current_id:
|
||||
return IndexOrdering.LEXICOGRAPHIC
|
||||
prev_id = current_id
|
||||
|
||||
return IndexOrdering.INTEGER
|
||||
|
||||
- except subprocess.TimeoutExpired:
|
||||
- log.warning(" dbscan timed out for %s", index_file)
|
||||
- return IndexOrdering.UNKNOWN
|
||||
except OSError as e:
|
||||
log.warning(" Error running dbscan: %s", e)
|
||||
return IndexOrdering.UNKNOWN
|
||||
@@ -375,8 +384,7 @@ def dbtasks_index_check(inst, log, args):
|
||||
|
||||
# Track all issues found
|
||||
all_ok = True
|
||||
- mismatches = [] # (backend, index_name) tuples needing reindex
|
||||
- missing_matching_rules = [] # (backend, index_name) tuples missing integerOrderingMatch
|
||||
+ config_fixes = [] # (backend, index_name, action) tuples: action is "add_mr" or "remove_mr"
|
||||
scan_limits_to_remove = [] # (backend, index_name) tuples with nsIndexIDListScanLimit
|
||||
ancestorid_configs_to_remove = [] # backend names with ancestorid config entries
|
||||
remove_ancestorid_from_defaults = False # Flag to remove from cn=default indexes
|
||||
@@ -409,13 +417,6 @@ def dbtasks_index_check(inst, log, args):
|
||||
|
||||
if disk_ordering == IndexOrdering.UNKNOWN:
|
||||
log.info(" %s - could not determine disk ordering, skipping", index_name)
|
||||
- # For parentid, still check if matching rule is missing
|
||||
- if index_name == "parentid":
|
||||
- config_has_int_order = _has_integer_ordering_match(dse_ldif, backend, index_name)
|
||||
- if not config_has_int_order:
|
||||
- log.warning(" %s - missing integerOrderingMatch in config", index_name)
|
||||
- missing_matching_rules.append((backend, index_name))
|
||||
- all_ok = False
|
||||
continue
|
||||
|
||||
config_has_int_order = _has_integer_ordering_match(dse_ldif, backend, index_name)
|
||||
@@ -423,18 +424,15 @@ def dbtasks_index_check(inst, log, args):
|
||||
log.info(" %s - config: %s, disk: %s",
|
||||
index_name, config_desc, disk_ordering.value)
|
||||
|
||||
- # For parentid, the desired state is always integer ordering
|
||||
+ # Both orderings are valid for parentid, but config must match disk.
|
||||
if index_name == "parentid":
|
||||
- if not config_has_int_order:
|
||||
- log.warning(" %s - missing integerOrderingMatch in config", index_name)
|
||||
- if (backend, index_name) not in missing_matching_rules:
|
||||
- missing_matching_rules.append((backend, index_name))
|
||||
+ if config_has_int_order and disk_ordering == IndexOrdering.LEXICOGRAPHIC:
|
||||
+ log.warning(" %s - MISMATCH: config has integerOrderingMatch but disk is lexicographic", index_name)
|
||||
+ config_fixes.append((backend, index_name, "remove_mr"))
|
||||
all_ok = False
|
||||
-
|
||||
- if disk_ordering == IndexOrdering.LEXICOGRAPHIC:
|
||||
- log.warning(" %s - disk ordering is lexicographic, needs reindex", index_name)
|
||||
- if (backend, index_name) not in mismatches:
|
||||
- mismatches.append((backend, index_name))
|
||||
+ elif not config_has_int_order and disk_ordering == IndexOrdering.INTEGER:
|
||||
+ log.warning(" %s - MISMATCH: config is lexicographic but disk has integer ordering", index_name)
|
||||
+ config_fixes.append((backend, index_name, "add_mr"))
|
||||
all_ok = False
|
||||
|
||||
# Handle issues
|
||||
@@ -480,26 +478,27 @@ def dbtasks_index_check(inst, log, args):
|
||||
log.error(" Failed to remove ancestorid config from backend %s: %s", backend, e)
|
||||
return False
|
||||
|
||||
- # Add missing matching rules to dse.ldif
|
||||
- for backend, index_name in missing_matching_rules:
|
||||
+ # Fix config-vs-disk ordering mismatches by adjusting config to match disk
|
||||
+ for backend, index_name, action in config_fixes:
|
||||
index_dn = "cn={},cn=index,cn={},cn=ldbm database,cn=plugins,cn=config".format(
|
||||
index_name, backend
|
||||
)
|
||||
- log.info(" Adding integerOrderingMatch to %s in backend %s...", index_name, backend)
|
||||
- try:
|
||||
- dse_ldif.add(index_dn, "nsMatchingRule", "integerOrderingMatch")
|
||||
- log.info(" Updated dse.ldif with integerOrderingMatch for %s", index_name)
|
||||
- except Exception as e:
|
||||
- log.error(" Failed to update dse.ldif for %s: %s", index_name, e)
|
||||
- return False
|
||||
-
|
||||
- # Reindex indexes with disk ordering issues
|
||||
- for backend, index_name in mismatches:
|
||||
- log.info(" Reindexing %s in backend %s...", index_name, backend)
|
||||
- if not inst.db2index(bename=backend, attrs=[index_name]):
|
||||
- log.error(" Failed to reindex %s", index_name)
|
||||
- return False
|
||||
- log.info(" Reindex of %s completed successfully", index_name)
|
||||
+ if action == "add_mr":
|
||||
+ log.info(" Adding integerOrderingMatch to %s in backend %s...", index_name, backend)
|
||||
+ try:
|
||||
+ dse_ldif.add(index_dn, "nsMatchingRule", "integerOrderingMatch")
|
||||
+ log.info(" Updated dse.ldif with integerOrderingMatch for %s", index_name)
|
||||
+ except Exception as e:
|
||||
+ log.error(" Failed to update dse.ldif for %s: %s", index_name, e)
|
||||
+ return False
|
||||
+ elif action == "remove_mr":
|
||||
+ log.info(" Removing integerOrderingMatch from %s in backend %s...", index_name, backend)
|
||||
+ try:
|
||||
+ dse_ldif.delete(index_dn, "nsMatchingRule", "integerOrderingMatch")
|
||||
+ log.info(" Removed integerOrderingMatch from %s", index_name)
|
||||
+ except Exception as e:
|
||||
+ log.error(" Failed to remove integerOrderingMatch from %s: %s", index_name, e)
|
||||
+ return False
|
||||
|
||||
log.info("All issues fixed")
|
||||
return True
|
||||
@@ -563,5 +562,5 @@ def create_parser(subcommands):
|
||||
index_check_parser.add_argument('backend', nargs='?', default=None,
|
||||
help="Backend to check. If not specified, all backends are checked.")
|
||||
index_check_parser.add_argument('--fix', action='store_true', default=False,
|
||||
- help="Fix mismatches by reindexing affected indexes")
|
||||
+ help="Fix mismatches by adjusting config to match on-disk data")
|
||||
index_check_parser.set_defaults(func=dbtasks_index_check)
|
||||
--
|
||||
2.52.0
|
||||
|
||||
227
0055-Issue-7236-Fix-GSSAPI-tests-7237.patch
Normal file
227
0055-Issue-7236-Fix-GSSAPI-tests-7237.patch
Normal file
@ -0,0 +1,227 @@
|
||||
From 26feecae026581e39a43f001faff59e81a92c03d Mon Sep 17 00:00:00 2001
|
||||
From: Lenka Doudova <mirielka@users.noreply.github.com>
|
||||
Date: Wed, 18 Feb 2026 14:33:49 +0100
|
||||
Subject: [PATCH] Issue 7236 - Fix GSSAPI tests (#7237)
|
||||
|
||||
* Issue 7236 - Fix GSSAPI tests
|
||||
|
||||
Description:
|
||||
Fix for failing GSSAPI tests
|
||||
Add GSSAPI_ACK variable to pytest workflow for proper execution in
|
||||
Github CI
|
||||
|
||||
Relates: #7236
|
||||
Author: Lenka Doudova
|
||||
Reviewer: Barbora Simonova, Viktor Ashirov
|
||||
---
|
||||
.github/workflows/lmdbpytest.yml | 2 +-
|
||||
.github/workflows/pytest.yml | 2 +-
|
||||
.../tests/suites/gssapi/simple_gssapi_test.py | 2 +
|
||||
.../suites/gssapi_repl/gssapi_repl_test.py | 43 +++++---------
|
||||
src/lib389/lib389/topologies.py | 57 +++++++++++++++++++
|
||||
5 files changed, 76 insertions(+), 30 deletions(-)
|
||||
|
||||
diff --git a/.github/workflows/lmdbpytest.yml b/.github/workflows/lmdbpytest.yml
|
||||
index 2d0a122bf..376090bf6 100644
|
||||
--- a/.github/workflows/lmdbpytest.yml
|
||||
+++ b/.github/workflows/lmdbpytest.yml
|
||||
@@ -120,7 +120,7 @@ jobs:
|
||||
sudo docker exec $CID sh -c "systemctl enable --now cockpit.socket"
|
||||
sudo docker exec $CID sh -c "mkdir -p /workspace/assets/cores && chmod 777 /workspace{,/assets{,/cores}}"
|
||||
sudo docker exec $CID sh -c "echo '/workspace/assets/cores/core.%e.%P' > /proc/sys/kernel/core_pattern"
|
||||
- sudo docker exec -e WEBUI=1 -e NSSLAPD_DB_LIB=mdb -e DEBUG=pw:api -e PASSWD="${PASSWD}" $CID py.test --suppress-no-test-exit-code -m "not flaky" --junit-xml=pytest.xml --html=pytest.html --browser=firefox --browser=chromium -v dirsrvtests/tests/suites/${{ matrix.suite }}
|
||||
+ sudo docker exec -e WEBUI=1 -e NSSLAPD_DB_LIB=mdb -e DEBUG=pw:api -e PASSWD="${PASSWD}" -e GSSAPI_ACK=1 $CID py.test --suppress-no-test-exit-code -m "not flaky" --junit-xml=pytest.xml --html=pytest.html --browser=firefox --browser=chromium -v dirsrvtests/tests/suites/${{ matrix.suite }}
|
||||
|
||||
- name: Make the results file readable by all
|
||||
if: always()
|
||||
diff --git a/.github/workflows/pytest.yml b/.github/workflows/pytest.yml
|
||||
index 8a543be85..a51553656 100644
|
||||
--- a/.github/workflows/pytest.yml
|
||||
+++ b/.github/workflows/pytest.yml
|
||||
@@ -125,7 +125,7 @@ jobs:
|
||||
echo "Tests skipped because read-only Berkeley Database is installed." > pytest.html
|
||||
echo "<?xml version="1.0" encoding="utf-8"?>'Tests skipped because read-only Berkeley Database is installed.'" > pytest.xml
|
||||
else
|
||||
- sudo docker exec -e WEBUI=1 -e NSSLAPD_DB_LIB=bdb -e DEBUG=pw:api -e PASSWD="${PASSWD}" $CID py.test --suppress-no-test-exit-code -m "not flaky" --junit-xml=pytest.xml --html=pytest.html --browser=firefox --browser=chromium -v dirsrvtests/tests/suites/${{ matrix.suite }}
|
||||
+ sudo docker exec -e WEBUI=1 -e NSSLAPD_DB_LIB=bdb -e DEBUG=pw:api -e PASSWD="${PASSWD}" -e GSSAPI_ACK=1 $CID py.test --suppress-no-test-exit-code -m "not flaky" --junit-xml=pytest.xml --html=pytest.html --browser=firefox --browser=chromium -v dirsrvtests/tests/suites/${{ matrix.suite }}
|
||||
fi
|
||||
|
||||
- name: Make the results file readable by all
|
||||
diff --git a/dirsrvtests/tests/suites/gssapi/simple_gssapi_test.py b/dirsrvtests/tests/suites/gssapi/simple_gssapi_test.py
|
||||
index be6f68a9a..e48de3491 100644
|
||||
--- a/dirsrvtests/tests/suites/gssapi/simple_gssapi_test.py
|
||||
+++ b/dirsrvtests/tests/suites/gssapi/simple_gssapi_test.py
|
||||
@@ -34,6 +34,8 @@ def testuser(topology_st_gssapi):
|
||||
})
|
||||
# Give them a krb princ
|
||||
user.create_keytab()
|
||||
+ # Make krb5 config readable by everyone for the tests to work
|
||||
+ os.chmod(user._instance.realm.krb5confrealm, 0o644)
|
||||
return user
|
||||
|
||||
@gssapi_ack
|
||||
diff --git a/dirsrvtests/tests/suites/gssapi_repl/gssapi_repl_test.py b/dirsrvtests/tests/suites/gssapi_repl/gssapi_repl_test.py
|
||||
index 402684aab..fa7fc9c24 100644
|
||||
--- a/dirsrvtests/tests/suites/gssapi_repl/gssapi_repl_test.py
|
||||
+++ b/dirsrvtests/tests/suites/gssapi_repl/gssapi_repl_test.py
|
||||
@@ -10,7 +10,7 @@ import pytest
|
||||
from lib389.tasks import *
|
||||
from lib389.utils import *
|
||||
from lib389.agreement import *
|
||||
-from lib389.topologies import topology_m2
|
||||
+from lib389.topologies import topology_m2_gssapi, gssapi_ack
|
||||
|
||||
pytestmark = pytest.mark.tier2
|
||||
|
||||
@@ -69,25 +69,8 @@ def _allow_machine_account(inst, name):
|
||||
(ldap.MOD_REPLACE, 'nsDS5ReplicaBindDN', f"uid={name},ou=Machines,{DEFAULT_SUFFIX}".encode('utf-8'))
|
||||
])
|
||||
|
||||
-def _verify_etc_hosts():
|
||||
- #Check if /etc/hosts is compatible with the test
|
||||
- NEEDED_HOSTS = ( ('ldapkdc.example.com', '127.0.0.1'),
|
||||
- ('ldapkdc1.example.com', '127.0.1.1'),
|
||||
- ('ldapkdc2.example.com', '127.0.2.1'))
|
||||
- found_hosts = {}
|
||||
- with open('/etc/hosts','r') as f:
|
||||
- for l in f:
|
||||
- s = l.split()
|
||||
- if len(s) < 2:
|
||||
- continue
|
||||
- for nh in NEEDED_HOSTS:
|
||||
- if (s[0] == nh[1] and s[1] == nh[0]):
|
||||
- found_hosts[s[1]] = True
|
||||
- return len(found_hosts) == len(NEEDED_HOSTS)
|
||||
-
|
||||
-@pytest.mark.skipif(not _verify_etc_hosts(), reason="/etc/hosts does not contains the needed hosts.")
|
||||
-@pytest.mark.skipif(True, reason="Test disabled because it requires specific kerberos requirement (server principal, keytab, etc ...")
|
||||
-def test_gssapi_repl(topology_m2):
|
||||
+@gssapi_ack
|
||||
+def test_gssapi_repl(topology_m2_gssapi):
|
||||
"""Test gssapi authenticated replication agreement of two suppliers using KDC
|
||||
|
||||
:id: 552850aa-afc3-473e-9c39-aae802b46f11
|
||||
@@ -112,8 +95,8 @@ def test_gssapi_repl(topology_m2):
|
||||
6. Test User should be created on M1 and M2 both
|
||||
7. Test User should be created on M1 and M2 both
|
||||
"""
|
||||
- supplier1 = topology_m2.ms["supplier1"]
|
||||
- supplier2 = topology_m2.ms["supplier2"]
|
||||
+ supplier1 = topology_m2_gssapi.ms["supplier1"]
|
||||
+ supplier2 = topology_m2_gssapi.ms["supplier2"]
|
||||
|
||||
# Create the locations on each supplier for the other to bind to.
|
||||
_create_machine_ou(supplier1)
|
||||
@@ -134,10 +117,9 @@ def test_gssapi_repl(topology_m2):
|
||||
# Creating agreement from supplier 1 to supplier 2
|
||||
|
||||
# Set the replica bind method to sasl gssapi
|
||||
- properties = {RA_NAME: r'meTo_$host:$port',
|
||||
+ properties = {RA_NAME: 'meTo_' + supplier2.host + ':' + str(supplier2.port),
|
||||
RA_METHOD: 'SASL/GSSAPI',
|
||||
RA_TRANSPORT_PROT: defaultProperties[REPLICATION_TRANSPORT]}
|
||||
- supplier1.agreement.delete(suffix=SUFFIX, consumer_host=supplier2.host, consumer_port=supplier2.port)
|
||||
m1_m2_agmt = supplier1.agreement.create(suffix=SUFFIX, host=supplier2.host, port=supplier2.port, properties=properties)
|
||||
if not m1_m2_agmt:
|
||||
log.fatal("Fail to create a supplier -> supplier replica agreement")
|
||||
@@ -147,10 +129,9 @@ def test_gssapi_repl(topology_m2):
|
||||
# Creating agreement from supplier 2 to supplier 1
|
||||
|
||||
# Set the replica bind method to sasl gssapi
|
||||
- properties = {RA_NAME: r'meTo_$host:$port',
|
||||
+ properties = {RA_NAME: 'meTo_' + supplier1.host + ':' + str(supplier1.port),
|
||||
RA_METHOD: 'SASL/GSSAPI',
|
||||
RA_TRANSPORT_PROT: defaultProperties[REPLICATION_TRANSPORT]}
|
||||
- supplier2.agreement.delete(suffix=SUFFIX, consumer_host=supplier1.host, consumer_port=supplier1.port)
|
||||
m2_m1_agmt = supplier2.agreement.create(suffix=SUFFIX, host=supplier1.host, port=supplier1.port, properties=properties)
|
||||
if not m2_m1_agmt:
|
||||
log.fatal("Fail to create a supplier -> supplier replica agreement")
|
||||
@@ -169,9 +150,15 @@ def test_gssapi_repl(topology_m2):
|
||||
|
||||
# Check replication is working...
|
||||
if supplier1.testReplication(DEFAULT_SUFFIX, supplier2):
|
||||
- log.info('Replication is working.')
|
||||
+ log.info('Replication is working: supplier1 -> supplier2')
|
||||
else:
|
||||
- log.fatal('Replication is not working.')
|
||||
+ log.fatal('Replication is not working: supplier1 -> supplier2')
|
||||
+ assert False
|
||||
+
|
||||
+ if supplier2.testReplication(DEFAULT_SUFFIX, supplier1):
|
||||
+ log.info('Replication is working: supplier2 -> supplier1')
|
||||
+ else:
|
||||
+ log.fatal('Replication is not working: supplier2 -> supplier1')
|
||||
assert False
|
||||
|
||||
# Add a user to supplier 1
|
||||
diff --git a/src/lib389/lib389/topologies.py b/src/lib389/lib389/topologies.py
|
||||
index 33341f669..84e620cb3 100644
|
||||
--- a/src/lib389/lib389/topologies.py
|
||||
+++ b/src/lib389/lib389/topologies.py
|
||||
@@ -499,6 +499,63 @@ def topology_m2(request):
|
||||
topology.logcap = LogCapture()
|
||||
return topology
|
||||
|
||||
+@pytest.fixture(scope="module")
|
||||
+def topology_m2_gssapi(request):
|
||||
+ """Create Replication Deployment with two suppliers with GSSAPI enabled.
|
||||
+
|
||||
+ Similar to topology_st_gssapi but for two suppliers. Configures Kerberos
|
||||
+ realm, principals and keytabs for ldap/ldapkdc1.<domain> and ldap/ldapkdc2.<domain>,
|
||||
+ SASL mappings, and disables SSL port on both instances so GSSAPI can be used.
|
||||
+ """
|
||||
+ hostname = socket.gethostname().split('.', 1)
|
||||
+ assert len(hostname) == 2
|
||||
+ domain = hostname[1]
|
||||
+ REALM = domain.upper()
|
||||
+ host_supplier_1 = 'ldapkdc1.' + domain
|
||||
+ host_supplier_2 = 'ldapkdc2.' + domain
|
||||
+
|
||||
+ topology = create_topology({ReplicaRole.SUPPLIER: 2}, request=request,
|
||||
+ cleanup_cb=lambda x: krb.destroy_realm())
|
||||
+
|
||||
+ supplier1 = topology.ms["supplier1"]
|
||||
+ supplier2 = topology.ms["supplier2"]
|
||||
+ supplier1.host = host_supplier_1
|
||||
+ supplier2.host = host_supplier_2
|
||||
+
|
||||
+ krb = MitKrb5(realm=REALM, debug=DEBUGGING)
|
||||
+ if krb.check_realm():
|
||||
+ krb.destroy_realm()
|
||||
+ krb.create_realm()
|
||||
+
|
||||
+ krb.create_principal(principal=f'ldap/{host_supplier_1}')
|
||||
+ krb.create_principal(principal=f'ldap/{host_supplier_2}')
|
||||
+ krb.create_keytab(principal=f'ldap/{host_supplier_1}', keytab='/etc/krb5.keytab')
|
||||
+ krb.create_keytab(principal=f'ldap/{host_supplier_2}', keytab='/etc/krb5.keytab')
|
||||
+
|
||||
+ os.chown('/etc/krb5.keytab', supplier1.get_user_uid(), supplier1.get_group_gid())
|
||||
+
|
||||
+ for inst, host in [(supplier1, host_supplier_1), (supplier2, host_supplier_2)]:
|
||||
+ saslmappings = SaslMappings(inst)
|
||||
+ for m in saslmappings.list():
|
||||
+ m.delete()
|
||||
+ saslmappings.create(properties={
|
||||
+ 'cn': 'suffix map',
|
||||
+ 'nsSaslMapRegexString': '\\(.*\\)',
|
||||
+ 'nsSaslMapBaseDNTemplate': inst.creation_suffix,
|
||||
+ 'nsSaslMapFilterTemplate': '(uid=\\1)'
|
||||
+ })
|
||||
+ inst.realm = krb
|
||||
+ inst.config.set('nsslapd-localhost', host)
|
||||
+ inst.sslport = None
|
||||
+
|
||||
+ supplier1.restart()
|
||||
+ supplier2.restart()
|
||||
+ supplier1.clearTmpDir(__file__)
|
||||
+ supplier2.clearTmpDir(__file__)
|
||||
+
|
||||
+ topology.logcap = LogCapture()
|
||||
+ return topology
|
||||
+
|
||||
|
||||
@pytest.fixture(scope="module")
|
||||
def topology_m3(request):
|
||||
--
|
||||
2.52.0
|
||||
|
||||
260
0056-Issue-6753-Port-ticket-49039-test.patch
Normal file
260
0056-Issue-6753-Port-ticket-49039-test.patch
Normal file
@ -0,0 +1,260 @@
|
||||
From a4ae29afc6547e8231b933cfa1b95d7f7b37a25c Mon Sep 17 00:00:00 2001
|
||||
From: Lenka Doudova <lryznaro@redhat.com>
|
||||
Date: Tue, 10 Feb 2026 05:45:32 +0100
|
||||
Subject: [PATCH] Issue 6753 - Port ticket 49039 test
|
||||
|
||||
Description:
|
||||
Port ticket 49039 test into
|
||||
dirsrvtests/tests/suites/password/pwp_test.py
|
||||
|
||||
Relates: #6753
|
||||
Author: Lenka Doudova
|
||||
Assisted by: Cursor
|
||||
Reviewer: Barbora Simonova, Viktor Ashirov
|
||||
---
|
||||
dirsrvtests/tests/suites/password/pwp_test.py | 83 +++++++++++-
|
||||
dirsrvtests/tests/tickets/ticket49039_test.py | 127 ------------------
|
||||
2 files changed, 82 insertions(+), 128 deletions(-)
|
||||
delete mode 100644 dirsrvtests/tests/tickets/ticket49039_test.py
|
||||
|
||||
diff --git a/dirsrvtests/tests/suites/password/pwp_test.py b/dirsrvtests/tests/suites/password/pwp_test.py
|
||||
index 663d9bea9..6dae08cb2 100644
|
||||
--- a/dirsrvtests/tests/suites/password/pwp_test.py
|
||||
+++ b/dirsrvtests/tests/suites/password/pwp_test.py
|
||||
@@ -9,11 +9,14 @@
|
||||
"""
|
||||
|
||||
import os
|
||||
+import subprocess
|
||||
import pytest
|
||||
from lib389.topologies import topology_st as topo
|
||||
from lib389.idm.user import UserAccounts, UserAccount
|
||||
-from lib389._constants import DEFAULT_SUFFIX
|
||||
+from lib389.idm.directorymanager import DirectoryManager
|
||||
+from lib389._constants import DEFAULT_SUFFIX, PASSWORD
|
||||
from lib389.config import Config
|
||||
+from lib389.pwpolicy import PwPolicyManager
|
||||
from lib389.idm.group import Group
|
||||
from lib389.utils import ds_is_older
|
||||
import ldap
|
||||
@@ -512,6 +515,84 @@ def test_passwordlockout(topo, _fix_password):
|
||||
_change_password_with_own(topo, user.dn, 'dby3rs2', 'secreter')
|
||||
|
||||
|
||||
+def test_password_must_change_ignores_min_age(topo):
|
||||
+ """Test that passwordMinAge does not block password update when the password was reset.
|
||||
+
|
||||
+ :id: a1b2c3d4-e5f6-4903-9abc-def012345678
|
||||
+ :setup: Standalone instance
|
||||
+ :steps:
|
||||
+ 1. Enable TLS (for ldappasswd StartTLS)
|
||||
+ 2. Set global policy via PwPolicyManager: passwordMustChange, passwordExp,
|
||||
+ passwordMaxAge, passwordMinAge (high), passwordChange
|
||||
+ 3. Bind as Directory Manager
|
||||
+ 4. Create user
|
||||
+ 5. Reset user password as Directory Manager
|
||||
+ 6. User binds and changes own password (must succeed; min age must not block)
|
||||
+ 7. Rebind as Directory Manager, reset user password again
|
||||
+ 8. Run ldappasswd as user (StartTLS) to change password to password2
|
||||
+ 9. Bind as user with password2 to verify
|
||||
+ 10. Cleanup: delete user
|
||||
+ :expectedresults:
|
||||
+ 1. TLS enabled
|
||||
+ 2. Policy set successfully
|
||||
+ 3. Bind succeeds
|
||||
+ 4. User created
|
||||
+ 5. Reset succeeds
|
||||
+ 6. User password change succeeds (min age does not block after reset)
|
||||
+ 7. Reset succeeds
|
||||
+ 8. ldappasswd succeeds
|
||||
+ 9. Bind succeeds
|
||||
+ 10. User deleted
|
||||
+ """
|
||||
+
|
||||
+ topo.standalone.enable_tls()
|
||||
+
|
||||
+ policy = PwPolicyManager(topo.standalone)
|
||||
+ policy.set_global_policy(properties={'nsslapd-pwpolicy-local': 'on',
|
||||
+ 'passwordMustChange': 'on',
|
||||
+ 'passwordExp': 'on',
|
||||
+ 'passwordMaxAge': '86400000',
|
||||
+ 'passwordMinAge': '8640000',
|
||||
+ 'passwordChange': 'on'})
|
||||
+ dm = DirectoryManager(topo.standalone)
|
||||
+ dm.bind()
|
||||
+
|
||||
+ user = _create_user(topo, 'user', 'Test User', '1002', PASSWORD)
|
||||
+ try:
|
||||
+ # Reset password as Directory Manager
|
||||
+ user.replace('userpassword', PASSWORD)
|
||||
+ time.sleep(1)
|
||||
+
|
||||
+ # Reset password as user (must succeed; min age must not block after reset)
|
||||
+ user.rebind(PASSWORD)
|
||||
+ user.replace('userpassword', PASSWORD)
|
||||
+ time.sleep(1)
|
||||
+
|
||||
+ # Reset again as Directory Manager
|
||||
+ dm.rebind(PASSWORD)
|
||||
+ user.replace('userpassword', PASSWORD)
|
||||
+ time.sleep(1)
|
||||
+
|
||||
+ # Change password through ldappasswd as user to ensure functionality
|
||||
+ env = os.environ.copy()
|
||||
+ env['LDAPTLS_CACERTDIR'] = topo.standalone.get_cert_dir()
|
||||
+ cmd = [
|
||||
+ 'ldappasswd',
|
||||
+ '-ZZ','-H', f"ldap://{topo.standalone.host}:{topo.standalone.port}",
|
||||
+ '-D', user.dn, '-w', PASSWORD,
|
||||
+ '-a', PASSWORD, '-s', 'password2',
|
||||
+ user.dn,
|
||||
+ ]
|
||||
+ result = subprocess.run(cmd, env=env, capture_output=True, text=True)
|
||||
+ assert result.returncode == 0, f'ldappasswd failed: {result.stderr}'
|
||||
+
|
||||
+ # Bind as user with new password
|
||||
+ user.bind('password2')
|
||||
+ finally:
|
||||
+ dm.rebind(PASSWORD)
|
||||
+ user.delete()
|
||||
+
|
||||
+
|
||||
if __name__ == "__main__":
|
||||
CURRENT_FILE = os.path.realpath(__file__)
|
||||
pytest.main("-s -v %s" % CURRENT_FILE)
|
||||
diff --git a/dirsrvtests/tests/tickets/ticket49039_test.py b/dirsrvtests/tests/tickets/ticket49039_test.py
|
||||
deleted file mode 100644
|
||||
index 0313f69a3..000000000
|
||||
--- a/dirsrvtests/tests/tickets/ticket49039_test.py
|
||||
+++ /dev/null
|
||||
@@ -1,127 +0,0 @@
|
||||
-# --- BEGIN COPYRIGHT BLOCK ---
|
||||
-# Copyright (C) 2022 Red Hat, Inc.
|
||||
-# All rights reserved.
|
||||
-#
|
||||
-# License: GPL (version 3 or any later version).
|
||||
-# See LICENSE for details.
|
||||
-# --- END COPYRIGHT BLOCK ---
|
||||
-#
|
||||
-import time
|
||||
-import ldap
|
||||
-import logging
|
||||
-import pytest
|
||||
-import os
|
||||
-from lib389 import Entry
|
||||
-from lib389._constants import *
|
||||
-from lib389.properties import *
|
||||
-from lib389.tasks import *
|
||||
-from lib389.utils import *
|
||||
-from lib389.topologies import topology_st as topo
|
||||
-from lib389.pwpolicy import PwPolicyManager
|
||||
-
|
||||
-
|
||||
-pytestmark = pytest.mark.tier2
|
||||
-
|
||||
-DEBUGGING = os.getenv("DEBUGGING", default=False)
|
||||
-if DEBUGGING:
|
||||
- logging.getLogger(__name__).setLevel(logging.DEBUG)
|
||||
-else:
|
||||
- logging.getLogger(__name__).setLevel(logging.INFO)
|
||||
-log = logging.getLogger(__name__)
|
||||
-
|
||||
-USER_DN = 'uid=user,dc=example,dc=com'
|
||||
-
|
||||
-
|
||||
-def test_ticket49039(topo):
|
||||
- """Test "password must change" verses "password min age". Min age should not
|
||||
- block password update if the password was reset.
|
||||
- """
|
||||
-
|
||||
- # Setup SSL (for ldappasswd test)
|
||||
- topo.standalone.enable_tls()
|
||||
-
|
||||
- # Configure password policy
|
||||
- try:
|
||||
- policy = PwPolicyManager(topo.standalone)
|
||||
- policy.set_global_policy(properties={'nsslapd-pwpolicy-local': 'on',
|
||||
- 'passwordMustChange': 'on',
|
||||
- 'passwordExp': 'on',
|
||||
- 'passwordMaxAge': '86400000',
|
||||
- 'passwordMinAge': '8640000',
|
||||
- 'passwordChange': 'on'})
|
||||
- except ldap.LDAPError as e:
|
||||
- log.fatal('Failed to set password policy: ' + str(e))
|
||||
-
|
||||
- # Add user, bind, and set password
|
||||
- try:
|
||||
- topo.standalone.add_s(Entry((USER_DN, {
|
||||
- 'objectclass': 'top extensibleObject'.split(),
|
||||
- 'uid': 'user1',
|
||||
- 'userpassword': PASSWORD
|
||||
- })))
|
||||
- except ldap.LDAPError as e:
|
||||
- log.fatal('Failed to add user: error ' + e.args[0]['desc'])
|
||||
- assert False
|
||||
-
|
||||
- # Reset password as RootDN
|
||||
- try:
|
||||
- topo.standalone.modify_s(USER_DN, [(ldap.MOD_REPLACE, 'userpassword', ensure_bytes(PASSWORD))])
|
||||
- except ldap.LDAPError as e:
|
||||
- log.fatal('Failed to bind: error ' + e.args[0]['desc'])
|
||||
- assert False
|
||||
-
|
||||
- time.sleep(1)
|
||||
-
|
||||
- # Reset password as user
|
||||
- try:
|
||||
- topo.standalone.simple_bind_s(USER_DN, PASSWORD)
|
||||
- except ldap.LDAPError as e:
|
||||
- log.fatal('Failed to bind: error ' + e.args[0]['desc'])
|
||||
- assert False
|
||||
-
|
||||
- try:
|
||||
- topo.standalone.modify_s(USER_DN, [(ldap.MOD_REPLACE, 'userpassword', ensure_bytes(PASSWORD))])
|
||||
- except ldap.LDAPError as e:
|
||||
- log.fatal('Failed to change password: error ' + e.args[0]['desc'])
|
||||
- assert False
|
||||
-
|
||||
- ###################################
|
||||
- # Make sure ldappasswd also works
|
||||
- ###################################
|
||||
-
|
||||
- # Reset password as RootDN
|
||||
- try:
|
||||
- topo.standalone.simple_bind_s(DN_DM, PASSWORD)
|
||||
- except ldap.LDAPError as e:
|
||||
- log.fatal('Failed to bind as rootdn: error ' + e.args[0]['desc'])
|
||||
- assert False
|
||||
-
|
||||
- try:
|
||||
- topo.standalone.modify_s(USER_DN, [(ldap.MOD_REPLACE, 'userpassword', ensure_bytes(PASSWORD))])
|
||||
- except ldap.LDAPError as e:
|
||||
- log.fatal('Failed to bind: error ' + e.args[0]['desc'])
|
||||
- assert False
|
||||
-
|
||||
- time.sleep(1)
|
||||
-
|
||||
- # Run ldappasswd as the User.
|
||||
- os.environ["LDAPTLS_CACERTDIR"] = topo.standalone.get_cert_dir()
|
||||
- cmd = ('ldappasswd' + ' -h ' + topo.standalone.host + ' -Z -p 38901 -D ' + USER_DN +
|
||||
- ' -w password -a password -s password2 ' + USER_DN)
|
||||
- os.system(cmd)
|
||||
- time.sleep(1)
|
||||
-
|
||||
- try:
|
||||
- topo.standalone.simple_bind_s(USER_DN, "password2")
|
||||
- except ldap.LDAPError as e:
|
||||
- log.fatal('Failed to bind: error ' + e.args[0]['desc'])
|
||||
- assert False
|
||||
-
|
||||
- log.info('Test Passed')
|
||||
-
|
||||
-
|
||||
-if __name__ == '__main__':
|
||||
- # Run isolated
|
||||
- # -s for DEBUG mode
|
||||
- CURRENT_FILE = os.path.realpath(__file__)
|
||||
- pytest.main("-s %s" % CURRENT_FILE)
|
||||
--
|
||||
2.52.0
|
||||
|
||||
952
0057-Issue-5853-Update-concread-to-0.5.10.patch
Normal file
952
0057-Issue-5853-Update-concread-to-0.5.10.patch
Normal file
@ -0,0 +1,952 @@
|
||||
From 0e2d9c4288873446dcb3d8bff61c558ff2b6681a Mon Sep 17 00:00:00 2001
|
||||
From: Viktor Ashirov <vashirov@redhat.com>
|
||||
Date: Mon, 23 Feb 2026 09:49:52 +0100
|
||||
Subject: [PATCH] Issue 5853 - Update concread to 0.5.10
|
||||
|
||||
Description:
|
||||
Update concread to 0.5.10 and update Cargo.lock
|
||||
|
||||
Relates: https://github.com/389ds/389-ds-base/issues/5853
|
||||
|
||||
Reviewed by: @droideck (Thanks!)
|
||||
---
|
||||
src/Cargo.lock | 515 +++++++++++++++++++++++----------------
|
||||
src/librslapd/Cargo.toml | 2 +-
|
||||
2 files changed, 312 insertions(+), 205 deletions(-)
|
||||
|
||||
diff --git a/src/Cargo.lock b/src/Cargo.lock
|
||||
index 87aeee852..425371478 100644
|
||||
--- a/src/Cargo.lock
|
||||
+++ b/src/Cargo.lock
|
||||
@@ -2,27 +2,18 @@
|
||||
# It is not intended for manual editing.
|
||||
version = 3
|
||||
|
||||
-[[package]]
|
||||
-name = "addr2line"
|
||||
-version = "0.24.2"
|
||||
-source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
-checksum = "dfbe277e56a376000877090da837660b4427aad530e3028d44e0bffe4f89a1c1"
|
||||
-dependencies = [
|
||||
- "gimli",
|
||||
-]
|
||||
-
|
||||
-[[package]]
|
||||
-name = "adler2"
|
||||
-version = "2.0.1"
|
||||
-source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
-checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa"
|
||||
-
|
||||
[[package]]
|
||||
name = "allocator-api2"
|
||||
version = "0.2.21"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "683d7910e743518b0e34f1186f92494becacb047c7b6bf616c96772180fef923"
|
||||
|
||||
+[[package]]
|
||||
+name = "anyhow"
|
||||
+version = "1.0.102"
|
||||
+source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
+checksum = "7f202df86484c868dbad7eaa557ef785d5c66295e41b460ef922eca0723b842c"
|
||||
+
|
||||
[[package]]
|
||||
name = "atty"
|
||||
version = "0.2.14"
|
||||
@@ -40,21 +31,6 @@ version = "1.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8"
|
||||
|
||||
-[[package]]
|
||||
-name = "backtrace"
|
||||
-version = "0.3.75"
|
||||
-source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
-checksum = "6806a6321ec58106fea15becdad98371e28d92ccbc7c8f1b3b6dd724fe8f1002"
|
||||
-dependencies = [
|
||||
- "addr2line",
|
||||
- "cfg-if",
|
||||
- "libc",
|
||||
- "miniz_oxide",
|
||||
- "object",
|
||||
- "rustc-demangle",
|
||||
- "windows-targets",
|
||||
-]
|
||||
-
|
||||
[[package]]
|
||||
name = "base64"
|
||||
version = "0.13.1"
|
||||
@@ -69,9 +45,9 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
|
||||
|
||||
[[package]]
|
||||
name = "bitflags"
|
||||
-version = "2.9.1"
|
||||
+version = "2.11.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
-checksum = "1b8e56985ec62d17e9c1001dc89c88ecd7dc08e47eba5ec7c29c7b5eeecde967"
|
||||
+checksum = "843867be96c8daad0d758b57df9392b6d8d271134fce549de6ce169ff98a92af"
|
||||
|
||||
[[package]]
|
||||
name = "byteorder"
|
||||
@@ -86,8 +62,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "da6bc11b07529f16944307272d5bd9b22530bc7d05751717c9d416586cedab49"
|
||||
dependencies = [
|
||||
"clap",
|
||||
- "heck",
|
||||
- "indexmap",
|
||||
+ "heck 0.4.1",
|
||||
+ "indexmap 1.9.3",
|
||||
"log",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
@@ -100,10 +76,11 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "cc"
|
||||
-version = "1.2.27"
|
||||
+version = "1.2.56"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
-checksum = "d487aa071b5f64da6f19a3e848e3578944b726ee5a4854b82172f02aa876bfdc"
|
||||
+checksum = "aebf35691d1bfb0ac386a69bac2fde4dd276fb618cf8bf4f5318fe285e821bb2"
|
||||
dependencies = [
|
||||
+ "find-msvc-tools",
|
||||
"jobserver",
|
||||
"libc",
|
||||
"shlex",
|
||||
@@ -111,9 +88,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "cfg-if"
|
||||
-version = "1.0.1"
|
||||
+version = "1.0.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
-checksum = "9555578bc9e57714c812a1f84e4fc5b4d21fcb063490c624de019f7464c91268"
|
||||
+checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801"
|
||||
|
||||
[[package]]
|
||||
name = "clap"
|
||||
@@ -124,7 +101,7 @@ dependencies = [
|
||||
"atty",
|
||||
"bitflags 1.3.2",
|
||||
"clap_lex",
|
||||
- "indexmap",
|
||||
+ "indexmap 1.9.3",
|
||||
"strsim",
|
||||
"termcolor",
|
||||
"textwrap",
|
||||
@@ -141,14 +118,14 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "concread"
|
||||
-version = "0.5.6"
|
||||
+version = "0.5.10"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
-checksum = "7b639eeaa550eba0c8be45b292d5e272e6d29bfdffb4df6925d651ed9ed10fd6"
|
||||
+checksum = "6588e9e68e11207fb9a5aabd88765187969e6bcba98763c40bcad87b2a73e9f5"
|
||||
dependencies = [
|
||||
"crossbeam-epoch",
|
||||
"crossbeam-queue",
|
||||
"crossbeam-utils",
|
||||
- "foldhash",
|
||||
+ "foldhash 0.2.0",
|
||||
"lru",
|
||||
"smallvec",
|
||||
"sptr",
|
||||
@@ -210,9 +187,9 @@ checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f"
|
||||
|
||||
[[package]]
|
||||
name = "errno"
|
||||
-version = "0.3.12"
|
||||
+version = "0.3.14"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
-checksum = "cea14ef9355e3beab063703aa9dab15afd25f0667c341310c1e5274bb1d0da18"
|
||||
+checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"windows-sys",
|
||||
@@ -232,17 +209,29 @@ checksum = "93804560e638370a8be6d59ce71ed803e55e230abdbf42598e666b41adda9b1f"
|
||||
dependencies = [
|
||||
"base64",
|
||||
"byteorder",
|
||||
- "getrandom 0.2.16",
|
||||
+ "getrandom 0.2.17",
|
||||
"openssl",
|
||||
"zeroize",
|
||||
]
|
||||
|
||||
+[[package]]
|
||||
+name = "find-msvc-tools"
|
||||
+version = "0.1.9"
|
||||
+source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
+checksum = "5baebc0774151f905a1a2cc41989300b1e6fbb29aff0ceffa1064fdd3088d582"
|
||||
+
|
||||
[[package]]
|
||||
name = "foldhash"
|
||||
version = "0.1.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2"
|
||||
|
||||
+[[package]]
|
||||
+name = "foldhash"
|
||||
+version = "0.2.0"
|
||||
+source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
+checksum = "77ce24cb58228fbb8aa041425bb1050850ac19177686ea6e0f41a70416f56fdb"
|
||||
+
|
||||
[[package]]
|
||||
name = "foreign-types"
|
||||
version = "0.3.2"
|
||||
@@ -260,32 +249,39 @@ checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b"
|
||||
|
||||
[[package]]
|
||||
name = "getrandom"
|
||||
-version = "0.2.16"
|
||||
+version = "0.2.17"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
-checksum = "335ff9f135e4384c8150d6f27c6daed433577f86b4750418338c01a1a2528592"
|
||||
+checksum = "ff2abc00be7fca6ebc474524697ae276ad847ad0a6b3faa4bcb027e9a4614ad0"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"libc",
|
||||
- "wasi 0.11.1+wasi-snapshot-preview1",
|
||||
+ "wasi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "getrandom"
|
||||
-version = "0.3.3"
|
||||
+version = "0.3.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
-checksum = "26145e563e54f2cadc477553f1ec5ee650b00862f0a58bcd12cbdc5f0ea2d2f4"
|
||||
+checksum = "899def5c37c4fd7b2664648c28120ecec138e4d395b459e5ca34f9cce2dd77fd"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"libc",
|
||||
"r-efi",
|
||||
- "wasi 0.14.2+wasi-0.2.4",
|
||||
+ "wasip2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
-name = "gimli"
|
||||
-version = "0.31.1"
|
||||
+name = "getrandom"
|
||||
+version = "0.4.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
-checksum = "07e28edb80900c19c28f1072f2e8aeca7fa06b23cd4169cefe1af5aa3260783f"
|
||||
+checksum = "139ef39800118c7683f2fd3c98c1b23c09ae076556b435f8e9064ae108aaeeec"
|
||||
+dependencies = [
|
||||
+ "cfg-if",
|
||||
+ "libc",
|
||||
+ "r-efi",
|
||||
+ "wasip2",
|
||||
+ "wasip3",
|
||||
+]
|
||||
|
||||
[[package]]
|
||||
name = "hashbrown"
|
||||
@@ -295,13 +291,22 @@ checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888"
|
||||
|
||||
[[package]]
|
||||
name = "hashbrown"
|
||||
-version = "0.15.4"
|
||||
+version = "0.15.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
-checksum = "5971ac85611da7067dbfcabef3c70ebb5606018acd9e2a3903a0da507521e0d5"
|
||||
+checksum = "9229cfe53dfd69f0609a49f65461bd93001ea1ef889cd5529dd176593f5338a1"
|
||||
+dependencies = [
|
||||
+ "foldhash 0.1.5",
|
||||
+]
|
||||
+
|
||||
+[[package]]
|
||||
+name = "hashbrown"
|
||||
+version = "0.16.1"
|
||||
+source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
+checksum = "841d1cc9bed7f9236f321df977030373f4a4163ae1a7dbfe1a51a2c1a51d9100"
|
||||
dependencies = [
|
||||
"allocator-api2",
|
||||
"equivalent",
|
||||
- "foldhash",
|
||||
+ "foldhash 0.2.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -310,6 +315,12 @@ version = "0.4.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8"
|
||||
|
||||
+[[package]]
|
||||
+name = "heck"
|
||||
+version = "0.5.0"
|
||||
+source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
+checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea"
|
||||
+
|
||||
[[package]]
|
||||
name = "hermit-abi"
|
||||
version = "0.1.19"
|
||||
@@ -319,6 +330,12 @@ dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
||||
+[[package]]
|
||||
+name = "id-arena"
|
||||
+version = "2.3.0"
|
||||
+source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
+checksum = "3d3067d79b975e8844ca9eb072e16b31c3c1c36928edf9c6789548c524d0d954"
|
||||
+
|
||||
[[package]]
|
||||
name = "indexmap"
|
||||
version = "1.9.3"
|
||||
@@ -329,27 +346,45 @@ dependencies = [
|
||||
"hashbrown 0.12.3",
|
||||
]
|
||||
|
||||
+[[package]]
|
||||
+name = "indexmap"
|
||||
+version = "2.13.0"
|
||||
+source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
+checksum = "7714e70437a7dc3ac8eb7e6f8df75fd8eb422675fc7678aff7364301092b1017"
|
||||
+dependencies = [
|
||||
+ "equivalent",
|
||||
+ "hashbrown 0.16.1",
|
||||
+ "serde",
|
||||
+ "serde_core",
|
||||
+]
|
||||
+
|
||||
[[package]]
|
||||
name = "itoa"
|
||||
-version = "1.0.15"
|
||||
+version = "1.0.17"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
-checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c"
|
||||
+checksum = "92ecc6618181def0457392ccd0ee51198e065e016d1d527a7ac1b6dc7c1f09d2"
|
||||
|
||||
[[package]]
|
||||
name = "jobserver"
|
||||
-version = "0.1.33"
|
||||
+version = "0.1.34"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
-checksum = "38f262f097c174adebe41eb73d66ae9c06b2844fb0da69969647bbddd9b0538a"
|
||||
+checksum = "9afb3de4395d6b3e67a780b6de64b51c978ecf11cb9a462c66be7d4ca9039d33"
|
||||
dependencies = [
|
||||
- "getrandom 0.3.3",
|
||||
+ "getrandom 0.3.4",
|
||||
"libc",
|
||||
]
|
||||
|
||||
+[[package]]
|
||||
+name = "leb128fmt"
|
||||
+version = "0.1.0"
|
||||
+source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
+checksum = "09edd9e8b54e49e587e4f6295a7d29c3ea94d469cb40ab8ca70b288248a81db2"
|
||||
+
|
||||
[[package]]
|
||||
name = "libc"
|
||||
-version = "0.2.174"
|
||||
+version = "0.2.182"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
-checksum = "1171693293099992e19cddea4e8b849964e9846f4acee11b3948bcc337be8776"
|
||||
+checksum = "6800badb6cb2082ffd7b6a67e6125bb39f18782f793520caee8cb8846be06112"
|
||||
|
||||
[[package]]
|
||||
name = "librnsslapd"
|
||||
@@ -372,48 +407,30 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "linux-raw-sys"
|
||||
-version = "0.9.4"
|
||||
+version = "0.11.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
-checksum = "cd945864f07fe9f5371a27ad7b52a172b4b499999f1d97574c9fa68373937e12"
|
||||
+checksum = "df1d3c3b53da64cf5760482273a98e575c651a67eec7f77df96b5b642de8f039"
|
||||
|
||||
[[package]]
|
||||
name = "log"
|
||||
-version = "0.4.27"
|
||||
+version = "0.4.29"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
-checksum = "13dc2df351e3202783a1fe0d44375f7295ffb4049267b0f3018346dc122a1d94"
|
||||
+checksum = "5e5032e24019045c762d3c0f28f5b6b8bbf38563a65908389bf7978758920897"
|
||||
|
||||
[[package]]
|
||||
name = "lru"
|
||||
-version = "0.13.0"
|
||||
+version = "0.16.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
-checksum = "227748d55f2f0ab4735d87fd623798cb6b664512fe979705f829c9f81c934465"
|
||||
+checksum = "a1dc47f592c06f33f8e3aea9591776ec7c9f9e4124778ff8a3c3b87159f7e593"
|
||||
dependencies = [
|
||||
- "hashbrown 0.15.4",
|
||||
+ "hashbrown 0.16.1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "memchr"
|
||||
-version = "2.7.5"
|
||||
-source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
-checksum = "32a282da65faaf38286cf3be983213fcf1d2e2a58700e808f83f4ea9a4804bc0"
|
||||
-
|
||||
-[[package]]
|
||||
-name = "miniz_oxide"
|
||||
-version = "0.8.9"
|
||||
-source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
-checksum = "1fa76a2c86f704bdb222d66965fb3d63269ce38518b83cb0575fca855ebb6316"
|
||||
-dependencies = [
|
||||
- "adler2",
|
||||
-]
|
||||
-
|
||||
-[[package]]
|
||||
-name = "object"
|
||||
-version = "0.36.7"
|
||||
+version = "2.8.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
-checksum = "62948e14d923ea95ea2c7c86c71013138b66525b86bdc08d2dcc262bdb497b87"
|
||||
-dependencies = [
|
||||
- "memchr",
|
||||
-]
|
||||
+checksum = "f8ca58f447f06ed17d5fc4043ce1b10dd205e060fb3ce5b979b8ed8e59ff3f79"
|
||||
|
||||
[[package]]
|
||||
name = "once_cell"
|
||||
@@ -423,11 +440,11 @@ checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d"
|
||||
|
||||
[[package]]
|
||||
name = "openssl"
|
||||
-version = "0.10.73"
|
||||
+version = "0.10.75"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
-checksum = "8505734d46c8ab1e19a1dce3aef597ad87dcb4c37e7188231769bd6bd51cebf8"
|
||||
+checksum = "08838db121398ad17ab8531ce9de97b244589089e290a384c900cb9ff7434328"
|
||||
dependencies = [
|
||||
- "bitflags 2.9.1",
|
||||
+ "bitflags 2.11.0",
|
||||
"cfg-if",
|
||||
"foreign-types",
|
||||
"libc",
|
||||
@@ -444,14 +461,14 @@ checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
- "syn 2.0.103",
|
||||
+ "syn 2.0.117",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "openssl-sys"
|
||||
-version = "0.9.109"
|
||||
+version = "0.9.111"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
-checksum = "90096e2e47630d78b7d1c20952dc621f957103f8bc2c8359ec81290d75238571"
|
||||
+checksum = "82cab2d520aa75e3c58898289429321eb788c3106963d0dc886ec7a5f4adc321"
|
||||
dependencies = [
|
||||
"cc",
|
||||
"libc",
|
||||
@@ -496,6 +513,16 @@ version = "0.3.32"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7edddbd0b52d732b21ad9a5fab5c704c14cd949e5e9a1ec5929a24fded1b904c"
|
||||
|
||||
+[[package]]
|
||||
+name = "prettyplease"
|
||||
+version = "0.2.37"
|
||||
+source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
+checksum = "479ca8adacdd7ce8f1fb39ce9ecccbfe93a3f1344b3d0d97f20bc0196208f62b"
|
||||
+dependencies = [
|
||||
+ "proc-macro2",
|
||||
+ "syn 2.0.117",
|
||||
+]
|
||||
+
|
||||
[[package]]
|
||||
name = "proc-macro-hack"
|
||||
version = "0.5.20+deprecated"
|
||||
@@ -504,9 +531,9 @@ checksum = "dc375e1527247fe1a97d8b7156678dfe7c1af2fc075c9a4db3690ecd2a148068"
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
-version = "1.0.95"
|
||||
+version = "1.0.106"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
-checksum = "02b3e5e68a3a1a02aad3ec490a98007cbc13c37cbe84a3cd7b8e406d76e7f778"
|
||||
+checksum = "8fd00f0bb2e90d81d1044c2b32617f68fcb9fa3bb7640c23e9c748e53fb30934"
|
||||
dependencies = [
|
||||
"unicode-ident",
|
||||
]
|
||||
@@ -526,9 +553,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "quote"
|
||||
-version = "1.0.40"
|
||||
+version = "1.0.44"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
-checksum = "1885c039570dc00dcb4ff087a89e185fd56bae234ddc7f056a945bf36467248d"
|
||||
+checksum = "21b2ebcf727b7760c461f091f9f0f539b77b8e87f2fd88131e7f1b433b3cece4"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
]
|
||||
@@ -539,19 +566,13 @@ version = "5.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f"
|
||||
|
||||
-[[package]]
|
||||
-name = "rustc-demangle"
|
||||
-version = "0.1.25"
|
||||
-source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
-checksum = "989e6739f80c4ad5b13e0fd7fe89531180375b18520cc8c82080e4dc4035b84f"
|
||||
-
|
||||
[[package]]
|
||||
name = "rustix"
|
||||
-version = "1.0.7"
|
||||
+version = "1.1.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
-checksum = "c71e83d6afe7ff64890ec6b71d6a69bb8a610ab78ce364b3352876bb4c801266"
|
||||
+checksum = "146c9e247ccc180c1f61615433868c99f3de3ae256a30a43b49f67c2d9171f34"
|
||||
dependencies = [
|
||||
- "bitflags 2.9.1",
|
||||
+ "bitflags 2.11.0",
|
||||
"errno",
|
||||
"libc",
|
||||
"linux-raw-sys",
|
||||
@@ -559,41 +580,52 @@ dependencies = [
|
||||
]
|
||||
|
||||
[[package]]
|
||||
-name = "ryu"
|
||||
-version = "1.0.20"
|
||||
+name = "semver"
|
||||
+version = "1.0.27"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
-checksum = "28d3b2b1366ec20994f1fd18c3c594f05c5dd4bc44d8bb0c1c632c8d6829481f"
|
||||
+checksum = "d767eb0aabc880b29956c35734170f26ed551a859dbd361d140cdbeca61ab1e2"
|
||||
|
||||
[[package]]
|
||||
name = "serde"
|
||||
-version = "1.0.219"
|
||||
+version = "1.0.228"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
-checksum = "5f0e2c6ed6606019b4e29e69dbaba95b11854410e5347d525002456dbbb786b6"
|
||||
+checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e"
|
||||
+dependencies = [
|
||||
+ "serde_core",
|
||||
+ "serde_derive",
|
||||
+]
|
||||
+
|
||||
+[[package]]
|
||||
+name = "serde_core"
|
||||
+version = "1.0.228"
|
||||
+source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
+checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad"
|
||||
dependencies = [
|
||||
"serde_derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_derive"
|
||||
-version = "1.0.219"
|
||||
+version = "1.0.228"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
-checksum = "5b0276cf7f2c73365f7157c8123c21cd9a50fbbd844757af28ca1f5925fc2a00"
|
||||
+checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
- "syn 2.0.103",
|
||||
+ "syn 2.0.117",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_json"
|
||||
-version = "1.0.140"
|
||||
+version = "1.0.149"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
-checksum = "20068b6e96dc6c9bd23e01df8827e6c7e1f2fddd43c21810382803c136b99373"
|
||||
+checksum = "83fc039473c5595ace860d8c4fafa220ff474b3fc6bfdb4293327f1a37e94d86"
|
||||
dependencies = [
|
||||
"itoa",
|
||||
"memchr",
|
||||
- "ryu",
|
||||
"serde",
|
||||
+ "serde_core",
|
||||
+ "zmij",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -649,9 +681,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
-version = "2.0.103"
|
||||
+version = "2.0.117"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
-checksum = "e4307e30089d6fd6aff212f2da3a1f9e32f3223b1f010fb09b7c95f90f3ca1e8"
|
||||
+checksum = "e665b8803e7b1d2a727f4023456bbbbe74da67099c585258af0ad9c5013b9b99"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
@@ -660,12 +692,12 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "tempfile"
|
||||
-version = "3.20.0"
|
||||
+version = "3.25.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
-checksum = "e8a64e3985349f2441a1a9ef0b853f869006c3855f2cda6862a94d26ebb9d6a1"
|
||||
+checksum = "0136791f7c95b1f6dd99f9cc786b91bb81c3800b639b3478e561ddb7be95e5f1"
|
||||
dependencies = [
|
||||
"fastrand",
|
||||
- "getrandom 0.3.3",
|
||||
+ "getrandom 0.4.1",
|
||||
"once_cell",
|
||||
"rustix",
|
||||
"windows-sys",
|
||||
@@ -688,11 +720,10 @@ checksum = "c13547615a44dc9c452a8a534638acdf07120d4b6847c8178705da06306a3057"
|
||||
|
||||
[[package]]
|
||||
name = "tokio"
|
||||
-version = "1.45.1"
|
||||
+version = "1.49.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
-checksum = "75ef51a33ef1da925cea3e4eb122833cb377c61439ca401b770f54902b806779"
|
||||
+checksum = "72a2903cd7736441aac9df9d7688bd0ce48edccaadf181c3b90be801e81d3d86"
|
||||
dependencies = [
|
||||
- "backtrace",
|
||||
"pin-project-lite",
|
||||
]
|
||||
|
||||
@@ -707,9 +738,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "tracing"
|
||||
-version = "0.1.41"
|
||||
+version = "0.1.44"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
-checksum = "784e0ac535deb450455cbfa28a6f0df145ea1bb7ae51b821cf5e7927fdcfbdd0"
|
||||
+checksum = "63e71662fa4b2a2c3a26f570f037eb95bb1f85397f3cd8076caed2f026a6d100"
|
||||
dependencies = [
|
||||
"pin-project-lite",
|
||||
"tracing-attributes",
|
||||
@@ -718,29 +749,35 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "tracing-attributes"
|
||||
-version = "0.1.30"
|
||||
+version = "0.1.31"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
-checksum = "81383ab64e72a7a8b8e13130c49e3dab29def6d0c7d76a03087b3cf71c5c6903"
|
||||
+checksum = "7490cfa5ec963746568740651ac6781f701c9c5ea257c58e057f3ba8cf69e8da"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
- "syn 2.0.103",
|
||||
+ "syn 2.0.117",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tracing-core"
|
||||
-version = "0.1.34"
|
||||
+version = "0.1.36"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
-checksum = "b9d12581f227e93f094d3af2ae690a574abb8a2b9b7a96e7cfe9647b2b617678"
|
||||
+checksum = "db97caf9d906fbde555dd62fa95ddba9eecfd14cb388e4f491a66d74cd5fb79a"
|
||||
dependencies = [
|
||||
"once_cell",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "unicode-ident"
|
||||
-version = "1.0.18"
|
||||
+version = "1.0.24"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
-checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512"
|
||||
+checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75"
|
||||
+
|
||||
+[[package]]
|
||||
+name = "unicode-xid"
|
||||
+version = "0.2.6"
|
||||
+source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
+checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853"
|
||||
|
||||
[[package]]
|
||||
name = "uuid"
|
||||
@@ -748,7 +785,7 @@ version = "0.8.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bc5cf98d8186244414c848017f0e2676b3fcb46807f6668a97dfe67359a3c4b7"
|
||||
dependencies = [
|
||||
- "getrandom 0.2.16",
|
||||
+ "getrandom 0.2.17",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -764,12 +801,55 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b"
|
||||
|
||||
[[package]]
|
||||
-name = "wasi"
|
||||
-version = "0.14.2+wasi-0.2.4"
|
||||
+name = "wasip2"
|
||||
+version = "1.0.2+wasi-0.2.9"
|
||||
+source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
+checksum = "9517f9239f02c069db75e65f174b3da828fe5f5b945c4dd26bd25d89c03ebcf5"
|
||||
+dependencies = [
|
||||
+ "wit-bindgen",
|
||||
+]
|
||||
+
|
||||
+[[package]]
|
||||
+name = "wasip3"
|
||||
+version = "0.4.0+wasi-0.3.0-rc-2026-01-06"
|
||||
+source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
+checksum = "5428f8bf88ea5ddc08faddef2ac4a67e390b88186c703ce6dbd955e1c145aca5"
|
||||
+dependencies = [
|
||||
+ "wit-bindgen",
|
||||
+]
|
||||
+
|
||||
+[[package]]
|
||||
+name = "wasm-encoder"
|
||||
+version = "0.244.0"
|
||||
+source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
+checksum = "990065f2fe63003fe337b932cfb5e3b80e0b4d0f5ff650e6985b1048f62c8319"
|
||||
+dependencies = [
|
||||
+ "leb128fmt",
|
||||
+ "wasmparser",
|
||||
+]
|
||||
+
|
||||
+[[package]]
|
||||
+name = "wasm-metadata"
|
||||
+version = "0.244.0"
|
||||
+source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
+checksum = "bb0e353e6a2fbdc176932bbaab493762eb1255a7900fe0fea1a2f96c296cc909"
|
||||
+dependencies = [
|
||||
+ "anyhow",
|
||||
+ "indexmap 2.13.0",
|
||||
+ "wasm-encoder",
|
||||
+ "wasmparser",
|
||||
+]
|
||||
+
|
||||
+[[package]]
|
||||
+name = "wasmparser"
|
||||
+version = "0.244.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
-checksum = "9683f9a5a998d873c0d21fcbe3c083009670149a8fab228644b8bd36b2c48cb3"
|
||||
+checksum = "47b807c72e1bac69382b3a6fb3dbe8ea4c0ed87ff5629b8685ae6b9a611028fe"
|
||||
dependencies = [
|
||||
- "wit-bindgen-rt",
|
||||
+ "bitflags 2.11.0",
|
||||
+ "hashbrown 0.15.5",
|
||||
+ "indexmap 2.13.0",
|
||||
+ "semver",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -790,9 +870,9 @@ checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
|
||||
|
||||
[[package]]
|
||||
name = "winapi-util"
|
||||
-version = "0.1.9"
|
||||
+version = "0.1.11"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
-checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb"
|
||||
+checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22"
|
||||
dependencies = [
|
||||
"windows-sys",
|
||||
]
|
||||
@@ -804,103 +884,130 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
|
||||
|
||||
[[package]]
|
||||
-name = "windows-sys"
|
||||
-version = "0.59.0"
|
||||
+name = "windows-link"
|
||||
+version = "0.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
-checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b"
|
||||
-dependencies = [
|
||||
- "windows-targets",
|
||||
-]
|
||||
+checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5"
|
||||
|
||||
[[package]]
|
||||
-name = "windows-targets"
|
||||
-version = "0.52.6"
|
||||
+name = "windows-sys"
|
||||
+version = "0.61.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
-checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973"
|
||||
+checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc"
|
||||
dependencies = [
|
||||
- "windows_aarch64_gnullvm",
|
||||
- "windows_aarch64_msvc",
|
||||
- "windows_i686_gnu",
|
||||
- "windows_i686_gnullvm",
|
||||
- "windows_i686_msvc",
|
||||
- "windows_x86_64_gnu",
|
||||
- "windows_x86_64_gnullvm",
|
||||
- "windows_x86_64_msvc",
|
||||
+ "windows-link",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
-name = "windows_aarch64_gnullvm"
|
||||
-version = "0.52.6"
|
||||
+name = "wit-bindgen"
|
||||
+version = "0.51.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
-checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3"
|
||||
-
|
||||
-[[package]]
|
||||
-name = "windows_aarch64_msvc"
|
||||
-version = "0.52.6"
|
||||
-source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
-checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469"
|
||||
-
|
||||
-[[package]]
|
||||
-name = "windows_i686_gnu"
|
||||
-version = "0.52.6"
|
||||
-source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
-checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b"
|
||||
-
|
||||
-[[package]]
|
||||
-name = "windows_i686_gnullvm"
|
||||
-version = "0.52.6"
|
||||
-source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
-checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66"
|
||||
+checksum = "d7249219f66ced02969388cf2bb044a09756a083d0fab1e566056b04d9fbcaa5"
|
||||
+dependencies = [
|
||||
+ "wit-bindgen-rust-macro",
|
||||
+]
|
||||
|
||||
[[package]]
|
||||
-name = "windows_i686_msvc"
|
||||
-version = "0.52.6"
|
||||
+name = "wit-bindgen-core"
|
||||
+version = "0.51.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
-checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66"
|
||||
+checksum = "ea61de684c3ea68cb082b7a88508a8b27fcc8b797d738bfc99a82facf1d752dc"
|
||||
+dependencies = [
|
||||
+ "anyhow",
|
||||
+ "heck 0.5.0",
|
||||
+ "wit-parser",
|
||||
+]
|
||||
|
||||
[[package]]
|
||||
-name = "windows_x86_64_gnu"
|
||||
-version = "0.52.6"
|
||||
+name = "wit-bindgen-rust"
|
||||
+version = "0.51.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
-checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78"
|
||||
+checksum = "b7c566e0f4b284dd6561c786d9cb0142da491f46a9fbed79ea69cdad5db17f21"
|
||||
+dependencies = [
|
||||
+ "anyhow",
|
||||
+ "heck 0.5.0",
|
||||
+ "indexmap 2.13.0",
|
||||
+ "prettyplease",
|
||||
+ "syn 2.0.117",
|
||||
+ "wasm-metadata",
|
||||
+ "wit-bindgen-core",
|
||||
+ "wit-component",
|
||||
+]
|
||||
|
||||
[[package]]
|
||||
-name = "windows_x86_64_gnullvm"
|
||||
-version = "0.52.6"
|
||||
+name = "wit-bindgen-rust-macro"
|
||||
+version = "0.51.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
-checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d"
|
||||
+checksum = "0c0f9bfd77e6a48eccf51359e3ae77140a7f50b1e2ebfe62422d8afdaffab17a"
|
||||
+dependencies = [
|
||||
+ "anyhow",
|
||||
+ "prettyplease",
|
||||
+ "proc-macro2",
|
||||
+ "quote",
|
||||
+ "syn 2.0.117",
|
||||
+ "wit-bindgen-core",
|
||||
+ "wit-bindgen-rust",
|
||||
+]
|
||||
|
||||
[[package]]
|
||||
-name = "windows_x86_64_msvc"
|
||||
-version = "0.52.6"
|
||||
+name = "wit-component"
|
||||
+version = "0.244.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
-checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec"
|
||||
+checksum = "9d66ea20e9553b30172b5e831994e35fbde2d165325bec84fc43dbf6f4eb9cb2"
|
||||
+dependencies = [
|
||||
+ "anyhow",
|
||||
+ "bitflags 2.11.0",
|
||||
+ "indexmap 2.13.0",
|
||||
+ "log",
|
||||
+ "serde",
|
||||
+ "serde_derive",
|
||||
+ "serde_json",
|
||||
+ "wasm-encoder",
|
||||
+ "wasm-metadata",
|
||||
+ "wasmparser",
|
||||
+ "wit-parser",
|
||||
+]
|
||||
|
||||
[[package]]
|
||||
-name = "wit-bindgen-rt"
|
||||
-version = "0.39.0"
|
||||
+name = "wit-parser"
|
||||
+version = "0.244.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
-checksum = "6f42320e61fe2cfd34354ecb597f86f413484a798ba44a8ca1165c58d42da6c1"
|
||||
+checksum = "ecc8ac4bc1dc3381b7f59c34f00b67e18f910c2c0f50015669dde7def656a736"
|
||||
dependencies = [
|
||||
- "bitflags 2.9.1",
|
||||
+ "anyhow",
|
||||
+ "id-arena",
|
||||
+ "indexmap 2.13.0",
|
||||
+ "log",
|
||||
+ "semver",
|
||||
+ "serde",
|
||||
+ "serde_derive",
|
||||
+ "serde_json",
|
||||
+ "unicode-xid",
|
||||
+ "wasmparser",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zeroize"
|
||||
-version = "1.8.1"
|
||||
+version = "1.8.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
-checksum = "ced3678a2879b30306d323f4542626697a464a97c0a07c9aebf7ebca65cd4dde"
|
||||
+checksum = "b97154e67e32c85465826e8bcc1c59429aaaf107c1e4a9e53c8d8ccd5eff88d0"
|
||||
dependencies = [
|
||||
"zeroize_derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zeroize_derive"
|
||||
-version = "1.4.2"
|
||||
+version = "1.4.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
-checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69"
|
||||
+checksum = "85a5b4158499876c763cb03bc4e49185d3cccbabb15b33c627f7884f43db852e"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
- "syn 2.0.103",
|
||||
+ "syn 2.0.117",
|
||||
]
|
||||
+
|
||||
+[[package]]
|
||||
+name = "zmij"
|
||||
+version = "1.0.21"
|
||||
+source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
+checksum = "b8848ee67ecc8aedbaf3e4122217aff892639231befc6a1b58d29fff4c2cabaa"
|
||||
diff --git a/src/librslapd/Cargo.toml b/src/librslapd/Cargo.toml
|
||||
index f38f93f4c..61100f381 100644
|
||||
--- a/src/librslapd/Cargo.toml
|
||||
+++ b/src/librslapd/Cargo.toml
|
||||
@@ -16,7 +16,7 @@ crate-type = ["staticlib", "lib"]
|
||||
[dependencies]
|
||||
slapd = { path = "../slapd" }
|
||||
libc = "0.2"
|
||||
-concread = "0.5.6"
|
||||
+concread = "0.5.10"
|
||||
|
||||
[build-dependencies]
|
||||
cbindgen = "0.26"
|
||||
--
|
||||
2.53.0
|
||||
|
||||
@ -0,0 +1,86 @@
|
||||
From 8b4dbf35ace326e5b836982d428e7029313a2247 Mon Sep 17 00:00:00 2001
|
||||
From: Mark Reynolds <mreynolds@redhat.com>
|
||||
Date: Mon, 23 Feb 2026 12:37:26 -0500
|
||||
Subject: [PATCH] Issue 7271 - plugins that create threads need to update
|
||||
active thread count
|
||||
|
||||
Description:
|
||||
|
||||
Plugins that create threads need to up to the global active thread count.
|
||||
Otherwise when the server is being stopped the plugin's close function gets
|
||||
called while these threads are still running and still using the plugin
|
||||
configuration. This can lead to crashes.
|
||||
|
||||
relates: https://github.com/389ds/389-ds-base/issues/7271
|
||||
|
||||
Reviewed by: progier & tbordaz (Thanks!!)
|
||||
---
|
||||
ldap/servers/plugins/replication/repl5_protocol.c | 5 +++++
|
||||
ldap/servers/plugins/retrocl/retrocl_trim.c | 7 ++++++-
|
||||
2 files changed, 11 insertions(+), 1 deletion(-)
|
||||
|
||||
diff --git a/ldap/servers/plugins/replication/repl5_protocol.c b/ldap/servers/plugins/replication/repl5_protocol.c
|
||||
index 5d4a0e455..bc9580319 100644
|
||||
--- a/ldap/servers/plugins/replication/repl5_protocol.c
|
||||
+++ b/ldap/servers/plugins/replication/repl5_protocol.c
|
||||
@@ -23,6 +23,7 @@
|
||||
|
||||
#include "repl5.h"
|
||||
#include "repl5_prot_private.h"
|
||||
+#include "slap.h"
|
||||
|
||||
#define PROTOCOL_5_INCREMENTAL 1
|
||||
#define PROTOCOL_5_TOTAL 2
|
||||
@@ -237,6 +238,8 @@ prot_thread_main(void *arg)
|
||||
return;
|
||||
}
|
||||
|
||||
+ g_incr_active_threadcnt();
|
||||
+
|
||||
set_thread_private_agmtname(agmt_get_long_name(agmt));
|
||||
|
||||
done = 0;
|
||||
@@ -301,6 +304,8 @@ prot_thread_main(void *arg)
|
||||
done = 1;
|
||||
}
|
||||
}
|
||||
+
|
||||
+ g_decr_active_threadcnt();
|
||||
}
|
||||
|
||||
/*
|
||||
diff --git a/ldap/servers/plugins/retrocl/retrocl_trim.c b/ldap/servers/plugins/retrocl/retrocl_trim.c
|
||||
index 8fcd3d32b..8fdc75c62 100644
|
||||
--- a/ldap/servers/plugins/retrocl/retrocl_trim.c
|
||||
+++ b/ldap/servers/plugins/retrocl/retrocl_trim.c
|
||||
@@ -243,6 +243,8 @@ trim_changelog(void)
|
||||
|
||||
now_interval = slapi_current_rel_time_t(); /* monotonic time for interval */
|
||||
|
||||
+ g_incr_active_threadcnt();
|
||||
+
|
||||
PR_Lock(ts.ts_s_trim_mutex);
|
||||
max_age = ts.ts_c_max_age;
|
||||
trim_interval = ts.ts_c_trim_interval;
|
||||
@@ -257,7 +259,7 @@ trim_changelog(void)
|
||||
*/
|
||||
done = 0;
|
||||
now_maxage = slapi_current_utc_time(); /* real time for trim candidates */
|
||||
- while (!done && retrocl_trimming == 1) {
|
||||
+ while (!done && retrocl_trimming == 1 && !slapi_is_shutting_down()) {
|
||||
int did_delete;
|
||||
|
||||
did_delete = 0;
|
||||
@@ -309,6 +311,9 @@ trim_changelog(void)
|
||||
"trim_changelog: removed %d change records\n",
|
||||
num_deleted);
|
||||
}
|
||||
+
|
||||
+ g_decr_active_threadcnt();
|
||||
+
|
||||
return rc;
|
||||
}
|
||||
|
||||
--
|
||||
2.53.0
|
||||
|
||||
@ -0,0 +1,40 @@
|
||||
From ffe1909e69ab2aecef396f31cf95cdcecd992782 Mon Sep 17 00:00:00 2001
|
||||
From: Mark Reynolds <mreynolds@redhat.com>
|
||||
Date: Mon, 23 Feb 2026 12:10:32 -0500
|
||||
Subject: [PATCH] Issue 7273 - In a chaining environment binding as remote user
|
||||
causes an invalid error in the logs
|
||||
|
||||
Description:
|
||||
|
||||
In a database link/chaining environment you can bind as a remote user, and
|
||||
this triggers an error when trying to "upgrade_on_bind" as the user does not
|
||||
locally have a userpassword since it's remote. There is no strong case to
|
||||
log an error in this situation.
|
||||
|
||||
relates: http://github.com/389ds/389-ds-base/issues/7273
|
||||
|
||||
Reviewed by: vashirov(Thanks!)
|
||||
---
|
||||
ldap/servers/slapd/pw.c | 6 ++----
|
||||
1 file changed, 2 insertions(+), 4 deletions(-)
|
||||
|
||||
diff --git a/ldap/servers/slapd/pw.c b/ldap/servers/slapd/pw.c
|
||||
index c53ecf23d..88e32537d 100644
|
||||
--- a/ldap/servers/slapd/pw.c
|
||||
+++ b/ldap/servers/slapd/pw.c
|
||||
@@ -3564,10 +3564,8 @@ int32_t update_pw_encoding(Slapi_PBlock *orig_pb, Slapi_Entry *e, Slapi_DN *sdn,
|
||||
* Does the entry have a pw?
|
||||
*/
|
||||
if (e == NULL || slapi_entry_attr_find(e, SLAPI_USERPWD_ATTR, &pw) != 0 || pw == NULL) {
|
||||
- slapi_log_err(SLAPI_LOG_WARNING,
|
||||
- "update_pw_encoding", "Could not read password attribute on '%s'\n",
|
||||
- dn);
|
||||
- res = -1;
|
||||
+ /* The entry does not have a userpassword attribute so there is nothing to do.
|
||||
+ * This typically happens when chaining is involved. */
|
||||
goto free_and_return;
|
||||
}
|
||||
|
||||
--
|
||||
2.53.0
|
||||
|
||||
@ -0,0 +1,46 @@
|
||||
From 3a233116a564fc339aee1021913c995504951d86 Mon Sep 17 00:00:00 2001
|
||||
From: Simon Pichugin <spichugi@redhat.com>
|
||||
Date: Tue, 24 Feb 2026 09:28:24 -0800
|
||||
Subject: [PATCH] Issue 7279 - UI - Fix typo in export certificate dialog
|
||||
(#7280)
|
||||
|
||||
Description: Fix typo "cetificate" -> "certificate" in the
|
||||
export certificate dialog message.
|
||||
|
||||
Fixes: https://github.com/389ds/389-ds-base/issues/7279
|
||||
|
||||
Reviewed by: @vashirov (Thanks!)
|
||||
---
|
||||
src/cockpit/389-console/po/ja.po | 2 +-
|
||||
src/cockpit/389-console/src/lib/security/securityModals.jsx | 2 +-
|
||||
2 files changed, 2 insertions(+), 2 deletions(-)
|
||||
|
||||
diff --git a/src/cockpit/389-console/po/ja.po b/src/cockpit/389-console/po/ja.po
|
||||
index 27d21bf7a..12c10ded4 100644
|
||||
--- a/src/cockpit/389-console/po/ja.po
|
||||
+++ b/src/cockpit/389-console/po/ja.po
|
||||
@@ -11560,7 +11560,7 @@ msgstr "証明書のエクスポート:"
|
||||
#: src/lib/security/securityModals.jsx:58
|
||||
msgid ""
|
||||
"Enter the full path and file name, if the path portion is omitted the "
|
||||
-"cetificate is written to the server's certificate directory "
|
||||
+"certificate is written to the server's certificate directory "
|
||||
msgstr ""
|
||||
"ファイル名を含むフルパスを入力してください。パス部分を省略した場合、証明書は"
|
||||
"サーバの証明書ディレクトリに書き込まれます。"
|
||||
diff --git a/src/cockpit/389-console/src/lib/security/securityModals.jsx b/src/cockpit/389-console/src/lib/security/securityModals.jsx
|
||||
index 4d6631fd2..f32d47596 100644
|
||||
--- a/src/cockpit/389-console/src/lib/security/securityModals.jsx
|
||||
+++ b/src/cockpit/389-console/src/lib/security/securityModals.jsx
|
||||
@@ -55,7 +55,7 @@ export class ExportCertModal extends React.Component {
|
||||
}
|
||||
|
||||
const title = <>{_("Export Certificate:")} <i>{nickName}</i></>;
|
||||
- const desc = <>{_("Enter the full path and file name, if the path portion is omitted the cetificate is written to the server's certificate directory ")}<i>{certDir}</i></>;
|
||||
+ const desc = <>{_("Enter the full path and file name, if the path portion is omitted the certificate is written to the server's certificate directory ")}<i>{certDir}</i></>;
|
||||
|
||||
return (
|
||||
<Modal
|
||||
--
|
||||
2.53.0
|
||||
|
||||
1178
0061-Issue-7275-UI-Improve-password-policy-field-validati.patch
Normal file
1178
0061-Issue-7275-UI-Improve-password-policy-field-validati.patch
Normal file
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,34 @@
|
||||
From f4d8df66187c0d1db6c9f5c9aa55946a8564de4b Mon Sep 17 00:00:00 2001
|
||||
From: Sam Morris <sam@robots.org.uk>
|
||||
Date: Wed, 25 Feb 2026 12:30:01 +0000
|
||||
Subject: [PATCH] Issue 7246 - correct formatting of 'Gen as CSN' in dsctl
|
||||
get-nsstate output (#7247)
|
||||
|
||||
Description: CSNs are formatted as hexadecimal, but the replica id and
|
||||
sequence number are displayed in decomal.
|
||||
|
||||
Fix: use correct format specifiers for hexadecimal output.
|
||||
|
||||
Fixes: https://github.com/389ds/389-ds-base/issues/7246
|
||||
|
||||
Signed-off-by: Sam Morris <sam@robots.org.uk>
|
||||
---
|
||||
src/lib389/lib389/dseldif.py | 2 +-
|
||||
1 file changed, 1 insertion(+), 1 deletion(-)
|
||||
|
||||
diff --git a/src/lib389/lib389/dseldif.py b/src/lib389/lib389/dseldif.py
|
||||
index 7834d9468..eac17aa4c 100644
|
||||
--- a/src/lib389/lib389/dseldif.py
|
||||
+++ b/src/lib389/lib389/dseldif.py
|
||||
@@ -407,7 +407,7 @@ class DSEldif(DSLint):
|
||||
'endian': endian,
|
||||
'rid': str(rid),
|
||||
'gen_time': str(sampled_time),
|
||||
- 'gencsn': "%08x%04d%04d0000" % (sampled_time, seq_num, rid),
|
||||
+ 'gencsn': "%08x%04x%04x0000" % (sampled_time, seq_num, rid),
|
||||
'gen_time_str': time.ctime(sampled_time),
|
||||
'local_offset': str(local_offset),
|
||||
'local_offset_str': print_nice_time(local_offset),
|
||||
--
|
||||
2.53.0
|
||||
|
||||
93
0063-Security-fix-for-CVE-2025-14905.patch
Normal file
93
0063-Security-fix-for-CVE-2025-14905.patch
Normal file
@ -0,0 +1,93 @@
|
||||
From 2e424110def2e3998f6045e136fb0d43f47b7f5a Mon Sep 17 00:00:00 2001
|
||||
From: tbordaz <tbordaz@redhat.com>
|
||||
Date: Wed, 25 Feb 2026 14:06:42 +0100
|
||||
Subject: [PATCH] Security fix for CVE-2025-14905
|
||||
|
||||
Description:
|
||||
A vulnerability was found in the 389 Directory Server.
|
||||
The 389 Directory Server present a risk of heap buffer overflow that
|
||||
can be exploited to excute a Denial of Service and potential Remote
|
||||
Code Execution
|
||||
|
||||
References:
|
||||
- https://access.redhat.com/security/cve/CVE-2025-14905
|
||||
- https://bugzilla.redhat.com/show_bug.cgi?id=2423624
|
||||
---
|
||||
ldap/servers/slapd/schema.c | 47 ++++++++++++++++++++++++++++++-------
|
||||
1 file changed, 38 insertions(+), 9 deletions(-)
|
||||
|
||||
diff --git a/ldap/servers/slapd/schema.c b/ldap/servers/slapd/schema.c
|
||||
index 9ef4ee4bf..7712a720d 100644
|
||||
--- a/ldap/servers/slapd/schema.c
|
||||
+++ b/ldap/servers/slapd/schema.c
|
||||
@@ -1410,6 +1410,7 @@ schema_attr_enum_callback(struct asyntaxinfo *asip, void *arg)
|
||||
const char *attr_desc, *syntaxoid;
|
||||
char *outp, syntaxlengthbuf[128];
|
||||
int i;
|
||||
+ int nb_aliases = 0;
|
||||
|
||||
vals[0] = &val;
|
||||
|
||||
@@ -1435,6 +1436,7 @@ schema_attr_enum_callback(struct asyntaxinfo *asip, void *arg)
|
||||
if (asip->asi_aliases != NULL) {
|
||||
for (i = 0; asip->asi_aliases[i] != NULL; ++i) {
|
||||
aliaslen += strlen(asip->asi_aliases[i]);
|
||||
+ nb_aliases++;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1452,15 +1454,42 @@ schema_attr_enum_callback(struct asyntaxinfo *asip, void *arg)
|
||||
* XXX: 256 is a magic number... it must be big enough to account for
|
||||
* all of the fixed sized items we output.
|
||||
*/
|
||||
- sizedbuffer_allocate(aew->psbAttrTypes, 256 + strlen(asip->asi_oid) +
|
||||
- strlen(asip->asi_name) +
|
||||
- aliaslen + strlen_null_ok(attr_desc) +
|
||||
- strlen(syntaxoid) +
|
||||
- strlen_null_ok(asip->asi_superior) +
|
||||
- strlen_null_ok(asip->asi_mr_equality) +
|
||||
- strlen_null_ok(asip->asi_mr_ordering) +
|
||||
- strlen_null_ok(asip->asi_mr_substring) +
|
||||
- strcat_extensions(NULL, asip->asi_extensions));
|
||||
+ {
|
||||
+ int asi_oid_strlen = strlen(asip->asi_oid) + 8; /* "( %s NAME " */
|
||||
+ int asi_name_strlen = strlen(asip->asi_name) + 6; /* "( '%s' ...)" */
|
||||
+ int asi_aliases_strlen = aliaslen + nb_aliases * 3; /* "'%s' " */
|
||||
+ int asi_desc_strlen = strlen_null_ok(attr_desc) + 7; /* "DESC '%s'" */
|
||||
+ int asi_syntaxoid_strlen = strlen("SYNTAX ") + strlen(syntaxoid) + strlen(syntaxlengthbuf);
|
||||
+ int asi_superior_strlen = strlen("SUP ") + strlen_null_ok(asip->asi_superior);
|
||||
+ int asi_mr_equality_strlen = strlen("EQUALITY ") + strlen_null_ok(asip->asi_mr_equality);
|
||||
+ int asi_mr_ordering_strlen = strlen("ORDERING ") + strlen_null_ok(asip->asi_mr_ordering);
|
||||
+ int asi_mr_substring_strlen = strlen("SUBSTR ") + strlen_null_ok(asip->asi_mr_substring);
|
||||
+ int asi_flags_strlen = strlen("SINGLE-VALUE ") +
|
||||
+ strlen(schema_obsolete_with_spaces) +
|
||||
+ strlen(schema_collective_with_spaces) +
|
||||
+ strlen(schema_nousermod_with_spaces) +
|
||||
+ strlen("USAGE distributedOperation ") +
|
||||
+ strlen("USAGE dSAOperation ") +
|
||||
+ strlen("USAGE directoryOperation ");
|
||||
+ int asi_extension_strlen = strcat_extensions(NULL, asip->asi_extensions);
|
||||
+
|
||||
+ if (aew->enquote_sup_oc) {
|
||||
+ /* it enquote the syntax oid */
|
||||
+ asi_syntaxoid_strlen += 2;
|
||||
+ }
|
||||
+
|
||||
+ sizedbuffer_allocate(aew->psbAttrTypes, 256 + asi_oid_strlen +
|
||||
+ asi_name_strlen +
|
||||
+ asi_aliases_strlen +
|
||||
+ asi_desc_strlen +
|
||||
+ asi_syntaxoid_strlen +
|
||||
+ asi_superior_strlen +
|
||||
+ asi_mr_equality_strlen +
|
||||
+ asi_mr_ordering_strlen +
|
||||
+ asi_mr_substring_strlen +
|
||||
+ asi_extension_strlen +
|
||||
+ asi_flags_strlen);
|
||||
+ }
|
||||
|
||||
/*
|
||||
* Overall strategy is to maintain a pointer to the next location in
|
||||
--
|
||||
2.53.0
|
||||
|
||||
654
0064-Issue-7267-MDB_BAD_VALSIZE-error-when-updating-index.patch
Normal file
654
0064-Issue-7267-MDB_BAD_VALSIZE-error-when-updating-index.patch
Normal file
@ -0,0 +1,654 @@
|
||||
From c7e1eb08eb36fd9a16c745935f56fa4a4b2a99df Mon Sep 17 00:00:00 2001
|
||||
From: progier389 <progier@redhat.com>
|
||||
Date: Wed, 25 Feb 2026 18:00:24 +0100
|
||||
Subject: [PATCH] Issue 7267 - MDB_BAD_VALSIZE error when updating index
|
||||
(#7268)
|
||||
|
||||
* Issue 7267 - MDB_BAD_VALSIZE error when updating index
|
||||
* Improve import log when writer fails
|
||||
* Fix Sourcery AI comments
|
||||
* Fix INDEX_KEY_LENGTH typo
|
||||
|
||||
Problem with the key prefix handling when key is too long and must be hashed.
|
||||
The issue is that the # that is prepended is not reset when iterating over the valueset values (Ending up with very long prefix)
|
||||
|
||||
Also refactored the code to avoid duplicate the code that prepare the key from the attribute value (used when updating the index or retrieving a value from an index)
|
||||
|
||||
Issue: #7267
|
||||
|
||||
Reviewed by: @tbordaz , @vashirov (Thanks!)
|
||||
|
||||
Co-authored-by: Viktor Ashirov <vashirov@redhat.com>
|
||||
|
||||
---------
|
||||
|
||||
Co-authored-by: Viktor Ashirov <vashirov@redhat.com>
|
||||
---
|
||||
.../tests/suites/indexes/regression_test.py | 58 +++++++
|
||||
ldap/servers/slapd/back-ldbm/attrcrypt.h | 2 +-
|
||||
ldap/servers/slapd/back-ldbm/back-ldbm.h | 2 +
|
||||
.../slapd/back-ldbm/db-bdb/bdb_import.c | 39 +----
|
||||
.../back-ldbm/db-mdb/mdb_import_threads.c | 46 ++++-
|
||||
ldap/servers/slapd/back-ldbm/index.c | 161 +++++++-----------
|
||||
ldap/servers/slapd/back-ldbm/ldbm_attrcrypt.c | 13 +-
|
||||
.../servers/slapd/back-ldbm/proto-back-ldbm.h | 2 +-
|
||||
ldap/servers/slapd/log.c | 43 +++++
|
||||
ldap/servers/slapd/slapi-private.h | 2 +
|
||||
10 files changed, 224 insertions(+), 144 deletions(-)
|
||||
|
||||
diff --git a/dirsrvtests/tests/suites/indexes/regression_test.py b/dirsrvtests/tests/suites/indexes/regression_test.py
|
||||
index 8176d6db0..a4218a2b5 100644
|
||||
--- a/dirsrvtests/tests/suites/indexes/regression_test.py
|
||||
+++ b/dirsrvtests/tests/suites/indexes/regression_test.py
|
||||
@@ -1022,6 +1022,64 @@ def test_idl_range_limit(topo, add_some_entries):
|
||||
assert len(entries) == 3
|
||||
|
||||
|
||||
+def test_large_multivalued_sn_attribute(topo):
|
||||
+ """Test adding a user entry with 512 values for sn attribute, each 512 bytes
|
||||
+
|
||||
+ :id: 8f2a9b3c-e8d7-11ef-9a5f-482ae39447e5
|
||||
+ :setup: Standalone Instance
|
||||
+ :steps:
|
||||
+ 1. Create a user with 512 sn values, each 512 bytes long
|
||||
+ 2. Verify the user was created successfully
|
||||
+ 3. Search for the user and verify all sn values are present
|
||||
+ 4. Clean up the user entry
|
||||
+ :expectedresults:
|
||||
+ 1. User is created successfully
|
||||
+ 2. User entry exists
|
||||
+ 3. All 512 sn values are present and have correct length
|
||||
+ 4. User is deleted successfully
|
||||
+ """
|
||||
+
|
||||
+ inst = topo.standalone
|
||||
+ users = UserAccounts(inst, DEFAULT_SUFFIX)
|
||||
+
|
||||
+ log.info("Creating user with 512 sn values, each 512 bytes")
|
||||
+
|
||||
+ # Generate 512 unique sn values, each 512 bytes long
|
||||
+ # Use a pattern that makes each value unique but predictable
|
||||
+ sn_values = []
|
||||
+ for i in range(512):
|
||||
+ # Create a 512-byte value with unique identifier at the start
|
||||
+ value = f'sn_value_{i:04d}_' + 'x' * (512 - len(f'sn_value_{i:04d}_'))
|
||||
+ sn_values.append(value)
|
||||
+
|
||||
+ # Create the user with first sn value
|
||||
+ user_name = 'test_user_large_sn'
|
||||
+ user = users.create(properties={
|
||||
+ 'uid': user_name,
|
||||
+ 'cn': user_name,
|
||||
+ 'sn': sn_values,
|
||||
+ 'uidNumber': '99999',
|
||||
+ 'gidNumber': '99999',
|
||||
+ 'homeDirectory': f'/home/{user_name}'
|
||||
+ })
|
||||
+
|
||||
+ # Verify the entry was created and has all sn values
|
||||
+ log.info("Verifying all sn values are present")
|
||||
+ sn_attr_values = user.get_attr_vals_utf8('sn')
|
||||
+
|
||||
+ assert len(sn_attr_values) == 512, f"Expected 512 sn values, got {len(sn_attr_values)}"
|
||||
+
|
||||
+ # Verify each value has the correct length
|
||||
+ for idx, value in enumerate(sn_attr_values):
|
||||
+ assert len(value) == 512, f"sn value {idx} has length {len(value)}, expected 512"
|
||||
+
|
||||
+ log.info("Successfully created and verified user with 512 sn values of 512 bytes each")
|
||||
+
|
||||
+ # Clean up
|
||||
+ user.delete()
|
||||
+ log.info("User entry deleted successfully")
|
||||
+
|
||||
+
|
||||
if __name__ == "__main__":
|
||||
# Run isolated
|
||||
# -s for DEBUG mode
|
||||
diff --git a/ldap/servers/slapd/back-ldbm/attrcrypt.h b/ldap/servers/slapd/back-ldbm/attrcrypt.h
|
||||
index d653ba951..dcbea80fe 100644
|
||||
--- a/ldap/servers/slapd/back-ldbm/attrcrypt.h
|
||||
+++ b/ldap/servers/slapd/back-ldbm/attrcrypt.h
|
||||
@@ -10,7 +10,7 @@
|
||||
#include <config.h>
|
||||
#endif
|
||||
|
||||
-/* Private tructures and #defines used in the attribute encryption code. */
|
||||
+/* Private structures and #defines used in the attribute encryption code. */
|
||||
|
||||
#ifndef _ATTRCRYPT_H_
|
||||
#define _ATTRCRYPT_H_
|
||||
diff --git a/ldap/servers/slapd/back-ldbm/back-ldbm.h b/ldap/servers/slapd/back-ldbm/back-ldbm.h
|
||||
index e23e7ff43..92aa1ddbb 100644
|
||||
--- a/ldap/servers/slapd/back-ldbm/back-ldbm.h
|
||||
+++ b/ldap/servers/slapd/back-ldbm/back-ldbm.h
|
||||
@@ -104,6 +104,8 @@ typedef unsigned short u_int16_t;
|
||||
*/
|
||||
#define BE_CHANGELOG_FILE "replication_changelog"
|
||||
|
||||
+#define INDEX_KEY_LENGTH(lenval,lenprefix) (lenval+lenprefix+2)
|
||||
+
|
||||
#define BDB_IMPL "bdb"
|
||||
#define BDB_BACKEND "libback-ldbm" /* This backend plugin */
|
||||
#define BDB_NEWIDL "newidl" /* new idl format */
|
||||
diff --git a/ldap/servers/slapd/back-ldbm/db-bdb/bdb_import.c b/ldap/servers/slapd/back-ldbm/db-bdb/bdb_import.c
|
||||
index a6cb10aec..489433801 100644
|
||||
--- a/ldap/servers/slapd/back-ldbm/db-bdb/bdb_import.c
|
||||
+++ b/ldap/servers/slapd/back-ldbm/db-bdb/bdb_import.c
|
||||
@@ -75,9 +75,9 @@ static IDList *bdb_idl_union_allids(backend *be, struct attrinfo *ai, IDList *a,
|
||||
#define DEBUG_SUBCOUNT_MSG(msg, ...) { debug_subcount(__FUNCTION__, __LINE__, (msg), __VA_ARGS__); }
|
||||
#define DUMP_SUBCOUNT_KEY(msg, key, ret) { debug_subcount(__FUNCTION__, __LINE__, "ret=%d size=%u ulen=%u doff=%u dlen=%u", \
|
||||
ret, (key).size, (key).ulen, (key).doff, (key).dlen); \
|
||||
- if (ret == 0) hexadump(msg, (key).data, 0, (key).size); \
|
||||
+ if (ret == 0) slapi_log_hexadump(SLAPI_LOG_INFO, msg, (key).data, (key).size); \
|
||||
else if (ret == DB_BUFFER_SMALL) \
|
||||
- hexadump(msg, (key).data, 0, (key).ulen); }
|
||||
+ slapi_log_hexadump(SLAPI_LOG_INFO, msg, (key).data, (key).ulen); }
|
||||
|
||||
static void
|
||||
debug_subcount(const char *funcname, int line, char *msg, ...)
|
||||
@@ -90,41 +90,6 @@ debug_subcount(const char *funcname, int line, char *msg, ...)
|
||||
slapi_log_err(SLAPI_LOG_INFO, (char*)funcname, "DEBUG SUBCOUNT [%d] %s\n", line, buff);
|
||||
}
|
||||
|
||||
-/*
|
||||
- * Dump a memory buffer in hexa and ascii in error log
|
||||
- *
|
||||
- * addr - The memory buffer address.
|
||||
- * len - The memory buffer lenght.
|
||||
- */
|
||||
-static void
|
||||
-hexadump(char *msg, const void *addr, size_t offset, size_t len)
|
||||
-{
|
||||
-#define HEXADUMP_TAB 4
|
||||
-/* 4 characters per bytes: 2 hexa digits, 1 space and the ascii */
|
||||
-#define HEXADUMP_BUF_SIZE (4*16+HEXADUMP_TAB)
|
||||
- char hexdigit[] = "0123456789ABCDEF";
|
||||
-
|
||||
- const unsigned char *pt = addr;
|
||||
- char buff[HEXADUMP_BUF_SIZE+1];
|
||||
- memset (buff, ' ', HEXADUMP_BUF_SIZE);
|
||||
- buff[HEXADUMP_BUF_SIZE] = '\0';
|
||||
- while (len > 0) {
|
||||
- int dpl;
|
||||
- for (dpl = 0; dpl < 16 && len>0; dpl++, len--) {
|
||||
- buff[3*dpl] = hexdigit[((*pt) >> 4) & 0xf];
|
||||
- buff[3*dpl+1] = hexdigit[(*pt) & 0xf];
|
||||
- buff[3*16+HEXADUMP_TAB+dpl] = (*pt>=0x20 && *pt<0x7f) ? *pt : '.';
|
||||
- pt++;
|
||||
- }
|
||||
- for (;dpl < 16; dpl++) {
|
||||
- buff[3*dpl] = ' ';
|
||||
- buff[3*dpl+1] = ' ';
|
||||
- buff[3*16+HEXADUMP_TAB+dpl] = ' ';
|
||||
- }
|
||||
- slapi_log_err(SLAPI_LOG_INFO, msg, "[0x%08lx] %s\n", offset, buff);
|
||||
- offset += 16;
|
||||
- }
|
||||
-}
|
||||
#else
|
||||
#define DEBUG_SUBCOUNT_MSG(msg, ...)
|
||||
#define DUMP_SUBCOUNT_KEY(msg, key, ret)
|
||||
diff --git a/ldap/servers/slapd/back-ldbm/db-mdb/mdb_import_threads.c b/ldap/servers/slapd/back-ldbm/db-mdb/mdb_import_threads.c
|
||||
index 65b29343e..b969790da 100644
|
||||
--- a/ldap/servers/slapd/back-ldbm/db-mdb/mdb_import_threads.c
|
||||
+++ b/ldap/servers/slapd/back-ldbm/db-mdb/mdb_import_threads.c
|
||||
@@ -1123,6 +1123,21 @@ dbmdb_import_entry_info_by_backentry(mdb_privdb_t *db, BulkQueueData_t *bqdata,
|
||||
return dnrc;
|
||||
}
|
||||
|
||||
+/* Log wqelmt details */
|
||||
+void
|
||||
+log_wqelmt(int loglvl, char *fname, WorkerQueueData_t *wqelmt)
|
||||
+{
|
||||
+ if (wqelmt->dn) {
|
||||
+ slapi_log_err(loglvl, fname, "log_wqelmt: dn=%s\n", wqelmt->dn);
|
||||
+ }
|
||||
+ if (wqelmt->filename && wqelmt->lineno) {
|
||||
+ slapi_log_err(loglvl, fname, "log_wqelmt: ldif=%s[%d]\n", wqelmt->filename, wqelmt->lineno);
|
||||
+ }
|
||||
+ if (wqelmt->data) {
|
||||
+ size_t len = wqelmt->datalen ? wqelmt->datalen : strlen(wqelmt->data);
|
||||
+ slapi_log_hexadump(loglvl, "log_wqelmt:data", wqelmt->data, len);
|
||||
+ }
|
||||
+}
|
||||
|
||||
/* producer thread for ldif import case:
|
||||
* read through the given file list, parsing entries (str2entry), assigning
|
||||
@@ -1255,6 +1270,7 @@ dbmdb_import_producer(void *param)
|
||||
import_log_notice(job, SLAPI_LOG_ERR, "dbmdb_import_producer",
|
||||
"ns_slapd software error: unexpected dbmdb_import_entry_info return code: %d.",
|
||||
wqelmt.dnrc);
|
||||
+ log_wqelmt(SLAPI_LOG_ERR, "dbmdb_import_producer", &wqelmt);
|
||||
abort();
|
||||
case DNRC_OK:
|
||||
case DNRC_SUFFIX:
|
||||
@@ -1758,6 +1774,7 @@ dbmdb_index_producer(void *param)
|
||||
import_log_notice(job, SLAPI_LOG_ERR, "dbmdb_index_producer",
|
||||
"ns_slapd software error: unexpected dbmdb_import_entry_info return code: %d.",
|
||||
tmpslot.dnrc);
|
||||
+ log_wqelmt(SLAPI_LOG_ERR, "dbmdb_index_producer", &tmpslot);
|
||||
abort();
|
||||
case DNRC_OK:
|
||||
case DNRC_SUFFIX:
|
||||
@@ -3936,10 +3953,24 @@ dbmdb_import_writer(void*param)
|
||||
if (!txn) {
|
||||
MDB_STAT_STEP(stats, MDB_STAT_TXNSTART, stats_enabled);
|
||||
rc = TXN_BEGIN(ctx->ctx->env, NULL, 0, &txn);
|
||||
+ if (rc) {
|
||||
+ slapi_log_err(SLAPI_LOG_ERR, "dbmdb_import_writer",
|
||||
+ "Failed to begin a txn. Error is 0x%x: %s.\n",
|
||||
+ rc, mdb_strerror(rc));
|
||||
+ }
|
||||
}
|
||||
if (!rc) {
|
||||
MDB_STAT_STEP(stats, MDB_STAT_WRITE, stats_enabled);
|
||||
rc = MDB_PUT(txn, slot->dbi->dbi, &slot->key, &slot->data, 0);
|
||||
+ if (rc) {
|
||||
+ slapi_log_err(SLAPI_LOG_ERR, "dbmdb_import_writer",
|
||||
+ "Failed to write record in dbi %s. Error is 0x%x: %s.\n",
|
||||
+ slot->dbi->dbname, rc, mdb_strerror(rc));
|
||||
+ slapi_log_hexadump(SLAPI_LOG_ERR, "dbmdb_import_writer:key",
|
||||
+ slot->key.mv_data, slot->key.mv_size);
|
||||
+ slapi_log_hexadump(SLAPI_LOG_ERR, "dbmdb_import_writer:data",
|
||||
+ slot->data.mv_data, slot->data.mv_size);
|
||||
+ }
|
||||
}
|
||||
MDB_STAT_STEP(stats, MDB_STAT_RUN, stats_enabled);
|
||||
nextslot = slot->next;
|
||||
@@ -3953,6 +3984,9 @@ dbmdb_import_writer(void*param)
|
||||
rc = TXN_COMMIT(txn);
|
||||
MDB_STAT_STEP(stats, MDB_STAT_RUN, stats_enabled);
|
||||
if (rc) {
|
||||
+ slapi_log_err(SLAPI_LOG_ERR, "dbmdb_import_writer",
|
||||
+ "Failed to commit the txn. Error is 0x%x: %s.\n",
|
||||
+ rc, mdb_strerror(rc));
|
||||
break;
|
||||
}
|
||||
count = 0;
|
||||
@@ -3965,6 +3999,10 @@ dbmdb_import_writer(void*param)
|
||||
MDB_STAT_STEP(stats, MDB_STAT_RUN, stats_enabled);
|
||||
if (!rc) {
|
||||
txn = NULL;
|
||||
+ } else {
|
||||
+ slapi_log_err(SLAPI_LOG_ERR, "dbmdb_import_writer",
|
||||
+ "Failed to commit the txn. Error is 0x%x: %s.\n",
|
||||
+ rc, mdb_strerror(rc));
|
||||
}
|
||||
}
|
||||
if (txn) {
|
||||
@@ -3977,13 +4015,17 @@ dbmdb_import_writer(void*param)
|
||||
if (!rc) {
|
||||
/* Ensure that all data are written on disk */
|
||||
rc = mdb_env_sync(ctx->ctx->env, 1);
|
||||
+ if (rc) {
|
||||
+ slapi_log_err(SLAPI_LOG_ERR, "dbmdb_import_writer",
|
||||
+ "mdb_env_sync failed. Error is 0x%x: %s.\n",
|
||||
+ rc, mdb_strerror(rc));
|
||||
+ }
|
||||
}
|
||||
MDB_STAT_END(stats, stats_enabled);
|
||||
|
||||
if (rc) {
|
||||
slapi_log_err(SLAPI_LOG_ERR, "dbmdb_import_writer",
|
||||
- "Failed to write in the database. Error is 0x%x: %s.\n",
|
||||
- rc, mdb_strerror(rc));
|
||||
+ "Aborting import after failure.\n");
|
||||
thread_abort(info);
|
||||
} else if (stats_enabled) {
|
||||
char buf[200];
|
||||
diff --git a/ldap/servers/slapd/back-ldbm/index.c b/ldap/servers/slapd/back-ldbm/index.c
|
||||
index a5004be19..c108bce3c 100644
|
||||
--- a/ldap/servers/slapd/back-ldbm/index.c
|
||||
+++ b/ldap/servers/slapd/back-ldbm/index.c
|
||||
@@ -881,6 +881,67 @@ index_read(
|
||||
return index_read_ext(be, (char *)type, indextype, val, txn, err, NULL);
|
||||
}
|
||||
|
||||
+/* Prepare an index key (hashed if too long, encrypted if needed from attribute value */
|
||||
+int
|
||||
+prepare_key(backend *be, struct attrinfo *a, char **buf, size_t *buflen,
|
||||
+ int flags, const char *prefix, const struct berval *bvp, dbi_val_t *key)
|
||||
+{
|
||||
+ /* Key format is [Hash?] [prefix] [val] [\0] */
|
||||
+ struct ldbminfo *li = (struct ldbminfo *)be->be_database->plg_private;
|
||||
+ size_t plen = strlen(prefix);
|
||||
+ struct berval *hashed_bvp = NULL;
|
||||
+ struct berval *encrypted_bvp = NULL;
|
||||
+ int rc = 0;
|
||||
+
|
||||
+ /* Hash large index key if necessary */
|
||||
+ if (INDEX_KEY_LENGTH(bvp->bv_len,plen) >= li->li_max_key_len) {
|
||||
+ rc = attrcrypt_hash_large_index_key(be, prefix, a, bvp, &hashed_bvp);
|
||||
+ if (rc) {
|
||||
+ slapi_log_err(SLAPI_LOG_ERR, "index_read_ext_allids",
|
||||
+ "Failed to hash large index key for %s\n", a->ai_type);
|
||||
+ return rc;
|
||||
+ } else {
|
||||
+ bvp = hashed_bvp;
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ /* Encrypt the index key if necessary */
|
||||
+ if (rc == 0 && a->ai_attrcrypt && (0 == (flags & BE_INDEX_DONT_ENCRYPT))) {
|
||||
+ rc = attrcrypt_encrypt_index_key(be, a, bvp, &encrypted_bvp);
|
||||
+ if (rc) {
|
||||
+ slapi_log_err(SLAPI_LOG_ERR, "addordel_values_sv",
|
||||
+ "Failed to encrypt index key for %s\n", a->ai_type);
|
||||
+ } else {
|
||||
+ bvp = encrypted_bvp;
|
||||
+ }
|
||||
+ }
|
||||
+ if (hashed_bvp) {
|
||||
+ prefix = slapi_ch_smprintf("%c%s",HASH_PREFIX, prefix);
|
||||
+ plen++;
|
||||
+ }
|
||||
+ if (buf && buflen) {
|
||||
+ if (plen+bvp->bv_len+1 > *buflen) {
|
||||
+ *buflen = plen+bvp->bv_len+1;
|
||||
+ *buf = slapi_ch_realloc(*buf, *buflen);
|
||||
+ }
|
||||
+ dblayer_value_concat(be, key, *buf, *buflen, prefix, plen, bvp->bv_val, bvp->bv_len, "", 1);
|
||||
+ } else {
|
||||
+ dblayer_value_concat(be, key, NULL, 0, prefix, plen, bvp->bv_val, bvp->bv_len, "", 1);
|
||||
+ }
|
||||
+
|
||||
+ if (hashed_bvp) {
|
||||
+ ber_bvfree(hashed_bvp);
|
||||
+ hashed_bvp = NULL;
|
||||
+ slapi_ch_free_string((char**)&prefix);
|
||||
+ }
|
||||
+ if (encrypted_bvp) {
|
||||
+ ber_bvfree(encrypted_bvp);
|
||||
+ encrypted_bvp = NULL;
|
||||
+ }
|
||||
+ return rc;
|
||||
+}
|
||||
+
|
||||
+
|
||||
/*
|
||||
* Extended version of index_read.
|
||||
* The unindexed flag can be used to distinguish between a
|
||||
@@ -917,7 +978,6 @@ index_read_ext_allids(
|
||||
struct berval *hashed_val = NULL;
|
||||
int is_and = 0;
|
||||
unsigned int ai_flags = 0;
|
||||
- struct ldbminfo *li = (struct ldbminfo *)be->be_database->plg_private;
|
||||
|
||||
*err = 0;
|
||||
|
||||
@@ -1028,36 +1088,7 @@ index_read_ext_allids(
|
||||
}
|
||||
|
||||
if (val != NULL) {
|
||||
- size_t vlen;
|
||||
- int ret = 0;
|
||||
-
|
||||
- /* If necessary, hash this index key */
|
||||
- if (val->bv_len >= li->li_max_key_len) {
|
||||
- ret = attrcrypt_hash_large_index_key(be, &prefix, ai, val, &hashed_val);
|
||||
- if (ret) {
|
||||
- slapi_log_err(SLAPI_LOG_ERR, "index_read_ext_allids",
|
||||
- "Failed to hash large index key for %s\n", basetype);
|
||||
- *err = DBI_RC_OTHER;
|
||||
- index_free_prefix(prefix);
|
||||
- slapi_ch_free_string(&basetmp);
|
||||
- return (NULL);
|
||||
- }
|
||||
- if (hashed_val) {
|
||||
- val = hashed_val;
|
||||
- }
|
||||
- }
|
||||
- /* If necessary, encrypt this index key */
|
||||
- ret = attrcrypt_encrypt_index_key(be, ai, val, &encrypted_val);
|
||||
- if (ret) {
|
||||
- slapi_log_err(SLAPI_LOG_ERR, "index_read_ext_allids",
|
||||
- "Failed to encrypt index key for %s\n", basetype);
|
||||
- }
|
||||
- if (encrypted_val) {
|
||||
- val = encrypted_val;
|
||||
- }
|
||||
- vlen = val->bv_len;
|
||||
- dblayer_value_concat(be, &key, buf, sizeof(buf),
|
||||
- prefix, strlen(prefix), val->bv_val, vlen, "", 1);
|
||||
+ (void) prepare_key(be, ai, NULL, 0, 0, prefix, val, &key);
|
||||
} else {
|
||||
dblayer_value_concat(be, &key, buf, sizeof(buf), prefix, strlen(prefix),
|
||||
"", 1, NULL, 0);
|
||||
@@ -1824,6 +1855,7 @@ index_range_read(
|
||||
return index_range_read_ext(pb, be, type, indextype, operator, val, nextval, range, txn, err, 0);
|
||||
}
|
||||
|
||||
+
|
||||
static int
|
||||
addordel_values_sv(
|
||||
backend *be,
|
||||
@@ -1842,15 +1874,10 @@ addordel_values_sv(
|
||||
int i = 0;
|
||||
dbi_val_t key = {0};
|
||||
dbi_txn_t *db_txn = NULL;
|
||||
- size_t plen, vlen, len;
|
||||
char *tmpbuf = NULL;
|
||||
size_t tmpbuflen = 0;
|
||||
- char *realbuf;
|
||||
char *prefix = NULL;
|
||||
const struct berval *bvp;
|
||||
- struct berval *hashed_bvp = NULL;
|
||||
- struct berval *encrypted_bvp = NULL;
|
||||
- struct ldbminfo *li = (struct ldbminfo *)be->be_database->plg_private;
|
||||
char *index_id = get_index_name(be, db, a);
|
||||
|
||||
slapi_log_err(SLAPI_LOG_TRACE, "addordel_values_sv", "%s_values\n",
|
||||
@@ -1889,66 +1916,14 @@ addordel_values_sv(
|
||||
return (rc);
|
||||
}
|
||||
|
||||
- plen = strlen(prefix);
|
||||
for (i = 0; vals[i] != NULL; i++) {
|
||||
bvp = slapi_value_get_berval(vals[i]);
|
||||
|
||||
- /* Hash large index key if necessary */
|
||||
- if (bvp->bv_len >= li->li_max_key_len) {
|
||||
- rc = attrcrypt_hash_large_index_key(be, &prefix, a, bvp, &hashed_bvp);
|
||||
- if (rc) {
|
||||
- slapi_log_err(SLAPI_LOG_ERR, "index_read_ext_allids",
|
||||
- "Failed to hash large index key for %s\n", a->ai_type);
|
||||
- break;
|
||||
- } else {
|
||||
- bvp = hashed_bvp;
|
||||
- plen = strlen(prefix);
|
||||
- }
|
||||
- }
|
||||
- /* Encrypt the index key if necessary */
|
||||
- {
|
||||
- if (a->ai_attrcrypt && (0 == (flags & BE_INDEX_DONT_ENCRYPT))) {
|
||||
- rc = attrcrypt_encrypt_index_key(be, a, bvp, &encrypted_bvp);
|
||||
- if (rc) {
|
||||
- slapi_log_err(SLAPI_LOG_ERR, "addordel_values_sv",
|
||||
- "Failed to encrypt index key for %s\n", a->ai_type);
|
||||
- } else {
|
||||
- bvp = encrypted_bvp;
|
||||
- }
|
||||
- }
|
||||
+ rc = prepare_key(be, a, &tmpbuf, &tmpbuflen, flags, prefix, bvp, &key);
|
||||
+ if (rc) {
|
||||
+ break;
|
||||
}
|
||||
|
||||
- vlen = bvp->bv_len;
|
||||
- len = plen + vlen;
|
||||
-
|
||||
- if (len < tmpbuflen) {
|
||||
- realbuf = tmpbuf;
|
||||
- } else {
|
||||
- tmpbuf = slapi_ch_realloc(tmpbuf, len + 1);
|
||||
- tmpbuflen = len + 1;
|
||||
- realbuf = tmpbuf;
|
||||
- }
|
||||
-
|
||||
- assert(realbuf); /* For coverity */
|
||||
- memcpy(realbuf, prefix, plen);
|
||||
- memcpy(realbuf + plen, bvp->bv_val, vlen);
|
||||
- realbuf[len] = '\0';
|
||||
- /* Free the encrypted berval if necessary */
|
||||
- if (hashed_bvp) {
|
||||
- ber_bvfree(hashed_bvp);
|
||||
- hashed_bvp = NULL;
|
||||
- }
|
||||
- if (encrypted_bvp) {
|
||||
- ber_bvfree(encrypted_bvp);
|
||||
- encrypted_bvp = NULL;
|
||||
- }
|
||||
- /* should be okay to use USERMEM here because we know what
|
||||
- * the key is and it should never return a different value
|
||||
- * than the one we pass in.
|
||||
- */
|
||||
- dblayer_value_set_buffer(be, &key, realbuf, plen + vlen + 1);
|
||||
- key.ulen = tmpbuflen;
|
||||
-
|
||||
if (slapi_is_loglevel_set(LDAP_DEBUG_TRACE)) {
|
||||
char encbuf[BUFSIZ];
|
||||
|
||||
@@ -1981,10 +1956,6 @@ addordel_values_sv(
|
||||
ldbm_nasty(NASTY_MSG("addordel_values_sv"), index_id, 1130, rc);
|
||||
break;
|
||||
}
|
||||
- if (NULL != key.dptr && realbuf != key.dptr) { /* realloc'ed */
|
||||
- tmpbuf = key.dptr;
|
||||
- tmpbuflen = key.size;
|
||||
- }
|
||||
}
|
||||
index_free_prefix(prefix);
|
||||
if (tmpbuf != NULL) {
|
||||
diff --git a/ldap/servers/slapd/back-ldbm/ldbm_attrcrypt.c b/ldap/servers/slapd/back-ldbm/ldbm_attrcrypt.c
|
||||
index 124810426..6655633fc 100644
|
||||
--- a/ldap/servers/slapd/back-ldbm/ldbm_attrcrypt.c
|
||||
+++ b/ldap/servers/slapd/back-ldbm/ldbm_attrcrypt.c
|
||||
@@ -1079,15 +1079,15 @@ attrcrypt_decrypt_index_key(backend *be,
|
||||
* : NULL - no hash or failure
|
||||
*/
|
||||
int
|
||||
-attrcrypt_hash_large_index_key(backend *be, char **prefix, struct attrinfo *ai, const struct berval *in, struct berval **out)
|
||||
+attrcrypt_hash_large_index_key(backend *be, const char *prefix, struct attrinfo *ai, const struct berval *in, struct berval **out)
|
||||
{
|
||||
int ret = 0;
|
||||
struct berval *out_berval = NULL;
|
||||
struct ldbminfo *li = (struct ldbminfo *)be->be_database->plg_private;
|
||||
- char *new_prefix;
|
||||
+ size_t final_key_len = INDEX_KEY_LENGTH(in->bv_len, strlen(prefix));
|
||||
|
||||
/* If the index key is too long (i.e mdb case) we must hash it */
|
||||
- if (in->bv_len >= li->li_max_key_len) {
|
||||
+ if (final_key_len >= li->li_max_key_len) {
|
||||
PK11Context *c = PK11_CreateDigestContext(SEC_OID_MD5);
|
||||
if (c != NULL) {
|
||||
unsigned char hash[32];
|
||||
@@ -1101,16 +1101,13 @@ attrcrypt_hash_large_index_key(backend *be, char **prefix, struct attrinfo *ai,
|
||||
return ENOMEM;
|
||||
}
|
||||
slapi_log_err(SLAPI_LOG_TRACE, "attrcrypt_hash_large_index_key",
|
||||
- "Key lenght (%lu) >= max key lenght (%lu) so key must be hashed\n", in->bv_len, li->li_max_key_len);
|
||||
+ "Key lenght (%lu) >= max key lenght (%lu) so key must be hashed\n", final_key_len, li->li_max_key_len);
|
||||
slapi_be_set_flag(be, SLAPI_BE_FLAG_DONT_BYPASS_FILTERTEST);
|
||||
PK11_DigestBegin(c);
|
||||
/* Compute hash for the key without the prefix */
|
||||
PK11_DigestOp(c, (unsigned char *)in->bv_val, in->bv_len);
|
||||
PK11_DigestFinal(c, hash, &hashLen, sizeof hash);
|
||||
- /* Add HASH_PREFIX before the prefix */
|
||||
- new_prefix = slapi_ch_smprintf("%c%s", HASH_PREFIX, *prefix);
|
||||
- index_free_prefix(*prefix);
|
||||
- *prefix = new_prefix;
|
||||
+
|
||||
/* Build the key: hash value in hexa */
|
||||
hkey = slapi_ch_malloc(1+2*sizeof hash);
|
||||
out_berval->bv_val = hkey;
|
||||
diff --git a/ldap/servers/slapd/back-ldbm/proto-back-ldbm.h b/ldap/servers/slapd/back-ldbm/proto-back-ldbm.h
|
||||
index 30a7aa11f..c882dac7b 100644
|
||||
--- a/ldap/servers/slapd/back-ldbm/proto-back-ldbm.h
|
||||
+++ b/ldap/servers/slapd/back-ldbm/proto-back-ldbm.h
|
||||
@@ -622,7 +622,7 @@ int attrcrypt_encrypt_entry_inplace(backend *be, const struct backentry *inout);
|
||||
int attrcrypt_encrypt_entry(backend *be, const struct backentry *in, struct backentry **out);
|
||||
int attrcrypt_encrypt_index_key(backend *be, struct attrinfo *ai, const struct berval *in, struct berval **out);
|
||||
int attrcrypt_decrypt_index_key(backend *be, struct attrinfo *ai, const struct berval *in, struct berval **out);
|
||||
-int attrcrypt_hash_large_index_key(backend *be, char **prefix, struct attrinfo *ai, const struct berval *in, struct berval **out);
|
||||
+int attrcrypt_hash_large_index_key(backend *be, const char *prefix, struct attrinfo *ai, const struct berval *in, struct berval **out);
|
||||
int attrcrypt_init(ldbm_instance *li);
|
||||
int attrcrypt_cleanup_private(ldbm_instance *li);
|
||||
|
||||
diff --git a/ldap/servers/slapd/log.c b/ldap/servers/slapd/log.c
|
||||
index 80c07382a..93101494b 100644
|
||||
--- a/ldap/servers/slapd/log.c
|
||||
+++ b/ldap/servers/slapd/log.c
|
||||
@@ -93,6 +93,10 @@ static int slapi_log_map[] = {
|
||||
#define FLUSH PR_TRUE
|
||||
#define NO_FLUSH PR_FALSE
|
||||
|
||||
+#define HEXADUMP_TAB 4
|
||||
+/* 4 characters per bytes: 2 hexa digits, 1 space and the ascii */
|
||||
+#define HEXADUMP_BUF_SIZE (4*16+HEXADUMP_TAB)
|
||||
+
|
||||
/**************************************************************************
|
||||
* PROTOTYPES
|
||||
*************************************************************************/
|
||||
@@ -3133,6 +3137,45 @@ slapi_log_backtrace(int loglevel)
|
||||
}
|
||||
}
|
||||
|
||||
+/*
|
||||
+ * Dump a memory buffer in hexa and ascii in error log
|
||||
+ *
|
||||
+ * addr - The memory buffer address.
|
||||
+ * len - The memory buffer lenght.
|
||||
+ */
|
||||
+void
|
||||
+slapi_log_hexadump(int loglevel, char *fname, const void *addr, size_t len)
|
||||
+{
|
||||
+ char hexdigit[] = "0123456789ABCDEF";
|
||||
+ const unsigned char *pt = addr;
|
||||
+ char buff[HEXADUMP_BUF_SIZE+1];
|
||||
+ size_t offset = 0;
|
||||
+
|
||||
+ if (!slapi_is_loglevel_set(loglevel)) {
|
||||
+ return;
|
||||
+ }
|
||||
+ memset (buff, ' ', HEXADUMP_BUF_SIZE);
|
||||
+ buff[HEXADUMP_BUF_SIZE] = '\0';
|
||||
+ while (len > 0) {
|
||||
+ int dpl;
|
||||
+ for (dpl = 0; dpl < 16 && len>0; dpl++, len--) {
|
||||
+ buff[3*dpl] = hexdigit[((*pt) >> 4) & 0xf];
|
||||
+ buff[3*dpl+1] = hexdigit[(*pt) & 0xf];
|
||||
+ buff[3*16+HEXADUMP_TAB+dpl] = (*pt>=0x20 && *pt<0x7f) ? *pt : '.';
|
||||
+ pt++;
|
||||
+ }
|
||||
+ for (;dpl < 16; dpl++) {
|
||||
+ buff[3*dpl] = ' ';
|
||||
+ buff[3*dpl+1] = ' ';
|
||||
+ buff[3*16+HEXADUMP_TAB+dpl] = ' ';
|
||||
+ }
|
||||
+ slapi_log_err(loglevel, fname, "[0x%08lx] %s\n", offset, buff);
|
||||
+ offset += 16;
|
||||
+ }
|
||||
+}
|
||||
+
|
||||
+
|
||||
+
|
||||
/******************************************************************************
|
||||
* write in the access log
|
||||
******************************************************************************/
|
||||
diff --git a/ldap/servers/slapd/slapi-private.h b/ldap/servers/slapd/slapi-private.h
|
||||
index 72f4cd6f0..2da37ff6e 100644
|
||||
--- a/ldap/servers/slapd/slapi-private.h
|
||||
+++ b/ldap/servers/slapd/slapi-private.h
|
||||
@@ -1527,6 +1527,8 @@ void slapi_pblock_set_task_warning(Slapi_PBlock *pb, task_warning warning);
|
||||
int slapi_exists_or_add_internal(Slapi_DN *dn, const char *filter, const char *entry, const char *modifier_name);
|
||||
|
||||
void slapi_log_backtrace(int loglevel);
|
||||
+void slapi_log_hexadump(int loglevel, char *fname, const void *addr, size_t len);
|
||||
+
|
||||
|
||||
/*
|
||||
* accesslog.c
|
||||
--
|
||||
2.53.0
|
||||
|
||||
291
0065-Issue-7271-implement-a-pre-close-plugin-function.patch
Normal file
291
0065-Issue-7271-implement-a-pre-close-plugin-function.patch
Normal file
@ -0,0 +1,291 @@
|
||||
From 6fc10eae5b44989c703b67bdca157202d35d8a92 Mon Sep 17 00:00:00 2001
|
||||
From: Mark Reynolds <mreynolds@redhat.com>
|
||||
Date: Mon, 2 Mar 2026 14:31:29 -0500
|
||||
Subject: [PATCH] Issue 7271 - implement a pre-close plugin function
|
||||
|
||||
Description:
|
||||
|
||||
replication protocol could benefit from being notifioed the shutdown process
|
||||
has started before calling the "close" plugin function. This would allow the
|
||||
replication protocol to wake up and adjust its active thread count to prevent
|
||||
a hang during shutdown.
|
||||
|
||||
relates: https://github.com/389ds/389-ds-base/issues/7271
|
||||
|
||||
Reviewed by: progier, spichugi, and vashirov(Thanks!!!)
|
||||
---
|
||||
ldap/servers/plugins/replication/repl5_init.c | 19 +++++++---
|
||||
ldap/servers/slapd/daemon.c | 8 +++--
|
||||
ldap/servers/slapd/pblock.c | 36 +++++++++++++------
|
||||
ldap/servers/slapd/plugin.c | 24 ++++++++++++-
|
||||
ldap/servers/slapd/proto-slap.h | 3 +-
|
||||
ldap/servers/slapd/slap.h | 3 +-
|
||||
ldap/servers/slapd/slapi-plugin.h | 3 +-
|
||||
7 files changed, 74 insertions(+), 22 deletions(-)
|
||||
|
||||
diff --git a/ldap/servers/plugins/replication/repl5_init.c b/ldap/servers/plugins/replication/repl5_init.c
|
||||
index 5047fb8dc..395744db8 100644
|
||||
--- a/ldap/servers/plugins/replication/repl5_init.c
|
||||
+++ b/ldap/servers/plugins/replication/repl5_init.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) 2026 Red Hat, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* License: GPL (version 3 or any later version).
|
||||
@@ -122,6 +122,8 @@ static PRUintn thread_private_agmtname; /* thread private index for logging*/
|
||||
static PRUintn thread_private_cache;
|
||||
static PRUintn thread_primary_csn;
|
||||
|
||||
+static int multisupplier_pre_stop(Slapi_PBlock *pb __attribute__((unused)));
|
||||
+
|
||||
char *
|
||||
get_thread_private_agmtname()
|
||||
{
|
||||
@@ -836,10 +838,6 @@ multisupplier_stop(Slapi_PBlock *pb __attribute__((unused)))
|
||||
int rc = 0; /* OK */
|
||||
|
||||
if (!multisupplier_stopped_flag) {
|
||||
- if (!is_ldif_dump) {
|
||||
- /* Shut down replication agreements */
|
||||
- agmtlist_shutdown();
|
||||
- }
|
||||
/* if we are cleaning a ruv, stop */
|
||||
stop_ruv_cleaning();
|
||||
/* unregister backend state change notification */
|
||||
@@ -853,6 +851,16 @@ multisupplier_stop(Slapi_PBlock *pb __attribute__((unused)))
|
||||
return rc;
|
||||
}
|
||||
|
||||
+static int
|
||||
+multisupplier_pre_stop(Slapi_PBlock *pb __attribute__((unused)))
|
||||
+{
|
||||
+ if (!multisupplier_stopped_flag) {
|
||||
+ /* Shut down replication agreements which will stop all the
|
||||
+ * replication protocol threads */
|
||||
+ agmtlist_shutdown();
|
||||
+ }
|
||||
+ return 0;
|
||||
+}
|
||||
|
||||
PRBool
|
||||
multisupplier_started()
|
||||
@@ -900,6 +908,7 @@ replication_multisupplier_plugin_init(Slapi_PBlock *pb)
|
||||
rc = slapi_pblock_set(pb, SLAPI_PLUGIN_DESCRIPTION, (void *)&multisupplierdesc);
|
||||
rc = slapi_pblock_set(pb, SLAPI_PLUGIN_START_FN, (void *)multisupplier_start);
|
||||
rc = slapi_pblock_set(pb, SLAPI_PLUGIN_CLOSE_FN, (void *)multisupplier_stop);
|
||||
+ rc = slapi_pblock_set(pb, SLAPI_PLUGIN_PRE_CLOSE_FN, (void *)multisupplier_pre_stop);
|
||||
|
||||
/* Register the plugin interfaces we implement */
|
||||
/* preop acquires csn generator handle */
|
||||
diff --git a/ldap/servers/slapd/daemon.c b/ldap/servers/slapd/daemon.c
|
||||
index 19c6d7e48..7a2ca2ae2 100644
|
||||
--- a/ldap/servers/slapd/daemon.c
|
||||
+++ b/ldap/servers/slapd/daemon.c
|
||||
@@ -1,6 +1,6 @@
|
||||
/** BEGIN COPYRIGHT BLOCK
|
||||
* Copyright (C) 2001 Sun Microsystems, Inc. Used by permission.
|
||||
- * Copyright (C) 2021 Red Hat, Inc.
|
||||
+ * Copyright (C) 2026 Red Hat, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* License: GPL (version 3 or any later version).
|
||||
@@ -1416,6 +1416,10 @@ slapd_daemon(daemon_ports_t *ports)
|
||||
task_cancel_all();
|
||||
}
|
||||
|
||||
+ /* Call plugin pre close functions */
|
||||
+ plugin_pre_closeall();
|
||||
+
|
||||
+ /* Now wait for active threads to terminate */
|
||||
threads = g_get_active_threadcnt();
|
||||
if (threads > 0) {
|
||||
slapi_log_err(SLAPI_LOG_INFO, "slapd_daemon",
|
||||
@@ -3178,7 +3182,7 @@ void
|
||||
wait4certs_refresh(daemon_ports_t *ports)
|
||||
{
|
||||
/*
|
||||
- * Block listening and accept threads until
|
||||
+ * Block listening and accept threads until
|
||||
* certificates refresh is complete
|
||||
* Note:
|
||||
* Listening threads have a NULL ports
|
||||
diff --git a/ldap/servers/slapd/pblock.c b/ldap/servers/slapd/pblock.c
|
||||
index 24cb9b15e..3405e6da6 100644
|
||||
--- a/ldap/servers/slapd/pblock.c
|
||||
+++ b/ldap/servers/slapd/pblock.c
|
||||
@@ -1,12 +1,12 @@
|
||||
-/** BEGIN COPYRIGHT BLOCK
|
||||
- * Copyright (C) 2001 Sun Microsystems, Inc. Used by permission.
|
||||
- * Copyright (C) 2005-2025 Red Hat, Inc.
|
||||
- * Copyright (C) 2009 Hewlett-Packard Development Company, L.P.
|
||||
- * All rights reserved.
|
||||
- *
|
||||
- * License: GPL (version 3 or any later version).
|
||||
- * See LICENSE for details.
|
||||
- * END COPYRIGHT BLOCK **/
|
||||
+ /** BEGIN COPYRIGHT BLOCK
|
||||
+ * Copyright (C) 2001 Sun Microsystems, Inc. Used by permission.
|
||||
+ * Copyright (C) 2026 Red Hat, Inc.
|
||||
+ * Copyright (C) 2009 Hewlett-Packard Development Company, L.P.
|
||||
+ * All rights reserved.
|
||||
+ *
|
||||
+ * License: GPL (version 3 or any later version).
|
||||
+ * See LICENSE for details.
|
||||
+ * END COPYRIGHT BLOCK **/
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
@@ -1008,6 +1008,13 @@ slapi_pblock_get_plugin_close_fn(Slapi_PBlock *pblock, void *value)
|
||||
return 0;
|
||||
}
|
||||
|
||||
+static int32_t
|
||||
+slapi_pblock_get_plugin_pre_close_fn(Slapi_PBlock *pblock, void *value)
|
||||
+{
|
||||
+ (*(IFP *)value) = pblock->pb_plugin->plg_pre_close;
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
static int32_t
|
||||
slapi_pblock_get_plugin_cleanup_fn(Slapi_PBlock *pblock, void *value)
|
||||
{
|
||||
@@ -4100,6 +4107,13 @@ slapi_pblock_set_plugin_close_fn(Slapi_PBlock *pblock, void *value)
|
||||
return 0;
|
||||
}
|
||||
|
||||
+static int32_t
|
||||
+slapi_pblock_set_plugin_pre_close_fn(Slapi_PBlock *pblock, void *value)
|
||||
+{
|
||||
+ pblock->pb_plugin->plg_pre_close = (IFP)value;
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
static int32_t
|
||||
slapi_pblock_set_plugin_cleanup_fn(Slapi_PBlock *pblock, void *value)
|
||||
{
|
||||
@@ -6948,7 +6962,7 @@ static int32_t (*get_cbtable[])(Slapi_PBlock *, void *) = {
|
||||
slapi_pblock_get_plugin_db_abandon_fn,
|
||||
slapi_pblock_get_plugin_db_config_fn,
|
||||
slapi_pblock_get_plugin_close_fn,
|
||||
- NULL, /* slot 211 available */
|
||||
+ slapi_pblock_get_plugin_pre_close_fn,
|
||||
slapi_pblock_get_plugin_start_fn,
|
||||
slapi_pblock_get_plugin_db_seq_fn,
|
||||
slapi_pblock_get_plugin_db_entry_fn,
|
||||
@@ -8923,7 +8937,7 @@ static int32_t (*set_cbtable[])(Slapi_PBlock *, void *) = {
|
||||
slapi_pblock_set_plugin_db_abandon_fn,
|
||||
slapi_pblock_set_plugin_db_config_fn,
|
||||
slapi_pblock_set_plugin_close_fn,
|
||||
- NULL, /* slot 211 available */
|
||||
+ slapi_pblock_set_plugin_pre_close_fn,
|
||||
slapi_pblock_set_plugin_start_fn,
|
||||
slapi_pblock_set_plugin_db_seq_fn,
|
||||
slapi_pblock_set_plugin_db_entry_fn,
|
||||
diff --git a/ldap/servers/slapd/plugin.c b/ldap/servers/slapd/plugin.c
|
||||
index 52d25e19e..95b8de894 100644
|
||||
--- a/ldap/servers/slapd/plugin.c
|
||||
+++ b/ldap/servers/slapd/plugin.c
|
||||
@@ -1,6 +1,6 @@
|
||||
/** BEGIN COPYRIGHT BLOCK
|
||||
* Copyright (C) 2001 Sun Microsystems, Inc. Used by permission.
|
||||
- * Copyright (C) 2021 Red Hat, Inc.
|
||||
+ * Copyright (C) 2026 Red Hat, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* License: GPL (version 3 or any later version).
|
||||
@@ -1847,6 +1847,28 @@ plugin_dependency_closeall(void)
|
||||
}
|
||||
}
|
||||
|
||||
+/* Call the pre close functions of all the plugins */
|
||||
+void
|
||||
+plugin_pre_closeall(void)
|
||||
+{
|
||||
+ Slapi_PBlock *pb = NULL;
|
||||
+ int plugins_pre_closed = 0;
|
||||
+ int index = 0;
|
||||
+
|
||||
+ while (plugins_pre_closed < global_plugins_started) {
|
||||
+ if (global_plugin_shutdown_order[index].name) {
|
||||
+ if (!global_plugin_shutdown_order[index].removed) {
|
||||
+ pb = slapi_pblock_new();
|
||||
+ plugin_call_one(global_plugin_shutdown_order[index].plugin,
|
||||
+ SLAPI_PLUGIN_PRE_CLOSE_FN, pb);
|
||||
+ slapi_pblock_destroy(pb);
|
||||
+ }
|
||||
+ plugins_pre_closed++;
|
||||
+ }
|
||||
+ index++;
|
||||
+ }
|
||||
+}
|
||||
+
|
||||
void
|
||||
plugin_freeall(void)
|
||||
{
|
||||
diff --git a/ldap/servers/slapd/proto-slap.h b/ldap/servers/slapd/proto-slap.h
|
||||
index 455d6d718..ce7e4f047 100644
|
||||
--- a/ldap/servers/slapd/proto-slap.h
|
||||
+++ b/ldap/servers/slapd/proto-slap.h
|
||||
@@ -1,6 +1,6 @@
|
||||
/** BEGIN COPYRIGHT BLOCK
|
||||
* Copyright (C) 2001 Sun Microsystems, Inc. Used by permission.
|
||||
- * Copyright (C) 2021-2025 Red Hat, Inc.
|
||||
+ * Copyright (C) 2026 Red Hat, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* License: GPL (version 3 or any later version).
|
||||
@@ -997,6 +997,7 @@ int plugin_call_exop_plugins(Slapi_PBlock *pb, struct slapdplugin *p);
|
||||
Slapi_Backend *plugin_extended_op_getbackend(Slapi_PBlock *pb, struct slapdplugin *p);
|
||||
const char *plugin_extended_op_oid2string(const char *oid);
|
||||
void plugin_closeall(int close_backends, int close_globals);
|
||||
+void plugin_pre_closeall(void);
|
||||
void plugin_dependency_freeall(void);
|
||||
void plugin_startall(int argc, char **argv, char **plugin_list);
|
||||
void plugin_get_plugin_dependencies(char *plugin_name, char ***names);
|
||||
diff --git a/ldap/servers/slapd/slap.h b/ldap/servers/slapd/slap.h
|
||||
index d494931c2..3e50f77b2 100644
|
||||
--- a/ldap/servers/slapd/slap.h
|
||||
+++ b/ldap/servers/slapd/slap.h
|
||||
@@ -1,7 +1,7 @@
|
||||
/** BEGIN COPYRIGHT BLOCK
|
||||
* Copyright (C) 2001 Sun Microsystems, Inc. Used by permission.
|
||||
* Copyright (C) 2009 Hewlett-Packard Development Company, L.P.
|
||||
- * Copyright (C) 2009-2025 Red Hat, Inc.
|
||||
+ * Copyright (C) 2026 Red Hat, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Contributors:
|
||||
@@ -1046,6 +1046,7 @@ struct slapdplugin
|
||||
char *plg_libpath; /* library path for dll/so */
|
||||
char *plg_initfunc; /* init symbol */
|
||||
IFP plg_close; /* close function */
|
||||
+ IFP plg_pre_close; /* pre close function */
|
||||
Slapi_PluginDesc plg_desc; /* vendor's info */
|
||||
char *plg_name; /* used for plugin rdn in cn=config */
|
||||
struct slapdplugin *plg_next; /* for plugin lists */
|
||||
diff --git a/ldap/servers/slapd/slapi-plugin.h b/ldap/servers/slapd/slapi-plugin.h
|
||||
index 85bbf8fa1..70677123d 100644
|
||||
--- a/ldap/servers/slapd/slapi-plugin.h
|
||||
+++ b/ldap/servers/slapd/slapi-plugin.h
|
||||
@@ -1,6 +1,6 @@
|
||||
/* BEGIN COPYRIGHT BLOCK
|
||||
* Copyright (C) 2001 Sun Microsystems, Inc. Used by permission.
|
||||
- * Copyright (C) 2021 Red Hat, Inc.
|
||||
+ * Copyright (C) 2026 Red Hat, Inc.
|
||||
* Copyright (C) 2009 Hewlett-Packard Development Company, L.P.
|
||||
* All rights reserved.
|
||||
*
|
||||
@@ -7085,6 +7085,7 @@ typedef struct slapi_plugindesc
|
||||
|
||||
/* miscellaneous plugin functions */
|
||||
#define SLAPI_PLUGIN_CLOSE_FN 210
|
||||
+#define SLAPI_PLUGIN_PRE_CLOSE_FN 211
|
||||
#define SLAPI_PLUGIN_START_FN 212
|
||||
#define SLAPI_PLUGIN_CLEANUP_FN 232
|
||||
#define SLAPI_PLUGIN_POSTSTART_FN 233
|
||||
--
|
||||
2.53.0
|
||||
|
||||
@ -0,0 +1,34 @@
|
||||
From 5cc3edb9732784e8a3edb033962b6312f2bb7b55 Mon Sep 17 00:00:00 2001
|
||||
From: Mark Reynolds <mreynolds@redhat.com>
|
||||
Date: Wed, 4 Mar 2026 14:47:01 -0500
|
||||
Subject: [PATCH] Issue 7271 - Add new plugin pre-close function check to
|
||||
plugin_invoke_plugin_pb
|
||||
|
||||
Description:
|
||||
|
||||
In plugin_invoke_plugin_pb we were not checking for the new pre-close function
|
||||
which led to an error in the logs: pb_op is NULL. In a debug build this leads
|
||||
to an assertion error at shutdown.
|
||||
|
||||
relates: https://github.com/389ds/389-ds-base/issues/7271
|
||||
|
||||
Reviewed by: vashirov(Thanks!)
|
||||
---
|
||||
ldap/servers/slapd/plugin.c | 1 +
|
||||
1 file changed, 1 insertion(+)
|
||||
|
||||
diff --git a/ldap/servers/slapd/plugin.c b/ldap/servers/slapd/plugin.c
|
||||
index 95b8de894..c1b0571eb 100644
|
||||
--- a/ldap/servers/slapd/plugin.c
|
||||
+++ b/ldap/servers/slapd/plugin.c
|
||||
@@ -3647,6 +3647,7 @@ plugin_invoke_plugin_pb(struct slapdplugin *plugin, int operation, Slapi_PBlock
|
||||
if (operation == SLAPI_PLUGIN_START_FN ||
|
||||
operation == SLAPI_PLUGIN_POSTSTART_FN ||
|
||||
operation == SLAPI_PLUGIN_CLOSE_FN ||
|
||||
+ operation == SLAPI_PLUGIN_PRE_CLOSE_FN ||
|
||||
operation == SLAPI_PLUGIN_CLEANUP_FN ||
|
||||
operation == SLAPI_PLUGIN_BE_PRE_CLOSE_FN ||
|
||||
operation == SLAPI_PLUGIN_BE_POST_OPEN_FN ||
|
||||
--
|
||||
2.53.0
|
||||
|
||||
4
389-ds-base-devel.README
Normal file
4
389-ds-base-devel.README
Normal file
@ -0,0 +1,4 @@
|
||||
For detailed information on developing plugins for 389 Directory Server visit
|
||||
|
||||
https://www.port389.org/docs/389ds/design/plugins.html
|
||||
https://github.com/389ds/389-ds-base/blob/main/src/slapi_r_plugin/README.md
|
||||
1233
389-ds-base.spec
Normal file
1233
389-ds-base.spec
Normal file
File diff suppressed because it is too large
Load Diff
3
389-ds-base.sysusers
Normal file
3
389-ds-base.sysusers
Normal file
@ -0,0 +1,3 @@
|
||||
#Type Name ID GECOS Home directory Shell
|
||||
g dirsrv 389
|
||||
u dirsrv 389:389 "user for 389-ds-base" /usr/share/dirsrv/ /sbin/nologin
|
||||
File diff suppressed because it is too large
Load Diff
@ -1,119 +0,0 @@
|
||||
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
|
||||
|
||||
@ -1,27 +0,0 @@
|
||||
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,147 +0,0 @@
|
||||
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
|
||||
|
||||
@ -1,840 +0,0 @@
|
||||
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
|
||||
|
||||
@ -1,83 +0,0 @@
|
||||
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
|
||||
|
||||
@ -1,108 +0,0 @@
|
||||
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
|
||||
|
||||
@ -1,213 +0,0 @@
|
||||
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
|
||||
|
||||
@ -1,143 +0,0 @@
|
||||
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
|
||||
|
||||
@ -1,44 +0,0 @@
|
||||
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
|
||||
|
||||
@ -1,30 +0,0 @@
|
||||
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
|
||||
|
||||
@ -1,220 +0,0 @@
|
||||
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
|
||||
|
||||
@ -1,163 +0,0 @@
|
||||
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,4 +0,0 @@
|
||||
For detailed information on developing plugins for
|
||||
389 Directory Server visit.
|
||||
|
||||
http://port389/wiki/Plugins
|
||||
@ -1,16 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
DATE=`date +%Y%m%d`
|
||||
# use a real tag name here
|
||||
VERSION=1.3.5.14
|
||||
PKGNAME=389-ds-base
|
||||
TAG=${TAG:-$PKGNAME-$VERSION}
|
||||
URL="https://git.fedorahosted.org/git/?p=389/ds.git;a=snapshot;h=$TAG;sf=tgz"
|
||||
SRCNAME=$PKGNAME-$VERSION
|
||||
|
||||
wget -O $SRCNAME.tar.gz "$URL"
|
||||
|
||||
echo convert tgz format to tar.bz2 format
|
||||
|
||||
gunzip $PKGNAME-$VERSION.tar.gz
|
||||
bzip2 $PKGNAME-$VERSION.tar
|
||||
File diff suppressed because it is too large
Load Diff
4
sources
Normal file
4
sources
Normal file
@ -0,0 +1,4 @@
|
||||
SHA512 (389-ds-base-3.2.0.tar.bz2) = 9ff6aa56b30863c619f4f324344dca72cc883236bfe8d94520e8469d9e306f54b373ee2504eda18dcb0ecda33f915a3e64a6f3cdaa93a69b74d901caa48545e1
|
||||
SHA512 (jemalloc-5.3.0.tar.bz2) = 22907bb052096e2caffb6e4e23548aecc5cc9283dce476896a2b1127eee64170e3562fa2e7db9571298814a7a2c7df6e8d1fbe152bd3f3b0c1abec22a2de34b1
|
||||
SHA512 (libdb-5.3.28-59.tar.bz2) = 731a434fa2e6487ebb05c458b0437456eb9f7991284beb08cb3e21931e23bdeddddbc95bfabe3a2f9f029fe69cd33a2d4f0f5ce6a9811e9c3b940cb6fde4bf79
|
||||
SHA512 (vendor-3.2.0-4.tar.gz) = b7daee9351fd007ef54a51a3c41f15fa5538b04341a85df2e889728569139ec4b94fcd7f167a2886bc29b40a01866774f6e6f78fbd80021e3fcfe393a95d7efb
|
||||
Loading…
Reference in New Issue
Block a user