import 389-ds-base-1.4.3.23-14.module+el8.5.0+14377+c731dc97

This commit is contained in:
CentOS Sources 2022-03-15 05:10:16 -04:00 committed by Stepan Oksanichenko
parent 7900475d31
commit 21cd549720
5 changed files with 936 additions and 1 deletions

View File

@ -0,0 +1,105 @@
From 1cdb49e70e35ad69e76be10f93233cdf504375df Mon Sep 17 00:00:00 2001
From: Mark Reynolds <mreynolds@redhat.com>
Date: Thu, 16 Dec 2021 16:13:08 -0500
Subject: [PATCH 1/4] CVE-2021-4091 (BZ#2030367) double-free of the virtual
attribute context in persistent search
description:
A search is processed by a worker using a private pblock.
If the search is persistent, the worker spawn a thread
and kind of duplicate its private pblock so that the spawn
thread continue to process the persistent search.
Then worker ends the initial search, reinit (free) its private pblock,
and returns monitoring the wait_queue.
When the persistent search completes, it frees the duplicated
pblock.
The problem is that private pblock and duplicated pblock
are referring to a same structure (pb_vattr_context).
That lead to a double free
Fix:
When cloning the pblock (slapi_pblock_clone) make sure
to transfert the references inside the original (private)
pblock to the target (cloned) one
That includes pb_vattr_context pointer.
Reviewed by: Mark Reynolds, James Chapman, Pierre Rogier (Thanks !)
---
ldap/servers/slapd/connection.c | 8 +++++---
ldap/servers/slapd/pblock.c | 14 ++++++++++++--
2 files changed, 17 insertions(+), 5 deletions(-)
diff --git a/ldap/servers/slapd/connection.c b/ldap/servers/slapd/connection.c
index e0c1a52d2..fc7ed9c4a 100644
--- a/ldap/servers/slapd/connection.c
+++ b/ldap/servers/slapd/connection.c
@@ -1823,9 +1823,11 @@ connection_threadmain()
pthread_mutex_unlock(&(conn->c_mutex));
}
/* ps_add makes a shallow copy of the pb - so we
- * can't free it or init it here - just set operation to NULL.
- * ps_send_results will call connection_remove_operation_ext to free it
- */
+ * can't free it or init it here - just set operation to NULL.
+ * ps_send_results will call connection_remove_operation_ext to free it
+ * The connection_thread private pblock ('pb') has be cloned and should only
+ * be reinit (slapi_pblock_init)
+ */
slapi_pblock_set(pb, SLAPI_OPERATION, NULL);
slapi_pblock_init(pb);
} else {
diff --git a/ldap/servers/slapd/pblock.c b/ldap/servers/slapd/pblock.c
index a64986aeb..c78d1250f 100644
--- a/ldap/servers/slapd/pblock.c
+++ b/ldap/servers/slapd/pblock.c
@@ -292,6 +292,12 @@ _pblock_assert_pb_deprecated(Slapi_PBlock *pblock)
}
}
+/* It clones the pblock
+ * the content of the source pblock is transfered
+ * to the target pblock (returned)
+ * The source pblock should not be used for any operation
+ * it needs to be reinit (slapi_pblock_init)
+ */
Slapi_PBlock *
slapi_pblock_clone(Slapi_PBlock *pb)
{
@@ -312,28 +318,32 @@ slapi_pblock_clone(Slapi_PBlock *pb)
if (pb->pb_task != NULL) {
_pblock_assert_pb_task(new_pb);
*(new_pb->pb_task) = *(pb->pb_task);
+ memset(pb->pb_task, 0, sizeof(slapi_pblock_task));
}
if (pb->pb_mr != NULL) {
_pblock_assert_pb_mr(new_pb);
*(new_pb->pb_mr) = *(pb->pb_mr);
+ memset(pb->pb_mr, 0, sizeof(slapi_pblock_matching_rule));
}
if (pb->pb_misc != NULL) {
_pblock_assert_pb_misc(new_pb);
*(new_pb->pb_misc) = *(pb->pb_misc);
+ memset(pb->pb_misc, 0, sizeof(slapi_pblock_misc));
}
if (pb->pb_intop != NULL) {
_pblock_assert_pb_intop(new_pb);
*(new_pb->pb_intop) = *(pb->pb_intop);
- /* set pwdpolicy to NULL so this clone allocates its own policy */
- new_pb->pb_intop->pwdpolicy = NULL;
+ memset(pb->pb_intop, 0, sizeof(slapi_pblock_intop));
}
if (pb->pb_intplugin != NULL) {
_pblock_assert_pb_intplugin(new_pb);
*(new_pb->pb_intplugin) = *(pb->pb_intplugin);
+ memset(pb->pb_intplugin, 0,sizeof(slapi_pblock_intplugin));
}
if (pb->pb_deprecated != NULL) {
_pblock_assert_pb_deprecated(new_pb);
*(new_pb->pb_deprecated) = *(pb->pb_deprecated);
+ memset(pb->pb_deprecated, 0, sizeof(slapi_pblock_deprecated));
}
#ifdef PBLOCK_ANALYTICS
new_pb->analytics = NULL;
--
2.31.1

View File

@ -0,0 +1,228 @@
From cf620c104ac50af48e48e8c0040820b6c073f7d7 Mon Sep 17 00:00:00 2001
From: Firstyear <william@blackhats.net.au>
Date: Thu, 19 Aug 2021 10:46:00 +1000
Subject: [PATCH 2/4] Issue 4775 - Add entryuuid CLI and Fixup (#4776)
Bug Description: EntryUUID when added was missing it's CLI
and helpers for fixups.
Fix Description: Add the CLI elements.
fixes: https://github.com/389ds/389-ds-base/issues/4775
Author: William Brown <william@blackhats.net.au>
Review by: @mreynolds389 (thanks!)
---
src/lib389/lib389/cli_conf/plugin.py | 2 +
.../lib389/cli_conf/plugins/entryuuid.py | 39 +++++++++++++++++++
src/plugins/entryuuid/src/lib.rs | 34 +++++++++-------
3 files changed, 60 insertions(+), 15 deletions(-)
create mode 100644 src/lib389/lib389/cli_conf/plugins/entryuuid.py
diff --git a/src/lib389/lib389/cli_conf/plugin.py b/src/lib389/lib389/cli_conf/plugin.py
index b50837cb8..1bd0c70db 100644
--- a/src/lib389/lib389/cli_conf/plugin.py
+++ b/src/lib389/lib389/cli_conf/plugin.py
@@ -27,6 +27,7 @@ from lib389.cli_conf.plugins import passthroughauth as cli_passthroughauth
from lib389.cli_conf.plugins import retrochangelog as cli_retrochangelog
from lib389.cli_conf.plugins import automember as cli_automember
from lib389.cli_conf.plugins import posix_winsync as cli_posix_winsync
+from lib389.cli_conf.plugins import entryuuid as cli_entryuuid
SINGULAR = Plugin
MANY = Plugins
@@ -113,6 +114,7 @@ def create_parser(subparsers):
cli_passthroughauth.create_parser(subcommands)
cli_retrochangelog.create_parser(subcommands)
cli_posix_winsync.create_parser(subcommands)
+ cli_entryuuid.create_parser(subcommands)
list_parser = subcommands.add_parser('list', help="List current configured (enabled and disabled) plugins")
list_parser.set_defaults(func=plugin_list)
diff --git a/src/lib389/lib389/cli_conf/plugins/entryuuid.py b/src/lib389/lib389/cli_conf/plugins/entryuuid.py
new file mode 100644
index 000000000..6c86bff4b
--- /dev/null
+++ b/src/lib389/lib389/cli_conf/plugins/entryuuid.py
@@ -0,0 +1,39 @@
+# --- BEGIN COPYRIGHT BLOCK ---
+# Copyright (C) 2021 William Brown <william@blackhats.net.au>
+# All rights reserved.
+#
+# License: GPL (version 3 or any later version).
+# See LICENSE for details.
+# --- END COPYRIGHT BLOCK ---
+
+import ldap
+from lib389.plugins import EntryUUIDPlugin
+from lib389.cli_conf import add_generic_plugin_parsers, generic_object_edit, generic_object_add
+
+def do_fixup(inst, basedn, log, args):
+ plugin = EntryUUIDPlugin(inst)
+ log.info('Attempting to add task entry...')
+ if not plugin.status():
+ log.error("'%s' is disabled. Fix up task can't be executed" % plugin.rdn)
+ return
+ fixup_task = plugin.fixup(args.DN, args.filter)
+ fixup_task.wait()
+ exitcode = fixup_task.get_exit_code()
+ if exitcode != 0:
+ log.error('EntryUUID fixup task has failed. Please, check the error log for more - %s' % exitcode)
+ else:
+ log.info('Successfully added task entry')
+
+def create_parser(subparsers):
+ referint = subparsers.add_parser('entryuuid', help='Manage and configure EntryUUID plugin')
+ subcommands = referint.add_subparsers(help='action')
+
+ add_generic_plugin_parsers(subcommands, EntryUUIDPlugin)
+
+ fixup = subcommands.add_parser('fixup', help='Run the fix-up task for EntryUUID plugin')
+ fixup.set_defaults(func=do_fixup)
+ fixup.add_argument('DN', help="Base DN that contains entries to fix up")
+ fixup.add_argument('-f', '--filter',
+ help='Filter for entries to fix up.\n If omitted, all entries under base DN'
+ 'will have their EntryUUID attribute regenerated if not present.')
+
diff --git a/src/plugins/entryuuid/src/lib.rs b/src/plugins/entryuuid/src/lib.rs
index 0197c5e83..29a9f1258 100644
--- a/src/plugins/entryuuid/src/lib.rs
+++ b/src/plugins/entryuuid/src/lib.rs
@@ -33,7 +33,7 @@ fn assign_uuid(e: &mut EntryRef) {
// 🚧 safety barrier 🚧
if e.contains_attr("entryUUID") {
log_error!(
- ErrorLevel::Trace,
+ ErrorLevel::Plugin,
"assign_uuid -> entryUUID exists, skipping dn {}",
sdn.to_dn_string()
);
@@ -47,7 +47,7 @@ fn assign_uuid(e: &mut EntryRef) {
if sdn.is_below_suffix(&*config_sdn) || sdn.is_below_suffix(&*schema_sdn) {
// We don't need to assign to these suffixes.
log_error!(
- ErrorLevel::Trace,
+ ErrorLevel::Plugin,
"assign_uuid -> not assigning to {:?} as part of system suffix",
sdn.to_dn_string()
);
@@ -57,7 +57,7 @@ fn assign_uuid(e: &mut EntryRef) {
// Generate a new Uuid.
let u: Uuid = Uuid::new_v4();
log_error!(
- ErrorLevel::Trace,
+ ErrorLevel::Plugin,
"assign_uuid -> assigning {:?} to dn {}",
u,
sdn.to_dn_string()
@@ -78,13 +78,13 @@ impl SlapiPlugin3 for EntryUuid {
fn betxn_pre_add(pb: &mut PblockRef) -> Result<(), PluginError> {
if pb.get_is_replicated_operation() {
log_error!(
- ErrorLevel::Trace,
+ ErrorLevel::Plugin,
"betxn_pre_add -> replicated operation, will not change"
);
return Ok(());
}
- log_error!(ErrorLevel::Trace, "betxn_pre_add -> start");
+ log_error!(ErrorLevel::Plugin, "betxn_pre_add -> start");
let mut e = pb.get_op_add_entryref().map_err(|_| PluginError::Pblock)?;
assign_uuid(&mut e);
@@ -105,7 +105,7 @@ impl SlapiPlugin3 for EntryUuid {
.first()
.ok_or_else(|| {
log_error!(
- ErrorLevel::Trace,
+ ErrorLevel::Plugin,
"task_validate basedn error -> empty value array?"
);
LDAPError::Operation
@@ -113,7 +113,7 @@ impl SlapiPlugin3 for EntryUuid {
.as_ref()
.try_into()
.map_err(|e| {
- log_error!(ErrorLevel::Trace, "task_validate basedn error -> {:?}", e);
+ log_error!(ErrorLevel::Plugin, "task_validate basedn error -> {:?}", e);
LDAPError::Operation
})?,
None => return Err(LDAPError::ObjectClassViolation),
@@ -124,7 +124,7 @@ impl SlapiPlugin3 for EntryUuid {
.first()
.ok_or_else(|| {
log_error!(
- ErrorLevel::Trace,
+ ErrorLevel::Plugin,
"task_validate filter error -> empty value array?"
);
LDAPError::Operation
@@ -132,7 +132,7 @@ impl SlapiPlugin3 for EntryUuid {
.as_ref()
.try_into()
.map_err(|e| {
- log_error!(ErrorLevel::Trace, "task_validate filter error -> {:?}", e);
+ log_error!(ErrorLevel::Plugin, "task_validate filter error -> {:?}", e);
LDAPError::Operation
})?,
None => {
@@ -144,7 +144,11 @@ impl SlapiPlugin3 for EntryUuid {
// Error if the first filter is empty?
// Now, to make things faster, we wrap the filter in a exclude term.
- let raw_filter = format!("(&{}(!(entryuuid=*)))", raw_filter);
+ let raw_filter = if !raw_filter.starts_with('(') && !raw_filter.ends_with('(') {
+ format!("(&({})(!(entryuuid=*)))", raw_filter)
+ } else {
+ format!("(&{}(!(entryuuid=*)))", raw_filter)
+ };
Ok(FixupData { basedn, raw_filter })
}
@@ -155,7 +159,7 @@ impl SlapiPlugin3 for EntryUuid {
fn task_handler(_task: &Task, data: Self::TaskData) -> Result<Self::TaskData, PluginError> {
log_error!(
- ErrorLevel::Trace,
+ ErrorLevel::Plugin,
"task_handler -> start thread with -> {:?}",
data
);
@@ -195,12 +199,12 @@ impl SlapiPlugin3 for EntryUuid {
}
fn start(_pb: &mut PblockRef) -> Result<(), PluginError> {
- log_error!(ErrorLevel::Trace, "plugin start");
+ log_error!(ErrorLevel::Plugin, "plugin start");
Ok(())
}
fn close(_pb: &mut PblockRef) -> Result<(), PluginError> {
- log_error!(ErrorLevel::Trace, "plugin close");
+ log_error!(ErrorLevel::Plugin, "plugin close");
Ok(())
}
}
@@ -212,7 +216,7 @@ pub fn entryuuid_fixup_mapfn(e: &EntryRef, _data: &()) -> Result<(), PluginError
/* Sanity check that entryuuid doesn't already exist */
if e.contains_attr("entryUUID") {
log_error!(
- ErrorLevel::Trace,
+ ErrorLevel::Plugin,
"skipping fixup for -> {}",
sdn.to_dn_string()
);
@@ -232,7 +236,7 @@ pub fn entryuuid_fixup_mapfn(e: &EntryRef, _data: &()) -> Result<(), PluginError
match lmod.execute() {
Ok(_) => {
- log_error!(ErrorLevel::Trace, "fixed-up -> {}", sdn.to_dn_string());
+ log_error!(ErrorLevel::Plugin, "fixed-up -> {}", sdn.to_dn_string());
Ok(())
}
Err(e) => {
--
2.31.1

View File

@ -0,0 +1,443 @@
From 0ed5299841733f9e18c5fe8c6a3f9d3fbd49c75a Mon Sep 17 00:00:00 2001
From: Firstyear <william@blackhats.net.au>
Date: Fri, 20 Aug 2021 09:18:50 +1000
Subject: [PATCH 3/4] Issue 4877 - RFE - EntryUUID to validate UUIDs on fixup
(#4878)
Bug Description: Due to changing the syntax of EntryUUID's
to string, we may have invalid EntryUUID's imported into
the database.
Fix Description: To resolve this during a fixup we validate
that Uuid's have a valid syntax. If they do not, we regenerate
them.
fixes: https://github.com/389ds/389-ds-base/issues/4877
Author: William Brown <william@blackhats.net.au>
Review by: @mreynolds389
---
.../entryuuid/localhost-userRoot-invalid.ldif | 233 ++++++++++++++++++
.../tests/suites/entryuuid/basic_test.py | 58 ++++-
src/plugins/entryuuid/src/lib.rs | 28 ++-
src/slapi_r_plugin/src/pblock.rs | 4 +-
src/slapi_r_plugin/src/value.rs | 10 +-
5 files changed, 322 insertions(+), 11 deletions(-)
create mode 100644 dirsrvtests/tests/data/entryuuid/localhost-userRoot-invalid.ldif
diff --git a/dirsrvtests/tests/data/entryuuid/localhost-userRoot-invalid.ldif b/dirsrvtests/tests/data/entryuuid/localhost-userRoot-invalid.ldif
new file mode 100644
index 000000000..9703babed
--- /dev/null
+++ b/dirsrvtests/tests/data/entryuuid/localhost-userRoot-invalid.ldif
@@ -0,0 +1,233 @@
+version: 1
+
+# entry-id: 1
+dn: dc=example,dc=com
+objectClass: top
+objectClass: domain
+dc: example
+description: dc=example,dc=com
+creatorsName: cn=Directory Manager
+modifiersName: cn=Directory Manager
+createTimestamp: 20200325015542Z
+modifyTimestamp: 20200325015542Z
+nsUniqueId: a2b33229-6e3b11ea-8de0c78c-83e27eda
+aci: (targetattr="dc || description || objectClass")(targetfilter="(objectClas
+ s=domain)")(version 3.0; acl "Enable anyone domain read"; allow (read, search
+ , compare)(userdn="ldap:///anyone");)
+aci: (targetattr="ou || objectClass")(targetfilter="(objectClass=organizationa
+ lUnit)")(version 3.0; acl "Enable anyone ou read"; allow (read, search, compa
+ re)(userdn="ldap:///anyone");)
+
+# entry-id: 2
+dn: cn=389_ds_system,dc=example,dc=com
+objectClass: top
+objectClass: nscontainer
+objectClass: ldapsubentry
+cn: 389_ds_system
+creatorsName: cn=Directory Manager
+modifiersName: cn=Directory Manager
+createTimestamp: 20200325015542Z
+modifyTimestamp: 20200325015542Z
+nsUniqueId: a2b3322a-6e3b11ea-8de0c78c-83e27eda
+
+# entry-id: 3
+dn: ou=groups,dc=example,dc=com
+objectClass: top
+objectClass: organizationalunit
+ou: groups
+aci: (targetattr="cn || member || gidNumber || nsUniqueId || description || ob
+ jectClass")(targetfilter="(objectClass=groupOfNames)")(version 3.0; acl "Enab
+ le anyone group read"; allow (read, search, compare)(userdn="ldap:///anyone")
+ ;)
+aci: (targetattr="member")(targetfilter="(objectClass=groupOfNames)")(version
+ 3.0; acl "Enable group_modify to alter members"; allow (write)(groupdn="ldap:
+ ///cn=group_modify,ou=permissions,dc=example,dc=com");)
+aci: (targetattr="cn || member || gidNumber || description || objectClass")(ta
+ rgetfilter="(objectClass=groupOfNames)")(version 3.0; acl "Enable group_admin
+ to manage groups"; allow (write, add, delete)(groupdn="ldap:///cn=group_admi
+ n,ou=permissions,dc=example,dc=com");)
+creatorsName: cn=Directory Manager
+modifiersName: cn=Directory Manager
+createTimestamp: 20200325015543Z
+modifyTimestamp: 20200325015543Z
+nsUniqueId: a2b3322b-6e3b11ea-8de0c78c-83e27eda
+
+# entry-id: 4
+dn: ou=people,dc=example,dc=com
+objectClass: top
+objectClass: organizationalunit
+ou: people
+aci: (targetattr="objectClass || description || nsUniqueId || uid || displayNa
+ me || loginShell || uidNumber || gidNumber || gecos || homeDirectory || cn ||
+ memberOf || mail || nsSshPublicKey || nsAccountLock || userCertificate")(tar
+ getfilter="(objectClass=posixaccount)")(version 3.0; acl "Enable anyone user
+ read"; allow (read, search, compare)(userdn="ldap:///anyone");)
+aci: (targetattr="displayName || legalName || userPassword || nsSshPublicKey")
+ (version 3.0; acl "Enable self partial modify"; allow (write)(userdn="ldap://
+ /self");)
+aci: (targetattr="legalName || telephoneNumber || mobile || sn")(targetfilter=
+ "(|(objectClass=nsPerson)(objectClass=inetOrgPerson))")(version 3.0; acl "Ena
+ ble self legalname read"; allow (read, search, compare)(userdn="ldap:///self"
+ );)
+aci: (targetattr="legalName || telephoneNumber")(targetfilter="(objectClass=ns
+ Person)")(version 3.0; acl "Enable user legalname read"; allow (read, search,
+ compare)(groupdn="ldap:///cn=user_private_read,ou=permissions,dc=example,dc=
+ com");)
+aci: (targetattr="uid || description || displayName || loginShell || uidNumber
+ || gidNumber || gecos || homeDirectory || cn || memberOf || mail || legalNam
+ e || telephoneNumber || mobile")(targetfilter="(&(objectClass=nsPerson)(objec
+ tClass=nsAccount))")(version 3.0; acl "Enable user admin create"; allow (writ
+ e, add, delete, read)(groupdn="ldap:///cn=user_admin,ou=permissions,dc=exampl
+ e,dc=com");)
+aci: (targetattr="uid || description || displayName || loginShell || uidNumber
+ || gidNumber || gecos || homeDirectory || cn || memberOf || mail || legalNam
+ e || telephoneNumber || mobile")(targetfilter="(&(objectClass=nsPerson)(objec
+ tClass=nsAccount))")(version 3.0; acl "Enable user modify to change users"; a
+ llow (write, read)(groupdn="ldap:///cn=user_modify,ou=permissions,dc=example,
+ dc=com");)
+aci: (targetattr="userPassword || nsAccountLock || userCertificate || nsSshPub
+ licKey")(targetfilter="(objectClass=nsAccount)")(version 3.0; acl "Enable use
+ r password reset"; allow (write, read)(groupdn="ldap:///cn=user_passwd_reset,
+ ou=permissions,dc=example,dc=com");)
+creatorsName: cn=Directory Manager
+modifiersName: cn=Directory Manager
+createTimestamp: 20200325015543Z
+modifyTimestamp: 20200325015543Z
+nsUniqueId: a2b3322c-6e3b11ea-8de0c78c-83e27eda
+
+# entry-id: 5
+dn: ou=permissions,dc=example,dc=com
+objectClass: top
+objectClass: organizationalunit
+ou: permissions
+creatorsName: cn=Directory Manager
+modifiersName: cn=Directory Manager
+createTimestamp: 20200325015543Z
+modifyTimestamp: 20200325015543Z
+nsUniqueId: a2b3322d-6e3b11ea-8de0c78c-83e27eda
+
+# entry-id: 6
+dn: ou=services,dc=example,dc=com
+objectClass: top
+objectClass: organizationalunit
+ou: services
+aci: (targetattr="objectClass || description || nsUniqueId || cn || memberOf |
+ | nsAccountLock ")(targetfilter="(objectClass=netscapeServer)")(version 3.0;
+ acl "Enable anyone service account read"; allow (read, search, compare)(userd
+ n="ldap:///anyone");)
+creatorsName: cn=Directory Manager
+modifiersName: cn=Directory Manager
+createTimestamp: 20200325015544Z
+modifyTimestamp: 20200325015544Z
+nsUniqueId: a2b3322e-6e3b11ea-8de0c78c-83e27eda
+
+# entry-id: 7
+dn: uid=demo_user,ou=people,dc=example,dc=com
+objectClass: top
+objectClass: nsPerson
+objectClass: nsAccount
+objectClass: nsOrgPerson
+objectClass: posixAccount
+uid: demo_user
+cn: Demo User
+displayName: Demo User
+legalName: Demo User Name
+uidNumber: 99998
+gidNumber: 99998
+homeDirectory: /var/empty
+loginShell: /bin/false
+nsAccountLock: true
+creatorsName: cn=Directory Manager
+modifiersName: cn=Directory Manager
+createTimestamp: 20200325015544Z
+modifyTimestamp: 20200325061615Z
+nsUniqueId: a2b3322f-6e3b11ea-8de0c78c-83e27eda
+entryUUID: INVALID_UUID
+
+# entry-id: 8
+dn: cn=demo_group,ou=groups,dc=example,dc=com
+objectClass: top
+objectClass: groupOfNames
+objectClass: posixGroup
+objectClass: nsMemberOf
+cn: demo_group
+gidNumber: 99999
+creatorsName: cn=Directory Manager
+modifiersName: cn=Directory Manager
+createTimestamp: 20200325015544Z
+modifyTimestamp: 20200325015544Z
+nsUniqueId: a2b33230-6e3b11ea-8de0c78c-83e27eda
+entryUUID: f6df8fe9-6b30-46aa-aa13-f0bf755371e8
+
+# entry-id: 9
+dn: cn=group_admin,ou=permissions,dc=example,dc=com
+objectClass: top
+objectClass: groupOfNames
+objectClass: nsMemberOf
+cn: group_admin
+creatorsName: cn=Directory Manager
+modifiersName: cn=Directory Manager
+createTimestamp: 20200325015545Z
+modifyTimestamp: 20200325015545Z
+nsUniqueId: a2b33231-6e3b11ea-8de0c78c-83e27eda
+
+# entry-id: 10
+dn: cn=group_modify,ou=permissions,dc=example,dc=com
+objectClass: top
+objectClass: groupOfNames
+objectClass: nsMemberOf
+cn: group_modify
+creatorsName: cn=Directory Manager
+modifiersName: cn=Directory Manager
+createTimestamp: 20200325015545Z
+modifyTimestamp: 20200325015545Z
+nsUniqueId: a2b33232-6e3b11ea-8de0c78c-83e27eda
+
+# entry-id: 11
+dn: cn=user_admin,ou=permissions,dc=example,dc=com
+objectClass: top
+objectClass: groupOfNames
+objectClass: nsMemberOf
+cn: user_admin
+creatorsName: cn=Directory Manager
+modifiersName: cn=Directory Manager
+createTimestamp: 20200325015545Z
+modifyTimestamp: 20200325015545Z
+nsUniqueId: a2b33233-6e3b11ea-8de0c78c-83e27eda
+
+# entry-id: 12
+dn: cn=user_modify,ou=permissions,dc=example,dc=com
+objectClass: top
+objectClass: groupOfNames
+objectClass: nsMemberOf
+cn: user_modify
+creatorsName: cn=Directory Manager
+modifiersName: cn=Directory Manager
+createTimestamp: 20200325015546Z
+modifyTimestamp: 20200325015546Z
+nsUniqueId: a2b33234-6e3b11ea-8de0c78c-83e27eda
+
+# entry-id: 13
+dn: cn=user_passwd_reset,ou=permissions,dc=example,dc=com
+objectClass: top
+objectClass: groupOfNames
+objectClass: nsMemberOf
+cn: user_passwd_reset
+creatorsName: cn=Directory Manager
+modifiersName: cn=Directory Manager
+createTimestamp: 20200325015546Z
+modifyTimestamp: 20200325015546Z
+nsUniqueId: a2b33235-6e3b11ea-8de0c78c-83e27eda
+
+# entry-id: 14
+dn: cn=user_private_read,ou=permissions,dc=example,dc=com
+objectClass: top
+objectClass: groupOfNames
+objectClass: nsMemberOf
+cn: user_private_read
+creatorsName: cn=Directory Manager
+modifiersName: cn=Directory Manager
+createTimestamp: 20200325015547Z
+modifyTimestamp: 20200325015547Z
+nsUniqueId: a2b33236-6e3b11ea-8de0c78c-83e27eda
+
diff --git a/dirsrvtests/tests/suites/entryuuid/basic_test.py b/dirsrvtests/tests/suites/entryuuid/basic_test.py
index 4d8a40909..41ffbfe10 100644
--- a/dirsrvtests/tests/suites/entryuuid/basic_test.py
+++ b/dirsrvtests/tests/suites/entryuuid/basic_test.py
@@ -10,6 +10,7 @@ import ldap
import pytest
import time
import shutil
+import uuid
from lib389.idm.user import nsUserAccounts, UserAccounts
from lib389.idm.account import Accounts
from lib389.idm.domain import Domain
@@ -217,7 +218,7 @@ def test_entryuuid_fixup_task(topology):
# 4. run the fix up
# For now set the log level to high!
- topology.standalone.config.loglevel(vals=(ErrorLog.DEFAULT,ErrorLog.TRACE))
+ topology.standalone.config.loglevel(vals=(ErrorLog.DEFAULT,ErrorLog.PLUGIN))
task = plug.fixup(DEFAULT_SUFFIX)
task.wait()
assert(task.is_complete() and task.get_exit_code() == 0)
@@ -242,3 +243,58 @@ def test_entryuuid_fixup_task(topology):
euuid_domain_2 = domain.get_attr_val_utf8('entryUUID')
assert(euuid_domain_2 == euuid_domain)
+@pytest.mark.skipif(not default_paths.rust_enabled or ds_is_older('1.4.2.0'), reason="Entryuuid is not available in older versions")
+def test_entryuuid_import_and_fixup_of_invalid_values(topology):
+ """ Test that when we import a database with an invalid entryuuid
+ that it is accepted *and* that subsequently we can fix the invalid
+ entryuuid during a fixup.
+
+ :id: ec8ef3a7-3cd2-4cbd-b6f1-2449fa17be75
+
+ :setup: Standalone instance
+
+ :steps:
+ 1. Import the db from the ldif
+ 2. Check the entryuuid is invalid
+ 3. Run the fixup
+ 4. Check the entryuuid is now valid (regenerated)
+
+ :expectedresults:
+ 1. Success
+ 2. The entryuuid is invalid
+ 3. Success
+ 4. The entryuuid is valid
+ """
+
+ # 1. Import the db
+ ldif_dir = topology.standalone.get_ldif_dir()
+ target_ldif = os.path.join(ldif_dir, 'localhost-userRoot-invalid.ldif')
+ import_ldif = os.path.join(DATADIR1, 'localhost-userRoot-invalid.ldif')
+ shutil.copyfile(import_ldif, target_ldif)
+ os.chmod(target_ldif, 0o777)
+
+ be = Backends(topology.standalone).get('userRoot')
+ task = be.import_ldif([target_ldif])
+ task.wait()
+ assert(task.is_complete() and task.get_exit_code() == 0)
+
+ # 2. Check the entryuuid is invalid
+ account = nsUserAccounts(topology.standalone, DEFAULT_SUFFIX).get("demo_user")
+ euuid = account.get_attr_val_utf8('entryUUID')
+ assert(euuid == "INVALID_UUID")
+
+ # 3. Run the fixup
+ topology.standalone.config.loglevel(vals=(ErrorLog.DEFAULT,ErrorLog.PLUGIN))
+ plug = EntryUUIDPlugin(topology.standalone)
+ task = plug.fixup(DEFAULT_SUFFIX)
+ task.wait()
+ assert(task.is_complete() and task.get_exit_code() == 0)
+ topology.standalone.config.loglevel(vals=(ErrorLog.DEFAULT,))
+
+ # 4. Check the entryuuid is valid
+ euuid = account.get_attr_val_utf8('entryUUID')
+ print(f"❄️ account entryUUID -> {euuid}");
+ assert(euuid != "INVALID_UUID")
+ # Raises an error if invalid
+ uuid.UUID(euuid)
+
diff --git a/src/plugins/entryuuid/src/lib.rs b/src/plugins/entryuuid/src/lib.rs
index 29a9f1258..ad3faef4b 100644
--- a/src/plugins/entryuuid/src/lib.rs
+++ b/src/plugins/entryuuid/src/lib.rs
@@ -144,11 +144,17 @@ impl SlapiPlugin3 for EntryUuid {
// Error if the first filter is empty?
// Now, to make things faster, we wrap the filter in a exclude term.
+
+ // 2021 - #4877 because we allow entryuuid to be strings, on import these may
+ // be invalid. As a result, we DO need to allow the fixup to check the entryuuid
+ // value is correct, so we can not exclude these during the search.
+ /*
let raw_filter = if !raw_filter.starts_with('(') && !raw_filter.ends_with('(') {
format!("(&({})(!(entryuuid=*)))", raw_filter)
} else {
format!("(&{}(!(entryuuid=*)))", raw_filter)
};
+ */
Ok(FixupData { basedn, raw_filter })
}
@@ -213,14 +219,20 @@ pub fn entryuuid_fixup_mapfn(e: &EntryRef, _data: &()) -> Result<(), PluginError
/* Supply a modification to the entry. */
let sdn = e.get_sdnref();
- /* Sanity check that entryuuid doesn't already exist */
- if e.contains_attr("entryUUID") {
- log_error!(
- ErrorLevel::Plugin,
- "skipping fixup for -> {}",
- sdn.to_dn_string()
- );
- return Ok(());
+ /* Check that entryuuid doesn't already exist, and is valid */
+ if let Some(valueset) = e.get_attr("entryUUID") {
+ if valueset.iter().all(|v| {
+ let u: Result<Uuid, _> = (&v).try_into();
+ u.is_ok()
+ }) {
+ // All values were valid uuid, move on!
+ log_error!(
+ ErrorLevel::Plugin,
+ "skipping fixup for -> {}",
+ sdn.to_dn_string()
+ );
+ return Ok(());
+ }
}
// Setup the modifications
diff --git a/src/slapi_r_plugin/src/pblock.rs b/src/slapi_r_plugin/src/pblock.rs
index 718ff2ca7..ac1b3c33b 100644
--- a/src/slapi_r_plugin/src/pblock.rs
+++ b/src/slapi_r_plugin/src/pblock.rs
@@ -281,7 +281,9 @@ impl PblockRef {
}
pub fn get_is_replicated_operation(&mut self) -> bool {
- let i = self.get_value_i32(PblockType::IsReplicationOperation).unwrap_or(0);
+ let i = self
+ .get_value_i32(PblockType::IsReplicationOperation)
+ .unwrap_or(0);
// Because rust returns the result of the last evaluation, we can
// just return if not equal 0.
i != 0
diff --git a/src/slapi_r_plugin/src/value.rs b/src/slapi_r_plugin/src/value.rs
index 46246837a..cd565295c 100644
--- a/src/slapi_r_plugin/src/value.rs
+++ b/src/slapi_r_plugin/src/value.rs
@@ -1,6 +1,6 @@
use crate::ber::{ol_berval, BerValRef};
use crate::dn::Sdn;
-use std::convert::{From, TryFrom};
+use std::convert::{From, TryFrom, TryInto};
use std::ffi::CString;
use std::iter::once;
use std::iter::FromIterator;
@@ -213,6 +213,14 @@ impl TryFrom<&ValueRef> for String {
}
}
+impl TryFrom<&ValueRef> for Uuid {
+ type Error = ();
+
+ fn try_from(value: &ValueRef) -> Result<Self, Self::Error> {
+ (&value.bvr).try_into().map_err(|_| ())
+ }
+}
+
impl TryFrom<&ValueRef> for Sdn {
type Error = ();
--
2.31.1

View File

@ -0,0 +1,143 @@
From 0bdb7445af5be162e18c26f7fc2b75536c467748 Mon Sep 17 00:00:00 2001
From: James Chapman <jachapma@redhat.com>
Date: Fri, 19 Feb 2021 16:32:22 +0000
Subject: [PATCH 4/4] Issue 4595 - Paged search lookthroughlimit bug (#4602)
Bug Description: During a paged search with lookthroughlimit enabled,
lookthroughcount is used to keep track of how many entries are
examined. A paged search reads ahead one entry to catch the end of the
search so it doesn't show the prompt when there are no more entries.
lookthroughcount doesn't take read ahead into account when tracking
how many entries have been examined.
Fix Description: Keep lookthroughcount in sync with read ahead by
by decrementing it during read ahead roll back.
Fixes: https://github.com/389ds/389-ds-base/issues/4595
Relates: https://github.com/389ds/389-ds-base/issues/4513
Reviewed by: droideck, mreynolds389, Firstyear, progier389 (Many thanks)
---
dirsrvtests/tests/suites/basic/basic_test.py | 85 ++++++++++++++++++++
ldap/servers/slapd/back-ldbm/ldbm_search.c | 1 +
2 files changed, 86 insertions(+)
diff --git a/dirsrvtests/tests/suites/basic/basic_test.py b/dirsrvtests/tests/suites/basic/basic_test.py
index 332f5bb8d..e1a31175a 100644
--- a/dirsrvtests/tests/suites/basic/basic_test.py
+++ b/dirsrvtests/tests/suites/basic/basic_test.py
@@ -95,6 +95,24 @@ def rootdse_attr(topology_st, request):
return rootdse_attr_name
+def change_conf_attr(topology_st, suffix, attr_name, attr_value):
+ """Change configuration attribute in the given suffix.
+
+ Returns previous attribute value.
+ """
+
+ entry = DSLdapObject(topology_st.standalone, suffix)
+
+ attr_value_bck = entry.get_attr_val_bytes(attr_name)
+ log.info('Set %s to %s. Previous value - %s. Modified suffix - %s.' % (
+ attr_name, attr_value, attr_value_bck, suffix))
+ if attr_value is None:
+ entry.remove_all(attr_name)
+ else:
+ entry.replace(attr_name, attr_value)
+ return attr_value_bck
+
+
def test_basic_ops(topology_st, import_example_ldif):
"""Tests adds, mods, modrdns, and deletes operations
@@ -607,6 +625,73 @@ def test_basic_searches(topology_st, import_example_ldif):
log.info('test_basic_searches: PASSED')
+@pytest.mark.parametrize('limit,resp',
+ ((('200'), 'PASS'),
+ (('50'), ldap.ADMINLIMIT_EXCEEDED)))
+def test_basic_search_lookthroughlimit(topology_st, limit, resp, import_example_ldif):
+ """
+ Tests normal search with lookthroughlimit set high and low.
+
+ :id: b5119970-6c9f-41b7-9649-de9233226fec
+
+ :setup: Standalone instance, add example.ldif to the database, search filter (uid=*).
+
+ :steps:
+ 1. Import ldif user file.
+ 2. Change lookthroughlimit to 200.
+ 3. Bind to server as low priv user
+ 4. Run search 1 with "high" lookthroughlimit.
+ 5. Change lookthroughlimit to 50.
+ 6. Run search 2 with "low" lookthroughlimit.
+ 8. Delete user from DB.
+ 9. Reset lookthroughlimit to original.
+
+ :expectedresults:
+ 1. First search should complete with no error.
+ 2. Second search should return ldap.ADMINLIMIT_EXCEEDED error.
+ """
+
+ log.info('Running test_basic_search_lookthroughlimit...')
+
+ search_filter = "(uid=*)"
+
+ ltl_orig = change_conf_attr(topology_st, 'cn=config,cn=ldbm database,cn=plugins,cn=config', 'nsslapd-lookthroughlimit', limit)
+
+ try:
+ users = UserAccounts(topology_st.standalone, DEFAULT_SUFFIX, rdn=None)
+ user = users.create_test_user()
+ user.replace('userPassword', PASSWORD)
+ except ldap.LDAPError as e:
+ log.fatal('Failed to create test user: error ' + e.args[0]['desc'])
+ assert False
+
+ try:
+ conn = UserAccount(topology_st.standalone, user.dn).bind(PASSWORD)
+ except ldap.LDAPError as e:
+ log.fatal('Failed to bind test user: error ' + e.args[0]['desc'])
+ assert False
+
+ try:
+ if resp == ldap.ADMINLIMIT_EXCEEDED:
+ with pytest.raises(ldap.ADMINLIMIT_EXCEEDED):
+ searchid = conn.search(DEFAULT_SUFFIX, ldap.SCOPE_SUBTREE, search_filter)
+ rtype, rdata = conn.result(searchid)
+ else:
+ searchid = conn.search(DEFAULT_SUFFIX, ldap.SCOPE_SUBTREE, search_filter)
+ rtype, rdata = conn.result(searchid)
+ assert(len(rdata) == 151) #151 entries in the imported ldif file using "(uid=*)"
+ except ldap.LDAPError as e:
+ log.fatal('Failed to perform search: error ' + e.args[0]['desc'])
+ assert False
+
+ finally:
+ #Cleanup
+ change_conf_attr(topology_st, 'cn=config,cn=ldbm database,cn=plugins,cn=config', 'nsslapd-lookthroughlimit', ltl_orig)
+ user.delete()
+
+ log.info('test_basic_search_lookthroughlimit: PASSED')
+
+
@pytest.fixture(scope="module")
def add_test_entry(topology_st, request):
# Add test entry
diff --git a/ldap/servers/slapd/back-ldbm/ldbm_search.c b/ldap/servers/slapd/back-ldbm/ldbm_search.c
index 6e22debde..d0f52b6f7 100644
--- a/ldap/servers/slapd/back-ldbm/ldbm_search.c
+++ b/ldap/servers/slapd/back-ldbm/ldbm_search.c
@@ -1885,6 +1885,7 @@ ldbm_back_prev_search_results(Slapi_PBlock *pb)
sr->sr_entry = NULL;
}
idl_iterator_decrement(&(sr->sr_current));
+ --sr->sr_lookthroughcount;
}
return;
}
--
2.31.1

View File

@ -48,7 +48,7 @@ ExcludeArch: i686
Summary: 389 Directory Server (base)
Name: 389-ds-base
Version: 1.4.3.23
Release: %{?relprefix}12%{?prerel}%{?dist}
Release: %{?relprefix}14%{?prerel}%{?dist}
License: GPLv3+
URL: https://www.port389.org
Group: System Environment/Daemons
@ -270,6 +270,10 @@ Patch30: 0030-Issue-4884-server-crashes-when-dnaInterval-attribute.patc
Patch31: 0031-Issue-4925-Performance-ACI-targetfilter-evaluation-r.patch
Patch32: 0032-Issue-4972-gecos-with-IA5-introduces-a-compatibility.patch
Patch33: 0033-Issue-4910-db-reindex-corrupts-RUV-tombstone-nsuique.patch
Patch34: 0034-CVE-2021-4091-BZ-2030367-double-free-of-the-virtual-.patch
Patch35: 0035-Issue-4775-Add-entryuuid-CLI-and-Fixup-4776.patch
Patch36: 0036-Issue-4877-RFE-EntryUUID-to-validate-UUIDs-on-fixup-.patch
Patch37: 0037-Issue-4595-Paged-search-lookthroughlimit-bug-4602.patch
%description
389 Directory Server is an LDAPv3 compliant server. The base package includes
@ -888,6 +892,18 @@ exit 0
%doc README.md
%changelog
* Wed Mar 2 2022 Thierry Bordaz <tbordaz@redhat.com> - 1.4.3.23-14
- Bump version to 1.4.3.23-14
- Resolves: Bug 2059893 - Paged search lookthroughlimit counter doesnt take read ahead into account
- Resolves: Bug 2060106 - Based on 1944494 (RFC 4530 entryUUID attribute) - plugin entryuuid failing
- Resolves: Bug 2060110 - double-free of the virtual attribute context in persistent search
* Mon Feb 21 2022 Thierry Bordaz <tbordaz@redhat.com> - 1.4.3.23-13
- Bump version to 1.4.3.23-13
- Resolves: Bug 2056488 - Paged search lookthroughlimit counter doesnt take read ahead into account
- Resolves: Bug 2047166 - Based on 1944494 (RFC 4530 entryUUID attribute) - plugin entryuuid failing
- Resolves: Bug 2056481 - double-free of the virtual attribute context in persistent search
* Thu Nov 18 2021 Mark Reynolds <mreynolds@redhat.com> - 1.4.3.23-12
- Bump version to 1.4.3.23-12
- Resolves: Bug 2024697 - DB corruption "_entryrdn_insert_key - Same DN (dn: nsuniqueid=ffffffff-ffffffff-ffffffff-ffffffff,<SUFFIX>) is already in the entryrdn file"