From b8da0bd964b73bb42d2c369d82535d50a38070ae Mon Sep 17 00:00:00 2001 From: CentOS Sources Date: Aug 16 2021 19:11:09 +0000 Subject: import 389-ds-base-1.4.3.23-7.module+el8.5.0+11979+655d714b --- diff --git a/.389-ds-base.metadata b/.389-ds-base.metadata index 46fc901..9ce4a90 100644 --- a/.389-ds-base.metadata +++ b/.389-ds-base.metadata @@ -1,2 +1,3 @@ c69c175a2f27053dffbfefac9c84ff16c7ff4cbf SOURCES/389-ds-base-1.4.3.23.tar.bz2 9e06b5cc57fd185379d007696da153893cf73e30 SOURCES/jemalloc-5.2.1.tar.bz2 +22b1ef11852864027e184bb4bee56286b855b703 SOURCES/vendor-1.4.3.23-2.tar.gz diff --git a/.gitignore b/.gitignore index 6114f52..3e96486 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ SOURCES/389-ds-base-1.4.3.23.tar.bz2 SOURCES/jemalloc-5.2.1.tar.bz2 +SOURCES/vendor-1.4.3.23-2.tar.gz diff --git a/SOURCES/0013-Issue-4797-ACL-IP-ADDRESS-evaluation-may-corrupt-c_i.patch b/SOURCES/0013-Issue-4797-ACL-IP-ADDRESS-evaluation-may-corrupt-c_i.patch new file mode 100644 index 0000000..db28cfa --- /dev/null +++ b/SOURCES/0013-Issue-4797-ACL-IP-ADDRESS-evaluation-may-corrupt-c_i.patch @@ -0,0 +1,52 @@ +From bc41bbb89405b2059b80e344b2d4c59ae39aabe6 Mon Sep 17 00:00:00 2001 +From: tbordaz +Date: Thu, 10 Jun 2021 15:03:27 +0200 +Subject: [PATCH 1/3] Issue 4797 - ACL IP ADDRESS evaluation may corrupt + c_isreplication_session connection flags (#4799) + +Bug description: + The fix for ticket #3764 was broken with a missing break in a + switch. The consequence is that while setting the client IP + address in the pblock (SLAPI_CONN_CLIENTNETADDR_ACLIP), the + connection is erroneously set as replication connection. + This can lead to crash or failure of testcase + test_access_from_certain_network_only_ip. + This bug was quite hidden until the fix for #4764 is + showing it more frequently + +Fix description: + Add the missing break + +relates: https://github.com/389ds/389-ds-base/issues/4797 + +Reviewed by: Mark Reynolds + +Platforms tested: F33 +--- + ldap/servers/slapd/pblock.c | 3 ++- + 1 file changed, 2 insertions(+), 1 deletion(-) + +diff --git a/ldap/servers/slapd/pblock.c b/ldap/servers/slapd/pblock.c +index fcac53839..a64986aeb 100644 +--- a/ldap/servers/slapd/pblock.c ++++ b/ldap/servers/slapd/pblock.c +@@ -2595,7 +2595,7 @@ slapi_pblock_set(Slapi_PBlock *pblock, int arg, void *value) + pblock->pb_conn->c_authtype = slapi_ch_strdup((char *)value); + pthread_mutex_unlock(&(pblock->pb_conn->c_mutex)); + break; +- case SLAPI_CONN_CLIENTNETADDR_ACLIP: ++ case SLAPI_CONN_CLIENTNETADDR_ACLIP: + if (pblock->pb_conn == NULL) { + break; + } +@@ -2603,6 +2603,7 @@ slapi_pblock_set(Slapi_PBlock *pblock, int arg, void *value) + slapi_ch_free((void **)&pblock->pb_conn->cin_addr_aclip); + pblock->pb_conn->cin_addr_aclip = (PRNetAddr *)value; + pthread_mutex_unlock(&(pblock->pb_conn->c_mutex)); ++ break; + case SLAPI_CONN_IS_REPLICATION_SESSION: + if (pblock->pb_conn == NULL) { + slapi_log_err(SLAPI_LOG_ERR, +-- +2.31.1 + diff --git a/SOURCES/0014-Issue-4396-Minor-memory-leak-in-backend-4558-4572.patch b/SOURCES/0014-Issue-4396-Minor-memory-leak-in-backend-4558-4572.patch new file mode 100644 index 0000000..eb16fcb --- /dev/null +++ b/SOURCES/0014-Issue-4396-Minor-memory-leak-in-backend-4558-4572.patch @@ -0,0 +1,79 @@ +From b3170e39519530c39d59202413b20e6bd466224d Mon Sep 17 00:00:00 2001 +From: James Chapman +Date: Wed, 27 Jan 2021 09:56:38 +0000 +Subject: [PATCH 2/3] Issue 4396 - Minor memory leak in backend (#4558) (#4572) + +Bug Description: As multiple suffixes per backend were no longer used, this +functionality has been replaced with a single suffix per backend. Legacy +code remains that adds multiple suffixes to the dse internal backend, +resulting in memory allocations that are lost. + +Also a minor typo is corrected in backend.c + +Fix Description: Calls to be_addsuffix on the DSE backend are removed +as they are never used. + +Fixes: https://github.com/389ds/389-ds-base/issues/4396 + +Reviewed by: mreynolds389, Firstyear, droideck (Thank you) +--- + ldap/servers/slapd/backend.c | 2 +- + ldap/servers/slapd/fedse.c | 12 +++--------- + 2 files changed, 4 insertions(+), 10 deletions(-) + +diff --git a/ldap/servers/slapd/backend.c b/ldap/servers/slapd/backend.c +index bc52b4643..5707504a9 100644 +--- a/ldap/servers/slapd/backend.c ++++ b/ldap/servers/slapd/backend.c +@@ -42,7 +42,7 @@ be_init(Slapi_Backend *be, const char *type, const char *name, int isprivate, in + } + be->be_monitordn = slapi_create_dn_string("cn=monitor,cn=%s,cn=%s,cn=plugins,cn=config", + name, type); +- if (NULL == be->be_configdn) { ++ if (NULL == be->be_monitordn) { + slapi_log_err(SLAPI_LOG_ERR, + "be_init", "Failed create instance monitor dn for " + "plugin %s, instance %s\n", +diff --git a/ldap/servers/slapd/fedse.c b/ldap/servers/slapd/fedse.c +index 0d645f909..7b820b540 100644 +--- a/ldap/servers/slapd/fedse.c ++++ b/ldap/servers/slapd/fedse.c +@@ -2827,7 +2827,7 @@ search_snmp(Slapi_PBlock *pb __attribute__((unused)), + } + + /* +- * Called from config.c to install the internal backends ++ * Called from main.c to install the internal backends + */ + int + setup_internal_backends(char *configdir) +@@ -2846,7 +2846,6 @@ setup_internal_backends(char *configdir) + Slapi_DN counters; + Slapi_DN snmp; + Slapi_DN root; +- Slapi_Backend *be; + Slapi_DN encryption; + Slapi_DN saslmapping; + Slapi_DN plugins; +@@ -2895,16 +2894,11 @@ setup_internal_backends(char *configdir) + dse_register_callback(pfedse, SLAPI_OPERATION_ADD, DSE_FLAG_PREOP, &saslmapping, LDAP_SCOPE_SUBTREE, "(objectclass=nsSaslMapping)", sasl_map_config_add, NULL, NULL); + dse_register_callback(pfedse, SLAPI_OPERATION_ADD, DSE_FLAG_PREOP, &plugins, LDAP_SCOPE_SUBTREE, "(objectclass=nsSlapdPlugin)", check_plugin_path, NULL, NULL); + +- be = be_new_internal(pfedse, "DSE", DSE_BACKEND, &fedse_plugin); +- be_addsuffix(be, &root); +- be_addsuffix(be, &monitor); +- be_addsuffix(be, &config); ++ be_new_internal(pfedse, "DSE", DSE_BACKEND, &fedse_plugin); + + /* +- * Now that the be's are in place, we can +- * setup the mapping tree. ++ * Now that the be's are in place, we can setup the mapping tree. + */ +- + if (mapping_tree_init()) { + slapi_log_err(SLAPI_LOG_EMERG, "setup_internal_backends", "Failed to init mapping tree\n"); + exit(1); +-- +2.31.1 + diff --git a/SOURCES/0015-Issue-4700-Regression-in-winsync-replication-agreeme.patch b/SOURCES/0015-Issue-4700-Regression-in-winsync-replication-agreeme.patch new file mode 100644 index 0000000..9e5231d --- /dev/null +++ b/SOURCES/0015-Issue-4700-Regression-in-winsync-replication-agreeme.patch @@ -0,0 +1,66 @@ +From 8d06fdf44b0d337f1e321e61ee1b22972ddea917 Mon Sep 17 00:00:00 2001 +From: tbordaz +Date: Fri, 2 Apr 2021 14:05:41 +0200 +Subject: [PATCH 3/3] Issue 4700 - Regression in winsync replication agreement + (#4712) + +Bug description: + #4396 fixes a memory leak but did not set 'cn=config' as + DSE backend. + It had no signicant impact unless with sidgen IPA plugin + +Fix description: + revert the portion of the #4364 patch that set be_suffix + in be_addsuffix, free the suffix before setting it + +relates: https://github.com/389ds/389-ds-base/issues/4700 + +Reviewed by: Pierre Rogier (thanks !) + +Platforms tested: F33 +--- + ldap/servers/slapd/backend.c | 3 ++- + ldap/servers/slapd/fedse.c | 6 +++++- + 2 files changed, 7 insertions(+), 2 deletions(-) + +diff --git a/ldap/servers/slapd/backend.c b/ldap/servers/slapd/backend.c +index 5707504a9..5db706841 100644 +--- a/ldap/servers/slapd/backend.c ++++ b/ldap/servers/slapd/backend.c +@@ -173,7 +173,8 @@ void + be_addsuffix(Slapi_Backend *be, const Slapi_DN *suffix) + { + if (be->be_state != BE_STATE_DELETED) { +- be->be_suffix = slapi_sdn_dup(suffix);; ++ slapi_sdn_free(&be->be_suffix); ++ be->be_suffix = slapi_sdn_dup(suffix); + } + } + +diff --git a/ldap/servers/slapd/fedse.c b/ldap/servers/slapd/fedse.c +index 7b820b540..44159c991 100644 +--- a/ldap/servers/slapd/fedse.c ++++ b/ldap/servers/slapd/fedse.c +@@ -2846,6 +2846,7 @@ setup_internal_backends(char *configdir) + Slapi_DN counters; + Slapi_DN snmp; + Slapi_DN root; ++ Slapi_Backend *be; + Slapi_DN encryption; + Slapi_DN saslmapping; + Slapi_DN plugins; +@@ -2894,7 +2895,10 @@ setup_internal_backends(char *configdir) + dse_register_callback(pfedse, SLAPI_OPERATION_ADD, DSE_FLAG_PREOP, &saslmapping, LDAP_SCOPE_SUBTREE, "(objectclass=nsSaslMapping)", sasl_map_config_add, NULL, NULL); + dse_register_callback(pfedse, SLAPI_OPERATION_ADD, DSE_FLAG_PREOP, &plugins, LDAP_SCOPE_SUBTREE, "(objectclass=nsSlapdPlugin)", check_plugin_path, NULL, NULL); + +- be_new_internal(pfedse, "DSE", DSE_BACKEND, &fedse_plugin); ++ be = be_new_internal(pfedse, "DSE", DSE_BACKEND, &fedse_plugin); ++ be_addsuffix(be, &root); ++ be_addsuffix(be, &monitor); ++ be_addsuffix(be, &config); + + /* + * Now that the be's are in place, we can setup the mapping tree. +-- +2.31.1 + diff --git a/SOURCES/0016-Issue-4725-Fix-compiler-warnings.patch b/SOURCES/0016-Issue-4725-Fix-compiler-warnings.patch new file mode 100644 index 0000000..2371384 --- /dev/null +++ b/SOURCES/0016-Issue-4725-Fix-compiler-warnings.patch @@ -0,0 +1,88 @@ +From 7345c51c68dfd90a704ccbb0e5b1e736af80f146 Mon Sep 17 00:00:00 2001 +From: Thierry Bordaz +Date: Mon, 17 May 2021 16:10:22 +0200 +Subject: [PATCH] Issue 4725 - Fix compiler warnings + +--- + ldap/servers/slapd/proto-slap.h | 2 +- + ldap/servers/slapd/pw.c | 9 ++++----- + ldap/servers/slapd/pw_retry.c | 2 -- + 3 files changed, 5 insertions(+), 8 deletions(-) + +diff --git a/ldap/servers/slapd/proto-slap.h b/ldap/servers/slapd/proto-slap.h +index 6ff178127..2768d5a1d 100644 +--- a/ldap/servers/slapd/proto-slap.h ++++ b/ldap/servers/slapd/proto-slap.h +@@ -1012,7 +1012,7 @@ int add_shadow_ext_password_attrs(Slapi_PBlock *pb, Slapi_Entry **e); + * pw_retry.c + */ + int update_pw_retry(Slapi_PBlock *pb); +-int update_trp_pw_usecount(Slapi_PBlock *pb, Slapi_Entry *e, int32_t use_count); ++int update_tpr_pw_usecount(Slapi_PBlock *pb, Slapi_Entry *e, int32_t use_count); + void pw_apply_mods(const Slapi_DN *sdn, Slapi_Mods *mods); + void pw_set_componentID(struct slapi_componentid *cid); + struct slapi_componentid *pw_get_componentID(void); +diff --git a/ldap/servers/slapd/pw.c b/ldap/servers/slapd/pw.c +index d98422513..2a167c8f1 100644 +--- a/ldap/servers/slapd/pw.c ++++ b/ldap/servers/slapd/pw.c +@@ -2622,7 +2622,6 @@ int + slapi_check_tpr_limits(Slapi_PBlock *pb, Slapi_Entry *bind_target_entry, int send_result) { + passwdPolicy *pwpolicy = NULL; + char *dn = NULL; +- int tpr_maxuse; + char *value; + time_t cur_time; + char *cur_time_str = NULL; +@@ -2638,7 +2637,7 @@ slapi_check_tpr_limits(Slapi_PBlock *pb, Slapi_Entry *bind_target_entry, int sen + return 0; + } + +- if (slapi_entry_attr_hasvalue(bind_target_entry, "pwdTPRReset", "TRUE") == NULL) { ++ if (!slapi_entry_attr_hasvalue(bind_target_entry, "pwdTPRReset", "TRUE")) { + /* the password was not reset by an admin while a TRP pwp was set, just returned */ + return 0; + } +@@ -2646,7 +2645,7 @@ slapi_check_tpr_limits(Slapi_PBlock *pb, Slapi_Entry *bind_target_entry, int sen + /* Check entry TPR max use */ + if (pwpolicy->pw_tpr_maxuse >= 0) { + uint use_count; +- value = slapi_entry_attr_get_ref(bind_target_entry, "pwdTPRUseCount"); ++ value = (char *) slapi_entry_attr_get_ref(bind_target_entry, "pwdTPRUseCount"); + if (value) { + /* max Use is enforced */ + use_count = strtoull(value, 0, 0); +@@ -2681,7 +2680,7 @@ slapi_check_tpr_limits(Slapi_PBlock *pb, Slapi_Entry *bind_target_entry, int sen + + /* Check entry TPR expiration at a specific time */ + if (pwpolicy->pw_tpr_delay_expire_at >= 0) { +- value = slapi_entry_attr_get_ref(bind_target_entry, "pwdTPRExpireAt"); ++ value = (char *) slapi_entry_attr_get_ref(bind_target_entry, "pwdTPRExpireAt"); + if (value) { + /* max Use is enforced */ + if (difftime(parse_genTime(cur_time_str), parse_genTime(value)) >= 0) { +@@ -2709,7 +2708,7 @@ slapi_check_tpr_limits(Slapi_PBlock *pb, Slapi_Entry *bind_target_entry, int sen + + /* Check entry TPR valid after a specific time */ + if (pwpolicy->pw_tpr_delay_valid_from >= 0) { +- value = slapi_entry_attr_get_ref(bind_target_entry, "pwdTPRValidFrom"); ++ value = (char *) slapi_entry_attr_get_ref(bind_target_entry, "pwdTPRValidFrom"); + if (value) { + /* validity after a specific time is enforced */ + if (difftime(parse_genTime(value), parse_genTime(cur_time_str)) >= 0) { +diff --git a/ldap/servers/slapd/pw_retry.c b/ldap/servers/slapd/pw_retry.c +index 5d13eb636..af54aa19d 100644 +--- a/ldap/servers/slapd/pw_retry.c ++++ b/ldap/servers/slapd/pw_retry.c +@@ -163,8 +163,6 @@ set_retry_cnt_and_time(Slapi_PBlock *pb, int count, time_t cur_time) + int + set_tpr_usecount_mods(Slapi_PBlock *pb, Slapi_Mods *smods, int count) + { +- char *timestr; +- time_t unlock_time; + char retry_cnt[16] = {0}; /* 1-65535 */ + const char *dn = NULL; + Slapi_DN *sdn = NULL; +-- +2.31.1 + diff --git a/SOURCES/0017-Issue-4814-_cl5_get_tod_expiration-may-crash-at-star.patch b/SOURCES/0017-Issue-4814-_cl5_get_tod_expiration-may-crash-at-star.patch new file mode 100644 index 0000000..6785c04 --- /dev/null +++ b/SOURCES/0017-Issue-4814-_cl5_get_tod_expiration-may-crash-at-star.patch @@ -0,0 +1,202 @@ +From 59266365eda8130abf6901263efae4c87586376a Mon Sep 17 00:00:00 2001 +From: Thierry Bordaz +Date: Mon, 28 Jun 2021 16:40:15 +0200 +Subject: [PATCH] Issue 4814 - _cl5_get_tod_expiration may crash at startup + +Bug description: + This bug exist only in 1.4.3 branch + In 1.4.3, CL open as a separated database so + compaction mechanism is started along a CL + mechanism (CL trimming). + The problem is that the configuration of the CL + compaction is done after the compaction mechanism + (is started). Depending on thread scheduling it + crashes + +Fix description: + Make sure configuration of compaction thread is + taken into account (cl5ConfigSetCompaction) before + the compaction thread starts (cl5open) + +relates: https://github.com/389ds/389-ds-base/issues/4814 + +Reviewed by: Mark Reynolds, Simon Pichugin (thanks !) + +Platforms tested: 8.5 +--- + ldap/servers/plugins/replication/cl5_api.c | 24 ++++++++++++------- + ldap/servers/plugins/replication/cl5_api.h | 10 +++++++- + ldap/servers/plugins/replication/cl5_config.c | 8 +++++-- + ldap/servers/plugins/replication/cl5_init.c | 4 +++- + ldap/servers/plugins/replication/cl5_test.c | 2 +- + .../servers/plugins/replication/repl_shared.h | 2 +- + 6 files changed, 35 insertions(+), 15 deletions(-) + +diff --git a/ldap/servers/plugins/replication/cl5_api.c b/ldap/servers/plugins/replication/cl5_api.c +index 4c5077b48..954b6b9e3 100644 +--- a/ldap/servers/plugins/replication/cl5_api.c ++++ b/ldap/servers/plugins/replication/cl5_api.c +@@ -1016,6 +1016,20 @@ cl5GetState() + return s_cl5Desc.dbState; + } + ++void ++cl5ConfigSetCompaction(int compactInterval, char *compactTime) ++{ ++ ++ if (compactInterval != CL5_NUM_IGNORE) { ++ s_cl5Desc.dbTrim.compactInterval = compactInterval; ++ } ++ ++ if (strcmp(compactTime, CL5_STR_IGNORE) != 0) { ++ s_cl5Desc.dbTrim.compactTime = slapi_ch_strdup(compactTime); ++ } ++ ++} ++ + /* Name: cl5ConfigTrimming + Description: sets changelog trimming parameters; changelog must be open. + Parameters: maxEntries - maximum number of entries in the chnagelog (in all files); +@@ -1026,7 +1040,7 @@ cl5GetState() + CL5_BAD_STATE if changelog is not open + */ + int +-cl5ConfigTrimming(int maxEntries, const char *maxAge, int compactInterval, char *compactTime, int trimInterval) ++cl5ConfigTrimming(int maxEntries, const char *maxAge, int trimInterval) + { + if (s_cl5Desc.dbState == CL5_STATE_NONE) { + slapi_log_err(SLAPI_LOG_ERR, repl_plugin_name_cl, +@@ -1058,14 +1072,6 @@ cl5ConfigTrimming(int maxEntries, const char *maxAge, int compactInterval, char + s_cl5Desc.dbTrim.maxEntries = maxEntries; + } + +- if (compactInterval != CL5_NUM_IGNORE) { +- s_cl5Desc.dbTrim.compactInterval = compactInterval; +- } +- +- if (strcmp(compactTime, CL5_STR_IGNORE) != 0) { +- s_cl5Desc.dbTrim.compactTime = slapi_ch_strdup(compactTime); +- } +- + if (trimInterval != CL5_NUM_IGNORE) { + s_cl5Desc.dbTrim.trimInterval = trimInterval; + } +diff --git a/ldap/servers/plugins/replication/cl5_api.h b/ldap/servers/plugins/replication/cl5_api.h +index 11db771f2..6aa48aec4 100644 +--- a/ldap/servers/plugins/replication/cl5_api.h ++++ b/ldap/servers/plugins/replication/cl5_api.h +@@ -227,6 +227,14 @@ int cl5ImportLDIF(const char *clDir, const char *ldifFile, Replica **replicas); + + int cl5GetState(void); + ++/* Name: cl5ConfigSetCompaction ++ * Description: sets the database compaction parameters ++ * Parameters: compactInterval - Interval for compaction default is 30days ++ * compactTime - Compact time default is 23:59 ++ * Return: void ++ */ ++void cl5ConfigSetCompaction(int compactInterval, char *compactTime); ++ + /* Name: cl5ConfigTrimming + Description: sets changelog trimming parameters + Parameters: maxEntries - maximum number of entries in the log; +@@ -236,7 +244,7 @@ int cl5GetState(void); + Return: CL5_SUCCESS if successful; + CL5_BAD_STATE if changelog has not been open + */ +-int cl5ConfigTrimming(int maxEntries, const char *maxAge, int compactInterval, char *compactTime, int trimInterval); ++int cl5ConfigTrimming(int maxEntries, const char *maxAge, int trimInterval); + + void cl5DestroyIterator(void *iterator); + +diff --git a/ldap/servers/plugins/replication/cl5_config.c b/ldap/servers/plugins/replication/cl5_config.c +index b32686788..a43534c9b 100644 +--- a/ldap/servers/plugins/replication/cl5_config.c ++++ b/ldap/servers/plugins/replication/cl5_config.c +@@ -197,6 +197,8 @@ changelog5_config_add(Slapi_PBlock *pb __attribute__((unused)), + + goto done; + } ++ /* Set compaction parameters */ ++ cl5ConfigSetCompaction(config.compactInterval, config.compactTime); + + /* start the changelog */ + rc = cl5Open(config.dir, &config.dbconfig); +@@ -212,7 +214,7 @@ changelog5_config_add(Slapi_PBlock *pb __attribute__((unused)), + } + + /* set trimming parameters */ +- rc = cl5ConfigTrimming(config.maxEntries, config.maxAge, config.compactInterval, config.compactTime, config.trimInterval); ++ rc = cl5ConfigTrimming(config.maxEntries, config.maxAge, config.trimInterval); + if (rc != CL5_SUCCESS) { + *returncode = 1; + if (returntext) { +@@ -548,6 +550,8 @@ changelog5_config_modify(Slapi_PBlock *pb, + slapi_log_err(SLAPI_LOG_REPL, repl_plugin_name_cl, + "changelog5_config_modify - Deleted the changelog at %s\n", currentDir); + } ++ /* Set compaction parameters */ ++ cl5ConfigSetCompaction(config.compactInterval, config.compactTime); + + rc = cl5Open(config.dir, &config.dbconfig); + if (rc != CL5_SUCCESS) { +@@ -575,7 +579,7 @@ changelog5_config_modify(Slapi_PBlock *pb, + if (config.maxEntries != CL5_NUM_IGNORE || + config.trimInterval != CL5_NUM_IGNORE || + strcmp(config.maxAge, CL5_STR_IGNORE) != 0) { +- rc = cl5ConfigTrimming(config.maxEntries, config.maxAge, config.compactInterval, config.compactTime, config.trimInterval); ++ rc = cl5ConfigTrimming(config.maxEntries, config.maxAge, config.trimInterval); + if (rc != CL5_SUCCESS) { + *returncode = 1; + if (returntext) { +diff --git a/ldap/servers/plugins/replication/cl5_init.c b/ldap/servers/plugins/replication/cl5_init.c +index 251859714..567e0274c 100644 +--- a/ldap/servers/plugins/replication/cl5_init.c ++++ b/ldap/servers/plugins/replication/cl5_init.c +@@ -45,6 +45,8 @@ changelog5_init() + rc = 0; /* OK */ + goto done; + } ++ /* Set compaction parameters */ ++ cl5ConfigSetCompaction(config.compactInterval, config.compactTime); + + /* start changelog */ + rc = cl5Open(config.dir, &config.dbconfig); +@@ -57,7 +59,7 @@ changelog5_init() + } + + /* set trimming parameters */ +- rc = cl5ConfigTrimming(config.maxEntries, config.maxAge, config.compactInterval, config.compactTime, config.trimInterval); ++ rc = cl5ConfigTrimming(config.maxEntries, config.maxAge, config.trimInterval); + if (rc != CL5_SUCCESS) { + slapi_log_err(SLAPI_LOG_ERR, repl_plugin_name_cl, + "changelog5_init: failed to configure changelog trimming\n"); +diff --git a/ldap/servers/plugins/replication/cl5_test.c b/ldap/servers/plugins/replication/cl5_test.c +index d6656653c..efb8c543a 100644 +--- a/ldap/servers/plugins/replication/cl5_test.c ++++ b/ldap/servers/plugins/replication/cl5_test.c +@@ -281,7 +281,7 @@ testTrimming() + rc = populateChangelog(300, NULL); + + if (rc == 0) +- rc = cl5ConfigTrimming(300, "1d", CHANGELOGDB_COMPACT_INTERVAL, CHANGELOGDB_TRIM_INTERVAL); ++ rc = cl5ConfigTrimming(300, "1d", CHANGELOGDB_TRIM_INTERVAL); + + interval = PR_SecondsToInterval(300); /* 5 min is default trimming interval */ + slapi_log_err(SLAPI_LOG_ERR, repl_plugin_name_cl, +diff --git a/ldap/servers/plugins/replication/repl_shared.h b/ldap/servers/plugins/replication/repl_shared.h +index 6708e12f7..b59b2bd27 100644 +--- a/ldap/servers/plugins/replication/repl_shared.h ++++ b/ldap/servers/plugins/replication/repl_shared.h +@@ -26,7 +26,7 @@ + + #define CHANGELOGDB_TRIM_INTERVAL 300 /* 5 minutes */ + #define CHANGELOGDB_COMPACT_INTERVAL 2592000 /* 30 days */ +-#define CHANGELOGDB_COMPACT_TIME "23:55" /* 30 days */ ++#define CHANGELOGDB_COMPACT_TIME "23:59" /* around midnight */ + + #define CONFIG_CHANGELOG_DIR_ATTRIBUTE "nsslapd-changelogdir" + #define CONFIG_CHANGELOG_MAXENTRIES_ATTRIBUTE "nsslapd-changelogmaxentries" +-- +2.31.1 + diff --git a/SOURCES/0018-Issue-4789-Temporary-password-rules-are-not-enforce-.patch b/SOURCES/0018-Issue-4789-Temporary-password-rules-are-not-enforce-.patch new file mode 100644 index 0000000..5ab86af --- /dev/null +++ b/SOURCES/0018-Issue-4789-Temporary-password-rules-are-not-enforce-.patch @@ -0,0 +1,51 @@ +From e7fdfe527a5f72674fe4b577a0555cabf8ec73a5 Mon Sep 17 00:00:00 2001 +From: tbordaz +Date: Mon, 7 Jun 2021 11:23:35 +0200 +Subject: [PATCH] Issue 4789 - Temporary password rules are not enforce with + local password policy (#4790) + +Bug description: + When allocating a password policy structure (new_passwdPolicy) + it is initialized with the local policy definition or + the global one. If it exists a local policy entry, the TPR + attributes (passwordTPRMaxUse, passwordTPRDelayValidFrom and + passwordTPRDelayExpireAt) are not taken into account. + +Fix description: + Take into account TPR attributes to initialize the policy + +relates: https://github.com/389ds/389-ds-base/issues/4789 + +Reviewed by: Simon Pichugin, William Brown + +Platforms tested: F34 +--- + ldap/servers/slapd/pw.c | 12 ++++++++++++ + 1 file changed, 12 insertions(+) + +diff --git a/ldap/servers/slapd/pw.c b/ldap/servers/slapd/pw.c +index 2a167c8f1..7680df41d 100644 +--- a/ldap/servers/slapd/pw.c ++++ b/ldap/servers/slapd/pw.c +@@ -2356,6 +2356,18 @@ new_passwdPolicy(Slapi_PBlock *pb, const char *dn) + if ((sval = attr_get_present_values(attr))) { + pwdpolicy->pw_dict_path = (char *)slapi_value_get_string(*sval); + } ++ } else if (!strcasecmp(attr_name, CONFIG_PW_TPR_MAXUSE)) { ++ if ((sval = attr_get_present_values(attr))) { ++ pwdpolicy->pw_tpr_maxuse = slapi_value_get_int(*sval); ++ } ++ } else if (!strcasecmp(attr_name, CONFIG_PW_TPR_DELAY_EXPIRE_AT)) { ++ if ((sval = attr_get_present_values(attr))) { ++ pwdpolicy->pw_tpr_delay_expire_at = slapi_value_get_int(*sval); ++ } ++ } else if (!strcasecmp(attr_name, CONFIG_PW_TPR_DELAY_VALID_FROM)) { ++ if ((sval = attr_get_present_values(attr))) { ++ pwdpolicy->pw_tpr_delay_valid_from = slapi_value_get_int(*sval); ++ } + } + } /* end of for() loop */ + if (pw_entry) { +-- +2.31.1 + diff --git a/SOURCES/0019-Issue-4788-CLI-should-support-Temporary-Password-Rul.patch b/SOURCES/0019-Issue-4788-CLI-should-support-Temporary-Password-Rul.patch new file mode 100644 index 0000000..f9e4266 --- /dev/null +++ b/SOURCES/0019-Issue-4788-CLI-should-support-Temporary-Password-Rul.patch @@ -0,0 +1,350 @@ +From 6a741b3ef50babf2ac2479437a38829204ffd438 Mon Sep 17 00:00:00 2001 +From: tbordaz +Date: Thu, 17 Jun 2021 16:22:09 +0200 +Subject: [PATCH] Issue 4788 - CLI should support Temporary Password Rules + attributes (#4793) + +Bug description: + Since #4725, password policy support temporary password rules. + CLI (dsconf) does not support this RFE and only direct ldap + operation can configure global/local password policy + +Fix description: + Update dsconf to support this new RFE. + To run successfully the testcase it relies on #4788 + +relates: #4788 + +Reviewed by: Simon Pichugin (thanks !!) + +Platforms tested: F34 +--- + .../password/pwdPolicy_attribute_test.py | 172 ++++++++++++++++-- + src/lib389/lib389/cli_conf/pwpolicy.py | 5 +- + src/lib389/lib389/pwpolicy.py | 5 +- + 3 files changed, 165 insertions(+), 17 deletions(-) + +diff --git a/dirsrvtests/tests/suites/password/pwdPolicy_attribute_test.py b/dirsrvtests/tests/suites/password/pwdPolicy_attribute_test.py +index aee3a91ad..085d0a373 100644 +--- a/dirsrvtests/tests/suites/password/pwdPolicy_attribute_test.py ++++ b/dirsrvtests/tests/suites/password/pwdPolicy_attribute_test.py +@@ -34,7 +34,7 @@ log = logging.getLogger(__name__) + + + @pytest.fixture(scope="module") +-def create_user(topology_st, request): ++def test_user(topology_st, request): + """User for binding operation""" + topology_st.standalone.config.set('nsslapd-auditlog-logging-enabled', 'on') + log.info('Adding test user {}') +@@ -56,10 +56,11 @@ def create_user(topology_st, request): + topology_st.standalone.simple_bind_s(DN_DM, PASSWORD) + + request.addfinalizer(fin) ++ return user + + + @pytest.fixture(scope="module") +-def password_policy(topology_st, create_user): ++def password_policy(topology_st, test_user): + """Set up password policy for subtree and user""" + + pwp = PwPolicyManager(topology_st.standalone) +@@ -71,7 +72,7 @@ def password_policy(topology_st, create_user): + pwp.create_user_policy(TEST_USER_DN, policy_props) + + @pytest.mark.skipif(ds_is_older('1.4.3.3'), reason="Not implemented") +-def test_pwd_reset(topology_st, create_user): ++def test_pwd_reset(topology_st, test_user): + """Test new password policy attribute "pwdReset" + + :id: 03db357b-4800-411e-a36e-28a534293004 +@@ -124,7 +125,7 @@ def test_pwd_reset(topology_st, create_user): + [('on', 'off', ldap.UNWILLING_TO_PERFORM), + ('off', 'off', ldap.UNWILLING_TO_PERFORM), + ('off', 'on', False), ('on', 'on', False)]) +-def test_change_pwd(topology_st, create_user, password_policy, ++def test_change_pwd(topology_st, test_user, password_policy, + subtree_pwchange, user_pwchange, exception): + """Verify that 'passwordChange' attr works as expected + User should have a priority over a subtree. +@@ -184,7 +185,7 @@ def test_change_pwd(topology_st, create_user, password_policy, + user.reset_password(TEST_USER_PWD) + + +-def test_pwd_min_age(topology_st, create_user, password_policy): ++def test_pwd_min_age(topology_st, test_user, password_policy): + """If we set passwordMinAge to some value, for example to 10, then it + should not allow the user to change the password within 10 seconds after + his previous change. +@@ -257,7 +258,7 @@ def test_pwd_min_age(topology_st, create_user, password_policy): + topology_st.standalone.simple_bind_s(DN_DM, PASSWORD) + user.reset_password(TEST_USER_PWD) + +-def test_global_tpr_maxuse_1(topology_st, create_user, request): ++def test_global_tpr_maxuse_1(topology_st, test_user, request): + """Test global TPR policy : passwordTPRMaxUse + Test that after passwordTPRMaxUse failures to bind + additional bind with valid password are failing with CONSTRAINT_VIOLATION +@@ -374,7 +375,7 @@ def test_global_tpr_maxuse_1(topology_st, create_user, request): + + request.addfinalizer(fin) + +-def test_global_tpr_maxuse_2(topology_st, create_user, request): ++def test_global_tpr_maxuse_2(topology_st, test_user, request): + """Test global TPR policy : passwordTPRMaxUse + Test that after less than passwordTPRMaxUse failures to bind + additional bind with valid password are successfull +@@ -474,7 +475,7 @@ def test_global_tpr_maxuse_2(topology_st, create_user, request): + + request.addfinalizer(fin) + +-def test_global_tpr_maxuse_3(topology_st, create_user, request): ++def test_global_tpr_maxuse_3(topology_st, test_user, request): + """Test global TPR policy : passwordTPRMaxUse + Test that after less than passwordTPRMaxUse failures to bind + A bind with valid password is successfull but passwordMustChange +@@ -587,7 +588,7 @@ def test_global_tpr_maxuse_3(topology_st, create_user, request): + + request.addfinalizer(fin) + +-def test_global_tpr_maxuse_4(topology_st, create_user, request): ++def test_global_tpr_maxuse_4(topology_st, test_user, request): + """Test global TPR policy : passwordTPRMaxUse + Test that a TPR attribute passwordTPRMaxUse + can be updated by DM but not the by user itself +@@ -701,7 +702,148 @@ def test_global_tpr_maxuse_4(topology_st, create_user, request): + + request.addfinalizer(fin) + +-def test_global_tpr_delayValidFrom_1(topology_st, create_user, request): ++def test_local_tpr_maxuse_5(topology_st, test_user, request): ++ """Test TPR local policy overpass global one: passwordTPRMaxUse ++ Test that after passwordTPRMaxUse failures to bind ++ additional bind with valid password are failing with CONSTRAINT_VIOLATION ++ ++ :id: c3919707-d804-445a-8754-8385b1072c42 ++ :customerscenario: False ++ :setup: Standalone instance ++ :steps: ++ 1. Global password policy Enable passwordMustChange ++ 2. Global password policy Set passwordTPRMaxUse=5 ++ 3. Global password policy Set passwordMaxFailure to a higher value to not disturb the test ++ 4. Local password policy Enable passwordMustChange ++ 5. Local password policy Set passwordTPRMaxUse=10 (higher than global) ++ 6. Bind with a wrong password 10 times and check INVALID_CREDENTIALS ++ 7. Check that passwordTPRUseCount got to the limit (5) ++ 8. Bind with a wrong password (CONSTRAINT_VIOLATION) ++ and check passwordTPRUseCount overpass the limit by 1 (11) ++ 9. Bind with a valid password 10 times and check CONSTRAINT_VIOLATION ++ and check passwordTPRUseCount increases ++ 10. Reset password policy configuration and remove local password from user ++ :expected results: ++ 1. Success ++ 2. Success ++ 3. Success ++ 4. Success ++ 5. Success ++ 6. Success ++ 7. Success ++ 8. Success ++ 9. Success ++ 10. Success ++ """ ++ ++ global_tpr_maxuse = 5 ++ # Set global password policy config, passwordMaxFailure being higher than ++ # passwordTPRMaxUse so that TPR is enforced first ++ topology_st.standalone.config.replace('passwordMustChange', 'on') ++ topology_st.standalone.config.replace('passwordMaxFailure', str(global_tpr_maxuse + 20)) ++ topology_st.standalone.config.replace('passwordTPRMaxUse', str(global_tpr_maxuse)) ++ time.sleep(.5) ++ ++ local_tpr_maxuse = global_tpr_maxuse + 5 ++ # Reset user's password with a local password policy ++ # that has passwordTPRMaxUse higher than global ++ #our_user = UserAccount(topology_st.standalone, TEST_USER_DN) ++ subprocess.call(['%s/dsconf' % topology_st.standalone.get_sbin_dir(), ++ 'slapd-standalone1', ++ 'localpwp', ++ 'adduser', ++ test_user.dn]) ++ subprocess.call(['%s/dsconf' % topology_st.standalone.get_sbin_dir(), ++ 'slapd-standalone1', ++ 'localpwp', ++ 'set', ++ '--pwptprmaxuse', ++ str(local_tpr_maxuse), ++ '--pwdmustchange', ++ 'on', ++ test_user.dn]) ++ test_user.replace('userpassword', PASSWORD) ++ time.sleep(.5) ++ ++ # look up to passwordTPRMaxUse with failing ++ # bind to check that the limits of TPR are enforced ++ for i in range(local_tpr_maxuse): ++ # Bind as user with a wrong password ++ with pytest.raises(ldap.INVALID_CREDENTIALS): ++ test_user.rebind('wrong password') ++ time.sleep(.5) ++ ++ # Check that pwdReset is TRUE ++ topology_st.standalone.simple_bind_s(DN_DM, PASSWORD) ++ #assert test_user.get_attr_val_utf8('pwdReset') == 'TRUE' ++ ++ # Check that pwdTPRReset is TRUE ++ assert test_user.get_attr_val_utf8('pwdTPRReset') == 'TRUE' ++ assert test_user.get_attr_val_utf8('pwdTPRUseCount') == str(i+1) ++ log.info("%dth failing bind (INVALID_CREDENTIALS) => pwdTPRUseCount = %d" % (i+1, i+1)) ++ ++ ++ # Now the #failures reached passwordTPRMaxUse ++ # Check that pwdReset is TRUE ++ topology_st.standalone.simple_bind_s(DN_DM, PASSWORD) ++ ++ # Check that pwdTPRReset is TRUE ++ assert test_user.get_attr_val_utf8('pwdTPRReset') == 'TRUE' ++ assert test_user.get_attr_val_utf8('pwdTPRUseCount') == str(local_tpr_maxuse) ++ log.info("last failing bind (INVALID_CREDENTIALS) => pwdTPRUseCount = %d" % (local_tpr_maxuse)) ++ ++ # Bind as user with wrong password --> ldap.CONSTRAINT_VIOLATION ++ with pytest.raises(ldap.CONSTRAINT_VIOLATION): ++ test_user.rebind("wrong password") ++ time.sleep(.5) ++ ++ # Check that pwdReset is TRUE ++ topology_st.standalone.simple_bind_s(DN_DM, PASSWORD) ++ ++ # Check that pwdTPRReset is TRUE ++ assert test_user.get_attr_val_utf8('pwdTPRReset') == 'TRUE' ++ assert test_user.get_attr_val_utf8('pwdTPRUseCount') == str(local_tpr_maxuse + 1) ++ log.info("failing bind (CONSTRAINT_VIOLATION) => pwdTPRUseCount = %d" % (local_tpr_maxuse + i)) ++ ++ # Now check that all next attempts with correct password are all in LDAP_CONSTRAINT_VIOLATION ++ # and passwordTPRRetryCount remains unchanged ++ # account is now similar to locked ++ for i in range(10): ++ # Bind as user with valid password ++ with pytest.raises(ldap.CONSTRAINT_VIOLATION): ++ test_user.rebind(PASSWORD) ++ time.sleep(.5) ++ ++ # Check that pwdReset is TRUE ++ topology_st.standalone.simple_bind_s(DN_DM, PASSWORD) ++ ++ # Check that pwdTPRReset is TRUE ++ # pwdTPRUseCount keeps increasing ++ assert test_user.get_attr_val_utf8('pwdTPRReset') == 'TRUE' ++ assert test_user.get_attr_val_utf8('pwdTPRUseCount') == str(local_tpr_maxuse + i + 2) ++ log.info("Rejected bind (CONSTRAINT_VIOLATION) => pwdTPRUseCount = %d" % (local_tpr_maxuse + i + 2)) ++ ++ ++ def fin(): ++ topology_st.standalone.restart() ++ # Reset password policy config ++ topology_st.standalone.simple_bind_s(DN_DM, PASSWORD) ++ topology_st.standalone.config.replace('passwordMustChange', 'off') ++ ++ # Remove local password policy from that entry ++ subprocess.call(['%s/dsconf' % topology_st.standalone.get_sbin_dir(), ++ 'slapd-standalone1', ++ 'localpwp', ++ 'remove', ++ test_user.dn]) ++ ++ # Reset user's password ++ test_user.replace('userpassword', TEST_USER_PWD) ++ ++ ++ request.addfinalizer(fin) ++ ++def test_global_tpr_delayValidFrom_1(topology_st, test_user, request): + """Test global TPR policy : passwordTPRDelayValidFrom + Test that a TPR password is not valid before reset time + + passwordTPRDelayValidFrom +@@ -766,7 +908,7 @@ def test_global_tpr_delayValidFrom_1(topology_st, create_user, request): + + request.addfinalizer(fin) + +-def test_global_tpr_delayValidFrom_2(topology_st, create_user, request): ++def test_global_tpr_delayValidFrom_2(topology_st, test_user, request): + """Test global TPR policy : passwordTPRDelayValidFrom + Test that a TPR password is valid after reset time + + passwordTPRDelayValidFrom +@@ -838,7 +980,7 @@ def test_global_tpr_delayValidFrom_2(topology_st, create_user, request): + + request.addfinalizer(fin) + +-def test_global_tpr_delayValidFrom_3(topology_st, create_user, request): ++def test_global_tpr_delayValidFrom_3(topology_st, test_user, request): + """Test global TPR policy : passwordTPRDelayValidFrom + Test that a TPR attribute passwordTPRDelayValidFrom + can be updated by DM but not the by user itself +@@ -940,7 +1082,7 @@ def test_global_tpr_delayValidFrom_3(topology_st, create_user, request): + + request.addfinalizer(fin) + +-def test_global_tpr_delayExpireAt_1(topology_st, create_user, request): ++def test_global_tpr_delayExpireAt_1(topology_st, test_user, request): + """Test global TPR policy : passwordTPRDelayExpireAt + Test that a TPR password is not valid after reset time + + passwordTPRDelayExpireAt +@@ -1010,7 +1152,7 @@ def test_global_tpr_delayExpireAt_1(topology_st, create_user, request): + + request.addfinalizer(fin) + +-def test_global_tpr_delayExpireAt_2(topology_st, create_user, request): ++def test_global_tpr_delayExpireAt_2(topology_st, test_user, request): + """Test global TPR policy : passwordTPRDelayExpireAt + Test that a TPR password is valid before reset time + + passwordTPRDelayExpireAt +@@ -1082,7 +1224,7 @@ def test_global_tpr_delayExpireAt_2(topology_st, create_user, request): + + request.addfinalizer(fin) + +-def test_global_tpr_delayExpireAt_3(topology_st, create_user, request): ++def test_global_tpr_delayExpireAt_3(topology_st, test_user, request): + """Test global TPR policy : passwordTPRDelayExpireAt + Test that a TPR attribute passwordTPRDelayExpireAt + can be updated by DM but not the by user itself +diff --git a/src/lib389/lib389/cli_conf/pwpolicy.py b/src/lib389/lib389/cli_conf/pwpolicy.py +index 2838afcb8..26af6e7ec 100644 +--- a/src/lib389/lib389/cli_conf/pwpolicy.py ++++ b/src/lib389/lib389/cli_conf/pwpolicy.py +@@ -255,6 +255,9 @@ def create_parser(subparsers): + set_parser.add_argument('--pwpinheritglobal', help="Set to \"on\" to allow local policies to inherit the global policy") + set_parser.add_argument('--pwddictcheck', help="Set to \"on\" to enforce CrackLib dictionary checking") + set_parser.add_argument('--pwddictpath', help="Filesystem path to specific/custom CrackLib dictionary files") ++ set_parser.add_argument('--pwptprmaxuse', help="Number of times a reset password can be used for authentication") ++ set_parser.add_argument('--pwptprdelayexpireat', help="Number of seconds after which a reset password expires") ++ set_parser.add_argument('--pwptprdelayvalidfrom', help="Number of seconds to wait before using a reset password to authenticated") + # delete local password policy + del_parser = local_subcommands.add_parser('remove', help='Remove a local password policy') + del_parser.set_defaults(func=del_local_policy) +@@ -291,4 +294,4 @@ def create_parser(subparsers): + ############################################# + set_parser.add_argument('DN', nargs=1, help='Set the local policy for this entry DN') + add_subtree_parser.add_argument('DN', nargs=1, help='Add/replace the subtree policy for this entry DN') +- add_user_parser.add_argument('DN', nargs=1, help='Add/replace the local password policy for this entry DN') +\ No newline at end of file ++ add_user_parser.add_argument('DN', nargs=1, help='Add/replace the local password policy for this entry DN') +diff --git a/src/lib389/lib389/pwpolicy.py b/src/lib389/lib389/pwpolicy.py +index 8653cb195..d2427933b 100644 +--- a/src/lib389/lib389/pwpolicy.py ++++ b/src/lib389/lib389/pwpolicy.py +@@ -65,7 +65,10 @@ class PwPolicyManager(object): + 'pwddictcheck': 'passworddictcheck', + 'pwddictpath': 'passworddictpath', + 'pwdallowhash': 'nsslapd-allow-hashed-passwords', +- 'pwpinheritglobal': 'nsslapd-pwpolicy-inherit-global' ++ 'pwpinheritglobal': 'nsslapd-pwpolicy-inherit-global', ++ 'pwptprmaxuse': 'passwordTPRMaxUse', ++ 'pwptprdelayexpireat': 'passwordTPRDelayExpireAt', ++ 'pwptprdelayvalidfrom': 'passwordTPRDelayValidFrom' + } + + def is_subtree_policy(self, dn): +-- +2.31.1 + diff --git a/SOURCES/0020-Issue-4447-Crash-when-the-Referential-Integrity-log-.patch b/SOURCES/0020-Issue-4447-Crash-when-the-Referential-Integrity-log-.patch new file mode 100644 index 0000000..193d44b --- /dev/null +++ b/SOURCES/0020-Issue-4447-Crash-when-the-Referential-Integrity-log-.patch @@ -0,0 +1,179 @@ +From 7b7217538908ae58df864ef5cd82e1d3303c189f Mon Sep 17 00:00:00 2001 +From: Mark Reynolds +Date: Mon, 7 Jun 2021 12:58:42 -0400 +Subject: [PATCH] Issue 4447 - Crash when the Referential Integrity log is + manually edited + +Bug Description: If the referint log is manually edited with a string + that is not a DN the server will crash when processing + the log. + +Fix Description: Check for NULL pointers when strtoking the file line. + +relates: https://github.com/389ds/389-ds-base/issues/4447 + +Reviewed by: firstyear(Thanks!) +--- + .../tests/suites/plugins/referint_test.py | 72 +++++++++++++++---- + ldap/servers/plugins/referint/referint.c | 7 ++ + src/lib389/lib389/plugins.py | 15 ++++ + 3 files changed, 80 insertions(+), 14 deletions(-) + +diff --git a/dirsrvtests/tests/suites/plugins/referint_test.py b/dirsrvtests/tests/suites/plugins/referint_test.py +index 02b985767..fda602545 100644 +--- a/dirsrvtests/tests/suites/plugins/referint_test.py ++++ b/dirsrvtests/tests/suites/plugins/referint_test.py +@@ -1,5 +1,5 @@ + # --- BEGIN COPYRIGHT BLOCK --- +-# Copyright (C) 2016 Red Hat, Inc. ++# Copyright (C) 2021 Red Hat, Inc. + # All rights reserved. + # + # License: GPL (version 3 or any later version). +@@ -12,13 +12,11 @@ Created on Dec 12, 2019 + @author: tbordaz + ''' + import logging +-import subprocess + import pytest + from lib389 import Entry +-from lib389.utils import * +-from lib389.plugins import * +-from lib389._constants import * +-from lib389.idm.user import UserAccounts, UserAccount ++from lib389.plugins import ReferentialIntegrityPlugin ++from lib389._constants import DEFAULT_SUFFIX ++from lib389.idm.user import UserAccounts + from lib389.idm.group import Groups + from lib389.topologies import topology_st as topo + +@@ -29,21 +27,27 @@ log = logging.getLogger(__name__) + ESCAPED_RDN_BASE = "foo\\,oo" + def _user_get_dn(no): + uid = '%s%d' % (ESCAPED_RDN_BASE, no) +- dn = 'uid=%s,%s' % (uid, SUFFIX) ++ dn = 'uid=%s,%s' % (uid, DEFAULT_SUFFIX) + return (uid, dn) + + def add_escaped_user(server, no): + (uid, dn) = _user_get_dn(no) + log.fatal('Adding user (%s): ' % dn) +- server.add_s(Entry((dn, {'objectclass': ['top', 'person', 'organizationalPerson', 'inetOrgPerson'], +- 'uid': [uid], +- 'sn' : [uid], +- 'cn' : [uid]}))) ++ users = UserAccounts(server, DEFAULT_SUFFIX, None) ++ user_properties = { ++ 'objectclass': ['top', 'person', 'organizationalPerson', 'inetOrgPerson', 'posixAccount'], ++ 'uid': uid, ++ 'cn' : uid, ++ 'sn' : uid, ++ 'uidNumber' : '1000', ++ 'gidNumber' : '2000', ++ 'homeDirectory' : '/home/testuser', ++ } ++ users.create(properties=user_properties) + return dn + +-@pytest.mark.ds50020 + def test_referential_false_failure(topo): +- """On MODRDN referential integrity can erronously fail ++ """On MODRDN referential integrity can erroneously fail + + :id: f77aeb80-c4c4-471b-8c1b-4733b714778b + :setup: Standalone Instance +@@ -100,6 +104,46 @@ def test_referential_false_failure(topo): + inst.restart() + + # Here if the bug is fixed, referential is able to update the member value +- inst.rename_s(user1.dn, 'uid=new_test_user_1001', newsuperior=SUFFIX, delold=0) ++ user1.rename('uid=new_test_user_1001', newsuperior=DEFAULT_SUFFIX, deloldrdn=False) + + ++def test_invalid_referint_log(topo): ++ """If there is an invalid log line in the referint log, make sure the server ++ does not crash at startup ++ ++ :id: 34807b5a-ab17-4281-ae48-4e3513e19145 ++ :setup: Standalone Instance ++ :steps: ++ 1. Set the referint log delay ++ 2. Create invalid log ++ 3. Start the server (no crash) ++ :expectedresults: ++ 1. Success ++ 2. Success ++ 3. Success ++ """ ++ ++ inst = topo.standalone ++ ++ # Set delay - required for log parsing at server startup ++ plugin = ReferentialIntegrityPlugin(inst) ++ plugin.enable() ++ plugin.set_update_delay('2') ++ logfile = plugin.get_log_file() ++ inst.restart() ++ ++ # Create invalid log ++ inst.stop() ++ with open(logfile, 'w') as log_fh: ++ log_fh.write("CRASH\n") ++ ++ # Start the instance ++ inst.start() ++ assert inst.status() ++ ++ ++if __name__ == '__main__': ++ # Run isolated ++ # -s for DEBUG mode ++ CURRENT_FILE = os.path.realpath(__file__) ++ pytest.main("-s %s" % CURRENT_FILE) +diff --git a/ldap/servers/plugins/referint/referint.c b/ldap/servers/plugins/referint/referint.c +index fd5356d72..28240c1f6 100644 +--- a/ldap/servers/plugins/referint/referint.c ++++ b/ldap/servers/plugins/referint/referint.c +@@ -1447,6 +1447,13 @@ referint_thread_func(void *arg __attribute__((unused))) + sdn = slapi_sdn_new_normdn_byref(ptoken); + ptoken = ldap_utf8strtok_r(NULL, delimiter, &iter); + ++ if (ptoken == NULL) { ++ /* Invalid line in referint log, skip it */ ++ slapi_log_err(SLAPI_LOG_ERR, REFERINT_PLUGIN_SUBSYSTEM, ++ "Skipping invalid referint log line: (%s)\n", thisline); ++ slapi_sdn_free(&sdn); ++ continue; ++ } + if (!strcasecmp(ptoken, "NULL")) { + tmprdn = NULL; + } else { +diff --git a/src/lib389/lib389/plugins.py b/src/lib389/lib389/plugins.py +index 2d88e60bd..b07e80022 100644 +--- a/src/lib389/lib389/plugins.py ++++ b/src/lib389/lib389/plugins.py +@@ -518,6 +518,21 @@ class ReferentialIntegrityPlugin(Plugin): + + self.set('referint-update-delay', str(value)) + ++ def get_log_file(self): ++ """Get referint log file""" ++ ++ return self.get_attr_val_utf8('referint-logfile') ++ ++ def get_log_file_formatted(self): ++ """Get referint log file""" ++ ++ return self.display_attr('referint-logfile') ++ ++ def set_log_file(self, value): ++ """Set referint log file""" ++ ++ self.set('referint-logfile', value) ++ + def get_membership_attr(self, formatted=False): + """Get referint-membership-attr attribute""" + +-- +2.31.1 + diff --git a/SOURCES/0021-Issue-4791-Missing-dependency-for-RetroCL-RFE.patch b/SOURCES/0021-Issue-4791-Missing-dependency-for-RetroCL-RFE.patch new file mode 100644 index 0000000..4810288 --- /dev/null +++ b/SOURCES/0021-Issue-4791-Missing-dependency-for-RetroCL-RFE.patch @@ -0,0 +1,114 @@ +From 964a153b420b26140e0bbddfbebb4a51aaa0e4ea Mon Sep 17 00:00:00 2001 +From: James Chapman +Date: Thu, 3 Jun 2021 15:16:22 +0000 +Subject: [PATCH 1/7] Issue 4791 - Missing dependency for RetroCL RFE + +Description: The RetroCL exclude attribute RFE is dependent on functionality of the + EntryUUID bug fix, that didn't make into the latest build. This breaks the + RetroCL exclude attr feature so we need to provide a workaround. + +Fixes: https://github.com/389ds/389-ds-base/issues/4791 + +Relates: https://github.com/389ds/389-ds-base/pull/4723 + +Relates: https://github.com/389ds/389-ds-base/issues/4224 + +Reviewed by: tbordaz, droideck (Thank you) +--- + .../tests/suites/retrocl/basic_test.py | 6 ++-- + .../lib389/cli_conf/plugins/retrochangelog.py | 35 +++++++++++++++++-- + 2 files changed, 36 insertions(+), 5 deletions(-) + +diff --git a/dirsrvtests/tests/suites/retrocl/basic_test.py b/dirsrvtests/tests/suites/retrocl/basic_test.py +index 112c73cb9..f3bc50f29 100644 +--- a/dirsrvtests/tests/suites/retrocl/basic_test.py ++++ b/dirsrvtests/tests/suites/retrocl/basic_test.py +@@ -17,7 +17,7 @@ from lib389.utils import * + from lib389.tasks import * + from lib389.cli_base import FakeArgs, connect_instance, disconnect_instance + from lib389.cli_base.dsrc import dsrc_arg_concat +-from lib389.cli_conf.plugins.retrochangelog import retrochangelog_add ++from lib389.cli_conf.plugins.retrochangelog import retrochangelog_add_attr + from lib389.idm.user import UserAccount, UserAccounts, nsUserAccounts + + pytestmark = pytest.mark.tier1 +@@ -122,7 +122,7 @@ def test_retrocl_exclude_attr_add(topology_st): + args.bindpw = None + args.prompt = False + args.exclude_attrs = ATTR_HOMEPHONE +- args.func = retrochangelog_add ++ args.func = retrochangelog_add_attr + dsrc_inst = dsrc_arg_concat(args, None) + inst = connect_instance(dsrc_inst, False, args) + result = args.func(inst, None, log, args) +@@ -255,7 +255,7 @@ def test_retrocl_exclude_attr_mod(topology_st): + args.bindpw = None + args.prompt = False + args.exclude_attrs = ATTR_CARLICENSE +- args.func = retrochangelog_add ++ args.func = retrochangelog_add_attr + dsrc_inst = dsrc_arg_concat(args, None) + inst = connect_instance(dsrc_inst, False, args) + result = args.func(inst, None, log, args) +diff --git a/src/lib389/lib389/cli_conf/plugins/retrochangelog.py b/src/lib389/lib389/cli_conf/plugins/retrochangelog.py +index 9940c6532..160fbb82d 100644 +--- a/src/lib389/lib389/cli_conf/plugins/retrochangelog.py ++++ b/src/lib389/lib389/cli_conf/plugins/retrochangelog.py +@@ -6,8 +6,13 @@ + # See LICENSE for details. + # --- END COPYRIGHT BLOCK --- + ++# JC Work around for missing dependency on https://github.com/389ds/389-ds-base/pull/4344 ++import ldap ++ + from lib389.plugins import RetroChangelogPlugin +-from lib389.cli_conf import add_generic_plugin_parsers, generic_object_edit ++# JC Work around for missing dependency https://github.com/389ds/389-ds-base/pull/4344 ++# from lib389.cli_conf import add_generic_plugin_parsers, generic_object_edit, generic_object_add_attr ++from lib389.cli_conf import add_generic_plugin_parsers, generic_object_edit, _args_to_attrs + + arg_to_attr = { + 'is_replicated': 'isReplicated', +@@ -18,12 +23,38 @@ arg_to_attr = { + 'exclude_attrs': 'nsslapd-exclude-attrs' + } + +- + def retrochangelog_edit(inst, basedn, log, args): + log = log.getChild('retrochangelog_edit') + plugin = RetroChangelogPlugin(inst) + generic_object_edit(plugin, log, args, arg_to_attr) + ++# JC Work around for missing dependency https://github.com/389ds/389-ds-base/pull/4344 ++def retrochangelog_add_attr(inst, basedn, log, args): ++ log = log.getChild('retrochangelog_add_attr') ++ plugin = RetroChangelogPlugin(inst) ++ generic_object_add_attr(plugin, log, args, arg_to_attr) ++ ++# JC Work around for missing dependency https://github.com/389ds/389-ds-base/pull/4344 ++def generic_object_add_attr(dsldap_object, log, args, arg_to_attr): ++ """Add an attribute to the entry. This differs to 'edit' as edit uses replace, ++ and this allows multivalues to be added. ++ ++ dsldap_object should be a single instance of DSLdapObject with a set dn ++ """ ++ log = log.getChild('generic_object_add_attr') ++ # Gather the attributes ++ attrs = _args_to_attrs(args, arg_to_attr) ++ ++ modlist = [] ++ for attr, value in attrs.items(): ++ if not isinstance(value, list): ++ value = [value] ++ modlist.append((ldap.MOD_ADD, attr, value)) ++ if len(modlist) > 0: ++ dsldap_object.apply_mods(modlist) ++ log.info("Successfully changed the %s", dsldap_object.dn) ++ else: ++ raise ValueError("There is nothing to set in the %s plugin entry" % dsldap_object.dn) + + def _add_parser_args(parser): + parser.add_argument('--is-replicated', choices=['TRUE', 'FALSE'], type=str.upper, +-- +2.31.1 + diff --git a/SOURCES/0022-Issue-4656-remove-problematic-language-from-ds-replc.patch b/SOURCES/0022-Issue-4656-remove-problematic-language-from-ds-replc.patch new file mode 100644 index 0000000..82d6945 --- /dev/null +++ b/SOURCES/0022-Issue-4656-remove-problematic-language-from-ds-replc.patch @@ -0,0 +1,642 @@ +From d2ac7e98d53cfe6c74c99ddf3504b1072418f05a Mon Sep 17 00:00:00 2001 +From: Mark Reynolds +Date: Thu, 11 Mar 2021 10:12:46 -0500 +Subject: [PATCH] Issue 4656 - remove problematic language from ds-replcheck + +Description: remove master from ds-replcheck and replace it with supplier + +relates: https://github.com/389ds/389-ds-base/issues/4656 + +Reviewed by: mreynolds + +e with '#' will be ignored, and an empty message aborts the commit. +--- + ldap/admin/src/scripts/ds-replcheck | 202 ++++++++++++++-------------- + 1 file changed, 101 insertions(+), 101 deletions(-) + +diff --git a/ldap/admin/src/scripts/ds-replcheck b/ldap/admin/src/scripts/ds-replcheck +index 169496e8f..f411f357a 100755 +--- a/ldap/admin/src/scripts/ds-replcheck ++++ b/ldap/admin/src/scripts/ds-replcheck +@@ -1,7 +1,7 @@ + #!/usr/bin/python3 + + # --- BEGIN COPYRIGHT BLOCK --- +-# Copyright (C) 2020 Red Hat, Inc. ++# Copyright (C) 2021 Red Hat, Inc. + # All rights reserved. + # + # License: GPL (version 3 or any later version). +@@ -63,7 +63,7 @@ def remove_entry(rentries, dn): + def get_ruv_time(ruv, rid): + """Take a RUV element (nsds50ruv attribute) and extract the timestamp from maxcsn + :param ruv - A lsit of RUV elements +- :param rid - The rid of the master to extractthe maxcsn time from ++ :param rid - The rid of the supplier to extract the maxcsn time from + :return: The time in seconds of the maxcsn, or 0 if there is no maxcsn, or -1 if + the rid was not found + """ +@@ -213,22 +213,22 @@ def get_ruv_state(opts): + :param opts - all the script options + :return - A text description of the replicaton state + """ +- mtime = get_ruv_time(opts['master_ruv'], opts['rid']) ++ mtime = get_ruv_time(opts['supplier_ruv'], opts['rid']) + rtime = get_ruv_time(opts['replica_ruv'], opts['rid']) + if mtime == -1: +- repl_state = "Replication State: Replica ID ({}) not found in Master's RUV".format(opts['rid']) ++ repl_state = "Replication State: Replica ID ({}) not found in Supplier's RUV".format(opts['rid']) + elif rtime == -1: + repl_state = "Replication State: Replica ID ({}) not found in Replica's RUV (not initialized?)".format(opts['rid']) + elif mtime == 0: +- repl_state = "Replication State: Master has not seen any updates" ++ repl_state = "Replication State: Supplier has not seen any updates" + elif rtime == 0: +- repl_state = "Replication State: Replica has not seen any changes from the Master" ++ repl_state = "Replication State: Replica has not seen any changes from the Supplier" + elif mtime > rtime: +- repl_state = "Replication State: Replica is behind Master by: {} seconds".format(mtime - rtime) ++ repl_state = "Replication State: Replica is behind Supplier by: {} seconds".format(mtime - rtime) + elif mtime < rtime: +- repl_state = "Replication State: Replica is ahead of Master by: {} seconds".format(rtime - mtime) ++ repl_state = "Replication State: Replica is ahead of Supplier by: {} seconds".format(rtime - mtime) + else: +- repl_state = "Replication State: Master and Replica are in perfect synchronization" ++ repl_state = "Replication State: Supplier and Replica are in perfect synchronization" + + return repl_state + +@@ -238,11 +238,11 @@ def get_ruv_report(opts): + :param opts - all the script options + :return - A text blob to display in the report + """ +- opts['master_ruv'].sort() ++ opts['supplier_ruv'].sort() + opts['replica_ruv'].sort() + +- report = "Master RUV:\n" +- for element in opts['master_ruv']: ++ report = "Supplier RUV:\n" ++ for element in opts['supplier_ruv']: + report += " %s\n" % (element) + report += "\nReplica RUV:\n" + for element in opts['replica_ruv']: +@@ -521,7 +521,7 @@ def get_ldif_ruv(LDIF, opts): + + def cmp_entry(mentry, rentry, opts): + """Compare the two entries, and return a "diff map" +- :param mentry - A Master entry ++ :param mentry - A Supplier entry + :param rentry - A Replica entry + :param opts - A Dict of the scripts options + :return - A Dict of the differences in the entry, or None +@@ -536,7 +536,7 @@ def cmp_entry(mentry, rentry, opts): + mlist = list(mentry.data.keys()) + + # +- # Check master ++ # Check Supplier + # + for mattr in mlist: + if mattr in opts['ignore']: +@@ -555,7 +555,7 @@ def cmp_entry(mentry, rentry, opts): + if not found: + diff['missing'].append("") + found = True +- diff['missing'].append(" - Master's State Info: %s" % (val)) ++ diff['missing'].append(" - Supplier's State Info: %s" % (val)) + diff['missing'].append(" - Date: %s\n" % (time.ctime(extract_time(val)))) + else: + # No state info, just move on +@@ -566,18 +566,18 @@ def cmp_entry(mentry, rentry, opts): + if report_conflict(rentry, mattr, opts) and report_conflict(mentry, mattr, opts): + diff['diff'].append(" - Attribute '%s' is different:" % mattr) + if 'nscpentrywsi' in mentry.data: +- # Process Master ++ # Process Supplier + found = False + for val in mentry.data['nscpentrywsi']: + if val.lower().startswith(mattr + ';'): + if not found: +- diff['diff'].append(" Master:") ++ diff['diff'].append(" Supplier:") + diff['diff'].append(" - Value: %s" % (val.split(':')[1].lstrip())) + diff['diff'].append(" - State Info: %s" % (val)) + diff['diff'].append(" - Date: %s\n" % (time.ctime(extract_time(val)))) + found = True + if not found: +- diff['diff'].append(" Master: ") ++ diff['diff'].append(" Supplier: ") + for val in mentry.data[mattr]: + # This is an "origin" value which means it's never been + # updated since replication was set up. So its the +@@ -605,7 +605,7 @@ def cmp_entry(mentry, rentry, opts): + diff['diff'].append("") + else: + # no state info, report what we got +- diff['diff'].append(" Master: ") ++ diff['diff'].append(" Supplier: ") + for val in mentry.data[mattr]: + diff['diff'].append(" - %s: %s" % (mattr, val)) + diff['diff'].append(" Replica: ") +@@ -622,9 +622,9 @@ def cmp_entry(mentry, rentry, opts): + continue + + if rattr not in mlist: +- # Master is missing the attribute ++ # Supplier is missing the attribute + if report_conflict(rentry, rattr, opts): +- diff['missing'].append(" - Master missing attribute: \"%s\"" % (rattr)) ++ diff['missing'].append(" - Supplier missing attribute: \"%s\"" % (rattr)) + diff_count += 1 + if 'nscpentrywsi' in rentry.data: + found = False +@@ -663,7 +663,7 @@ def do_offline_report(opts, output_file=None): + try: + MLDIF = open(opts['mldif'], "r") + except Exception as e: +- print('Failed to open Master LDIF: ' + str(e)) ++ print('Failed to open Supplier LDIF: ' + str(e)) + return + + try: +@@ -676,10 +676,10 @@ def do_offline_report(opts, output_file=None): + # Verify LDIF Files + try: + if opts['verbose']: +- print("Validating Master ldif file ({})...".format(opts['mldif'])) ++ print("Validating Supplier ldif file ({})...".format(opts['mldif'])) + LDIFRecordList(MLDIF).parse() + except ValueError: +- print('Master LDIF file in invalid, aborting...') ++ print('Supplier LDIF file in invalid, aborting...') + MLDIF.close() + RLDIF.close() + return +@@ -696,34 +696,34 @@ def do_offline_report(opts, output_file=None): + # Get all the dn's, and entry counts + if opts['verbose']: + print ("Gathering all the DN's...") +- master_dns = get_dns(MLDIF, opts['mldif'], opts) ++ supplier_dns = get_dns(MLDIF, opts['mldif'], opts) + replica_dns = get_dns(RLDIF, opts['rldif'], opts) +- if master_dns is None or replica_dns is None: ++ if supplier_dns is None or replica_dns is None: + print("Aborting scan...") + MLDIF.close() + RLDIF.close() + sys.exit(1) +- m_count = len(master_dns) ++ m_count = len(supplier_dns) + r_count = len(replica_dns) + + # Get DB RUV + if opts['verbose']: + print ("Gathering the database RUV's...") +- opts['master_ruv'] = get_ldif_ruv(MLDIF, opts) ++ opts['supplier_ruv'] = get_ldif_ruv(MLDIF, opts) + opts['replica_ruv'] = get_ldif_ruv(RLDIF, opts) + +- """ Compare the master entries with the replica's. Take our list of dn's from +- the master ldif and get that entry( dn) from the master and replica ldif. In ++ """ Compare the Supplier entries with the replica's. Take our list of dn's from ++ the Supplier ldif and get that entry( dn) from the Supplier and replica ldif. In + this phase we keep keep track of conflict/tombstone counts, and we check for + missing entries and entry differences. We only need to do the entry diff + checking in this phase - we do not need to do it when process the replica dn's + because if the entry exists in both LDIF's then we already checked or diffs +- while processing the master dn's. ++ while processing the Supplier dn's. + """ + if opts['verbose']: +- print ("Comparing Master to Replica...") ++ print ("Comparing Supplier to Replica...") + missing = False +- for dn in master_dns: ++ for dn in supplier_dns: + mresult = ldif_search(MLDIF, dn) + if mresult['entry'] is None and mresult['conflict'] is None and not mresult['tombstone']: + # Try from the beginning +@@ -736,7 +736,7 @@ def do_offline_report(opts, output_file=None): + rresult['conflict'] is not None or rresult['tombstone']): + """ We can safely remove this DN from the replica dn list as it + does not need to be checked again. This also speeds things up +- when doing the replica vs master phase. ++ when doing the replica vs Supplier phase. + """ + replica_dns.remove(dn) + +@@ -766,7 +766,7 @@ def do_offline_report(opts, output_file=None): + missing_report += (' Entries missing on Replica:\n') + missing = True + if mresult['entry'] and 'createtimestamp' in mresult['entry'].data: +- missing_report += (' - %s (Created on Master at: %s)\n' % ++ missing_report += (' - %s (Created on Supplier at: %s)\n' % + (dn, convert_timestamp(mresult['entry'].data['createtimestamp'][0]))) + else: + missing_report += (' - %s\n' % dn) +@@ -791,7 +791,7 @@ def do_offline_report(opts, output_file=None): + remaining conflict & tombstone entries as well. + """ + if opts['verbose']: +- print ("Comparing Replica to Master...") ++ print ("Comparing Replica to Supplier...") + MLDIF.seek(0) + RLDIF.seek(0) + missing = False +@@ -811,7 +811,7 @@ def do_offline_report(opts, output_file=None): + if mresult['entry'] is None and mresult['glue'] is None: + MLDIF.seek(rresult['idx']) # Set the LDIF cursor/index to the last good line + if not missing: +- missing_report += (' Entries missing on Master:\n') ++ missing_report += (' Entries missing on Supplier:\n') + missing = True + if rresult['entry'] and 'createtimestamp' in rresult['entry'].data: + missing_report += (' - %s (Created on Replica at: %s)\n' % +@@ -837,12 +837,12 @@ def do_offline_report(opts, output_file=None): + final_report += get_ruv_report(opts) + final_report += ('Entry Counts\n') + final_report += ('=====================================================\n\n') +- final_report += ('Master: %d\n' % (m_count)) ++ final_report += ('Supplier: %d\n' % (m_count)) + final_report += ('Replica: %d\n\n' % (r_count)) + + final_report += ('\nTombstones\n') + final_report += ('=====================================================\n\n') +- final_report += ('Master: %d\n' % (mtombstones)) ++ final_report += ('Supplier: %d\n' % (mtombstones)) + final_report += ('Replica: %d\n' % (rtombstones)) + + final_report += get_conflict_report(mconflicts, rconflicts, opts['conflicts']) +@@ -859,9 +859,9 @@ def do_offline_report(opts, output_file=None): + final_report += ('\nResult\n') + final_report += ('=====================================================\n\n') + if missing_report == "" and len(diff_report) == 0: +- final_report += ('No replication differences between Master and Replica\n') ++ final_report += ('No replication differences between Supplier and Replica\n') + else: +- final_report += ('There are replication differences between Master and Replica\n') ++ final_report += ('There are replication differences between Supplier and Replica\n') + + if output_file: + output_file.write(final_report) +@@ -871,8 +871,8 @@ def do_offline_report(opts, output_file=None): + + def check_for_diffs(mentries, mglue, rentries, rglue, report, opts): + """Online mode only - Check for diffs, return the updated report +- :param mentries - Master entries +- :param mglue - Master glue entries ++ :param mentries - Supplier entries ++ :param mglue - Supplier glue entries + :param rentries - Replica entries + :param rglue - Replica glue entries + :param report - A Dict of the entire report +@@ -947,8 +947,8 @@ def validate_suffix(ldapnode, suffix, hostname): + # Check suffix is replicated + try: + replica_filter = "(&(objectclass=nsds5replica)(nsDS5ReplicaRoot=%s))" % suffix +- master_replica = ldapnode.search_s("cn=config",ldap.SCOPE_SUBTREE,replica_filter) +- if (len(master_replica) != 1): ++ supplier_replica = ldapnode.search_s("cn=config",ldap.SCOPE_SUBTREE,replica_filter) ++ if (len(supplier_replica) != 1): + print("Error: Failed to validate suffix in {}. {} is not replicated.".format(hostname, suffix)) + return False + except ldap.LDAPError as e: +@@ -969,7 +969,7 @@ def connect_to_replicas(opts): + muri = "%s://%s" % (opts['mprotocol'], opts['mhost'].replace("/", "%2f")) + else: + muri = "%s://%s:%s/" % (opts['mprotocol'], opts['mhost'], opts['mport']) +- master = SimpleLDAPObject(muri) ++ supplier = SimpleLDAPObject(muri) + + if opts['rprotocol'].lower() == 'ldapi': + ruri = "%s://%s" % (opts['rprotocol'], opts['rhost'].replace("/", "%2f")) +@@ -978,23 +978,23 @@ def connect_to_replicas(opts): + replica = SimpleLDAPObject(ruri) + + # Set timeouts +- master.set_option(ldap.OPT_NETWORK_TIMEOUT, opts['timeout']) +- master.set_option(ldap.OPT_TIMEOUT, opts['timeout']) ++ supplier.set_option(ldap.OPT_NETWORK_TIMEOUT, opts['timeout']) ++ supplier.set_option(ldap.OPT_TIMEOUT, opts['timeout']) + replica.set_option(ldap.OPT_NETWORK_TIMEOUT, opts['timeout']) + replica.set_option(ldap.OPT_TIMEOUT, opts['timeout']) + + # Setup Secure Connection + if opts['certdir'] is not None: +- # Setup Master ++ # Setup Supplier + if opts['mprotocol'] != LDAPI: +- master.set_option(ldap.OPT_X_TLS_CACERTDIR, opts['certdir']) +- master.set_option(ldap.OPT_X_TLS_REQUIRE_CERT, ldap.OPT_X_TLS_HARD) ++ supplier.set_option(ldap.OPT_X_TLS_CACERTDIR, opts['certdir']) ++ supplier.set_option(ldap.OPT_X_TLS_REQUIRE_CERT, ldap.OPT_X_TLS_HARD) + if opts['mprotocol'] == LDAP: + # Do StartTLS + try: +- master.start_tls_s() ++ supplier.start_tls_s() + except ldap.LDAPError as e: +- print('TLS negotiation failed on Master: {}'.format(str(e))) ++ print('TLS negotiation failed on Supplier: {}'.format(str(e))) + exit(1) + + # Setup Replica +@@ -1006,17 +1006,17 @@ def connect_to_replicas(opts): + try: + replica.start_tls_s() + except ldap.LDAPError as e: +- print('TLS negotiation failed on Master: {}'.format(str(e))) ++ print('TLS negotiation failed on Supplier: {}'.format(str(e))) + exit(1) + +- # Open connection to master ++ # Open connection to Supplier + try: +- master.simple_bind_s(opts['binddn'], opts['bindpw']) ++ supplier.simple_bind_s(opts['binddn'], opts['bindpw']) + except ldap.SERVER_DOWN as e: + print(f"Cannot connect to {muri} ({str(e)})") + sys.exit(1) + except ldap.LDAPError as e: +- print("Error: Failed to authenticate to Master: ({}). " ++ print("Error: Failed to authenticate to Supplier: ({}). " + "Please check your credentials and LDAP urls are correct.".format(str(e))) + sys.exit(1) + +@@ -1034,7 +1034,7 @@ def connect_to_replicas(opts): + # Validate suffix + if opts['verbose']: + print ("Validating suffix ...") +- if not validate_suffix(master, opts['suffix'], opts['mhost']): ++ if not validate_suffix(supplier, opts['suffix'], opts['mhost']): + sys.exit(1) + + if not validate_suffix(replica,opts['suffix'], opts['rhost']): +@@ -1042,16 +1042,16 @@ def connect_to_replicas(opts): + + # Get the RUVs + if opts['verbose']: +- print ("Gathering Master's RUV...") ++ print ("Gathering Supplier's RUV...") + try: +- master_ruv = master.search_s(opts['suffix'], ldap.SCOPE_SUBTREE, RUV_FILTER, ['nsds50ruv']) +- if len(master_ruv) > 0: +- opts['master_ruv'] = ensure_list_str(master_ruv[0][1]['nsds50ruv']) ++ supplier_ruv = supplier.search_s(opts['suffix'], ldap.SCOPE_SUBTREE, RUV_FILTER, ['nsds50ruv']) ++ if len(supplier_ruv) > 0: ++ opts['supplier_ruv'] = ensure_list_str(supplier_ruv[0][1]['nsds50ruv']) + else: +- print("Error: Master does not have an RUV entry") ++ print("Error: Supplier does not have an RUV entry") + sys.exit(1) + except ldap.LDAPError as e: +- print("Error: Failed to get Master RUV entry: {}".format(str(e))) ++ print("Error: Failed to get Supplier RUV entry: {}".format(str(e))) + sys.exit(1) + + if opts['verbose']: +@@ -1067,12 +1067,12 @@ def connect_to_replicas(opts): + print("Error: Failed to get Replica RUV entry: {}".format(str(e))) + sys.exit(1) + +- # Get the master RID ++ # Get the Supplier RID + if opts['verbose']: +- print("Getting Master's replica ID") ++ print("Getting Supplier's replica ID") + try: + search_filter = "(&(objectclass=nsds5Replica)(nsDS5ReplicaRoot={})(nsDS5ReplicaId=*))".format(opts['suffix']) +- replica_entry = master.search_s("cn=config", ldap.SCOPE_SUBTREE, search_filter) ++ replica_entry = supplier.search_s("cn=config", ldap.SCOPE_SUBTREE, search_filter) + if len(replica_entry) > 0: + opts['rid'] = ensure_int(replica_entry[0][1]['nsDS5ReplicaId'][0]) + else: +@@ -1081,7 +1081,7 @@ def connect_to_replicas(opts): + print("Error: Failed to get Replica entry: {}".format(str(e))) + sys.exit(1) + +- return (master, replica, opts) ++ return (supplier, replica, opts) + + + def print_online_report(report, opts, output_file): +@@ -1104,11 +1104,11 @@ def print_online_report(report, opts, output_file): + final_report += get_ruv_report(opts) + final_report += ('Entry Counts\n') + final_report += ('=====================================================\n\n') +- final_report += ('Master: %d\n' % (report['m_count'])) ++ final_report += ('Supplier: %d\n' % (report['m_count'])) + final_report += ('Replica: %d\n\n' % (report['r_count'])) + final_report += ('\nTombstones\n') + final_report += ('=====================================================\n\n') +- final_report += ('Master: %d\n' % (report['mtombstones'])) ++ final_report += ('Supplier: %d\n' % (report['mtombstones'])) + final_report += ('Replica: %d\n' % (report['rtombstones'])) + final_report += report['conflict'] + missing = False +@@ -1121,7 +1121,7 @@ def print_online_report(report, opts, output_file): + final_report += (' Entries missing on Replica:\n') + for entry in report['r_missing']: + if 'createtimestamp' in entry.data: +- final_report += (' - %s (Created on Master at: %s)\n' % ++ final_report += (' - %s (Created on Supplier at: %s)\n' % + (entry.dn, convert_timestamp(entry.data['createtimestamp'][0]))) + else: + final_report += (' - %s\n' % (entry.dn)) +@@ -1129,7 +1129,7 @@ def print_online_report(report, opts, output_file): + if m_missing > 0: + if r_missing > 0: + final_report += ('\n') +- final_report += (' Entries missing on Master:\n') ++ final_report += (' Entries missing on Supplier:\n') + for entry in report['m_missing']: + if 'createtimestamp' in entry.data: + final_report += (' - %s (Created on Replica at: %s)\n' % +@@ -1146,9 +1146,9 @@ def print_online_report(report, opts, output_file): + final_report += ('\nResult\n') + final_report += ('=====================================================\n\n') + if not missing and len(report['diff']) == 0: +- final_report += ('No replication differences between Master and Replica\n') ++ final_report += ('No replication differences between Supplier and Replica\n') + else: +- final_report += ('There are replication differences between Master and Replica\n') ++ final_report += ('There are replication differences between Supplier and Replica\n') + + if output_file: + output_file.write(final_report) +@@ -1170,7 +1170,7 @@ def remove_state_info(entry): + + def get_conflict_report(mentries, rentries, verbose): + """Gather the conflict entry dn's for each replica +- :param mentries - Master entries ++ :param mentries - Supplier entries + :param rentries - Replica entries + :param verbose - verbose logging + :return - A text blob to dispaly in the report +@@ -1197,7 +1197,7 @@ def get_conflict_report(mentries, rentries, verbose): + report = "\n\nConflict Entries\n" + report += "=====================================================\n\n" + if len(m_conflicts) > 0: +- report += ('Master Conflict Entries: %d\n' % (len(m_conflicts))) ++ report += ('Supplier Conflict Entries: %d\n' % (len(m_conflicts))) + if verbose: + for entry in m_conflicts: + report += ('\n - %s\n' % (entry['dn'])) +@@ -1239,8 +1239,8 @@ def do_online_report(opts, output_file=None): + rconflicts = [] + mconflicts = [] + +- # Fire off paged searches on Master and Replica +- master, replica, opts = connect_to_replicas(opts) ++ # Fire off paged searches on Supplier and Replica ++ supplier, replica, opts = connect_to_replicas(opts) + + if opts['verbose']: + print('Start searching and comparing...') +@@ -1248,12 +1248,12 @@ def do_online_report(opts, output_file=None): + controls = [paged_ctrl] + req_pr_ctrl = controls[0] + try: +- master_msgid = master.search_ext(opts['suffix'], ldap.SCOPE_SUBTREE, +- "(|(objectclass=*)(objectclass=ldapsubentry)(objectclass=nstombstone))", +- ['*', 'createtimestamp', 'nscpentrywsi', 'nsds5replconflict'], +- serverctrls=controls) ++ supplier_msgid = supplier.search_ext(opts['suffix'], ldap.SCOPE_SUBTREE, ++ "(|(objectclass=*)(objectclass=ldapsubentry)(objectclass=nstombstone))", ++ ['*', 'createtimestamp', 'nscpentrywsi', 'nsds5replconflict'], ++ serverctrls=controls) + except ldap.LDAPError as e: +- print("Error: Failed to get Master entries: %s", str(e)) ++ print("Error: Failed to get Supplier entries: %s", str(e)) + sys.exit(1) + try: + replica_msgid = replica.search_ext(opts['suffix'], ldap.SCOPE_SUBTREE, +@@ -1268,11 +1268,11 @@ def do_online_report(opts, output_file=None): + while not m_done or not r_done: + try: + if not m_done: +- m_rtype, m_rdata, m_rmsgid, m_rctrls = master.result3(master_msgid) ++ m_rtype, m_rdata, m_rmsgid, m_rctrls = supplier.result3(supplier_msgid) + elif not r_done: + m_rdata = [] + except ldap.LDAPError as e: +- print("Error: Problem getting the results from the master: %s", str(e)) ++ print("Error: Problem getting the results from the Supplier: %s", str(e)) + sys.exit(1) + try: + if not r_done: +@@ -1299,7 +1299,7 @@ def do_online_report(opts, output_file=None): + report, opts) + + if not m_done: +- # Master ++ # Supplier + m_pctrls = [ + c + for c in m_rctrls +@@ -1310,11 +1310,11 @@ def do_online_report(opts, output_file=None): + try: + # Copy cookie from response control to request control + req_pr_ctrl.cookie = m_pctrls[0].cookie +- master_msgid = master.search_ext(opts['suffix'], ldap.SCOPE_SUBTREE, ++ supplier_msgid = supplier.search_ext(opts['suffix'], ldap.SCOPE_SUBTREE, + "(|(objectclass=*)(objectclass=ldapsubentry))", + ['*', 'createtimestamp', 'nscpentrywsi', 'conflictcsn', 'nsds5replconflict'], serverctrls=controls) + except ldap.LDAPError as e: +- print("Error: Problem searching the master: %s", str(e)) ++ print("Error: Problem searching the Supplier: %s", str(e)) + sys.exit(1) + else: + m_done = True # No more pages available +@@ -1354,7 +1354,7 @@ def do_online_report(opts, output_file=None): + print_online_report(report, opts, output_file) + + # unbind +- master.unbind_s() ++ supplier.unbind_s() + replica.unbind_s() + + +@@ -1367,18 +1367,18 @@ def init_online_params(args): + + # Make sure the URLs are different + if args.murl == args.rurl: +- print("Master and Replica LDAP URLs are the same, they must be different") ++ print("Supplier and Replica LDAP URLs are the same, they must be different") + sys.exit(1) + +- # Parse Master url ++ # Parse Supplier url + if not ldapurl.isLDAPUrl(args.murl): +- print("Master LDAP URL is invalid") ++ print("Supplier LDAP URL is invalid") + sys.exit(1) + murl = ldapurl.LDAPUrl(args.murl) + if murl.urlscheme in VALID_PROTOCOLS: + opts['mprotocol'] = murl.urlscheme + else: +- print('Unsupported ldap url protocol (%s) for Master, please use "ldaps" or "ldap"' % ++ print('Unsupported ldap url protocol (%s) for Supplier, please use "ldaps" or "ldap"' % + murl.urlscheme) + sys.exit(1) + +@@ -1520,7 +1520,7 @@ def offline_report(args): + print ("LDIF file ({}) is empty".format(ldif_dir)) + sys.exit(1) + if opts['mldif'] == opts['rldif']: +- print("The Master and Replica LDIF files must be different") ++ print("The Supplier and Replica LDIF files must be different") + sys.exit(1) + + OUTPUT_FILE = None +@@ -1547,7 +1547,7 @@ def get_state(args): + """Just do the RUV comparision + """ + opts = init_online_params(args) +- master, replica, opts = connect_to_replicas(opts) ++ supplier, replica, opts = connect_to_replicas(opts) + print(get_ruv_state(opts)) + + +@@ -1569,10 +1569,10 @@ def main(): + # Get state + state_parser = subparsers.add_parser('state', help="Get the current replicaton state between two replicas") + state_parser.set_defaults(func=get_state) +- state_parser.add_argument('-m', '--master-url', help='The LDAP URL for the Master server', +- dest='murl', default=None, required=True) ++ state_parser.add_argument('-m', '--supplier-url', help='The LDAP URL for the Supplier server', ++ dest='murl', default=None, required=True) + state_parser.add_argument('-r', '--replica-url', help='The LDAP URL for the Replica server', +- dest='rurl', required=True, default=None) ++ dest='rurl', required=True, default=None) + state_parser.add_argument('-b', '--suffix', help='Replicated suffix', dest='suffix', required=True) + state_parser.add_argument('-D', '--bind-dn', help='The Bind DN', required=True, dest='binddn', default=None) + state_parser.add_argument('-w', '--bind-pw', help='The Bind password', dest='bindpw', default=None) +@@ -1586,7 +1586,7 @@ def main(): + # Online mode + online_parser = subparsers.add_parser('online', help="Compare two online replicas for differences") + online_parser.set_defaults(func=online_report) +- online_parser.add_argument('-m', '--master-url', help='The LDAP URL for the Master server (REQUIRED)', ++ online_parser.add_argument('-m', '--supplier-url', help='The LDAP URL for the Supplier server (REQUIRED)', + dest='murl', default=None, required=True) + online_parser.add_argument('-r', '--replica-url', help='The LDAP URL for the Replica server (REQUIRED)', + dest='rurl', required=True, default=None) +@@ -1612,12 +1612,12 @@ def main(): + # Offline LDIF mode + offline_parser = subparsers.add_parser('offline', help="Compare two replication LDIF files for differences (LDIF file generated by 'db2ldif -r')") + offline_parser.set_defaults(func=offline_report) +- offline_parser.add_argument('-m', '--master-ldif', help='Master LDIF file', ++ offline_parser.add_argument('-m', '--supplier-ldif', help='Supplier LDIF file', + dest='mldif', default=None, required=True) + offline_parser.add_argument('-r', '--replica-ldif', help='Replica LDIF file', + dest='rldif', default=None, required=True) + offline_parser.add_argument('--rid', dest='rid', default=None, required=True, +- help='The Replica Identifer (rid) for the "Master" server') ++ help='The Replica Identifier (rid) for the "Supplier" server') + offline_parser.add_argument('-b', '--suffix', help='Replicated suffix', dest='suffix', required=True) + offline_parser.add_argument('-c', '--conflicts', help='Display verbose conflict information', action='store_true', + dest='conflicts', default=False) +-- +2.31.1 + diff --git a/SOURCES/0023-Issue-4443-Internal-unindexed-searches-in-syncrepl-r.patch b/SOURCES/0023-Issue-4443-Internal-unindexed-searches-in-syncrepl-r.patch new file mode 100644 index 0000000..3fd6f16 --- /dev/null +++ b/SOURCES/0023-Issue-4443-Internal-unindexed-searches-in-syncrepl-r.patch @@ -0,0 +1,373 @@ +From 55a47c1bfe1ce1c27e470384c4f1d50895db25f7 Mon Sep 17 00:00:00 2001 +From: Mark Reynolds +Date: Tue, 13 Jul 2021 14:18:03 -0400 +Subject: [PATCH] Issue 4443 - Internal unindexed searches in syncrepl/retro + changelog + +Bug Description: + +When a non-system index is added to a backend it is +disabled until the database is initialized or reindexed. +So in the case of the retro changelog the changenumber index +is alway disabled by default since it is never initialized. +This leads to unexpected unindexed searches of the retro +changelog. + +Fix Description: + +If an index has "nsSystemIndex" set to "true" then enable it +immediately. + +relates: https://github.com/389ds/389-ds-base/issues/4443 + +Reviewed by: spichugi & tbordaz(Thanks!!) +--- + .../tests/suites/retrocl/basic_test.py | 53 ++++++++------- + .../suites/retrocl/retrocl_indexing_test.py | 68 +++++++++++++++++++ + ldap/servers/plugins/retrocl/retrocl_create.c | 2 +- + .../slapd/back-ldbm/ldbm_index_config.c | 25 +++++-- + src/lib389/lib389/_mapped_object.py | 13 ++++ + 5 files changed, 130 insertions(+), 31 deletions(-) + create mode 100644 dirsrvtests/tests/suites/retrocl/retrocl_indexing_test.py + +diff --git a/dirsrvtests/tests/suites/retrocl/basic_test.py b/dirsrvtests/tests/suites/retrocl/basic_test.py +index f3bc50f29..84d513829 100644 +--- a/dirsrvtests/tests/suites/retrocl/basic_test.py ++++ b/dirsrvtests/tests/suites/retrocl/basic_test.py +@@ -8,7 +8,6 @@ + + import logging + import ldap +-import time + import pytest + from lib389.topologies import topology_st + from lib389.plugins import RetroChangelogPlugin +@@ -18,7 +17,8 @@ from lib389.tasks import * + from lib389.cli_base import FakeArgs, connect_instance, disconnect_instance + from lib389.cli_base.dsrc import dsrc_arg_concat + from lib389.cli_conf.plugins.retrochangelog import retrochangelog_add_attr +-from lib389.idm.user import UserAccount, UserAccounts, nsUserAccounts ++from lib389.idm.user import UserAccount, UserAccounts ++from lib389._mapped_object import DSLdapObjects + + pytestmark = pytest.mark.tier1 + +@@ -82,7 +82,7 @@ def test_retrocl_exclude_attr_add(topology_st): + + log.info('Adding user1') + try: +- user1 = users.create(properties={ ++ users.create(properties={ + 'sn': '1', + 'cn': 'user 1', + 'uid': 'user1', +@@ -97,17 +97,18 @@ def test_retrocl_exclude_attr_add(topology_st): + except ldap.ALREADY_EXISTS: + pass + except ldap.LDAPError as e: +- log.error("Failed to add user1") ++ log.error("Failed to add user1: " + str(e)) + + log.info('Verify homePhone and carLicense attrs are in the changelog changestring') + try: +- cllist = st.search_s(RETROCL_SUFFIX, ldap.SCOPE_SUBTREE, '(targetDn=%s)' % USER1_DN) ++ retro_changelog_suffix = DSLdapObjects(st, basedn=RETROCL_SUFFIX) ++ cllist = retro_changelog_suffix.filter(f'(targetDn={USER1_DN})') + except ldap.LDAPError as e: +- log.fatal("Changelog search failed, error: " +str(e)) ++ log.fatal("Changelog search failed, error: " + str(e)) + assert False + assert len(cllist) > 0 +- if cllist[0].hasAttr('changes'): +- clstr = (cllist[0].getValue('changes')).decode() ++ if cllist[0].present('changes'): ++ clstr = str(cllist[0].get_attr_vals_utf8('changes')) + assert ATTR_HOMEPHONE in clstr + assert ATTR_CARLICENSE in clstr + +@@ -134,7 +135,7 @@ def test_retrocl_exclude_attr_add(topology_st): + + log.info('Adding user2') + try: +- user2 = users.create(properties={ ++ users.create(properties={ + 'sn': '2', + 'cn': 'user 2', + 'uid': 'user2', +@@ -149,18 +150,18 @@ def test_retrocl_exclude_attr_add(topology_st): + except ldap.ALREADY_EXISTS: + pass + except ldap.LDAPError as e: +- log.error("Failed to add user2") ++ log.error("Failed to add user2: " + str(e)) + + log.info('Verify homePhone attr is not in the changelog changestring') + try: +- cllist = st.search_s(RETROCL_SUFFIX, ldap.SCOPE_SUBTREE, '(targetDn=%s)' % USER2_DN) ++ cllist = retro_changelog_suffix.filter(f'(targetDn={USER2_DN})') + assert len(cllist) > 0 +- if cllist[0].hasAttr('changes'): +- clstr = (cllist[0].getValue('changes')).decode() ++ if cllist[0].present('changes'): ++ clstr = str(cllist[0].get_attr_vals_utf8('changes')) + assert ATTR_HOMEPHONE not in clstr + assert ATTR_CARLICENSE in clstr + except ldap.LDAPError as e: +- log.fatal("Changelog search failed, error: " +str(e)) ++ log.fatal("Changelog search failed, error: " + str(e)) + assert False + + def test_retrocl_exclude_attr_mod(topology_st): +@@ -228,19 +229,20 @@ def test_retrocl_exclude_attr_mod(topology_st): + 'homeDirectory': '/home/user1', + 'userpassword': USER_PW}) + except ldap.ALREADY_EXISTS: +- pass ++ user1 = UserAccount(st, dn=USER1_DN) + except ldap.LDAPError as e: +- log.error("Failed to add user1") ++ log.error("Failed to add user1: " + str(e)) + + log.info('Verify homePhone and carLicense attrs are in the changelog changestring') + try: +- cllist = st.search_s(RETROCL_SUFFIX, ldap.SCOPE_SUBTREE, '(targetDn=%s)' % USER1_DN) ++ retro_changelog_suffix = DSLdapObjects(st, basedn=RETROCL_SUFFIX) ++ cllist = retro_changelog_suffix.filter(f'(targetDn={USER1_DN})') + except ldap.LDAPError as e: +- log.fatal("Changelog search failed, error: " +str(e)) ++ log.fatal("Changelog search failed, error: " + str(e)) + assert False + assert len(cllist) > 0 +- if cllist[0].hasAttr('changes'): +- clstr = (cllist[0].getValue('changes')).decode() ++ if cllist[0].present('changes'): ++ clstr = str(cllist[0].get_attr_vals_utf8('changes')) + assert ATTR_HOMEPHONE in clstr + assert ATTR_CARLICENSE in clstr + +@@ -267,24 +269,25 @@ def test_retrocl_exclude_attr_mod(topology_st): + + log.info('Modify user1 carLicense attribute') + try: +- st.modify_s(USER1_DN, [(ldap.MOD_REPLACE, ATTR_CARLICENSE, b"123WX321")]) ++ user1.replace(ATTR_CARLICENSE, "123WX321") + except ldap.LDAPError as e: + log.fatal('test_retrocl_exclude_attr_mod: Failed to update user1 attribute: error ' + e.message['desc']) + assert False + + log.info('Verify carLicense attr is not in the changelog changestring') + try: +- cllist = st.search_s(RETROCL_SUFFIX, ldap.SCOPE_SUBTREE, '(targetDn=%s)' % USER1_DN) ++ cllist = retro_changelog_suffix.filter(f'(targetDn={USER1_DN})') + assert len(cllist) > 0 + # There will be 2 entries in the changelog for this user, we are only + #interested in the second one, the modify operation. +- if cllist[1].hasAttr('changes'): +- clstr = (cllist[1].getValue('changes')).decode() ++ if cllist[1].present('changes'): ++ clstr = str(cllist[1].get_attr_vals_utf8('changes')) + assert ATTR_CARLICENSE not in clstr + except ldap.LDAPError as e: +- log.fatal("Changelog search failed, error: " +str(e)) ++ log.fatal("Changelog search failed, error: " + str(e)) + assert False + ++ + if __name__ == '__main__': + # Run isolated + # -s for DEBUG mode +diff --git a/dirsrvtests/tests/suites/retrocl/retrocl_indexing_test.py b/dirsrvtests/tests/suites/retrocl/retrocl_indexing_test.py +new file mode 100644 +index 000000000..b1dfe962c +--- /dev/null ++++ b/dirsrvtests/tests/suites/retrocl/retrocl_indexing_test.py +@@ -0,0 +1,68 @@ ++import logging ++import pytest ++import os ++from lib389._constants import RETROCL_SUFFIX, DEFAULT_SUFFIX ++from lib389.topologies import topology_st as topo ++from lib389.plugins import RetroChangelogPlugin ++from lib389.idm.user import UserAccounts ++from lib389._mapped_object import DSLdapObjects ++log = logging.getLogger(__name__) ++ ++ ++def test_indexing_is_online(topo): ++ """Test that the changenmumber index is online right after enabling the plugin ++ ++ :id: 16f4c001-9e0c-4448-a2b3-08ac1e85d40f ++ :setup: Standalone Instance ++ :steps: ++ 1. Enable retro cl ++ 2. Perform some updates ++ 3. Search for "(changenumber>=-1)", and it is not partially unindexed ++ 4. Search for "(&(changenumber>=-1)(targetuniqueid=*))", and it is not partially unindexed ++ :expectedresults: ++ 1. Success ++ 2. Success ++ 3. Success ++ 4. Success ++ """ ++ ++ # Enable plugin ++ topo.standalone.config.set('nsslapd-accesslog-logbuffering', 'off') ++ plugin = RetroChangelogPlugin(topo.standalone) ++ plugin.enable() ++ topo.standalone.restart() ++ ++ # Do a bunch of updates ++ users = UserAccounts(topo.standalone, DEFAULT_SUFFIX) ++ user_entry = users.create(properties={ ++ 'sn': '1', ++ 'cn': 'user 1', ++ 'uid': 'user1', ++ 'uidNumber': '11', ++ 'gidNumber': '111', ++ 'givenname': 'user1', ++ 'homePhone': '0861234567', ++ 'carLicense': '131D16674', ++ 'mail': 'user1@whereever.com', ++ 'homeDirectory': '/home' ++ }) ++ for count in range(0, 10): ++ user_entry.replace('mail', f'test{count}@test.com') ++ ++ # Search the retro cl, and check for error messages ++ filter_simple = '(changenumber>=-1)' ++ filter_compound = '(&(changenumber>=-1)(targetuniqueid=*))' ++ retro_changelog_suffix = DSLdapObjects(topo.standalone, basedn=RETROCL_SUFFIX) ++ retro_changelog_suffix.filter(filter_simple) ++ assert not topo.standalone.searchAccessLog('Partially Unindexed Filter') ++ ++ # Search the retro cl again with compound filter ++ retro_changelog_suffix.filter(filter_compound) ++ assert not topo.standalone.searchAccessLog('Partially Unindexed Filter') ++ ++ ++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/plugins/retrocl/retrocl_create.c b/ldap/servers/plugins/retrocl/retrocl_create.c +index 571e6899f..5bfde7831 100644 +--- a/ldap/servers/plugins/retrocl/retrocl_create.c ++++ b/ldap/servers/plugins/retrocl/retrocl_create.c +@@ -133,7 +133,7 @@ retrocl_create_be(const char *bedir) + val.bv_len = strlen(val.bv_val); + slapi_entry_add_values(e, "cn", vals); + +- val.bv_val = "false"; ++ val.bv_val = "true"; /* enables the index */ + val.bv_len = strlen(val.bv_val); + slapi_entry_add_values(e, "nssystemindex", vals); + +diff --git a/ldap/servers/slapd/back-ldbm/ldbm_index_config.c b/ldap/servers/slapd/back-ldbm/ldbm_index_config.c +index 9722d0ce7..38e7368e1 100644 +--- a/ldap/servers/slapd/back-ldbm/ldbm_index_config.c ++++ b/ldap/servers/slapd/back-ldbm/ldbm_index_config.c +@@ -25,7 +25,7 @@ int ldbm_instance_index_config_delete_callback(Slapi_PBlock *pb, Slapi_Entry *en + #define INDEXTYPE_NONE 1 + + static int +-ldbm_index_parse_entry(ldbm_instance *inst, Slapi_Entry *e, const char *trace_string, char **index_name, char *err_buf) ++ldbm_index_parse_entry(ldbm_instance *inst, Slapi_Entry *e, const char *trace_string, char **index_name, PRBool *is_system_index, char *err_buf) + { + Slapi_Attr *attr; + const struct berval *attrValue; +@@ -78,6 +78,15 @@ ldbm_index_parse_entry(ldbm_instance *inst, Slapi_Entry *e, const char *trace_st + } + } + ++ *is_system_index = PR_FALSE; ++ if (0 == slapi_entry_attr_find(e, "nsSystemIndex", &attr)) { ++ slapi_attr_first_value(attr, &sval); ++ attrValue = slapi_value_get_berval(sval); ++ if (strcasecmp(attrValue->bv_val, "true") == 0) { ++ *is_system_index = PR_TRUE; ++ } ++ } ++ + /* ok the entry is good to process, pass it to attr_index_config */ + if (attr_index_config(inst->inst_be, (char *)trace_string, 0, e, 0, 0, err_buf)) { + slapi_ch_free_string(index_name); +@@ -101,9 +110,10 @@ ldbm_index_init_entry_callback(Slapi_PBlock *pb __attribute__((unused)), + void *arg) + { + ldbm_instance *inst = (ldbm_instance *)arg; ++ PRBool is_system_index = PR_FALSE; + + returntext[0] = '\0'; +- *returncode = ldbm_index_parse_entry(inst, e, "from ldbm instance init", NULL, NULL); ++ *returncode = ldbm_index_parse_entry(inst, e, "from ldbm instance init", NULL, &is_system_index /* not used */, NULL); + if (*returncode == LDAP_SUCCESS) { + return SLAPI_DSE_CALLBACK_OK; + } else { +@@ -126,17 +136,21 @@ ldbm_instance_index_config_add_callback(Slapi_PBlock *pb __attribute__((unused)) + { + ldbm_instance *inst = (ldbm_instance *)arg; + char *index_name = NULL; ++ PRBool is_system_index = PR_FALSE; + + returntext[0] = '\0'; +- *returncode = ldbm_index_parse_entry(inst, e, "from DSE add", &index_name, returntext); ++ *returncode = ldbm_index_parse_entry(inst, e, "from DSE add", &index_name, &is_system_index, returntext); + if (*returncode == LDAP_SUCCESS) { + struct attrinfo *ai = NULL; + /* if the index is a "system" index, we assume it's being added by + * by the server, and it's okay for the index to go online immediately. + * if not, we set the index "offline" so it won't actually be used + * until someone runs db2index on it. ++ * If caller wants to add an index that they want to be online ++ * immediately they can also set "nsSystemIndex" to "true" in the ++ * index config entry (e.g. is_system_index). + */ +- if (!ldbm_attribute_always_indexed(index_name)) { ++ if (!is_system_index && !ldbm_attribute_always_indexed(index_name)) { + ainfo_get(inst->inst_be, index_name, &ai); + PR_ASSERT(ai != NULL); + ai->ai_indexmask |= INDEX_OFFLINE; +@@ -386,13 +400,14 @@ ldbm_instance_index_config_enable_index(ldbm_instance *inst, Slapi_Entry *e) + char *index_name = NULL; + int rc = LDAP_SUCCESS; + struct attrinfo *ai = NULL; ++ PRBool is_system_index = PR_FALSE; + + index_name = slapi_entry_attr_get_charptr(e, "cn"); + if (index_name) { + ainfo_get(inst->inst_be, index_name, &ai); + } + if (!ai) { +- rc = ldbm_index_parse_entry(inst, e, "from DSE add", &index_name, NULL); ++ rc = ldbm_index_parse_entry(inst, e, "from DSE add", &index_name, &is_system_index /* not used */, NULL); + } + if (rc == LDAP_SUCCESS) { + /* Assume the caller knows if it is OK to go online immediately */ +diff --git a/src/lib389/lib389/_mapped_object.py b/src/lib389/lib389/_mapped_object.py +index b6d778b01..fe610d175 100644 +--- a/src/lib389/lib389/_mapped_object.py ++++ b/src/lib389/lib389/_mapped_object.py +@@ -148,6 +148,19 @@ class DSLdapObject(DSLogging, DSLint): + + return True + ++ def search(self, scope="subtree", filter='objectclass=*'): ++ search_scope = ldap.SCOPE_SUBTREE ++ if scope == 'base': ++ search_scope = ldap.SCOPE_BASE ++ elif scope == 'one': ++ search_scope = ldap.SCOPE_ONE ++ elif scope == 'subtree': ++ search_scope = ldap.SCOPE_SUBTREE ++ return self._instance.search_ext_s(self._dn, search_scope, filter, ++ serverctrls=self._server_controls, ++ clientctrls=self._client_controls, ++ escapehatch='i am sure') ++ + def display(self, attrlist=['*']): + """Get an entry but represent it as a string LDIF + +-- +2.31.1 + diff --git a/SOURCES/0024-Issue-4817-BUG-locked-crypt-accounts-on-import-may-a.patch b/SOURCES/0024-Issue-4817-BUG-locked-crypt-accounts-on-import-may-a.patch new file mode 100644 index 0000000..32c0eb1 --- /dev/null +++ b/SOURCES/0024-Issue-4817-BUG-locked-crypt-accounts-on-import-may-a.patch @@ -0,0 +1,121 @@ +From 2f0218f91d35c83a2aaecb71849a54b2481390ab Mon Sep 17 00:00:00 2001 +From: Firstyear +Date: Fri, 9 Jul 2021 11:53:35 +1000 +Subject: [PATCH] Issue 4817 - BUG - locked crypt accounts on import may allow + all passwords (#4819) + +Bug Description: Due to mishanding of short dbpwd hashes, the +crypt_r algorithm was misused and was only comparing salts +in some cases, rather than checking the actual content +of the password. + +Fix Description: Stricter checks on dbpwd lengths to ensure +that content passed to crypt_r has at least 2 salt bytes and +1 hash byte, as well as stricter checks on ct_memcmp to ensure +that compared values are the same length, rather than potentially +allowing overruns/short comparisons. + +fixes: https://github.com/389ds/389-ds-base/issues/4817 + +Author: William Brown + +Review by: @mreynolds389 +--- + .../password/pwd_crypt_asterisk_test.py | 50 +++++++++++++++++++ + ldap/servers/plugins/pwdstorage/crypt_pwd.c | 20 +++++--- + 2 files changed, 64 insertions(+), 6 deletions(-) + create mode 100644 dirsrvtests/tests/suites/password/pwd_crypt_asterisk_test.py + +diff --git a/dirsrvtests/tests/suites/password/pwd_crypt_asterisk_test.py b/dirsrvtests/tests/suites/password/pwd_crypt_asterisk_test.py +new file mode 100644 +index 000000000..d76614db1 +--- /dev/null ++++ b/dirsrvtests/tests/suites/password/pwd_crypt_asterisk_test.py +@@ -0,0 +1,50 @@ ++# --- BEGIN COPYRIGHT BLOCK --- ++# Copyright (C) 2021 William Brown ++# All rights reserved. ++# ++# License: GPL (version 3 or any later version). ++# See LICENSE for details. ++# --- END COPYRIGHT BLOCK --- ++# ++import ldap ++import pytest ++from lib389.topologies import topology_st ++from lib389.idm.user import UserAccounts ++from lib389._constants import (DEFAULT_SUFFIX, PASSWORD) ++ ++pytestmark = pytest.mark.tier1 ++ ++def test_password_crypt_asterisk_is_rejected(topology_st): ++ """It was reported that {CRYPT}* was allowing all passwords to be ++ valid in the bind process. This checks that we should be rejecting ++ these as they should represent locked accounts. Similar, {CRYPT}! ++ ++ :id: 0b8f1a6a-f3eb-4443-985e-da14d0939dc3 ++ :setup: Single instance ++ :steps: 1. Set a password hash in with CRYPT and the content * ++ 2. Test a bind ++ 3. Set a password hash in with CRYPT and the content ! ++ 4. Test a bind ++ :expectedresults: ++ 1. Successfully set the values ++ 2. The bind fails ++ 3. Successfully set the values ++ 4. The bind fails ++ """ ++ topology_st.standalone.config.set('nsslapd-allow-hashed-passwords', 'on') ++ topology_st.standalone.config.set('nsslapd-enable-upgrade-hash', 'off') ++ ++ users = UserAccounts(topology_st.standalone, DEFAULT_SUFFIX) ++ user = users.create_test_user() ++ ++ user.set('userPassword', "{CRYPT}*") ++ ++ # Attempt to bind with incorrect password. ++ with pytest.raises(ldap.INVALID_CREDENTIALS): ++ badconn = user.bind('badpassword') ++ ++ user.set('userPassword', "{CRYPT}!") ++ # Attempt to bind with incorrect password. ++ with pytest.raises(ldap.INVALID_CREDENTIALS): ++ badconn = user.bind('badpassword') ++ +diff --git a/ldap/servers/plugins/pwdstorage/crypt_pwd.c b/ldap/servers/plugins/pwdstorage/crypt_pwd.c +index 9031b2199..1b37d41ed 100644 +--- a/ldap/servers/plugins/pwdstorage/crypt_pwd.c ++++ b/ldap/servers/plugins/pwdstorage/crypt_pwd.c +@@ -48,15 +48,23 @@ static unsigned char itoa64[] = /* 0 ... 63 => ascii - 64 */ + int + crypt_pw_cmp(const char *userpwd, const char *dbpwd) + { +- int rc; +- char *cp; ++ int rc = -1; ++ char *cp = NULL; ++ size_t dbpwd_len = strlen(dbpwd); + struct crypt_data data; + data.initialized = 0; + +- /* we use salt (first 2 chars) of encoded password in call to crypt_r() */ +- cp = crypt_r(userpwd, dbpwd, &data); +- if (cp) { +- rc = slapi_ct_memcmp(dbpwd, cp, strlen(dbpwd)); ++ /* ++ * there MUST be at least 2 chars of salt and some pw bytes, else this is INVALID and will ++ * allow any password to bind as we then only compare SALTS. ++ */ ++ if (dbpwd_len >= 3) { ++ /* we use salt (first 2 chars) of encoded password in call to crypt_r() */ ++ cp = crypt_r(userpwd, dbpwd, &data); ++ } ++ /* If these are not the same length, we can not proceed safely with memcmp. */ ++ if (cp && dbpwd_len == strlen(cp)) { ++ rc = slapi_ct_memcmp(dbpwd, cp, dbpwd_len); + } else { + rc = -1; + } +-- +2.31.1 + diff --git a/SOURCES/0025-Issue-4837-persistent-search-returns-entries-even-wh.patch b/SOURCES/0025-Issue-4837-persistent-search-returns-entries-even-wh.patch new file mode 100644 index 0000000..66643a1 --- /dev/null +++ b/SOURCES/0025-Issue-4837-persistent-search-returns-entries-even-wh.patch @@ -0,0 +1,39 @@ +From 31d53e7da585723e66b838dcf34b77ea7c9968c6 Mon Sep 17 00:00:00 2001 +From: tbordaz +Date: Wed, 21 Jul 2021 09:16:30 +0200 +Subject: [PATCH] Issue 4837 - persistent search returns entries even when an + error is returned by content-sync-plugin (#4838) + +Bug description: + When a ldap client sends a sync request control, the server response may contain a sync state control. + If the server fails to create the control the search should fail. + +Fix description: + In case the server fails to create the response control + logs the failure of the pre_search + +relates: https://github.com/389ds/389-ds-base/issues/4837 + +Reviewed by: Simon Pichugin + +Platforms tested: RH8.4 +--- + ldap/servers/plugins/sync/sync_refresh.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/ldap/servers/plugins/sync/sync_refresh.c b/ldap/servers/plugins/sync/sync_refresh.c +index 646ff760b..4cbb6a949 100644 +--- a/ldap/servers/plugins/sync/sync_refresh.c ++++ b/ldap/servers/plugins/sync/sync_refresh.c +@@ -213,7 +213,7 @@ sync_srch_refresh_pre_entry(Slapi_PBlock *pb) + Slapi_Entry *e; + slapi_pblock_get(pb, SLAPI_SEARCH_RESULT_ENTRY, &e); + LDAPControl **ctrl = (LDAPControl **)slapi_ch_calloc(2, sizeof(LDAPControl *)); +- sync_create_state_control(e, &ctrl[0], LDAP_SYNC_ADD, NULL); ++ rc = sync_create_state_control(e, &ctrl[0], LDAP_SYNC_ADD, NULL); + slapi_pblock_set(pb, SLAPI_SEARCH_CTRLS, ctrl); + } + return (rc); +-- +2.31.1 + diff --git a/SOURCES/0026-Hardcode-gost-crypt-passsword-storage-scheme.patch b/SOURCES/0026-Hardcode-gost-crypt-passsword-storage-scheme.patch new file mode 100644 index 0000000..aa701a0 --- /dev/null +++ b/SOURCES/0026-Hardcode-gost-crypt-passsword-storage-scheme.patch @@ -0,0 +1,49 @@ +From 616dc9964a4675dea2ab2c2efb9bd31c3903e29d Mon Sep 17 00:00:00 2001 +From: Mark Reynolds +Date: Mon, 26 Jul 2021 15:22:08 -0400 +Subject: [PATCH] Hardcode gost crypt passsword storage scheme + +--- + .../plugins/pwdstorage/gost_yescrypt.c | 22 ------------------- + 1 file changed, 22 deletions(-) + +diff --git a/ldap/servers/plugins/pwdstorage/gost_yescrypt.c b/ldap/servers/plugins/pwdstorage/gost_yescrypt.c +index 67b39395e..7b0d1653c 100644 +--- a/ldap/servers/plugins/pwdstorage/gost_yescrypt.c ++++ b/ldap/servers/plugins/pwdstorage/gost_yescrypt.c +@@ -11,7 +11,6 @@ + + #include + +-#ifdef XCRYPT_VERSION_STR + #include + int + gost_yescrypt_pw_cmp(const char *userpwd, const char *dbpwd) +@@ -64,24 +63,3 @@ gost_yescrypt_pw_enc(const char *pwd) + return enc; + } + +-#else +- +-/* +- * We do not have xcrypt, so always fail all checks. +- */ +-int +-gost_yescrypt_pw_cmp(const char *userpwd __attribute__((unused)), const char *dbpwd __attribute__((unused))) +-{ +- slapi_log_err(SLAPI_LOG_ERR, GOST_YESCRYPT_SCHEME_NAME, +- "Unable to use gost_yescrypt_pw_cmp, xcrypt is not available.\n"); +- return 1; +-} +- +-char * +-gost_yescrypt_pw_enc(const char *pwd __attribute__((unused))) +-{ +- slapi_log_err(SLAPI_LOG_ERR, GOST_YESCRYPT_SCHEME_NAME, +- "Unable to use gost_yescrypt_pw_enc, xcrypt is not available.\n"); +- return NULL; +-} +-#endif +-- +2.31.1 + diff --git a/SOURCES/Cargo.lock b/SOURCES/Cargo.lock new file mode 100644 index 0000000..1127ca0 --- /dev/null +++ b/SOURCES/Cargo.lock @@ -0,0 +1,565 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "ansi_term" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee49baf6cb617b853aa8d93bf420db2383fab46d314482ca2803b40d5fde979b" +dependencies = [ + "winapi", +] + +[[package]] +name = "atty" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" +dependencies = [ + "hermit-abi", + "libc", + "winapi", +] + +[[package]] +name = "autocfg" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a" + +[[package]] +name = "base64" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "904dfeac50f3cdaba28fc6f57fdcddb75f49ed61346676a78c4ffe55877802fd" + +[[package]] +name = "bitflags" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693" + +[[package]] +name = "byteorder" +version = "1.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" + +[[package]] +name = "cbindgen" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9daec6140ab4dcd38c3dd57e580b59a621172a526ac79f1527af760a55afeafd" +dependencies = [ + "clap", + "log", + "proc-macro2", + "quote", + "serde", + "serde_json", + "syn", + "tempfile", + "toml", +] + +[[package]] +name = "cc" +version = "1.0.68" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4a72c244c1ff497a746a7e1fb3d14bd08420ecda70c8f25c7112f2781652d787" +dependencies = [ + "jobserver", +] + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "clap" +version = "2.33.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "37e58ac78573c40708d45522f0d80fa2f01cc4f9b4e2bf749807255454312002" +dependencies = [ + "ansi_term", + "atty", + "bitflags", + "strsim", + "textwrap", + "unicode-width", + "vec_map", +] + +[[package]] +name = "entryuuid" +version = "0.1.0" +dependencies = [ + "cc", + "libc", + "paste", + "slapi_r_plugin", + "uuid", +] + +[[package]] +name = "entryuuid_syntax" +version = "0.1.0" +dependencies = [ + "cc", + "libc", + "paste", + "slapi_r_plugin", + "uuid", +] + +[[package]] +name = "fernet" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93804560e638370a8be6d59ce71ed803e55e230abdbf42598e666b41adda9b1f" +dependencies = [ + "base64", + "byteorder", + "getrandom", + "openssl", + "zeroize", +] + +[[package]] +name = "foreign-types" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1" +dependencies = [ + "foreign-types-shared", +] + +[[package]] +name = "foreign-types-shared" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" + +[[package]] +name = "getrandom" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7fcd999463524c52659517fe2cea98493cfe485d10565e7b0fb07dbba7ad2753" +dependencies = [ + "cfg-if", + "libc", + "wasi", +] + +[[package]] +name = "hermit-abi" +version = "0.1.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "322f4de77956e22ed0e5032c359a0f1273f1f7f0d79bfa3b8ffbc730d7fbcc5c" +dependencies = [ + "libc", +] + +[[package]] +name = "itoa" +version = "0.4.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd25036021b0de88a0aff6b850051563c6516d0bf53f8638938edbb9de732736" + +[[package]] +name = "jobserver" +version = "0.1.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "972f5ae5d1cb9c6ae417789196c803205313edde988685da5e3aae0827b9e7fd" +dependencies = [ + "libc", +] + +[[package]] +name = "lazy_static" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" + +[[package]] +name = "libc" +version = "0.2.95" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "789da6d93f1b866ffe175afc5322a4d76c038605a1c3319bb57b06967ca98a36" + +[[package]] +name = "librnsslapd" +version = "0.1.0" +dependencies = [ + "cbindgen", + "libc", + "slapd", +] + +[[package]] +name = "librslapd" +version = "0.1.0" +dependencies = [ + "cbindgen", + "libc", + "slapd", +] + +[[package]] +name = "log" +version = "0.4.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "51b9bbe6c47d51fc3e1a9b945965946b4c44142ab8792c50835a980d362c2710" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "once_cell" +version = "1.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af8b08b04175473088b46763e51ee54da5f9a164bc162f615b91bc179dbf15a3" + +[[package]] +name = "openssl" +version = "0.10.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d7830286ad6a3973c0f1d9b73738f69c76b739301d0229c4b96501695cbe4c8" +dependencies = [ + "bitflags", + "cfg-if", + "foreign-types", + "libc", + "once_cell", + "openssl-sys", +] + +[[package]] +name = "openssl-sys" +version = "0.9.63" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6b0d6fb7d80f877617dfcb014e605e2b5ab2fb0afdf27935219bb6bd984cb98" +dependencies = [ + "autocfg", + "cc", + "libc", + "pkg-config", + "vcpkg", +] + +[[package]] +name = "paste" +version = "0.1.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "45ca20c77d80be666aef2b45486da86238fabe33e38306bd3118fe4af33fa880" +dependencies = [ + "paste-impl", + "proc-macro-hack", +] + +[[package]] +name = "paste-impl" +version = "0.1.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d95a7db200b97ef370c8e6de0088252f7e0dfff7d047a28528e47456c0fc98b6" +dependencies = [ + "proc-macro-hack", +] + +[[package]] +name = "pkg-config" +version = "0.3.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3831453b3449ceb48b6d9c7ad7c96d5ea673e9b470a1dc578c2ce6521230884c" + +[[package]] +name = "ppv-lite86" +version = "0.2.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac74c624d6b2d21f425f752262f42188365d7b8ff1aff74c82e45136510a4857" + +[[package]] +name = "proc-macro-hack" +version = "0.5.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dbf0c48bc1d91375ae5c3cd81e3722dff1abcf81a30960240640d223f59fe0e5" + +[[package]] +name = "proc-macro2" +version = "1.0.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0d8caf72986c1a598726adc988bb5984792ef84f5ee5aa50209145ee8077038" +dependencies = [ + "unicode-xid", +] + +[[package]] +name = "quote" +version = "1.0.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3d0b9745dc2debf507c8422de05d7226cc1f0644216dfdfead988f9b1ab32a7" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "rand" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ef9e7e66b4468674bfcb0c81af8b7fa0bb154fa9f28eb840da5c447baeb8d7e" +dependencies = [ + "libc", + "rand_chacha", + "rand_core", + "rand_hc", +] + +[[package]] +name = "rand_chacha" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e12735cf05c9e10bf21534da50a147b924d555dc7a547c42e6bb2d5b6017ae0d" +dependencies = [ + "ppv-lite86", + "rand_core", +] + +[[package]] +name = "rand_core" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34cf66eb183df1c5876e2dcf6b13d57340741e8dc255b48e40a26de954d06ae7" +dependencies = [ + "getrandom", +] + +[[package]] +name = "rand_hc" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3190ef7066a446f2e7f42e239d161e905420ccab01eb967c9eb27d21b2322a73" +dependencies = [ + "rand_core", +] + +[[package]] +name = "redox_syscall" +version = "0.2.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "742739e41cd49414de871ea5e549afb7e2a3ac77b589bcbebe8c82fab37147fc" +dependencies = [ + "bitflags", +] + +[[package]] +name = "remove_dir_all" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3acd125665422973a33ac9d3dd2df85edad0f4ae9b00dafb1a05e43a9f5ef8e7" +dependencies = [ + "winapi", +] + +[[package]] +name = "rsds" +version = "0.1.0" + +[[package]] +name = "ryu" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "71d301d4193d031abdd79ff7e3dd721168a9572ef3fe51a1517aba235bd8f86e" + +[[package]] +name = "serde" +version = "1.0.126" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec7505abeacaec74ae4778d9d9328fe5a5d04253220a85c4ee022239fc996d03" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.126" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "963a7dbc9895aeac7ac90e74f34a5d5261828f79df35cbed41e10189d3804d43" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "serde_json" +version = "1.0.64" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "799e97dc9fdae36a5c8b8f2cae9ce2ee9fdce2058c57a93e6099d919fd982f79" +dependencies = [ + "itoa", + "ryu", + "serde", +] + +[[package]] +name = "slapd" +version = "0.1.0" +dependencies = [ + "fernet", +] + +[[package]] +name = "slapi_r_plugin" +version = "0.1.0" +dependencies = [ + "lazy_static", + "libc", + "paste", + "uuid", +] + +[[package]] +name = "strsim" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a" + +[[package]] +name = "syn" +version = "1.0.72" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1e8cdbefb79a9a5a65e0db8b47b723ee907b7c7f8496c76a1770b5c310bab82" +dependencies = [ + "proc-macro2", + "quote", + "unicode-xid", +] + +[[package]] +name = "synstructure" +version = "0.12.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b834f2d66f734cb897113e34aaff2f1ab4719ca946f9a7358dba8f8064148701" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "unicode-xid", +] + +[[package]] +name = "tempfile" +version = "3.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dac1c663cfc93810f88aed9b8941d48cabf856a1b111c29a40439018d870eb22" +dependencies = [ + "cfg-if", + "libc", + "rand", + "redox_syscall", + "remove_dir_all", + "winapi", +] + +[[package]] +name = "textwrap" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d326610f408c7a4eb6f51c37c330e496b08506c9457c9d34287ecc38809fb060" +dependencies = [ + "unicode-width", +] + +[[package]] +name = "toml" +version = "0.5.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a31142970826733df8241ef35dc040ef98c679ab14d7c3e54d827099b3acecaa" +dependencies = [ + "serde", +] + +[[package]] +name = "unicode-width" +version = "0.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9337591893a19b88d8d87f2cec1e73fad5cdfd10e5a6f349f498ad6ea2ffb1e3" + +[[package]] +name = "unicode-xid" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3" + +[[package]] +name = "uuid" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bc5cf98d8186244414c848017f0e2676b3fcb46807f6668a97dfe67359a3c4b7" +dependencies = [ + "getrandom", +] + +[[package]] +name = "vcpkg" +version = "0.2.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "025ce40a007e1907e58d5bc1a594def78e5573bb0b1160bc389634e8f12e4faa" + +[[package]] +name = "vec_map" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1bddf1187be692e79c5ffeab891132dfb0f236ed36a43c7ed39f1165ee20191" + +[[package]] +name = "wasi" +version = "0.10.2+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fd6fbd9a79829dd1ad0cc20627bf1ed606756a7f77edff7b66b7064f9cb327c6" + +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" + +[[package]] +name = "zeroize" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4756f7db3f7b5574938c3eb1c117038b8e07f95ee6718c0efad4ac21508f1efd" +dependencies = [ + "zeroize_derive", +] + +[[package]] +name = "zeroize_derive" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2c1e130bebaeab2f23886bf9acbaca14b092408c452543c857f66399cd6dab1" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "synstructure", +] diff --git a/SPECS/389-ds-base.spec b/SPECS/389-ds-base.spec index 4f85296..129ac96 100644 --- a/SPECS/389-ds-base.spec +++ b/SPECS/389-ds-base.spec @@ -16,7 +16,7 @@ ExcludeArch: i686 %global use_Socket6 0 %global use_asan 0 -%global use_rust 0 +%global use_rust 1 %global use_legacy 1 %global bundle_jemalloc 1 %if %{use_asan} @@ -42,10 +42,13 @@ ExcludeArch: i686 # set PIE flag %global _hardened_build 1 +# Filter argparse-manpage from autogenerated package Requires +%global __requires_exclude ^python.*argparse-manpage + Summary: 389 Directory Server (base) Name: 389-ds-base Version: 1.4.3.23 -Release: %{?relprefix}2%{?prerel}%{?dist} +Release: %{?relprefix}7%{?prerel}%{?dist} License: GPLv3+ URL: https://www.port389.org Group: System Environment/Daemons @@ -246,6 +249,21 @@ Patch09: 0009-Issue-4623-RFE-Monitor-the-current-DB-locks-4762.patch Patch10: 0010-Issue-4764-replicated-operation-sometime-checks-ACI-.patch Patch11: 0011-Issue-4778-RFE-Allow-setting-TOD-for-db-compaction-a.patch Patch12: 0012-Issue-4778-RFE-Add-changelog-compaction-task-in-1.4..patch +Patch13: 0013-Issue-4797-ACL-IP-ADDRESS-evaluation-may-corrupt-c_i.patch +Patch14: 0014-Issue-4396-Minor-memory-leak-in-backend-4558-4572.patch +Patch15: 0015-Issue-4700-Regression-in-winsync-replication-agreeme.patch +Patch16: 0016-Issue-4725-Fix-compiler-warnings.patch +Patch17: 0017-Issue-4814-_cl5_get_tod_expiration-may-crash-at-star.patch +Patch18: 0018-Issue-4789-Temporary-password-rules-are-not-enforce-.patch +Patch19: 0019-Issue-4788-CLI-should-support-Temporary-Password-Rul.patch +Patch20: 0020-Issue-4447-Crash-when-the-Referential-Integrity-log-.patch +Patch21: 0021-Issue-4791-Missing-dependency-for-RetroCL-RFE.patch +Patch22: 0022-Issue-4656-remove-problematic-language-from-ds-replc.patch +Patch23: 0023-Issue-4443-Internal-unindexed-searches-in-syncrepl-r.patch +Patch24: 0024-Issue-4817-BUG-locked-crypt-accounts-on-import-may-a.patch +Patch25: 0025-Issue-4837-persistent-search-returns-entries-even-wh.patch +Patch26: 0026-Hardcode-gost-crypt-passsword-storage-scheme.patch + %description 389 Directory Server is an LDAPv3 compliant server. The base package includes @@ -864,6 +882,33 @@ exit 0 %doc README.md %changelog +* Mon Jul 26 2021 Mark Reynolds - 1.4.3.23-7 +- Bump version to 1.4.3.23-7 +- Resolves: Bug 1983921 - persistent search returns entries even when an error is returned by content-sync-plugin + +* Fri Jul 16 2021 Mark Reynolds - 1.4.3.23-6 +- Bump version to 1.4.3.23-6 +- Resolves: Bug 1982787 - CRYPT password hash with asterisk allows any bind attempt to succeed + +* Thu Jul 15 2021 Mark Reynolds - 1.4.3.23-5 +- Bump version to 1.4.3.23-5 +- Resolves: Bug 1951020 - Internal unindexed searches in syncrepl +- Resolves: Bug 1978279 - ds-replcheck state output message has 'Master' instead of 'Supplier' + +* Tue Jun 29 2021 Mark Reynolds - 1.4.3.23-4 +- Bump version to 1.4.3.23-4 +- Resolves: Bug 1976906 - Instance crash at restart after changelog configuration +- Resolves: Bug 1480323 - ns-slapd crash at startup - Segmentation fault in strcmpi_fast() when the Referential Integrity log is manually edited +- Resolves: Bug 1967596 - Temporary password - add CLI and fix compiler errors + +* Thu Jun 17 2021 Mark Reynolds - 1.4.3.23-3 +- Bump version to 1.4.3.23-3 +- Resolves: Bug 1944494 - support for RFC 4530 entryUUID attribute +- Resolves: Bug 1967839 - ACIs are being evaluated against the Replication Manager account in a replication context +- Resolves: Bug 1970259 - A connection can be erroneously flagged as replication conn during evaluation of an aci with ip bind rule +- Resolves: Bug 1972590 - Large updates can reset the CLcache to the beginning of the changelog +- Resolves: Bug 1903221 - Memory leak in 389ds backend (Minor) + * Sun May 30 2021 Mark Reynolds - 1.4.3.23-2 - Bump version to 1.4.3.23-2 - Resolves: Bug 1812286 - RFE - Monitor the current DB locks ( nsslapd-db-current-locks )