2 commits - dirsrvtests/tests ldap/admin ldap/ldif ldap/servers Makefile.am Makefile.in
by Noriko Hosoi
Makefile.am | 3
Makefile.in | 3
dirsrvtests/tests/tickets/ticket48755_test.py | 222 ++++++++++++++++
ldap/admin/src/scripts/91reindex.pl.in | 103 +++++++
ldap/admin/src/scripts/91subtreereindex.pl | 8
ldap/admin/src/scripts/setup-ds.res.in | 1
ldap/ldif/template-dse.ldif.in | 1
ldap/servers/plugins/replication/repl5_replica_config.c | 7
ldap/servers/plugins/replication/repl5_tot_protocol.c | 169 ++++++++----
ldap/servers/slapd/back-ldbm/back-ldbm.h | 6
ldap/servers/slapd/back-ldbm/dblayer.c | 5
ldap/servers/slapd/back-ldbm/filterindex.c | 18 -
ldap/servers/slapd/back-ldbm/idl_new.c | 87 +++++-
ldap/servers/slapd/back-ldbm/index.c | 22 +
ldap/servers/slapd/back-ldbm/init.c | 2
ldap/servers/slapd/back-ldbm/misc.c | 1
ldap/servers/slapd/entry.c | 4
ldap/servers/slapd/slap.h | 4
ldap/servers/slapd/slapi-plugin.h | 8
19 files changed, 588 insertions(+), 86 deletions(-)
New commits:
commit e218a187678133455c4138481c825852e099298a
Author: Noriko Hosoi <nhosoi(a)redhat.com>
Date: Thu May 26 14:40:52 2016 -0700
Ticket #48755 - CI test: test case for ticket 48755
Description: moving an entry could make the online init fail
diff --git a/dirsrvtests/tests/tickets/ticket48755_test.py b/dirsrvtests/tests/tickets/ticket48755_test.py
new file mode 100644
index 0000000..e3b7b61
--- /dev/null
+++ b/dirsrvtests/tests/tickets/ticket48755_test.py
@@ -0,0 +1,222 @@
+# --- BEGIN COPYRIGHT BLOCK ---
+# Copyright (C) 2016 Red Hat, Inc.
+# All rights reserved.
+#
+# License: GPL (version 3 or any later version).
+# See LICENSE for details.
+# --- END COPYRIGHT BLOCK ---
+#
+import os
+import sys
+import time
+import shlex
+import subprocess
+import ldap
+import logging
+import pytest
+from lib389 import DirSrv, Entry, tools, tasks
+from lib389.tools import DirSrvTools
+from lib389._constants import *
+from lib389.properties import *
+from lib389.tasks import *
+from lib389.utils import *
+
+logging.getLogger(__name__).setLevel(logging.DEBUG)
+log = logging.getLogger(__name__)
+
+installation1_prefix = None
+
+m1_m2_agmt = None
+
+class TopologyReplication(object):
+ def __init__(self, master1, master2):
+ master1.open()
+ self.master1 = master1
+ master2.open()
+ self.master2 = master2
+
+
+(a)pytest.fixture(scope="module")
+def topology(request):
+ global installation1_prefix
+ if installation1_prefix:
+ args_instance[SER_DEPLOYED_DIR] = installation1_prefix
+
+ # Creating master 1...
+ master1 = DirSrv(verbose=False)
+ if installation1_prefix:
+ args_instance[SER_DEPLOYED_DIR] = installation1_prefix
+ args_instance[SER_HOST] = HOST_MASTER_1
+ args_instance[SER_PORT] = PORT_MASTER_1
+ args_instance[SER_SERVERID_PROP] = SERVERID_MASTER_1
+ args_instance[SER_CREATION_SUFFIX] = DEFAULT_SUFFIX
+ args_master = args_instance.copy()
+ master1.allocate(args_master)
+ instance_master1 = master1.exists()
+ if instance_master1:
+ master1.delete()
+ master1.create()
+ master1.open()
+ master1.replica.enableReplication(suffix=SUFFIX, role=REPLICAROLE_MASTER, replicaId=REPLICAID_MASTER_1)
+
+ # Creating master 2...
+ master2 = DirSrv(verbose=False)
+ if installation1_prefix:
+ args_instance[SER_DEPLOYED_DIR] = installation1_prefix
+ args_instance[SER_HOST] = HOST_MASTER_2
+ args_instance[SER_PORT] = PORT_MASTER_2
+ args_instance[SER_SERVERID_PROP] = SERVERID_MASTER_2
+ args_instance[SER_CREATION_SUFFIX] = DEFAULT_SUFFIX
+ args_master = args_instance.copy()
+ master2.allocate(args_master)
+ instance_master2 = master2.exists()
+ if instance_master2:
+ master2.delete()
+ master2.create()
+ master2.open()
+ master2.replica.enableReplication(suffix=SUFFIX, role=REPLICAROLE_MASTER, replicaId=REPLICAID_MASTER_2)
+
+ #
+ # Create all the agreements
+ #
+ # Creating agreement from master 1 to master 2
+ properties = {RA_NAME: r'meTo_%s:%s' % (master2.host, master2.port),
+ RA_BINDDN: defaultProperties[REPLICATION_BIND_DN],
+ RA_BINDPW: defaultProperties[REPLICATION_BIND_PW],
+ RA_METHOD: defaultProperties[REPLICATION_BIND_METHOD],
+ RA_TRANSPORT_PROT: defaultProperties[REPLICATION_TRANSPORT]}
+ global m1_m2_agmt
+ m1_m2_agmt = master1.agreement.create(suffix=SUFFIX, host=master2.host, port=master2.port, properties=properties)
+ if not m1_m2_agmt:
+ log.fatal("Fail to create a master -> master replica agreement")
+ sys.exit(1)
+ log.debug("%s created" % m1_m2_agmt)
+
+ # Creating agreement from master 2 to master 1
+ properties = {RA_NAME: r'meTo_%s:%s' % (master1.host, master1.port),
+ RA_BINDDN: defaultProperties[REPLICATION_BIND_DN],
+ RA_BINDPW: defaultProperties[REPLICATION_BIND_PW],
+ RA_METHOD: defaultProperties[REPLICATION_BIND_METHOD],
+ RA_TRANSPORT_PROT: defaultProperties[REPLICATION_TRANSPORT]}
+ m2_m1_agmt = master2.agreement.create(suffix=SUFFIX, host=master1.host, port=master1.port, properties=properties)
+ if not m2_m1_agmt:
+ log.fatal("Fail to create a master -> master replica agreement")
+ sys.exit(1)
+ log.debug("%s created" % m2_m1_agmt)
+
+ # Allow the replicas to get situated with the new agreements...
+ time.sleep(5)
+
+ #
+ # Initialize all the agreements
+ #
+ master1.agreement.init(SUFFIX, HOST_MASTER_2, PORT_MASTER_2)
+ master1.waitForReplInit(m1_m2_agmt)
+
+ # Check replication is working...
+ if master1.testReplication(DEFAULT_SUFFIX, master2):
+ log.info('Replication is working.')
+ else:
+ log.fatal('Replication is not working.')
+ assert False
+
+ # Delete each instance in the end
+ def fin():
+ master1.delete()
+ master2.delete()
+ request.addfinalizer(fin)
+
+ # Clear out the tmp dir
+ master1.clearTmpDir(__file__)
+
+ return TopologyReplication(master1, master2)
+
+
+(a)pytest.fixture(scope="module")
+
+def add_ou_entry(server, idx, myparent):
+ name = 'OU%d' % idx
+ dn = 'ou=%s,%s' % (name, myparent)
+ server.add_s(Entry((dn, {'objectclass': ['top', 'organizationalunit'],
+ 'ou': name})))
+
+def add_user_entry(server, idx, myparent):
+ name = 'tuser%d' % idx
+ dn = 'uid=%s,%s' % (name, myparent)
+ server.add_s(Entry((dn, {'objectclass': ['top', 'person', 'organizationalPerson', 'inetorgperson'],
+ 'givenname': 'test',
+ 'sn': 'user%d' % idx,
+ 'cn': 'Test User%d' % idx,
+ 'userpassword': 'password'})))
+
+def test_ticket48755(topology):
+ log.info("Ticket 48755 - moving an entry could make the online init fail")
+
+ M1 = topology.master1
+ M2 = topology.master2
+
+ log.info("Generating DIT_0")
+ idx = 0
+ add_ou_entry(M1, idx, DEFAULT_SUFFIX)
+
+ ou0 = 'ou=OU%d' % idx
+ parent0 = '%s,%s' % (ou0, DEFAULT_SUFFIX)
+ add_ou_entry(M1, idx, parent0)
+
+ parent00 = 'ou=OU%d,%s' % (idx, parent0)
+ for idx in range(0, 9):
+ add_user_entry(M1, idx, parent00)
+
+ log.info('%s => %s => %s => 10 USERS' % (DEFAULT_SUFFIX, parent0, parent00))
+
+ log.info("Generating DIT_1")
+ idx = 1
+ add_ou_entry(M1, idx, DEFAULT_SUFFIX)
+
+ parent1 = 'ou=OU%d,%s' % (idx, DEFAULT_SUFFIX)
+ add_ou_entry(M1, idx, parent1)
+
+ log.info("Moving %s to DIT_1" % parent00)
+ M1.rename_s(parent00, ou0, newsuperior=parent1, delold=1)
+
+ log.info("Moving %s to DIT_1" % parent0)
+ parent01 = '%s,%s' % (ou0, parent1)
+ M1.rename_s(parent0, ou0, newsuperior=parent01, delold=1)
+
+ parent001 = '%s,%s' % (ou0, parent01)
+ log.info("Moving USERS to %s" % parent0)
+ for idx in range(0, 9):
+ name = 'tuser%d' % idx
+ rdn = 'uid=%s' % name
+ dn = 'uid=%s,%s' % (name, parent01)
+ M1.rename_s(dn, rdn, newsuperior=parent001, delold=1)
+
+ log.info('%s => %s => %s => %s => 10 USERS' % (DEFAULT_SUFFIX, parent1, parent01, parent001))
+
+ log.info("Deleting 5 USERS to turn them into a tombstone entries")
+ for idx in range(5, 9):
+ name = 'tuser%d' % idx
+ rdn = 'uid=%s' % name
+ dn = 'uid=%s,%s' % (name, parent001)
+ M1.delete_s(dn)
+
+ log.info("Run Consumer Initialization.")
+ global m1_m2_agmt
+ M1.startReplication_async(m1_m2_agmt)
+ M1.waitForReplInit(m1_m2_agmt)
+
+ m1entries = M1.search_s(DEFAULT_SUFFIX, ldap.SCOPE_SUBTREE, '(|(objectclass=ldapsubentry)(objectclass=nstombstone)(nsuniqueid=*))')
+ m2entries = M2.search_s(DEFAULT_SUFFIX, ldap.SCOPE_SUBTREE, '(|(objectclass=ldapsubentry)(objectclass=nstombstone)(nsuniqueid=*))')
+
+ log.info("m1entry count - %d", len(m1entries))
+ log.info("m2entry count - %d", len(m2entries))
+
+ assert len(m1entries) == len(m2entries)
+ log.info('PASSED')
+
+if __name__ == '__main__':
+ # Run isolated
+ # -s for DEBUG mode
+
+ CURRENT_FILE = os.path.realpath(__file__)
+ pytest.main("-s %s" % CURRENT_FILE)
commit 3606b78bacce984ab2226755c5921dffac9552c2
Author: Noriko Hosoi <nhosoi(a)redhat.com>
Date: Thu May 26 15:14:06 2016 -0700
Ticket #48755 - moving an entry could make the online init fail
Bug Description: Online init (aka Total update, bulk import) scans the
primary id2entry db in the order of ID. If Entry A is moved under a
new superior Entry B which was generated after Entry A, when Entry A
is sent to a consumer using online init, its parent entry does not
exist on the consumer and the online init fails.
Fix Description:
- Added a command BACK_INFO_IS_ENTRYRDN to slapi_back_get_info, which
returns the status of entryrdn switch maintained in the backend.
- If slapi_backend_get_info(BACK_INFO_IS_ENTRYRDN) returns true for
the replicated backend, repl5_tot_run searches the entry with the
filter:
(|(parentid>=1)(objectclass=ldapsubentry)(objectclass=nstombstone))
instead of:
(|(objectclass=ldapsubentry)(objectclass=nstombstone)(nsuniqueid=*))".
- In addition, idl_new_range_fetch had to be modified so that ...
* A range search for parentid ignores nsslapd-idlistscanlimit by
setting SLAPI_OP_RANGE_NO_ALLIDS as well as it skips sorting the
IDlist by ID by setting SLAPI_OP_RANGE_NO_IDL_SORT.
* In case SLAPI_OP_RANGE_NO_IDL_SORT is set, idl_new_range_fetch
checks whether the key (in this case parentid) is in the IDlist.
If it exists, the ID is appended. If it does not, the ID is in
the leftover list and appended when the parent ID is found in the
IDlist.
- Increased the version of rdn-format-# in DBVERSION to 3.
- Upgrade script 91reindex.pl.in is added which reindex the parentid
index file in the integer order if the version of rdn-format-# in
DBVERSION is less than 3.
https://fedorahosted.org/389/ticket/48755
Reviewed by wibrown(a)redhat.com and lkrispen(a)redhat.com (Thanks, William and Ludwig!)
diff --git a/Makefile.am b/Makefile.am
index 824b745..cad1b61 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -839,7 +839,8 @@ update_DATA = ldap/admin/src/scripts/exampleupdate.pl \
ldap/admin/src/scripts/50AES-pbe-plugin.ldif\
ldap/admin/src/scripts/50updateconfig.ldif \
ldap/admin/src/scripts/52updateAESplugin.pl \
- ldap/admin/src/scripts/dnaplugindepends.ldif
+ ldap/admin/src/scripts/dnaplugindepends.ldif \
+ ldap/admin/src/scripts/91reindex.pl
update_SCRIPTS = ldap/admin/src/scripts/exampleupdate.sh
diff --git a/Makefile.in b/Makefile.in
index 0752ff1..a45a0d6 100644
--- a/Makefile.in
+++ b/Makefile.in
@@ -2240,7 +2240,8 @@ update_DATA = ldap/admin/src/scripts/exampleupdate.pl \
ldap/admin/src/scripts/50AES-pbe-plugin.ldif\
ldap/admin/src/scripts/50updateconfig.ldif \
ldap/admin/src/scripts/52updateAESplugin.pl \
- ldap/admin/src/scripts/dnaplugindepends.ldif
+ ldap/admin/src/scripts/dnaplugindepends.ldif \
+ ldap/admin/src/scripts/91reindex.pl
update_SCRIPTS = ldap/admin/src/scripts/exampleupdate.sh
diff --git a/ldap/admin/src/scripts/91reindex.pl.in b/ldap/admin/src/scripts/91reindex.pl.in
new file mode 100644
index 0000000..c861f64
--- /dev/null
+++ b/ldap/admin/src/scripts/91reindex.pl.in
@@ -0,0 +1,103 @@
+use Mozilla::LDAP::Conn;
+use Mozilla::LDAP::Utils qw(normalizeDN);
+use Mozilla::LDAP::API qw(:constant ldap_url_parse ldap_explode_dn);
+use DSUpdate qw(isOffline);
+
+sub runinst {
+ my ($inf, $inst, $dseldif, $conn) = @_;
+ my $rc, @errs;
+
+ # List of index to be reindexed
+ my @toreindex = qw(parentid);
+ # rdn-format value. See $rdn_format set below.
+ # If equal to or greater than this value, no need to reindex.
+ # If it needs to be unconditionally reindexed, set 0.
+ my @rdnconditions = (4)
+
+ my $config = $conn->search("cn=config", "base", "(objectclass=*)");
+ if (!$config) {
+ push @errs, ['error_finding_config_entry', 'cn=config',
+ $conn->getErrorString()];
+ return @errs;
+ }
+
+ ($rc, @errs) = isOffline($inf, $inst, $conn);
+ if (!$rc) {
+ return @errs;
+ }
+
+ my $reindex = "@sbindir@/db2index -Z $inst";
+ my @errs;
+ my $instconf = $conn->search("cn=ldbm database,cn=plugins,cn=config", "onelevel", "(objectclass=*)");
+ if (!$instconf) {
+ push @errs, ['error_finding_config_entry', 'cn=*,cn=ldbm database,cn=plugins,cn=config', $conn->getErrorString()];
+ return @errs;
+ }
+
+ my $dbconf = $conn->search("cn=config,cn=ldbm database,cn=plugins,cn=config", "base", "(objectclass=*)");
+ if (!$dbconf) {
+ push @errs, ['error_finding_config_entry',
+ 'cn=config,cn=ldbm database,cn=plugins,cn=config',
+ $conn->getErrorString()];
+ return @errs;
+ }
+
+ # Get the value of nsslapd-subtree-rename-switch.
+ my $switch = $dbconf->getValues('nsslapd-subtree-rename-switch');
+ if ("" eq $switch) {
+ return (); # subtree-rename-switch does not exist; do nothing.
+ } elsif ("off" eq $switch || "OFF" eq $switch) {
+ return (); # subtree-rename-switch is OFF; do nothing.
+ }
+
+ my $dbdir = $dbconf->getValues('nsslapd-directory');
+ my $dbversion0 = $dbdir . "/DBVERSION";
+ my $rdn_format = 0;
+ my $dbversionstr = "";
+ if (!open(DBVERSION, "$dbversion0")) {
+ push @errs, ['error_opening_file', $dbversion0, $!];
+ return @errs;
+ } else {
+ while (<DBVERSION>) {
+ if ($_ =~ /rdn-format/) {
+ $rdn_format = 1;
+ $dbversionstr = $_;
+ if ($_ =~ /rdn-format-1/) {
+ $rdn_format = 2;
+ } elsif ($_ =~ /rdn-format-2/) {
+ $rdn_format = 3;
+ } elsif ($_ =~ /rdn-format-3/) {
+ $rdn_format = 4;
+ } elsif ($_ =~ /rdn-format-4/) {
+ $rdn_format = 5;
+ } elsif ($_ =~ /rdn-format-5/) {
+ $rdn_format = 6;
+ } elsif ($_ =~ /rdn-format-/) {
+ # assume greater than -5
+ $rdn_format = 7;
+ }
+ }
+ }
+ close DBVERSION;
+ }
+
+ while ($instconf) {
+ my $backend= $instconf->getValues('cn');
+ if (($backend eq "config") || ($backend eq "monitor")) {
+ goto NEXT;
+ }
+
+ for (my $idx = 0; $ <= $#toreindex; $idx++) {
+ if (0 == $rdnconditions[$idx] || $rdnconditions[$idx] > $rdn_format) {
+ my $rc = system("$reindex -n $backend -t $idx");
+ if ($rc) {
+ push @errs, ["error_reindexng", $idx, $backend, $rc];
+ }
+ }
+ }
+NEXT:
+ $instconf = $conn->nextEntry();
+ }
+
+ return @errs;
+}
diff --git a/ldap/admin/src/scripts/91subtreereindex.pl b/ldap/admin/src/scripts/91subtreereindex.pl
index a031cc1..c4b40a3 100644
--- a/ldap/admin/src/scripts/91subtreereindex.pl
+++ b/ldap/admin/src/scripts/91subtreereindex.pl
@@ -51,14 +51,18 @@ sub runinst {
if ($_ =~ /rdn-format-1/) {
$is_rdn_format = 2;
}
- if ($_ =~ /rdn-format-2/) {
+ elsif ($_ =~ /rdn-format-2/) {
$is_rdn_format = 3;
}
+ elsif ($_ =~ /rdn-format-/) {
+ # assume greater than -2
+ $is_rdn_format = 4;
+ }
}
}
close DBVERSION;
- if (3 == $is_rdn_format) {
+ if (3 <= $is_rdn_format) {
return (); # DB already has the new rdn format.
}
diff --git a/ldap/admin/src/scripts/setup-ds.res.in b/ldap/admin/src/scripts/setup-ds.res.in
index fa37567..e46b858 100644
--- a/ldap/admin/src/scripts/setup-ds.res.in
+++ b/ldap/admin/src/scripts/setup-ds.res.in
@@ -209,3 +209,4 @@ error_opening_file = Opening file '%s' failed. Error: %s\n
error_format_error = '%s' has invalid format.\n
error_update_not_offline = Error: offline mode selected but the server [%s] is still running.\n
error_update_all = Failed to update all the Directory Server instances.\n
+error_reindexing = Failed to reindex '%s' in backend '%s'. Error: %s\n
diff --git a/ldap/ldif/template-dse.ldif.in b/ldap/ldif/template-dse.ldif.in
index f18c082..46b416b 100644
--- a/ldap/ldif/template-dse.ldif.in
+++ b/ldap/ldif/template-dse.ldif.in
@@ -930,6 +930,7 @@ objectclass: nsIndex
cn: parentid
nssystemindex: true
nsindextype: eq
+nsmatchingrule: integerOrderingMatch
dn: cn=seeAlso,cn=default indexes, cn=config,cn=ldbm database,cn=plugins,cn=config
objectclass: top
diff --git a/ldap/servers/plugins/replication/repl5_replica_config.c b/ldap/servers/plugins/replication/repl5_replica_config.c
index 14020b7..3330a62 100644
--- a/ldap/servers/plugins/replication/repl5_replica_config.c
+++ b/ldap/servers/plugins/replication/repl5_replica_config.c
@@ -405,7 +405,7 @@ replica_config_modify (Slapi_PBlock *pb, Slapi_Entry* entryBefore, Slapi_Entry*
{
if (apply_mods)
replica_set_precise_purging(r, 0);
- }
+ }
else
{
*returncode = LDAP_UNWILLING_TO_PERFORM;
@@ -567,8 +567,7 @@ replica_config_modify (Slapi_PBlock *pb, Slapi_Entry* entryBefore, Slapi_Entry*
{
if (apply_mods)
{
- if (apply_mods && config_attr_value[0])
- {
+ if (config_attr_value[0]) {
PRUint64 on_off = 0;
if (strcasecmp(config_attr_value, "on") == 0){
@@ -587,7 +586,7 @@ replica_config_modify (Slapi_PBlock *pb, Slapi_Entry* entryBefore, Slapi_Entry*
break;
}
replica_set_precise_purging(r, on_off);
- } else if (apply_mods) {
+ } else {
replica_set_precise_purging(r, 0);
}
}
diff --git a/ldap/servers/plugins/replication/repl5_tot_protocol.c b/ldap/servers/plugins/replication/repl5_tot_protocol.c
index d0c4402..03d0c3e 100644
--- a/ldap/servers/plugins/replication/repl5_tot_protocol.c
+++ b/ldap/servers/plugins/replication/repl5_tot_protocol.c
@@ -323,6 +323,10 @@ repl5_tot_run(Private_Repl_Protocol *prp)
int init_retry = 0;
Replica *replica;
ReplicaId rid = 0; /* Used to create the replica keep alive subentry */
+ Slapi_Entry *suffix = NULL;
+ char **instances = NULL;
+ Slapi_Backend *be = NULL;
+ int is_entryrdn = 0;
PR_ASSERT(NULL != prp);
@@ -354,21 +358,21 @@ retry:
*/
if (rc != ACQUIRE_SUCCESS)
{
- int optype, ldaprc, wait_retry;
- conn_get_error(prp->conn, &optype, &ldaprc);
- if (rc == ACQUIRE_TRANSIENT_ERROR && INIT_RETRY_MAX > init_retry++) {
- wait_retry = init_retry * INIT_RETRY_INT;
- slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name, "Warning: unable to "
- "acquire replica for total update, error: %d,"
+ int optype, ldaprc, wait_retry;
+ conn_get_error(prp->conn, &optype, &ldaprc);
+ if (rc == ACQUIRE_TRANSIENT_ERROR && INIT_RETRY_MAX > init_retry++) {
+ wait_retry = init_retry * INIT_RETRY_INT;
+ slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name, "Warning: unable to "
+ "acquire replica for total update, error: %d,"
" retrying in %d seconds.\n",
- ldaprc, wait_retry);
- DS_Sleep(PR_SecondsToInterval(wait_retry));
- goto retry;
- } else {
- agmt_set_last_init_status(prp->agmt, ldaprc,
- prp->last_acquire_response_code, 0, NULL);
- goto done;
- }
+ ldaprc, wait_retry);
+ DS_Sleep(PR_SecondsToInterval(wait_retry));
+ goto retry;
+ } else {
+ agmt_set_last_init_status(prp->agmt, ldaprc,
+ prp->last_acquire_response_code, 0, NULL);
+ goto done;
+ }
}
else if (prp->terminate)
{
@@ -405,48 +409,121 @@ retry:
and that the order implies that perent entry is always ahead of the
child entry in the list. Otherwise, the consumer would not be
properly updated because bulk import at the moment skips orphand entries. */
- /* XXXggood above assumption may not be valid if orphaned entry moved???? */
+ /* XXXggood above assumption may not be valid if orphaned entry moved???? */
agmt_set_last_init_status(prp->agmt, 0, 0, 0, "Total update in progress");
slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name, "Beginning total update of replica "
- "\"%s\".\n", agmt_get_long_name(prp->agmt));
+ "\"%s\".\n", agmt_get_long_name(prp->agmt));
/* RMREPL - need to send schema here */
pb = slapi_pblock_new ();
- /* we need to provide managedsait control so that referral entries can
- be replicated */
- ctrls = (LDAPControl **)slapi_ch_calloc (3, sizeof (LDAPControl *));
- ctrls[0] = create_managedsait_control ();
- ctrls[1] = create_backend_control(area_sdn);
+ replica = (Replica*) object_get_data(prp->replica_object);
+ /*
+ * Get the info about the entryrdn vs. entrydn from the backend.
+ * If NOT is_entryrdn, its ancestor entries are always found prior to an entry.
+ */
+ rc = slapi_lookup_instance_name_by_suffix((char *)slapi_sdn_get_dn(area_sdn), NULL, &instances, 1);
+ if (rc || !instances) {
+ slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name, "Warning: unable to "
+ "get the instance name for the suffix \"%s\".\n", slapi_sdn_get_dn(area_sdn));
+ goto done;
+ }
+ be = slapi_be_select_by_instance_name(instances[0]);
+ if (!be) {
+ slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name, "Warning: unable to "
+ "get the instance for the suffix \"%s\".\n", slapi_sdn_get_dn(area_sdn));
+ goto done;
+ }
+ rc = slapi_back_get_info(be, BACK_INFO_IS_ENTRYRDN, (void **)&is_entryrdn);
+ if (is_entryrdn) {
+ /*
+ * Supporting entries out of order -- parent could have a larger id than its children.
+ * Entires are retireved sorted by parentid without the allid threshold.
+ */
+ /* Get suffix */
+ rc = slapi_search_internal_get_entry(area_sdn, NULL, &suffix, repl_get_plugin_identity(PLUGIN_MULTIMASTER_REPLICATION));
+ if (rc) {
+ slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name, "Warning: unable to "
+ "get the suffix entry \"%s\".\n", slapi_sdn_get_dn(area_sdn));
+ goto done;
+ }
- /* Time to make sure it exists a keep alive subentry for that replica */
- replica = (Replica*) object_get_data(prp->replica_object);
- if (replica)
- {
- rid = replica_get_rid(replica);
- }
- replica_subentry_check(area_sdn, rid);
-
- slapi_search_internal_set_pb (pb, slapi_sdn_get_dn (area_sdn),
- LDAP_SCOPE_SUBTREE, "(|(objectclass=ldapsubentry)(objectclass=nstombstone)(nsuniqueid=*))", NULL, 0, ctrls, NULL,
- repl_get_plugin_identity (PLUGIN_MULTIMASTER_REPLICATION), 0);
-
- cb_data.prp = prp;
- cb_data.rc = 0;
- cb_data.num_entries = 0UL;
- cb_data.sleep_on_busy = 0UL;
- cb_data.last_busy = current_time ();
- cb_data.flowcontrol_detection = 0;
- cb_data.lock = PR_NewLock();
-
- /* This allows during perform_operation to check the callback data
- * especially to do flow contol on delta send msgid / recv msgid
- */
- conn_set_tot_update_cb(prp->conn, (void *) &cb_data);
+ cb_data.prp = prp;
+ cb_data.rc = 0;
+ cb_data.num_entries = 1UL;
+ cb_data.sleep_on_busy = 0UL;
+ cb_data.last_busy = current_time ();
+ cb_data.flowcontrol_detection = 0;
+ cb_data.lock = PR_NewLock();
+
+ /* This allows during perform_operation to check the callback data
+ * especially to do flow contol on delta send msgid / recv msgid
+ */
+ conn_set_tot_update_cb(prp->conn, (void *) &cb_data);
+
+ /* Send suffix first. */
+ rc = send_entry(suffix, (void *)&cb_data);
+ if (rc) {
+ slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name, "Warning: unable to "
+ "send the suffix entry \"%s\" to the consumer.\n", slapi_sdn_get_dn(area_sdn));
+ goto done;
+ }
+
+ /* we need to provide managedsait control so that referral entries can
+ be replicated */
+ ctrls = (LDAPControl **)slapi_ch_calloc (3, sizeof (LDAPControl *));
+ ctrls[0] = create_managedsait_control ();
+ ctrls[1] = create_backend_control(area_sdn);
+
+ /* Time to make sure it exists a keep alive subentry for that replica */
+ if (replica)
+ {
+ rid = replica_get_rid(replica);
+ }
+ replica_subentry_check(area_sdn, rid);
+ /* Send the subtree of the suffix in the order of parentid index plus ldapsubentry and nstombstone. */
+ slapi_search_internal_set_pb(pb, slapi_sdn_get_dn (area_sdn),
+ LDAP_SCOPE_SUBTREE, "(|(parentid>=1)(objectclass=ldapsubentry)(objectclass=nstombstone))", NULL, 0, ctrls, NULL,
+ repl_get_plugin_identity (PLUGIN_MULTIMASTER_REPLICATION), 0);
+ cb_data.num_entries = 0UL;
+ } else {
+ /* Original total update */
+ /* we need to provide managedsait control so that referral entries can
+ be replicated */
+ ctrls = (LDAPControl **)slapi_ch_calloc (3, sizeof (LDAPControl *));
+ ctrls[0] = create_managedsait_control ();
+ ctrls[1] = create_backend_control(area_sdn);
+
+ /* Time to make sure it exists a keep alive subentry for that replica */
+ replica = (Replica*) object_get_data(prp->replica_object);
+ if (replica)
+ {
+ rid = replica_get_rid(replica);
+ }
+ replica_subentry_check(area_sdn, rid);
+
+ slapi_search_internal_set_pb (pb, slapi_sdn_get_dn (area_sdn),
+ LDAP_SCOPE_SUBTREE, "(|(objectclass=ldapsubentry)(objectclass=nstombstone)(nsuniqueid=*))", NULL, 0, ctrls, NULL,
+ repl_get_plugin_identity (PLUGIN_MULTIMASTER_REPLICATION), 0);
+
+ cb_data.prp = prp;
+ cb_data.rc = 0;
+ cb_data.num_entries = 0UL;
+ cb_data.sleep_on_busy = 0UL;
+ cb_data.last_busy = current_time ();
+ cb_data.flowcontrol_detection = 0;
+ cb_data.lock = PR_NewLock();
+
+ /* This allows during perform_operation to check the callback data
+ * especially to do flow contol on delta send msgid / recv msgid
+ */
+ conn_set_tot_update_cb(prp->conn, (void *) &cb_data);
+ }
+
/* Before we get started on sending entries to the replica, we need to
* setup things for async propagation:
* 1. Create a thread that will read the LDAP results from the connection.
@@ -470,7 +547,7 @@ retry:
slapi_search_internal_callback_pb (pb, &cb_data /* callback data */,
get_result /* result callback */,
send_entry /* entry callback */,
- NULL /* referral callback*/);
+ NULL /* referral callback*/);
/*
* After completing the sending operation (or optionally failing), we need to clean up
diff --git a/ldap/servers/slapd/back-ldbm/back-ldbm.h b/ldap/servers/slapd/back-ldbm/back-ldbm.h
index 9499292..2d77a8a 100644
--- a/ldap/servers/slapd/back-ldbm/back-ldbm.h
+++ b/ldap/servers/slapd/back-ldbm/back-ldbm.h
@@ -132,7 +132,7 @@ typedef unsigned short u_int16_t;
#define BDB_BACKEND "libback-ldbm" /* This backend plugin */
#define BDB_NEWIDL "newidl" /* new idl format */
#define BDB_RDNFORMAT "rdn-format" /* Subtree rename enabled */
-#define BDB_RDNFORMAT_VERSION "2" /* rdn-format version (by default, 0) */
+#define BDB_RDNFORMAT_VERSION "3" /* rdn-format version (by default, 0) */
#define BDB_DNFORMAT "dn-4514" /* DN format RFC 4514 compliant */
#define BDB_DNFORMAT_VERSION "1" /* DN format version */
@@ -808,11 +808,11 @@ typedef struct _back_search_result_set
/* #define LDBM_ENTRYRDN_OID "2.16.840.1.113730.3.1.2097" */
#define LDBM_ANCESTORID_STR "ancestorid"
-#define LDBM_ENTRYDN_STR "entrydn"
+#define LDBM_ENTRYDN_STR SLAPI_ATTR_ENTRYDN
#define LDBM_ENTRYRDN_STR "entryrdn"
#define LDBM_NUMSUBORDINATES_STR "numsubordinates"
#define LDBM_TOMBSTONE_NUMSUBORDINATES_STR "tombstonenumsubordinates"
-#define LDBM_PARENTID_STR "parentid"
+#define LDBM_PARENTID_STR SLAPI_ATTR_PARENTID
/* Name of psuedo attribute used to track default indexes */
#define LDBM_PSEUDO_ATTR_DEFAULT ".default"
diff --git a/ldap/servers/slapd/back-ldbm/dblayer.c b/ldap/servers/slapd/back-ldbm/dblayer.c
index bc290cb..93d42be 100644
--- a/ldap/servers/slapd/back-ldbm/dblayer.c
+++ b/ldap/servers/slapd/back-ldbm/dblayer.c
@@ -7315,6 +7315,11 @@ ldbm_back_get_info(Slapi_Backend *be, int cmd, void **info)
}
break;
}
+ case BACK_INFO_IS_ENTRYRDN:
+ {
+ *(int *)info = entryrdn_get_switch();
+ break;
+ }
default:
break;
}
diff --git a/ldap/servers/slapd/back-ldbm/filterindex.c b/ldap/servers/slapd/back-ldbm/filterindex.c
index 9c14de4..9a7e7be 100644
--- a/ldap/servers/slapd/back-ldbm/filterindex.c
+++ b/ldap/servers/slapd/back-ldbm/filterindex.c
@@ -552,6 +552,7 @@ range_candidates(
struct berval *low = NULL, *high = NULL;
struct berval **lows = NULL, **highs = NULL;
back_txn txn = {NULL};
+ int operator = 0;
LDAPDebug(LDAP_DEBUG_TRACE, "=> range_candidates attr=%s\n", type, 0, 0);
@@ -578,18 +579,21 @@ range_candidates(
}
high = attr_value_lowest(highs, slapi_berval_cmp);
}
-
+ if (entryrdn_get_switch() && !strcasecmp(type, LDBM_PARENTID_STR)) {
+ /* parentid is treated specially that is needed for the bulk import. (See #48755) */
+ operator = SLAPI_OP_RANGE_NO_IDL_SORT|SLAPI_OP_RANGE_NO_ALLIDS;
+ }
if (low == NULL) {
- idl = index_range_read_ext(pb, be, type, (char*)indextype_EQUALITY,
- SLAPI_OP_LESS_OR_EQUAL,
+ operator |= SLAPI_OP_LESS_OR_EQUAL;
+ idl = index_range_read_ext(pb, be, type, (char*)indextype_EQUALITY, operator,
high, NULL, 0, &txn, err, allidslimit);
} else if (high == NULL) {
- idl = index_range_read_ext(pb, be, type, (char*)indextype_EQUALITY,
- SLAPI_OP_GREATER_OR_EQUAL,
+ operator |= SLAPI_OP_GREATER_OR_EQUAL;
+ idl = index_range_read_ext(pb, be, type, (char*)indextype_EQUALITY, operator,
low, NULL, 0, &txn, err, allidslimit);
} else {
- idl = index_range_read_ext(pb, be, type, (char*)indextype_EQUALITY,
- SLAPI_OP_LESS_OR_EQUAL,
+ operator |= SLAPI_OP_LESS_OR_EQUAL;
+ idl = index_range_read_ext(pb, be, type, (char*)indextype_EQUALITY, operator,
low, high, 1, &txn, err, allidslimit);
}
diff --git a/ldap/servers/slapd/back-ldbm/idl_new.c b/ldap/servers/slapd/back-ldbm/idl_new.c
index 25b3bfa..6ca6c96 100644
--- a/ldap/servers/slapd/back-ldbm/idl_new.c
+++ b/ldap/servers/slapd/back-ldbm/idl_new.c
@@ -350,17 +350,31 @@ error:
return idl;
}
+typedef struct _range_id_pair {
+ ID key;
+ ID id;
+} idl_range_id_pair;
/*
* Perform the range search in the idl layer instead of the index layer
* to improve the performance.
*/
+/*
+ * NOTE:
+ * In the total update (bulk import), an entry requires its ancestors already added.
+ * To guarantee it, the range search with parentid is used with setting the flag
+ * SLAPI_OP_RANGE_NO_IDL_SORT in operator.
+ *
+ * If the flag is set,
+ * 1. the IDList is not sorted by the ID.
+ * 2. holding to add an ID to the IDList unless the key is found in the IDList.
+ */
IDList *
idl_new_range_fetch(
- backend *be,
- DB* db,
- DBT *lowerkey,
+ backend *be,
+ DB* db,
+ DBT *lowerkey,
DBT *upperkey,
- DB_TXN *txn,
+ DB_TXN *txn,
struct attrinfo *ai,
int *flag_err,
int allidslimit,
@@ -380,7 +394,7 @@ idl_new_range_fetch(
size_t count = 0;
#ifdef DB_USE_BULK_FETCH
/* beware that a large buffer on the stack might cause a stack overflow on some platforms */
- char buffer[BULK_FETCH_BUFFER_SIZE];
+ char buffer[BULK_FETCH_BUFFER_SIZE];
void *ptr;
DBT dataret;
#endif
@@ -388,15 +402,21 @@ idl_new_range_fetch(
struct ldbminfo *li = (struct ldbminfo *)be->be_database->plg_private;
time_t curtime;
void *saved_key = NULL;
+ int coreop = operator & SLAPI_OP_RANGE;
+ ID key;
+ ID suffix;
+ idl_range_id_pair *leftover = NULL;
+ size_t leftoverlen = 32;
+ int leftovercnt = 0;
if (NULL == flag_err) {
return NULL;
}
- *flag_err = 0;
if (NEW_IDL_NOOP == *flag_err) {
return NULL;
}
+
dblayer_txn_init(li, &s_txn);
if (txn) {
dblayer_read_txn_begin(be, txn, &s_txn);
@@ -460,7 +480,7 @@ idl_new_range_fetch(
#ifdef DB_USE_BULK_FETCH
while (cur_key.data &&
(upperkey && upperkey->data ?
- ((operator == SLAPI_OP_LESS) ?
+ ((coreop == SLAPI_OP_LESS) ?
DBTcmp(&cur_key, upperkey, ai->ai_key_cmp_fn) < 0 :
DBTcmp(&cur_key, upperkey, ai->ai_key_cmp_fn) <= 0) :
PR_TRUE /* e.g., (x > a) */)) {
@@ -496,6 +516,9 @@ idl_new_range_fetch(
goto error;
}
}
+ if (operator & SLAPI_OP_RANGE_NO_IDL_SORT) {
+ key = (ID)strtol((char *)cur_key.data+1 , (char **)NULL, 10);
+ }
while (PR_TRUE) {
DB_MULTIPLE_NEXT(ptr, &data, dataret.data, dataret.size);
if (dataret.data == NULL) break;
@@ -524,7 +547,29 @@ idl_new_range_fetch(
/* note the last id read to check for dups */
lastid = id;
/* we got another ID, add it to our IDL */
- idl_rc = idl_append_extend(&idl, id);
+ if (operator & SLAPI_OP_RANGE_NO_IDL_SORT) {
+ if (!idl) {
+ /* First time. Keep the suffix ID. */
+ suffix = key;
+ idl_rc = idl_append_extend(&idl, id);
+ } else if ((key == suffix) || idl_id_is_in_idlist(idl, key)) {
+ /* the parent is the suffix or already in idl. */
+ idl_rc = idl_append_extend(&idl, id);
+ } else {
+ /* Otherwise, keep the {key,id} in leftover array */
+ if (!leftover) {
+ leftover = (idl_range_id_pair *)slapi_ch_calloc(leftoverlen, sizeof(idl_range_id_pair));
+ } else if (leftovercnt == leftoverlen) {
+ leftover = (idl_range_id_pair *)slapi_ch_realloc((char *)leftover, 2 * leftoverlen * sizeof(idl_range_id_pair));
+ memset(leftover + leftovercnt, 0, leftoverlen);
+ leftoverlen *= 2;
+ }
+ leftover[leftovercnt].key = key;
+ leftover[leftovercnt++].id = id;
+ }
+ } else {
+ idl_rc = idl_append_extend(&idl, id);
+ }
if (idl_rc) {
LDAPDebug1Arg(LDAP_DEBUG_ANY,
"unable to extend id list (err=%d)\n", idl_rc);
@@ -581,7 +626,7 @@ idl_new_range_fetch(
}
#else
while (upperkey && upperkey->data ?
- ((operator == SLAPI_OP_LESS) ?
+ ((coreop == SLAPI_OP_LESS) ?
DBTcmp(&cur_key, upperkey, ai->ai_key_cmp_fn) < 0 :
DBTcmp(&cur_key, upperkey, ai->ai_key_cmp_fn) <= 0) :
PR_TRUE /* e.g., (x > a) */) {
@@ -698,9 +743,27 @@ error:
*flag_err = ret;
/* sort idl */
- if (idl && !ALLIDS(idl)) {
- qsort((void *)&idl->b_ids[0], idl->b_nids,
- (size_t)sizeof(ID), idl_sort_cmp);
+ if (idl && !ALLIDS(idl) && !(operator & SLAPI_OP_RANGE_NO_IDL_SORT)) {
+ qsort((void *)&idl->b_ids[0], idl->b_nids, (size_t)sizeof(ID), idl_sort_cmp);
+ }
+ if (operator & SLAPI_OP_RANGE_NO_IDL_SORT) {
+ int i;
+ int left = leftovercnt;
+ while (left) {
+ for (i = 0; i < leftovercnt; i++) {
+ if (leftover[i].key && idl_id_is_in_idlist(idl, leftover[i].key)) {
+ idl_rc = idl_append_extend(&idl, leftover[i].id);
+ if (idl_rc) {
+ LDAPDebug1Arg(LDAP_DEBUG_ANY, "unable to extend id list (err=%d)\n", idl_rc);
+ idl_free(&idl);
+ return NULL;
+ }
+ leftover[i].key = 0;
+ left--;
+ }
+ }
+ }
+ slapi_ch_free((void **)&leftover);
}
return idl;
}
diff --git a/ldap/servers/slapd/back-ldbm/index.c b/ldap/servers/slapd/back-ldbm/index.c
index 00e78a7..81d6621 100644
--- a/ldap/servers/slapd/back-ldbm/index.c
+++ b/ldap/servers/slapd/back-ldbm/index.c
@@ -1232,6 +1232,7 @@ index_range_read_ext(
int timelimit = -1;
back_search_result_set *sr = NULL;
int isroot = 0;
+ int coreop = operator & SLAPI_OP_RANGE;
if (!pb) {
LDAPDebug(LDAP_DEBUG_ANY, "index_range_read: NULL pblock\n",
@@ -1278,7 +1279,7 @@ index_range_read_ext(
LDAPDebug1Arg(LDAP_DEBUG_TRACE, "index_range_read lookthrough_limit=%d\n",
lookthrough_limit);
- switch( operator ) {
+ switch( coreop ) {
case SLAPI_OP_LESS:
case SLAPI_OP_LESS_OR_EQUAL:
case SLAPI_OP_GREATER_OR_EQUAL:
@@ -1287,7 +1288,7 @@ index_range_read_ext(
default:
LDAPDebug( LDAP_DEBUG_ANY,
"<= index_range_read(%s,%s) NULL (operator %i)\n",
- type, prefix, operator );
+ type, prefix, coreop );
index_free_prefix(prefix);
return( NULL );
}
@@ -1343,7 +1344,7 @@ index_range_read_ext(
if (range != 1) { /* open range search */
char *tmpbuf = NULL;
/* this is a search with only one boundary value */
- switch( operator ) {
+ switch( coreop ) {
case SLAPI_OP_LESS:
case SLAPI_OP_LESS_OR_EQUAL:
lowerkey.dptr = slapi_ch_strdup(prefix);
@@ -1451,8 +1452,17 @@ index_range_read_ext(
cur_key.data = lowerkey.data;
cur_key.size = lowerkey.size;
lowerkey.data = NULL; /* Don't need this any more, since the memory will be freed from cur_key */
- if (operator == SLAPI_OP_GREATER) {
- *err = index_range_next_key(db,&cur_key,db_txn);
+ *err = 0;
+ if (coreop == SLAPI_OP_GREATER) {
+ *err = index_range_next_key(db, &cur_key, db_txn);
+ if (*err) {
+ LDAPDebug(LDAP_DEBUG_ANY, "<= index_range_read(%s,%s) op==GREATER, no next key: %i)\n",
+ type, prefix, *err );
+ goto error;
+ }
+ }
+ if (operator & SLAPI_OP_RANGE_NO_ALLIDS) {
+ *err = NEW_IDL_NO_ALLID;
}
if (idl_get_idl_new()) { /* new idl */
idl = idl_new_range_fetch(be, db, &cur_key, &upperkey, db_txn,
@@ -1462,7 +1472,7 @@ index_range_read_ext(
int retry_count = 0;
while (*err == 0 &&
(upperkey.data &&
- (operator == SLAPI_OP_LESS) ?
+ (coreop == SLAPI_OP_LESS) ?
DBTcmp(&cur_key, &upperkey, ai->ai_key_cmp_fn) < 0 :
DBTcmp(&cur_key, &upperkey, ai->ai_key_cmp_fn) <= 0)) {
/* exit the loop when we either run off the end of the table,
diff --git a/ldap/servers/slapd/back-ldbm/init.c b/ldap/servers/slapd/back-ldbm/init.c
index a531abb..04cc936 100644
--- a/ldap/servers/slapd/back-ldbm/init.c
+++ b/ldap/servers/slapd/back-ldbm/init.c
@@ -36,7 +36,7 @@ ldbm_back_add_schema( Slapi_PBlock *pb )
SLAPI_ATTR_FLAG_NOUSERMOD );
rc |= slapi_add_internal_attr_syntax( LDBM_PARENTID_STR,
- LDBM_PARENTID_OID, DIRSTRING_SYNTAX_OID, CASEIGNOREMATCH_NAME,
+ LDBM_PARENTID_OID, INTEGER_SYNTAX_OID, INTEGERMATCH_NAME,
SLAPI_ATTR_FLAG_SINGLE|SLAPI_ATTR_FLAG_NOUSERMOD );
rc |= slapi_add_internal_attr_syntax( "entryid",
diff --git a/ldap/servers/slapd/back-ldbm/misc.c b/ldap/servers/slapd/back-ldbm/misc.c
index fe3d01b..77c1e70 100644
--- a/ldap/servers/slapd/back-ldbm/misc.c
+++ b/ldap/servers/slapd/back-ldbm/misc.c
@@ -79,6 +79,7 @@ static const char *systemIndexes[] = {
SLAPI_ATTR_NSCP_ENTRYDN,
ATTR_NSDS5_REPLCONFLICT,
SLAPI_ATTR_ENTRYUSN,
+ SLAPI_ATTR_PARENTID,
NULL
};
diff --git a/ldap/servers/slapd/entry.c b/ldap/servers/slapd/entry.c
index 3124ff6..d38f970 100644
--- a/ldap/servers/slapd/entry.c
+++ b/ldap/servers/slapd/entry.c
@@ -3429,8 +3429,8 @@ slapi_entry_rename(Slapi_Entry *e, const char *newrdn, int deleteoldrdn, Slapi_D
/* We remove the parentid and entrydn since the backend will change these.
* We don't want to give the caller an inconsistent entry. */
- slapi_entry_attr_delete(e, "parentid");
- slapi_entry_attr_delete(e, "entrydn");
+ slapi_entry_attr_delete(e, SLAPI_ATTR_PARENTID);
+ slapi_entry_attr_delete(e, SLAPI_ATTR_ENTRYDN);
/* Build new DN. If newsuperior is set, just use "newrdn,newsuperior". If
* newsuperior is not set, need to add newrdn to old superior. */
diff --git a/ldap/servers/slapd/slap.h b/ldap/servers/slapd/slap.h
index a594a29..ea834f1 100644
--- a/ldap/servers/slapd/slap.h
+++ b/ldap/servers/slapd/slap.h
@@ -536,12 +536,16 @@ typedef int (*SyntaxEnumFunc)(char **names, Slapi_PluginDesc *plugindesc,
/* OIDs for some commonly used matching rules */
#define DNMATCH_OID "2.5.13.1" /* distinguishedNameMatch */
#define CASEIGNOREMATCH_OID "2.5.13.2" /* caseIgnoreMatch */
+#define INTEGERMATCH_OID "2.5.13.14" /* integerMatch */
+#define INTEGERORDERINGMATCH_OID "2.5.13.15" /* integerOrderingMatch */
#define INTFIRSTCOMPMATCH_OID "2.5.13.29" /* integerFirstComponentMatch */
#define OIDFIRSTCOMPMATCH_OID "2.5.13.30" /* objectIdentifierFirstComponentMatch */
/* Names for some commonly used matching rules */
#define DNMATCH_NAME "distinguishedNameMatch"
#define CASEIGNOREMATCH_NAME "caseIgnoreMatch"
+#define INTEGERMATCH_NAME "integerMatch"
+#define INTEGERORDERINGMATCH_NAME "integerOrderingMatch"
#define INTFIRSTCOMPMATCH_NAME "integerFirstComponentMatch"
#define OIDFIRSTCOMPMATCH_NAME "objectIdentifierFirstComponentMatch"
diff --git a/ldap/servers/slapd/slapi-plugin.h b/ldap/servers/slapd/slapi-plugin.h
index 294f2c3..856432e 100644
--- a/ldap/servers/slapd/slapi-plugin.h
+++ b/ldap/servers/slapd/slapi-plugin.h
@@ -483,6 +483,7 @@ NSPR_API(PRUint32) PR_fprintf(struct PRFileDesc* fd, const char *fmt, ...)
#define SLAPI_ATTR_ENTRYDN "entrydn"
#define SLAPI_ATTR_DN "dn"
#define SLAPI_ATTR_RDN "rdn"
+#define SLAPI_ATTR_PARENTID "parentid"
#define SLAPI_ATTR_UNIQUEID_LENGTH 10
#define SLAPI_ATTR_OBJECTCLASS_LENGTH 11
#define SLAPI_ATTR_VALUE_TOMBSTONE_LENGTH 11
@@ -494,6 +495,7 @@ NSPR_API(PRUint32) PR_fprintf(struct PRFileDesc* fd, const char *fmt, ...)
#define SLAPI_ATTR_ENTRYDN_LENGTH 7
#define SLAPI_ATTR_DN_LENGTH 2
#define SLAPI_ATTR_RDN_LENGTH 3
+#define SLAPI_ATTR_PARENTID_LENGTH 8
/* plugin shared config area */
#define SLAPI_PLUGIN_SHARED_CONFIG_AREA "nsslapd-pluginConfigArea"
@@ -7002,6 +7004,9 @@ typedef struct slapi_plugindesc {
#define SLAPI_OP_GREATER_OR_EQUAL 4
#define SLAPI_OP_GREATER 5
#define SLAPI_OP_SUBSTRING 6
+#define SLAPI_OP_RANGE 0xff
+#define SLAPI_OP_RANGE_NO_IDL_SORT 0x100
+#define SLAPI_OP_RANGE_NO_ALLIDS 0x200
/* Defined values of SLAPI_PLUGIN_MR_USAGE: */
#define SLAPI_PLUGIN_MR_USAGE_INDEX 0
@@ -7585,7 +7590,8 @@ enum
BACK_INFO_CRYPT_ENCRYPT_VALUE, /* Ctrl: clcrypt_encrypt_value */
BACK_INFO_CRYPT_DECRYPT_VALUE, /* Ctrl: clcrypt_decrypt_value */
BACK_INFO_DIRECTORY, /* Get the directory path */
- BACK_INFO_LOG_DIRECTORY /* Get the txn log directory */
+ BACK_INFO_LOG_DIRECTORY, /* Get the txn log directory */
+ BACK_INFO_IS_ENTRYRDN /* Get the flag for entryrdn */
};
struct _back_info_crypt_init {
7 years, 10 months
ldap/servers
by William Brown
ldap/servers/slapd/plugin.c | 8 +++++++-
1 file changed, 7 insertions(+), 1 deletion(-)
New commits:
commit 7f8c64fb4bb4b0de68272a3908ae4d07afdba49c
Author: William Brown <firstyear(a)redhat.com>
Date: Wed Jun 8 09:29:05 2016 +1000
Ticket 48870 - Correct plugin execution order due to changes in exop
Bug Description: Due to changes to make exop handling more efficient, an
issue was introduced where the execution of plugins was re-ordered. This meant
the precedence rules when two plugins are equal, were not the same.
Fix Description: Return early when we find the plugin, rather than continuing
to loop to find the "last match"
https://fedorahosted.org/389/ticket/48870
Author: wibrown
Review by: nhosoi, tbordaz (Thanks, and sorry for breaking it ...)
diff --git a/ldap/servers/slapd/plugin.c b/ldap/servers/slapd/plugin.c
index a2b2d75..440be98 100644
--- a/ldap/servers/slapd/plugin.c
+++ b/ldap/servers/slapd/plugin.c
@@ -504,7 +504,13 @@ plugin_determine_exop_plugins( const char *oid, struct slapdplugin **plugin)
if ( strcasecmp( oid, p->plg_exoids[i] ) == 0 ) {
*plugin = p;
rc = p->plg_type;
- break;
+ /* break; */
+ /* WB - 48870, return early so that we can get the plugin
+ * that is set earlier in the list. This prevents the
+ * need to change add_plugin_to_list, which may have
+ * side effects.
+ */
+ return rc;
}
}
}
7 years, 10 months
Branch '389-ds-base-1.3.4' - dirsrvtests/tests
by Noriko Hosoi
dirsrvtests/tests/tickets/ticket48799_test.py | 170 ++++++++++++++++++++++++++
1 file changed, 170 insertions(+)
New commits:
commit 85f02127be1138434b99741c0de40787bf72d1af
Author: William Brown <firstyear(a)redhat.com>
Date: Wed Apr 20 11:16:59 2016 +1000
Ticket 48799 - Test cases for objectClass values being dropped.
Bug Description: During replication, not all objectClasses were added to
the consumer.
Fix Description: This test case proves the issue and fix.
https://fedorahosted.org/389/ticket/48799
Author: wibrown
Review by: nhosoi
(cherry picked from commit dcec32787a8ba87f8a9e15324cc789acbac7426f)
diff --git a/dirsrvtests/tests/tickets/ticket48799_test.py b/dirsrvtests/tests/tickets/ticket48799_test.py
new file mode 100644
index 0000000..e92b5fd
--- /dev/null
+++ b/dirsrvtests/tests/tickets/ticket48799_test.py
@@ -0,0 +1,170 @@
+import os
+import sys
+import time
+import ldap
+import logging
+import pytest
+from lib389 import DirSrv, Entry, tools, tasks
+from lib389.tools import DirSrvTools
+from lib389._constants import *
+from lib389.properties import *
+from lib389.tasks import *
+from lib389.utils import *
+
+logging.getLogger(__name__).setLevel(logging.DEBUG)
+log = logging.getLogger(__name__)
+
+class TopologyReplication(object):
+ def __init__(self, master1, consumer1):
+ master1.open()
+ self.master1 = master1
+ consumer1.open()
+ self.consumer1 = consumer1
+
+
+(a)pytest.fixture(scope="module")
+def topology(request):
+ # Creating master 1...
+ master1 = DirSrv(verbose=False)
+ args_instance[SER_HOST] = HOST_MASTER_1
+ args_instance[SER_PORT] = PORT_MASTER_1
+ args_instance[SER_SERVERID_PROP] = SERVERID_MASTER_1
+ args_instance[SER_CREATION_SUFFIX] = DEFAULT_SUFFIX
+ args_master = args_instance.copy()
+ master1.allocate(args_master)
+ instance_master1 = master1.exists()
+ if instance_master1:
+ master1.delete()
+ master1.create()
+ master1.open()
+ master1.replica.enableReplication(suffix=SUFFIX, role=REPLICAROLE_MASTER, replicaId=REPLICAID_MASTER_1)
+
+ # Creating consumer 1...
+ consumer1 = DirSrv(verbose=False)
+ args_instance[SER_HOST] = HOST_CONSUMER_1
+ args_instance[SER_PORT] = PORT_CONSUMER_1
+ args_instance[SER_SERVERID_PROP] = SERVERID_CONSUMER_1
+ args_instance[SER_CREATION_SUFFIX] = DEFAULT_SUFFIX
+ args_consumer = args_instance.copy()
+ consumer1.allocate(args_consumer)
+ instance_consumer1 = consumer1.exists()
+ if instance_consumer1:
+ consumer1.delete()
+ consumer1.create()
+ consumer1.open()
+ consumer1.replica.enableReplication(suffix=SUFFIX, role=REPLICAROLE_CONSUMER, replicaId=CONSUMER_REPLICAID)
+
+ #
+ # Create all the agreements
+ #
+ # Creating agreement from master 1 to consumer 1
+ properties = {RA_NAME: r'meTo_$host:$port',
+ RA_BINDDN: defaultProperties[REPLICATION_BIND_DN],
+ RA_BINDPW: defaultProperties[REPLICATION_BIND_PW],
+ RA_METHOD: defaultProperties[REPLICATION_BIND_METHOD],
+ RA_TRANSPORT_PROT: defaultProperties[REPLICATION_TRANSPORT]}
+ m1_c1_agmt = master1.agreement.create(suffix=SUFFIX, host=consumer1.host, port=consumer1.port, properties=properties)
+ if not m1_c1_agmt:
+ log.fatal("Fail to create a hub -> consumer replica agreement")
+ sys.exit(1)
+ log.debug("%s created" % m1_c1_agmt)
+
+ # Allow the replicas to get situated with the new agreements...
+ time.sleep(5)
+
+ #
+ # Initialize all the agreements
+ #
+ master1.agreement.init(SUFFIX, HOST_CONSUMER_1, PORT_CONSUMER_1)
+ master1.waitForReplInit(m1_c1_agmt)
+
+ # Check replication is working...
+ if master1.testReplication(DEFAULT_SUFFIX, consumer1):
+ log.info('Replication is working.')
+ else:
+ log.fatal('Replication is not working.')
+ assert False
+
+ # Delete each instance in the end
+ def fin():
+ master1.delete()
+ consumer1.delete()
+ request.addfinalizer(fin)
+
+ # Clear out the tmp dir
+ master1.clearTmpDir(__file__)
+
+ return TopologyReplication(master1, consumer1)
+
+def _add_custom_schema(server):
+ attr_value = "( 10.0.9.2342.19200300.100.1.1 NAME 'customManager' EQUALITY distinguishedNameMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.12 X-ORIGIN 'user defined' )"
+ mod = [(ldap.MOD_ADD, 'attributeTypes', attr_value)]
+ server.modify_s('cn=schema', mod)
+
+ oc_value = "( 1.3.6.1.4.1.4843.2.1 NAME 'customPerson' SUP inetorgperson STRUCTURAL MAY (customManager) X-ORIGIN 'user defined' )"
+ mod = [(ldap.MOD_ADD, 'objectclasses', oc_value)]
+ server.modify_s('cn=schema', mod)
+
+def _create_user(server):
+ server.add_s(Entry((
+ "uid=testuser,ou=People,%s" % DEFAULT_SUFFIX,
+ {
+ 'objectClass' : "top account posixaccount".split(),
+ 'uid' : 'testuser',
+ 'gecos' : 'Test User',
+ 'cn' : 'testuser',
+ 'homedirectory' : '/home/testuser',
+ 'passwordexpirationtime' : '20160710184141Z',
+ 'userpassword' : '!',
+ 'uidnumber' : '1111212',
+ 'gidnumber' : '1111212',
+ 'loginshell' : '/bin/bash'
+ }
+ )))
+
+def _modify_user(server):
+ mod = [
+ (ldap.MOD_ADD, 'objectClass', ['customPerson']),
+ (ldap.MOD_ADD, 'sn', ['User']),
+ (ldap.MOD_ADD, 'customManager', ['cn=manager']),
+ ]
+ server.modify("uid=testuser,ou=People,%s" % DEFAULT_SUFFIX, mod)
+
+def test_ticket48799(topology):
+ """Write your replication testcase here.
+
+ To access each DirSrv instance use: topology.master1, topology.master2,
+ ..., topology.hub1, ..., topology.consumer1,...
+
+ Also, if you need any testcase initialization,
+ please, write additional fixture for that(include finalizer).
+ """
+
+ # Add the new schema element.
+ _add_custom_schema(topology.master1)
+ _add_custom_schema(topology.consumer1)
+
+ # Add a new user on the master.
+ _create_user(topology.master1)
+ # Modify the user on the master.
+ _modify_user(topology.master1)
+
+ # We need to wait for replication here.
+ time.sleep(15)
+
+ # Now compare the master vs consumer, and see if the objectClass was dropped.
+
+ master_entry = topology.master1.search_s("uid=testuser,ou=People,%s" % DEFAULT_SUFFIX, ldap.SCOPE_BASE, '(objectclass=*)', ['objectClass'])
+ consumer_entry = topology.consumer1.search_s("uid=testuser,ou=People,%s" % DEFAULT_SUFFIX, ldap.SCOPE_BASE, '(objectclass=*)', ['objectClass'])
+
+ assert(master_entry == consumer_entry)
+
+
+ log.info('Test complete')
+
+
+if __name__ == '__main__':
+ # Run isolated
+ # -s for DEBUG mode
+ CURRENT_FILE = os.path.realpath(__file__)
+ pytest.main("-s %s" % CURRENT_FILE)
7 years, 10 months
dirsrvtests/tests
by Noriko Hosoi
dirsrvtests/tests/tickets/ticket48799_test.py | 170 ++++++++++++++++++++++++++
1 file changed, 170 insertions(+)
New commits:
commit dcec32787a8ba87f8a9e15324cc789acbac7426f
Author: William Brown <firstyear(a)redhat.com>
Date: Wed Apr 20 11:16:59 2016 +1000
Ticket 48799 - Test cases for objectClass values being dropped.
Bug Description: During replication, not all objectClasses were added to
the consumer.
Fix Description: This test case proves the issue and fix.
https://fedorahosted.org/389/ticket/48799
Author: wibrown
Review by: nhosoi
diff --git a/dirsrvtests/tests/tickets/ticket48799_test.py b/dirsrvtests/tests/tickets/ticket48799_test.py
new file mode 100644
index 0000000..e92b5fd
--- /dev/null
+++ b/dirsrvtests/tests/tickets/ticket48799_test.py
@@ -0,0 +1,170 @@
+import os
+import sys
+import time
+import ldap
+import logging
+import pytest
+from lib389 import DirSrv, Entry, tools, tasks
+from lib389.tools import DirSrvTools
+from lib389._constants import *
+from lib389.properties import *
+from lib389.tasks import *
+from lib389.utils import *
+
+logging.getLogger(__name__).setLevel(logging.DEBUG)
+log = logging.getLogger(__name__)
+
+class TopologyReplication(object):
+ def __init__(self, master1, consumer1):
+ master1.open()
+ self.master1 = master1
+ consumer1.open()
+ self.consumer1 = consumer1
+
+
+(a)pytest.fixture(scope="module")
+def topology(request):
+ # Creating master 1...
+ master1 = DirSrv(verbose=False)
+ args_instance[SER_HOST] = HOST_MASTER_1
+ args_instance[SER_PORT] = PORT_MASTER_1
+ args_instance[SER_SERVERID_PROP] = SERVERID_MASTER_1
+ args_instance[SER_CREATION_SUFFIX] = DEFAULT_SUFFIX
+ args_master = args_instance.copy()
+ master1.allocate(args_master)
+ instance_master1 = master1.exists()
+ if instance_master1:
+ master1.delete()
+ master1.create()
+ master1.open()
+ master1.replica.enableReplication(suffix=SUFFIX, role=REPLICAROLE_MASTER, replicaId=REPLICAID_MASTER_1)
+
+ # Creating consumer 1...
+ consumer1 = DirSrv(verbose=False)
+ args_instance[SER_HOST] = HOST_CONSUMER_1
+ args_instance[SER_PORT] = PORT_CONSUMER_1
+ args_instance[SER_SERVERID_PROP] = SERVERID_CONSUMER_1
+ args_instance[SER_CREATION_SUFFIX] = DEFAULT_SUFFIX
+ args_consumer = args_instance.copy()
+ consumer1.allocate(args_consumer)
+ instance_consumer1 = consumer1.exists()
+ if instance_consumer1:
+ consumer1.delete()
+ consumer1.create()
+ consumer1.open()
+ consumer1.replica.enableReplication(suffix=SUFFIX, role=REPLICAROLE_CONSUMER, replicaId=CONSUMER_REPLICAID)
+
+ #
+ # Create all the agreements
+ #
+ # Creating agreement from master 1 to consumer 1
+ properties = {RA_NAME: r'meTo_$host:$port',
+ RA_BINDDN: defaultProperties[REPLICATION_BIND_DN],
+ RA_BINDPW: defaultProperties[REPLICATION_BIND_PW],
+ RA_METHOD: defaultProperties[REPLICATION_BIND_METHOD],
+ RA_TRANSPORT_PROT: defaultProperties[REPLICATION_TRANSPORT]}
+ m1_c1_agmt = master1.agreement.create(suffix=SUFFIX, host=consumer1.host, port=consumer1.port, properties=properties)
+ if not m1_c1_agmt:
+ log.fatal("Fail to create a hub -> consumer replica agreement")
+ sys.exit(1)
+ log.debug("%s created" % m1_c1_agmt)
+
+ # Allow the replicas to get situated with the new agreements...
+ time.sleep(5)
+
+ #
+ # Initialize all the agreements
+ #
+ master1.agreement.init(SUFFIX, HOST_CONSUMER_1, PORT_CONSUMER_1)
+ master1.waitForReplInit(m1_c1_agmt)
+
+ # Check replication is working...
+ if master1.testReplication(DEFAULT_SUFFIX, consumer1):
+ log.info('Replication is working.')
+ else:
+ log.fatal('Replication is not working.')
+ assert False
+
+ # Delete each instance in the end
+ def fin():
+ master1.delete()
+ consumer1.delete()
+ request.addfinalizer(fin)
+
+ # Clear out the tmp dir
+ master1.clearTmpDir(__file__)
+
+ return TopologyReplication(master1, consumer1)
+
+def _add_custom_schema(server):
+ attr_value = "( 10.0.9.2342.19200300.100.1.1 NAME 'customManager' EQUALITY distinguishedNameMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.12 X-ORIGIN 'user defined' )"
+ mod = [(ldap.MOD_ADD, 'attributeTypes', attr_value)]
+ server.modify_s('cn=schema', mod)
+
+ oc_value = "( 1.3.6.1.4.1.4843.2.1 NAME 'customPerson' SUP inetorgperson STRUCTURAL MAY (customManager) X-ORIGIN 'user defined' )"
+ mod = [(ldap.MOD_ADD, 'objectclasses', oc_value)]
+ server.modify_s('cn=schema', mod)
+
+def _create_user(server):
+ server.add_s(Entry((
+ "uid=testuser,ou=People,%s" % DEFAULT_SUFFIX,
+ {
+ 'objectClass' : "top account posixaccount".split(),
+ 'uid' : 'testuser',
+ 'gecos' : 'Test User',
+ 'cn' : 'testuser',
+ 'homedirectory' : '/home/testuser',
+ 'passwordexpirationtime' : '20160710184141Z',
+ 'userpassword' : '!',
+ 'uidnumber' : '1111212',
+ 'gidnumber' : '1111212',
+ 'loginshell' : '/bin/bash'
+ }
+ )))
+
+def _modify_user(server):
+ mod = [
+ (ldap.MOD_ADD, 'objectClass', ['customPerson']),
+ (ldap.MOD_ADD, 'sn', ['User']),
+ (ldap.MOD_ADD, 'customManager', ['cn=manager']),
+ ]
+ server.modify("uid=testuser,ou=People,%s" % DEFAULT_SUFFIX, mod)
+
+def test_ticket48799(topology):
+ """Write your replication testcase here.
+
+ To access each DirSrv instance use: topology.master1, topology.master2,
+ ..., topology.hub1, ..., topology.consumer1,...
+
+ Also, if you need any testcase initialization,
+ please, write additional fixture for that(include finalizer).
+ """
+
+ # Add the new schema element.
+ _add_custom_schema(topology.master1)
+ _add_custom_schema(topology.consumer1)
+
+ # Add a new user on the master.
+ _create_user(topology.master1)
+ # Modify the user on the master.
+ _modify_user(topology.master1)
+
+ # We need to wait for replication here.
+ time.sleep(15)
+
+ # Now compare the master vs consumer, and see if the objectClass was dropped.
+
+ master_entry = topology.master1.search_s("uid=testuser,ou=People,%s" % DEFAULT_SUFFIX, ldap.SCOPE_BASE, '(objectclass=*)', ['objectClass'])
+ consumer_entry = topology.consumer1.search_s("uid=testuser,ou=People,%s" % DEFAULT_SUFFIX, ldap.SCOPE_BASE, '(objectclass=*)', ['objectClass'])
+
+ assert(master_entry == consumer_entry)
+
+
+ log.info('Test complete')
+
+
+if __name__ == '__main__':
+ # Run isolated
+ # -s for DEBUG mode
+ CURRENT_FILE = os.path.realpath(__file__)
+ pytest.main("-s %s" % CURRENT_FILE)
7 years, 10 months
2 commits - ldap/servers
by William Brown
ldap/servers/slapd/backend_manager.c | 1 +
ldap/servers/slapd/daemon.c | 2 +-
ldap/servers/slapd/plugin.c | 12 +++++++++---
ldap/servers/slapd/proto-slap.h | 1 +
ldap/servers/slapd/util.c | 33 ++++++---------------------------
5 files changed, 18 insertions(+), 31 deletions(-)
New commits:
commit 0f4f838b29df3e33bf0751c80baf2d4e052856ef
Author: William Brown <firstyear(a)redhat.com>
Date: Thu Jun 2 11:04:05 2016 +1000
Ticket 48863 - remove check for vmsize from util_info_sys_pages
Bug Description: https://bugzilla.redhat.com/show_bug.cgi?id=1341709 and
on another instance could not allocate cache because it would exceed memory size
In both cases I suspect that that vmsize was low, and this causes the
util_info_sys_pages check to return a low pages number, which triggers the
checking to fail
Fix Description: Remove the checks related to vmsize. It's either too low,
or just too high and wrong. Either way, we never rely on it. Lets use reliable
data.
https://fedorahosted.org/389/ticket/48863
Author: wibrown
Review by: nhosoi (Thanks!)
diff --git a/ldap/servers/slapd/util.c b/ldap/servers/slapd/util.c
index 89c7579..3f17461 100644
--- a/ldap/servers/slapd/util.c
+++ b/ldap/servers/slapd/util.c
@@ -1538,7 +1538,7 @@ int util_info_sys_pages(size_t *pagesize, size_t *pages, size_t *procpages, size
* This space is a "funny number". It's a best effort based system
* where linux instead of telling us how much memory *actually* exists
* for us to use, gives us a virtual memory allocation which is the
- * value of ram + swap.
+ * value of ram + swap.... sometimes. Depends on platform.
*
* But none of these pages even exist or belong to us on the real system
* until will malloc them AND write a non-zero to them.
@@ -1569,13 +1569,12 @@ int util_info_sys_pages(size_t *pagesize, size_t *pages, size_t *procpages, size
* potentially allocate: generally, this will be MemAvailable.
*/
- size_t vmsize = 0;
size_t freesize = 0;
size_t rlimsize = 0;
*pagesize = getpagesize();
- /* Get the amount of freeram, rss, and the vmsize */
+ /* Get the amount of freeram, rss */
FILE *f;
char fn[40], s[80];
@@ -1596,9 +1595,6 @@ int util_info_sys_pages(size_t *pagesize, size_t *pages, size_t *procpages, size
/* VmRSS shows us what we are ACTUALLY using for proc pages
* Rather than "funny" pages.
*/
- if (strncmp(s, "VmSize:", 7) == 0) {
- sscanf(s+7, "%lu", (long unsigned int *)&vmsize);
- }
if (strncmp(s, "VmRSS:", 6) == 0) {
sscanf(s+6, "%lu", (long unsigned int *)procpages);
}
@@ -1641,29 +1637,11 @@ int util_info_sys_pages(size_t *pagesize, size_t *pages, size_t *procpages, size
/* This is in bytes, make it pages */
rlimsize = rlimsize / *pagesize;
}
- /* Now we have vmsize, the availpages from getrlimit, our freesize */
- vmsize /= (*pagesize / 1024);
- /* Pages is the total ram on the system. We should smaller of:
- * - vmsize
- * - pages
- */
- LDAPDebug(LDAP_DEBUG_TRACE,"util_info_sys_pages pages=%lu, vmsize=%lu, \n",
- (unsigned long) *pages, (unsigned long) vmsize,0);
-#if __GNUC__
-#if __x86_64__ || __ppc64__
- /* On 64bit platforms, vmsize is set high (VmSize), and doesn't change. IE 17tb */
- if (vmsize < *pages) {
- LDAPDebug(LDAP_DEBUG_TRACE,"util_info_sys_pages using vmsize for pages \n",0,0,0);
- *pages = vmsize;
- } else {
- LDAPDebug(LDAP_DEBUG_TRACE,"util_info_sys_pages using pages for pages \n",0,0,0);
- }
-#else
- /* On 32bit platforms, vmsize is set low (VmSize) and grows. */
+ /* Pages is the total ram on the system. */
+ LDAPDebug(LDAP_DEBUG_TRACE,"util_info_sys_pages pages=%lu, \n",
+ (unsigned long) *pages, 0,0);
LDAPDebug(LDAP_DEBUG_TRACE,"util_info_sys_pages using pages for pages \n",0,0,0);
-#endif
-#endif
/* Availpages is how much we *could* alloc. We should take the smallest:
* - pages
@@ -1809,6 +1787,7 @@ int util_is_cachesize_sane(size_t *cachesize)
* the remaining system mem to the cachesize instead, and log a warning
*/
*cachesize = (size_t)((availpages * 0.75 ) * pagesize);
+ slapi_log_error(SLAPI_LOG_FATAL, "util_is_cachesize_sane", "Available pages %lu, requested pages %lu, pagesize %lu\n", (unsigned long)availpages, (unsigned long)cachepages, (unsigned long)pagesize);
slapi_log_error(SLAPI_LOG_FATAL, "util_is_cachesize_sane", "WARNING adjusted cachesize to %lu\n", (unsigned long)*cachesize);
}
#else
commit e7aabb539a8f05bc9c1c5e90c4e4a2cbfbc826cb
Author: William Brown <firstyear(a)redhat.com>
Date: Tue Jun 7 13:49:20 2016 +1000
Ticket 48872 - Fix segfault and use after free in plugin shutdown
Bug Description: Plugin_closeall would free the plugin password name scheme
value *even* if the plugin was not a password scheme.
backend manager would call be_cleanup on the plugin *after* plugin_closeall was
run. This means plugin_closeall now does not close backends, and we call
plugin_free from the backend to do this.
Fix Description: Check the union type to make sure we free the pwdscheme
only on password plugins.
Expose plugin_free, and call it from the backend manager. Don't close backends
during the call to plugin_closeall.
https://fedorahosted.org/389/ticket/48872
Author: wibrown
Review by: mreynolds (Thanks!)
diff --git a/ldap/servers/slapd/backend_manager.c b/ldap/servers/slapd/backend_manager.c
index 9084d95..979144a 100644
--- a/ldap/servers/slapd/backend_manager.c
+++ b/ldap/servers/slapd/backend_manager.c
@@ -323,6 +323,7 @@ be_cleanupall()
slapi_pblock_set( &pb, SLAPI_BACKEND, backends[i] );
(*backends[i]->be_cleanup)( &pb );
+ plugin_free(backends[i]->be_database);
slapi_be_free(&backends[i]);
}
}
diff --git a/ldap/servers/slapd/daemon.c b/ldap/servers/slapd/daemon.c
index 862f6e7..90d1f7d 100644
--- a/ldap/servers/slapd/daemon.c
+++ b/ldap/servers/slapd/daemon.c
@@ -1318,7 +1318,7 @@ void slapd_daemon( daemon_ports_t *ports )
uniqueIDGenCleanup ();
}
- plugin_closeall( 1 /* Close Backends */, 1 /* Close Globals */);
+ plugin_closeall( 0 /* Close Backends */, 1 /* Close Globals */);
if ( ! in_referral_mode ) {
/* Close SNMP collator after the plugins closed...
diff --git a/ldap/servers/slapd/plugin.c b/ldap/servers/slapd/plugin.c
index 39bfcef..a2b2d75 100644
--- a/ldap/servers/slapd/plugin.c
+++ b/ldap/servers/slapd/plugin.c
@@ -52,7 +52,6 @@ static PRBool plugin_matches_operation (Slapi_DN *target_spec, PluginTargetData
static void plugin_config_init (struct pluginconfig *config);
static void plugin_config_cleanup (struct pluginconfig *config);
-static void plugin_free(struct slapdplugin *plugin);
static int plugin_config_set_action (int *action, char *value);
static struct pluginconfig* plugin_get_config (struct slapdplugin *plugin);
static void default_plugin_init();
@@ -1951,6 +1950,10 @@ plugin_closeall(int close_backends, int close_globals)
iterp = dep_plugin_entries;
while (iterp) {
nextp = iterp->next;
+ if (close_backends == 0 && iterp->plugin->plg_type == SLAPI_PLUGIN_DATABASE) {
+ iterp = nextp;
+ continue;
+ }
slapi_entry_free(iterp->e);
plugin_free(iterp->plugin);
slapi_ch_free((void **)&iterp);
@@ -2703,15 +2706,18 @@ plugin_add_descriptive_attributes( Slapi_Entry *e, struct slapdplugin *plugin )
/*
clean up the memory associated with the plugin
*/
-static void
+void
plugin_free(struct slapdplugin *plugin)
{
+ slapi_log_error(SLAPI_LOG_TRACE, "plugin_free", "Freeing %s \n", plugin->plg_name );
charray_free(plugin->plg_argv);
slapi_ch_free_string(&plugin->plg_libpath);
slapi_ch_free_string(&plugin->plg_initfunc);
slapi_ch_free_string(&plugin->plg_name);
slapi_ch_free_string(&plugin->plg_dn);
- slapi_ch_free_string(&plugin->plg_pwdstorageschemename);
+ if (plugin->plg_type == SLAPI_PLUGIN_PWD_STORAGE_SCHEME) {
+ slapi_ch_free_string(&plugin->plg_pwdstorageschemename);
+ }
release_componentid(plugin->plg_identity);
slapi_counter_destroy(&plugin->plg_op_counter);
if (!plugin->plg_group)
diff --git a/ldap/servers/slapd/proto-slap.h b/ldap/servers/slapd/proto-slap.h
index b8d7b86..1de1e38 100644
--- a/ldap/servers/slapd/proto-slap.h
+++ b/ldap/servers/slapd/proto-slap.h
@@ -879,6 +879,7 @@ void global_plugin_init();
int plugin_call_plugins( Slapi_PBlock *, int );
int plugin_setup(Slapi_Entry *plugin_entry, struct slapi_componentid *group,
slapi_plugin_init_fnptr initfunc, int add_to_dit, char *returntext);
+void plugin_free(struct slapdplugin *plugin);
int plugin_determine_exop_plugins( const char *oid, struct slapdplugin **plugin );
int plugin_call_exop_plugins( Slapi_PBlock *pb, struct slapdplugin *p );
Slapi_Backend * plugin_extended_op_getbackend( Slapi_PBlock *pb, struct slapdplugin *p);
7 years, 10 months
Branch '389-ds-base-1.3.3' - dirsrvtests/tests ldap/servers
by Mark Reynolds
dirsrvtests/tests/tickets/ticket47462_test.py | 389 ++++++++++++--------------
ldap/servers/slapd/daemon.c | 209 ++++---------
ldap/servers/slapd/pw.c | 4
ldap/servers/slapd/task.c | 346 +++++++++++++++++++++++
4 files changed, 592 insertions(+), 356 deletions(-)
New commits:
commit fd96d1bcfc854047e8796c07e44b134d560c495d
Author: Mark Reynolds <mreynolds(a)redhat.com>
Date: Tue Jun 7 10:02:42 2016 -0400
Ticket 48862 - At startup DES to AES password conversion causes timeout in start script
Bug Description: At server start all the backends are searches for entries that contain
DES password attributes as defined in the plugin. These are typically
unindexed searches, and if there is a very large backend this can cause
the server startup to timeout.
Fix Description: At startup only check "cn=config" for entries with DES password attributes.
A new "conversion" task has been created that can be run after startup
to search all backends(if a suffix is not specified), or specific backends.
dn: cn=convertPasswords, cn=des2aes,cn=tasks,cn=config
objectclass: top
objectclass: extensibleObject
suffix: dc=example,dc=com
suffix: dc=other,dc=suffix
Another bug was discovered in pw_rever_encode() in pw.c where a "for" loop
counter was accidentially reused by a second "for" loop. This could lead
to an infinite loop/hang.
Updated the CI test to perform the conversion task.
https://fedorahosted.org/389/ticket/48862
Reviewed by: nhosoi(Thanks!)
(cherry picked from commit 11f55f3dd2a2c44ddf7b5be54273401add13b1bc)
diff --git a/dirsrvtests/tests/tickets/ticket47462_test.py b/dirsrvtests/tests/tickets/ticket47462_test.py
index a909b37..2ac1478 100644
--- a/dirsrvtests/tests/tickets/ticket47462_test.py
+++ b/dirsrvtests/tests/tickets/ticket47462_test.py
@@ -1,19 +1,20 @@
-import os
+# --- BEGIN COPYRIGHT BLOCK ---
+# Copyright (C) 2015 Red Hat, Inc.
+# All rights reserved.
+#
+# License: GPL (version 3 or any later version).
+# See LICENSE for details.
+# --- END COPYRIGHT BLOCK ---
+#
import sys
import time
import ldap
import logging
-import socket
-import time
-import logging
import pytest
-import re
from lib389 import DirSrv, Entry, tools
from lib389.tools import DirSrvTools
from lib389._constants import *
from lib389.properties import *
-from constants import *
-from lib389._constants import *
logging.getLogger(__name__).setLevel(logging.DEBUG)
log = logging.getLogger(__name__)
@@ -31,6 +32,7 @@ AGMT_DN = ''
USER_DN = 'cn=test_user,' + DEFAULT_SUFFIX
USER1_DN = 'cn=test_user1,' + DEFAULT_SUFFIX
TEST_REPL_DN = 'cn=test repl,' + DEFAULT_SUFFIX
+DES2AES_TASK_DN = 'cn=convert,cn=des2aes,cn=tasks,cn=config'
class TopologyMaster1Master2(object):
@@ -47,27 +49,6 @@ def topology(request):
'''
This fixture is used to create a replicated topology for the 'module'.
The replicated topology is MASTER1 <-> Master2.
- At the beginning, It may exists a master2 instance and/or a master2 instance.
- It may also exists a backup for the master1 and/or the master2.
-
- Principle:
- If master1 instance exists:
- restart it
- If master2 instance exists:
- restart it
- If backup of master1 AND backup of master2 exists:
- create or rebind to master1
- create or rebind to master2
-
- restore master1 from backup
- restore master2 from backup
- else:
- Cleanup everything
- remove instances
- remove backups
- Create instances
- Initialize replication
- Create backups
'''
global installation1_prefix
global installation2_prefix
@@ -96,134 +77,69 @@ def topology(request):
args_master = args_instance.copy()
master2.allocate(args_master)
- # Get the status of the backups
- backup_master1 = master1.checkBackupFS()
- backup_master2 = master2.checkBackupFS()
-
# Get the status of the instance and restart it if it exists
instance_master1 = master1.exists()
- if instance_master1:
- master1.stop(timeout=10)
- master1.start(timeout=10)
-
instance_master2 = master2.exists()
- if instance_master2:
- master2.stop(timeout=10)
- master2.start(timeout=10)
-
- if backup_master1 and backup_master2:
- # The backups exist, assuming they are correct
- # we just re-init the instances with them
- if not instance_master1:
- master1.create()
- # Used to retrieve configuration information (dbdir, confdir...)
- master1.open()
-
- if not instance_master2:
- master2.create()
- # Used to retrieve configuration information (dbdir, confdir...)
- master2.open()
-
- # restore master1 from backup
- master1.stop(timeout=10)
- master1.restoreFS(backup_master1)
- master1.start(timeout=10)
-
- # restore master2 from backup
- master2.stop(timeout=10)
- master2.restoreFS(backup_master2)
- master2.start(timeout=10)
- else:
- # We should be here only in two conditions
- # - This is the first time a test involve master-consumer
- # so we need to create everything
- # - Something weird happened (instance/backup destroyed)
- # so we discard everything and recreate all
-
- # Remove all the backups. So even if we have a specific backup file
- # (e.g backup_master) we clear all backups that an instance my have created
- if backup_master1:
- master1.clearBackupFS()
- if backup_master2:
- master2.clearBackupFS()
-
- # Remove all the instances
- if instance_master1:
- master1.delete()
- if instance_master2:
- master2.delete()
-
- # Create the instances
- master1.create()
- master1.open()
- master2.create()
- master2.open()
- #
- # Now prepare the Master-Consumer topology
- #
- # First Enable replication
- master1.replica.enableReplication(suffix=SUFFIX, role=REPLICAROLE_MASTER, replicaId=REPLICAID_MASTER_1)
- master2.replica.enableReplication(suffix=SUFFIX, role=REPLICAROLE_MASTER, replicaId=REPLICAID_MASTER_2)
-
- # Initialize the supplier->consumer
-
- properties = {RA_NAME: r'meTo_$host:$port',
- RA_BINDDN: defaultProperties[REPLICATION_BIND_DN],
- RA_BINDPW: defaultProperties[REPLICATION_BIND_PW],
- RA_METHOD: defaultProperties[REPLICATION_BIND_METHOD],
- RA_TRANSPORT_PROT: defaultProperties[REPLICATION_TRANSPORT]}
- AGMT_DN = master1.agreement.create(suffix=SUFFIX, host=master2.host, port=master2.port, properties=properties)
- master1.agreement
- if not AGMT_DN:
- log.fatal("Fail to create a replica agreement")
- sys.exit(1)
-
- log.debug("%s created" % AGMT_DN)
-
- properties = {RA_NAME: r'meTo_$host:$port',
- RA_BINDDN: defaultProperties[REPLICATION_BIND_DN],
- RA_BINDPW: defaultProperties[REPLICATION_BIND_PW],
- RA_METHOD: defaultProperties[REPLICATION_BIND_METHOD],
- RA_TRANSPORT_PROT: defaultProperties[REPLICATION_TRANSPORT]}
- master2.agreement.create(suffix=DEFAULT_SUFFIX, host=master1.host, port=master1.port, properties=properties)
-
- master1.agreement.init(SUFFIX, HOST_MASTER_2, PORT_MASTER_2)
- master1.waitForReplInit(AGMT_DN)
-
- # Check replication is working fine
- master1.add_s(Entry((TEST_REPL_DN, {'objectclass': "top person".split(),
- 'sn': 'test_repl',
- 'cn': 'test_repl'})))
- loop = 0
- while loop <= 10:
- try:
- ent = master2.getEntry(TEST_REPL_DN, ldap.SCOPE_BASE, "(objectclass=*)")
- break
- except ldap.NO_SUCH_OBJECT:
- time.sleep(1)
- loop += 1
- if not ent:
- log.fatal('Replication is not working!')
- assert False
+ # Remove all the instances
+ if instance_master1:
+ master1.delete()
+ if instance_master2:
+ master2.delete()
- # Time to create the backups
- master1.stop(timeout=10)
- master1.backupfile = master1.backupFS()
- master1.start(timeout=10)
+ # Create the instances
+ master1.create()
+ master1.open()
+ master2.create()
+ master2.open()
- master2.stop(timeout=10)
- master2.backupfile = master2.backupFS()
- master2.start(timeout=10)
+ #
+ # Now prepare the Master-Consumer topology
+ #
+ # First Enable replication
+ master1.replica.enableReplication(suffix=SUFFIX, role=REPLICAROLE_MASTER, replicaId=REPLICAID_MASTER_1)
+ master2.replica.enableReplication(suffix=SUFFIX, role=REPLICAROLE_MASTER, replicaId=REPLICAID_MASTER_2)
+
+ # Initialize the supplier->consumer
+
+ properties = {RA_NAME: r'meTo_$host:$port',
+ RA_BINDDN: defaultProperties[REPLICATION_BIND_DN],
+ RA_BINDPW: defaultProperties[REPLICATION_BIND_PW],
+ RA_METHOD: defaultProperties[REPLICATION_BIND_METHOD],
+ RA_TRANSPORT_PROT: defaultProperties[REPLICATION_TRANSPORT]}
+ AGMT_DN = master1.agreement.create(suffix=SUFFIX, host=master2.host, port=master2.port, properties=properties)
+ master1.agreement
+ if not AGMT_DN:
+ log.fatal("Fail to create a replica agreement")
+ sys.exit(1)
+
+ log.debug("%s created" % AGMT_DN)
+
+ properties = {RA_NAME: r'meTo_$host:$port',
+ RA_BINDDN: defaultProperties[REPLICATION_BIND_DN],
+ RA_BINDPW: defaultProperties[REPLICATION_BIND_PW],
+ RA_METHOD: defaultProperties[REPLICATION_BIND_METHOD],
+ RA_TRANSPORT_PROT: defaultProperties[REPLICATION_TRANSPORT]}
+ master2.agreement.create(suffix=DEFAULT_SUFFIX, host=master1.host, port=master1.port, properties=properties)
+
+ master1.agreement.init(SUFFIX, HOST_MASTER_2, PORT_MASTER_2)
+ master1.waitForReplInit(AGMT_DN)
+
+ # Check replication is working fine
+ if master1.testReplication(DEFAULT_SUFFIX, master2):
+ log.info('Replication is working.')
+ else:
+ log.fatal('Replication is not working.')
+ assert False
# clear the tmp directory
master1.clearTmpDir(__file__)
- #
- # Here we have two instances master and consumer
- # with replication working. Either coming from a backup recovery
- # or from a fresh (re)init
- # Time to return the topology
+ def fin():
+ master1.delete()
+ master2.delete()
+ request.addfinalizer(fin)
+
return TopologyMaster1Master2(master1, master2)
@@ -234,31 +150,39 @@ def test_ticket47462(topology):
"""
#
- # First set config as if it's an older version. Set DES to use libdes-plugin,
- # MMR to depend on DES, delete the existing AES plugin, and set a DES password
- # for the replication agreement.
- #
-
+ # First set config as if it's an older version. Set DES to use
+ # libdes-plugin, MMR to depend on DES, delete the existing AES plugin,
+ # and set a DES password for the replication agreement.
#
# Add an extra attribute to the DES plugin args
#
try:
topology.master1.modify_s(DES_PLUGIN,
- [(ldap.MOD_REPLACE, 'nsslapd-pluginPath', 'libdes-plugin'),
- (ldap.MOD_ADD, 'nsslapd-pluginarg2', 'description')])
+ [(ldap.MOD_REPLACE, 'nsslapd-pluginEnabled', 'on')])
+ except ldap.LDAPError as e:
+ log.fatal('Failed to enable DES plugin, error: ' +
+ e.message['desc'])
+ assert False
- except ldap.LDAPError, e:
- log.fatal('Failed to reset DES plugin, error: ' + e.message['desc'])
+ try:
+ topology.master1.modify_s(DES_PLUGIN,
+ [(ldap.MOD_ADD, 'nsslapd-pluginarg2', 'description')])
+ except ldap.LDAPError as e:
+ log.fatal('Failed to reset DES plugin, error: ' +
+ e.message['desc'])
assert False
try:
topology.master1.modify_s(MMR_PLUGIN,
- [(ldap.MOD_DELETE, 'nsslapd-plugin-depends-on-named', 'AES')])
+ [(ldap.MOD_DELETE,
+ 'nsslapd-plugin-depends-on-named',
+ 'AES')])
except ldap.NO_SUCH_ATTRIBUTE:
pass
- except ldap.LDAPError, e:
- log.fatal('Failed to reset DES plugin, error: ' + e.message['desc'])
+ except ldap.LDAPError as e:
+ log.fatal('Failed to reset MMR plugin, error: ' +
+ e.message['desc'])
assert False
#
@@ -268,8 +192,9 @@ def test_ticket47462(topology):
topology.master1.delete_s(AES_PLUGIN)
except ldap.NO_SUCH_OBJECT:
pass
- except ldap.LDAPError, e:
- log.fatal('Failed to delete AES plugin, error: ' + e.message['desc'])
+ except ldap.LDAPError as e:
+ log.fatal('Failed to delete AES plugin, error: ' +
+ e.message['desc'])
assert False
# restart the server so we must use DES plugin
@@ -279,20 +204,23 @@ def test_ticket47462(topology):
# Get the agmt dn, and set the password
#
try:
- entry = topology.master1.search_s('cn=config', ldap.SCOPE_SUBTREE, 'objectclass=nsDS5ReplicationAgreement')
+ entry = topology.master1.search_s('cn=config', ldap.SCOPE_SUBTREE,
+ 'objectclass=nsDS5ReplicationAgreement')
if entry:
agmt_dn = entry[0].dn
log.info('Found agmt dn (%s)' % agmt_dn)
else:
log.fatal('No replication agreements!')
assert False
- except ldap.LDAPError, e:
- log.fatal('Failed to search for replica credentials: ' + e.message['desc'])
+ except ldap.LDAPError as e:
+ log.fatal('Failed to search for replica credentials: ' +
+ e.message['desc'])
assert False
try:
properties = {RA_BINDPW: "password"}
- topology.master1.agreement.setProperties(None, agmt_dn, None, properties)
+ topology.master1.agreement.setProperties(None, agmt_dn, None,
+ properties)
log.info('Successfully modified replication agreement')
except ValueError:
log.error('Failed to update replica agreement: ' + AGMT_DN)
@@ -305,12 +233,14 @@ def test_ticket47462(topology):
topology.master1.add_s(Entry((USER1_DN,
{'objectclass': "top person".split(),
'sn': 'sn',
+ 'description': 'DES value to convert',
'cn': 'test_user'})))
loop = 0
ent = None
while loop <= 10:
try:
- ent = topology.master2.getEntry(USER1_DN, ldap.SCOPE_BASE, "(objectclass=*)")
+ ent = topology.master2.getEntry(USER1_DN, ldap.SCOPE_BASE,
+ "(objectclass=*)")
break
except ldap.NO_SUCH_OBJECT:
time.sleep(1)
@@ -320,11 +250,20 @@ def test_ticket47462(topology):
assert False
else:
log.info('Replication test passed')
- except ldap.LDAPError, e:
+ except ldap.LDAPError as e:
log.fatal('Failed to add test user: ' + e.message['desc'])
assert False
#
+ # Add a backend (that has no entries)
+ #
+ try:
+ topology.master1.backend.create("o=empty", {BACKEND_NAME: "empty"})
+ except ldap.LDAPError as e:
+ log.fatal('Failed to create extra/empty backend: ' + e.message['desc'])
+ assert False
+
+ #
# Run the upgrade...
#
topology.master1.upgrade('online')
@@ -335,7 +274,8 @@ def test_ticket47462(topology):
# Check that the restart converted existing DES credentials
#
try:
- entry = topology.master1.search_s('cn=config', ldap.SCOPE_SUBTREE, 'nsDS5ReplicaCredentials=*')
+ entry = topology.master1.search_s('cn=config', ldap.SCOPE_SUBTREE,
+ 'nsDS5ReplicaCredentials=*')
if entry:
val = entry[0].getValue('nsDS5ReplicaCredentials')
if val.startswith('{AES-'):
@@ -344,26 +284,30 @@ def test_ticket47462(topology):
log.fatal('Failed to convert credentials from DES to AES!')
assert False
else:
- log.fatal('Failed to find any entries with nsDS5ReplicaCredentials ')
+ log.fatal('Failed to find entries with nsDS5ReplicaCredentials')
assert False
- except ldap.LDAPError, e:
- log.fatal('Failed to search for replica credentials: ' + e.message['desc'])
+ except ldap.LDAPError as e:
+ log.fatal('Failed to search for replica credentials: ' +
+ e.message['desc'])
assert False
#
- # Check that the AES plugin exists, and has all the attributes listed in DES plugin.
- # The attributes might not be in the expected order so check all the attributes.
+ # Check that the AES plugin exists, and has all the attributes listed in
+ # DES plugin. The attributes might not be in the expected order so check
+ # all the attributes.
#
try:
- entry = topology.master1.search_s(AES_PLUGIN, ldap.SCOPE_BASE, 'objectclass=*')
+ entry = topology.master1.search_s(AES_PLUGIN, ldap.SCOPE_BASE,
+ 'objectclass=*')
if not entry[0].hasValue('nsslapd-pluginarg0', 'description') and \
not entry[0].hasValue('nsslapd-pluginarg1', 'description') and \
not entry[0].hasValue('nsslapd-pluginarg2', 'description'):
- log.fatal('The AES plugin did not have the DES attribute copied over correctly')
+ log.fatal('The AES plugin did not have the DES attribute copied ' +
+ 'over correctly')
assert False
else:
log.info('The AES plugin was correctly setup')
- except ldap.LDAPError, e:
+ except ldap.LDAPError as e:
log.fatal('Failed to find AES plugin: ' + e.message['desc'])
assert False
@@ -371,13 +315,14 @@ def test_ticket47462(topology):
# Check that the MMR plugin was updated
#
try:
- entry = topology.master1.search_s(MMR_PLUGIN, ldap.SCOPE_BASE, 'objectclass=*')
+ entry = topology.master1.search_s(MMR_PLUGIN, ldap.SCOPE_BASE,
+ 'objectclass=*')
if not entry[0].hasValue('nsslapd-plugin-depends-on-named', 'AES'):
log.fatal('The MMR Plugin was not correctly updated')
assert False
else:
log.info('The MMR plugin was correctly updated')
- except ldap.LDAPError, e:
+ except ldap.LDAPError as e:
log.fatal('Failed to find AES plugin: ' + e.message['desc'])
assert False
@@ -385,13 +330,14 @@ def test_ticket47462(topology):
# Check that the DES plugin was correctly updated
#
try:
- entry = topology.master1.search_s(DES_PLUGIN, ldap.SCOPE_BASE, 'objectclass=*')
+ entry = topology.master1.search_s(DES_PLUGIN, ldap.SCOPE_BASE,
+ 'objectclass=*')
if not entry[0].hasValue('nsslapd-pluginPath', 'libpbe-plugin'):
log.fatal('The DES Plugin was not correctly updated')
assert False
else:
log.info('The DES plugin was correctly updated')
- except ldap.LDAPError, e:
+ except ldap.LDAPError as e:
log.fatal('Failed to find AES plugin: ' + e.message['desc'])
assert False
@@ -407,7 +353,8 @@ def test_ticket47462(topology):
ent = None
while loop <= 10:
try:
- ent = topology.master2.getEntry(USER_DN, ldap.SCOPE_BASE, "(objectclass=*)")
+ ent = topology.master2.getEntry(USER_DN, ldap.SCOPE_BASE,
+ "(objectclass=*)")
break
except ldap.NO_SUCH_OBJECT:
time.sleep(1)
@@ -417,36 +364,70 @@ def test_ticket47462(topology):
assert False
else:
log.info('Replication test passed')
- except ldap.LDAPError, e:
+ except ldap.LDAPError as e:
log.fatal('Failed to add test user: ' + e.message['desc'])
assert False
+ # Check the entry
+ log.info('Entry before running task...')
+ try:
+ entry = topology.master1.search_s(USER1_DN,
+ ldap.SCOPE_BASE,
+ 'objectclass=*')
+ if entry:
+ print(str(entry))
+ else:
+ log.fatal('Failed to find entries')
+ assert False
+ except ldap.LDAPError as e:
+ log.fatal('Failed to search for entries: ' +
+ e.message['desc'])
+ assert False
+
#
- # If we got here the test passed
+ # Test the DES2AES Task on USER1_DN
#
- log.info('Test PASSED')
-
-
-def test_ticket47462_final(topology):
- topology.master1.stop(timeout=10)
- topology.master2.stop(timeout=10)
-
+ try:
+ topology.master1.add_s(Entry((DES2AES_TASK_DN,
+ {'objectclass': ['top',
+ 'extensibleObject'],
+ 'suffix': DEFAULT_SUFFIX,
+ 'cn': 'convert'})))
+ except ldap.LDAPError as e:
+ log.fatal('Failed to add task entry: ' + e.message['desc'])
+ assert False
-def run_isolated():
- '''
- run_isolated is used to run these test cases independently of a test scheduler (xunit, py.test..)
- To run isolated without py.test, you need to
- - edit this file and comment '@pytest.fixture' line before 'topology' function.
- - set the installation prefix
- - run this program
- '''
- global installation1_prefix
- global installation2_prefix
- installation1_prefix = None
- installation2_prefix = None
+ # Wait for task
+ task_entry = Entry(DES2AES_TASK_DN)
+ (done, exitCode) = topology.master1.tasks.checkTask(task_entry, True)
+ if exitCode:
+ log.fatal("Error: des2aes task exited with %d" % (exitCode))
+ assert False
- topo = topology(True)
- test_ticket47462(topo)
+ # Check the entry
+ try:
+ entry = topology.master1.search_s(USER1_DN,
+ ldap.SCOPE_BASE,
+ 'objectclass=*')
+ if entry:
+ val = entry[0].getValue('description')
+ print(str(entry[0]))
+ if val.startswith('{AES-'):
+ log.info('Task: DES credentials have been converted to AES')
+ else:
+ log.fatal('Task: Failed to convert credentials from DES to ' +
+ 'AES! (%s)' % (val))
+ assert False
+ else:
+ log.fatal('Failed to find entries')
+ assert False
+ except ldap.LDAPError as e:
+ log.fatal('Failed to search for entries: ' +
+ e.message['desc'])
+ assert False
if __name__ == '__main__':
- run_isolated()
+ # Run isolated
+ # -s for DEBUG mode
+ CURRENT_FILE = os.path.realpath(__file__)
+ pytest.main("-s %s" % CURRENT_FILE)
diff --git a/ldap/servers/slapd/daemon.c b/ldap/servers/slapd/daemon.c
index 11f51bc..8876110 100644
--- a/ldap/servers/slapd/daemon.c
+++ b/ldap/servers/slapd/daemon.c
@@ -950,13 +950,12 @@ convert_pbe_des_to_aes()
Slapi_Entry **entries = NULL;
struct slapdplugin *plugin = NULL;
char **attrs = NULL;
- char **backends = NULL;
char *val = NULL;
- int converted_des = 0;
+ int converted_des_passwd = 0;
int result = -1;
int have_aes = 0;
int have_des = 0;
- int i = 0, ii = 0, be_idx = 0;
+ int i = 0, ii = 0;
/*
* Check that AES plugin is enabled, and grab all the unique
@@ -990,91 +989,56 @@ convert_pbe_des_to_aes()
if(have_aes && have_des){
/*
- * Build a list of all the backend dn's
+ * Find any entries in cn=config that contain DES passwords and convert
+ * them to AES
*/
- Slapi_Backend *be = NULL;
- struct suffixlist *list;
- char *cookie = NULL;
-
- LDAPDebug(LDAP_DEBUG_ANY, "convert_pbe_des_to_aes: "
- "Converting DES passwords to AES...\n",0,0,0);
-
- be = slapi_get_first_backend(&cookie);
- while (be){
- int suffix_idx = 0;
- int count = slapi_counter_get_value(be->be_suffixcounter);
-
- list = be->be_suffixlist;
- for (suffix_idx = 0; list && suffix_idx < count; suffix_idx++) {
- char *suffix = (char *)slapi_sdn_get_ndn(list->be_suffix);
- if(charray_inlist(backends, suffix) || strlen(suffix) == 0){
- list = list->next;
- continue;
- }
- charray_add(&backends, slapi_ch_strdup(suffix));
- list = list->next;
- }
- be = slapi_get_next_backend (cookie);
- }
- slapi_ch_free ((void **)&cookie);
+ slapi_log_error(SLAPI_LOG_HOUSE, "convert_pbe_des_to_aes",
+ "Converting DES passwords to AES...\n");
- /*
- * Search for the password attributes
- */
for (i = 0; attrs && attrs[i]; i++){
char *filter = PR_smprintf("%s=*", attrs[i]);
- /*
- * Loop over all the backends looking for the password attribute
- */
- for(be_idx = 0; backends && backends[be_idx]; be_idx++){
- pb = slapi_pblock_new();
- slapi_search_internal_set_pb(pb, backends[be_idx],
- LDAP_SCOPE_SUBTREE, filter, NULL, 0, NULL, NULL,
- (void *)plugin_get_default_component_id(),
- SLAPI_OP_FLAG_IGNORE_UNINDEXED);
- slapi_search_internal_pb(pb);
- slapi_pblock_get(pb, SLAPI_PLUGIN_INTOP_RESULT, &result);
- if (LDAP_SUCCESS != result) {
- LDAPDebug(LDAP_DEBUG_ANY,"convert_pbe_des_to_aes: "
- "failed to search for password on (%s) error (%d)\n",
- backends[be_idx], result, 0);
- goto done;
- }
- slapi_pblock_get(pb, SLAPI_PLUGIN_INTOP_SEARCH_ENTRIES, &entries);
- for (ii = 0; entries && entries[ii]; ii++){
- if((val = slapi_entry_attr_get_charptr(entries[ii], attrs[i]))){
- if(strlen(val) >= 5 && strncmp(val,"{DES}", 5) == 0){
- /*
- * We have a DES encoded password, convert it AES
- */
- Slapi_PBlock *mod_pb = NULL;
- Slapi_Value *sval = NULL;
- LDAPMod mod_replace;
- LDAPMod *mods[2];
- char *replace_val[2];
- char *passwd = NULL;
-
- /* decode the DES password */
- if(pw_rever_decode(val, &passwd, attrs[i]) == -1){
- LDAPDebug(LDAP_DEBUG_ANY,"convert_pbe_des_to_aes: "
- "failed to decode existing DES password for (%s)\n",
- slapi_entry_get_dn(entries[ii]), 0, 0);
- converted_des = 0;
- goto done;
- }
- /* encode the password */
+ pb = slapi_pblock_new();
+ slapi_search_internal_set_pb(pb, "cn=config",
+ LDAP_SCOPE_SUBTREE, filter, NULL, 0, NULL, NULL,
+ (void *)plugin_get_default_component_id(),
+ SLAPI_OP_FLAG_IGNORE_UNINDEXED);
+ slapi_search_internal_pb(pb);
+ slapi_pblock_get(pb, SLAPI_PLUGIN_INTOP_SEARCH_ENTRIES, &entries);
+ for (ii = 0; entries && entries[ii]; ii++){
+ if((val = slapi_entry_attr_get_charptr(entries[ii], attrs[i]))){
+ if(strlen(val) >= 5 && strncmp(val,"{DES}", 5) == 0){
+ /*
+ * We have a DES encoded password, convert it to AES
+ */
+ Slapi_PBlock *mod_pb = NULL;
+ Slapi_Value *sval = NULL;
+ LDAPMod mod_replace;
+ LDAPMod *mods[2];
+ char *replace_val[2];
+ char *passwd = NULL;
+ int rc = 0;
+
+ /* decode the DES password */
+ if(pw_rever_decode(val, &passwd, attrs[i]) == -1){
+ slapi_log_error(SLAPI_LOG_FATAL ,"convert_pbe_des_to_aes",
+ "Failed to decode existing DES password for (%s)\n",
+ slapi_entry_get_dn(entries[ii]));
+ rc = -1;
+ }
+
+ /* encode the password */
+ if (rc == 0){
sval = slapi_value_new_string(passwd);
if(pw_rever_encode(&sval, attrs[i]) == -1){
- LDAPDebug(LDAP_DEBUG_ANY,"convert_pbe_des_to_aes: "
+ slapi_log_error(SLAPI_LOG_FATAL, "convert_pbe_des_to_aes",
"failed to encode AES password for (%s)\n",
- slapi_entry_get_dn(entries[ii]), 0, 0);
- slapi_ch_free_string(&passwd);
- slapi_value_free(&sval);
- converted_des = 0;
- goto done;
+ slapi_entry_get_dn(entries[ii]));
+ rc = -1;
}
+ }
+ if (rc == 0){
/* replace the attribute in the entry */
replace_val[0] = (char *)slapi_value_get_string(sval);
replace_val[1] = NULL;
@@ -1091,88 +1055,35 @@ convert_pbe_des_to_aes()
slapi_pblock_get(pb, SLAPI_PLUGIN_INTOP_RESULT, &result);
if (LDAP_SUCCESS != result) {
- LDAPDebug(LDAP_DEBUG_ANY,"convert_pbe_des_to_aes: "
- "failed to convert password for (%s) error (%d)\n",
- slapi_entry_get_dn(entries[ii]), result, 0);
- converted_des = -1;
+ slapi_log_error(SLAPI_LOG_FATAL, "convert_pbe_des_to_aes"
+ "Failed to convert password for (%s) error (%d)\n",
+ slapi_entry_get_dn(entries[ii]), result);
} else {
- LDAPDebug(LDAP_DEBUG_ANY,"convert_pbe_des_to_aes: "
- "successfully converted password for (%s)\n",
- slapi_entry_get_dn(entries[ii]), result, 0);
- converted_des = 1;
-
- }
- slapi_ch_free_string(&passwd);
- slapi_value_free(&sval);
- slapi_pblock_destroy(mod_pb);
- if(result){
- goto done;
+ slapi_log_error(SLAPI_LOG_HOUSE, "convert_pbe_des_to_aes",
+ "Successfully converted password for (%s)\n",
+ slapi_entry_get_dn(entries[ii]));
+ converted_des_passwd = 1;
}
}
- slapi_ch_free_string(&val);
+ slapi_ch_free_string(&passwd);
+ slapi_value_free(&sval);
+ slapi_pblock_destroy(mod_pb);
}
+ slapi_ch_free_string(&val);
}
- slapi_free_search_results_internal(pb);
- slapi_pblock_destroy(pb);
- pb = NULL;
}
+ slapi_free_search_results_internal(pb);
+ slapi_pblock_destroy(pb);
+ pb = NULL;
slapi_ch_free_string(&filter);
}
- }
-
-done:
- charray_free(attrs);
- charray_free(backends);
- slapi_free_search_results_internal(pb);
- slapi_pblock_destroy(pb);
-
- if (have_aes && have_des){
- /*
- * If a conversion attempt did not fail, disable DES plugin
- */
- if(converted_des != -1){
- /*
- * Disable the DES plugin - this also prevents potentially expensive
- * searches at every server startup.
- */
- LDAPMod mod_replace;
- LDAPMod *mods[2];
- char *replace_val[2];
- char *des_dn = "cn=DES,cn=Password Storage Schemes,cn=plugins,cn=config";
-
- replace_val[0] = "off";
- replace_val[1] = NULL;
- mod_replace.mod_op = LDAP_MOD_REPLACE;
- mod_replace.mod_type = "nsslapd-pluginEnabled";
- mod_replace.mod_values = replace_val;
- mods[0] = &mod_replace;
- mods[1] = 0;
- pb = slapi_pblock_new();
- slapi_modify_internal_set_pb(pb, des_dn, mods, 0, 0,
- (void *)plugin_get_default_component_id(), 0);
- slapi_modify_internal_pb(pb);
- slapi_pblock_get(pb, SLAPI_PLUGIN_INTOP_RESULT, &result);
- if (LDAP_SUCCESS != result) {
- LDAPDebug(LDAP_DEBUG_ANY,"convert_pbe_des_to_aes: "
- "Failed to disable DES plugin (%s), error (%d)\n",
- des_dn, result, 0);
- } else {
- LDAPDebug(LDAP_DEBUG_ANY,"convert_pbe_des_to_aes: "
- "Successfully disabled DES plugin (%s)\n",
- des_dn, 0, 0);
- }
- slapi_pblock_destroy(pb);
- }
- if(converted_des == 1){
- LDAPDebug(LDAP_DEBUG_ANY,"convert_pbe_des_to_aes: "
- "Finished - all DES passwords have been converted to AES.\n",
- 0, 0, 0);
- } else if (converted_des == 0){
- LDAPDebug(LDAP_DEBUG_ANY, "convert_pbe_des_to_aes: "
- "Finished - no DES passwords to convert.\n",0,0,0);
+ if (!converted_des_passwd){
+ slapi_log_error(SLAPI_LOG_HOUSE, "convert_pbe_des_to_aes",
+ "No DES passwords found to convert.\n");
}
}
+ charray_free(attrs);
}
void slapd_daemon( daemon_ports_t *ports )
diff --git a/ldap/servers/slapd/pw.c b/ldap/servers/slapd/pw.c
index 538d915..f16ac98 100644
--- a/ldap/servers/slapd/pw.c
+++ b/ldap/servers/slapd/pw.c
@@ -552,10 +552,10 @@ pw_rever_encode(Slapi_Value **vals, char * attr_name)
for ( p = get_plugin_list(PLUGIN_LIST_REVER_PWD_STORAGE_SCHEME); p != NULL; p = p->plg_next )
{
char *L_attr = NULL;
- int i = 0;
+ int i = 0, ii = 0;
/* Get the appropriate encoding function */
- for ( L_attr = p->plg_argv[i]; i<p->plg_argc; L_attr = p->plg_argv[++i] )
+ for ( L_attr = p->plg_argv[ii]; ii<p->plg_argc; L_attr = p->plg_argv[++ii] )
{
if (slapi_attr_types_equivalent(L_attr, attr_name))
{
diff --git a/ldap/servers/slapd/task.c b/ldap/servers/slapd/task.c
index 19a52a3..38f83e6 100644
--- a/ldap/servers/slapd/task.c
+++ b/ldap/servers/slapd/task.c
@@ -82,6 +82,8 @@ static int shutting_down = 0;
#define TASK_TOMBSTONE_FIXUP_BACKEND "backend"
#define TASK_TOMBSTONE_FIXUP_SUFFIX "suffix"
#define TASK_TOMBSTONE_FIXUP_STRIPCSN "stripcsn"
+#define TASK_DES2AES "des2aes task"
+
#define LOG_BUFFER 256
/* if the cumul. log gets larger than this, it's truncated: */
@@ -112,8 +114,10 @@ static const char *fetch_attr(Slapi_Entry *e, const char *attrname,
const char *default_val);
static Slapi_Entry *get_internal_entry(Slapi_PBlock *pb, char *dn);
static void modify_internal_entry(char *dn, LDAPMod **mods);
-
static void fixup_tombstone_task_destructor(Slapi_Task *task);
+static void task_des2aes_thread(void *arg);
+static void des2aes_task_destructor(Slapi_Task *task);
+
/***********************************
* Public Functions
@@ -2459,6 +2463,345 @@ fixup_tombstone_task_destructor(Slapi_Task *task)
"fixup_tombstone_task_destructor <--\n" );
}
+/*
+ * des2aes Task
+ *
+ * Convert any DES passwords to AES
+ *
+ * dn: cn=convertPasswords, cn=des2aes,cn=tasks,cn=config
+ * objectclass: top
+ * objectclass: extensibleObject
+ * suffix: dc=example,dc=com (If empty all backends are checked)
+ * suffix: dc=other,dc=suffix
+ */
+struct task_des2aes_data
+{
+ char **suffixes;
+ Slapi_Task *task;
+};
+
+static int
+task_des2aes(Slapi_PBlock *pb, Slapi_Entry *e, Slapi_Entry *eAfter,
+ int *returncode, char *returntext, void *arg)
+{
+ struct task_des2aes_data *task_data = NULL;
+ PRThread *thread = NULL;
+ Slapi_Task *task = NULL;
+ char **suffix = NULL;
+ char **bases = NULL;
+ int rc = SLAPI_DSE_CALLBACK_OK;
+
+ /* Get the suffixes */
+ if((suffix = slapi_entry_attr_get_charray(e, "suffix"))){
+ int i;
+ for (i = 0; suffix && suffix[i]; i++){
+ /* Make sure "suffix" is NUL terminated string */
+ char *dn = slapi_create_dn_string("%s", suffix[i]);
+
+ if(dn){
+ if(slapi_dn_syntax_check(pb, dn, 1)){
+ /* invalid suffix name */
+ PR_snprintf(returntext, SLAPI_DSE_RETURNTEXT_SIZE,
+ "Invalid DN syntax (%s) specified for \"suffix\"\n",
+ suffix[i]);
+ *returncode = LDAP_INVALID_DN_SYNTAX;
+ slapi_ch_free_string(&dn);
+ rc = SLAPI_DSE_CALLBACK_ERROR;
+ goto error;
+ } else {
+ slapi_ch_array_add(&bases, dn);
+ }
+ } else{
+ PR_snprintf(returntext, SLAPI_DSE_RETURNTEXT_SIZE,
+ "Invalid DN (%s) specified for \"suffix\"\n", suffix[i]);
+ *returncode = LDAP_INVALID_DN_SYNTAX;
+ rc = SLAPI_DSE_CALLBACK_ERROR;
+ goto error;
+ }
+ }
+ }
+
+ /* Build the task data and fire off a thread to perform the conversion */
+ task = slapi_new_task(slapi_entry_get_ndn(e));
+
+ /* register our destructor for cleaning up our private data */
+ slapi_task_set_destructor_fn(task, des2aes_task_destructor);
+ task_data = (struct task_des2aes_data *)slapi_ch_calloc(1, sizeof(struct task_des2aes_data));
+ task_data->suffixes = bases;
+ task_data->task = task;
+
+ /* Start the conversion thread */
+ thread = PR_CreateThread(PR_USER_THREAD, task_des2aes_thread,
+ (void *)task_data, PR_PRIORITY_NORMAL, PR_GLOBAL_THREAD,
+ PR_UNJOINABLE_THREAD, SLAPD_DEFAULT_THREAD_STACKSIZE);
+ if (thread == NULL) {
+ PR_snprintf(returntext, SLAPI_DSE_RETURNTEXT_SIZE,
+ "unable to create des2aes thread!\n");
+ slapi_log_error(SLAPI_LOG_FATAL, TASK_DES2AES,
+ "unable to create des2aes thread!\n");
+ *returncode = LDAP_OPERATIONS_ERROR;
+ slapi_task_finish(task, *returncode);
+ rc = SLAPI_DSE_CALLBACK_ERROR;
+ }
+
+error:
+ if (rc == SLAPI_DSE_CALLBACK_ERROR){
+ slapi_ch_array_free(bases);
+ slapi_ch_array_free(suffix);
+ slapi_ch_free((void **)&task_data);
+ }
+ return rc;
+}
+
+static void
+task_des2aes_thread(void *arg)
+{
+ struct task_des2aes_data *task_data = arg;
+ Slapi_PBlock *pb = NULL;
+ Slapi_Entry **entries = NULL;
+ Slapi_Task *task = task_data->task;
+ struct slapdplugin *plugin = NULL;
+ char **attrs = NULL;
+ char **backends = NULL;
+ char *val = NULL;
+ int converted_des_passwd = 0;
+ int result = -1;
+ int have_aes = 0;
+ int have_des = 0;
+ int i = 0, ii = 0, be_idx = 0;
+ int rc = 0;
+
+ /*
+ * Check that AES plugin is enabled, and grab all the unique
+ * password attributes.
+ */
+ for ( plugin = get_plugin_list(PLUGIN_LIST_REVER_PWD_STORAGE_SCHEME);
+ plugin != NULL;
+ plugin = plugin->plg_next )
+ {
+ char *plugin_arg = NULL;
+
+ if(plugin->plg_started && strcasecmp(plugin->plg_name, "AES") == 0){
+ /* We have the AES plugin, and its enabled */
+ have_aes = 1;
+ }
+ if(plugin->plg_started && strcasecmp(plugin->plg_name, "DES") == 0){
+ /* We have the DES plugin, and its enabled */
+ have_des = 1;
+ }
+ /* Gather all the unique password attributes from all the PBE plugins */
+ for ( i = 0, plugin_arg = plugin->plg_argv[i];
+ i < plugin->plg_argc;
+ plugin_arg = plugin->plg_argv[++i] )
+ {
+ if(charray_inlist(attrs, plugin_arg)){
+ continue;
+ }
+ charray_add(&attrs, slapi_ch_strdup(plugin_arg));
+ }
+ }
+
+ if(have_aes && have_des){
+ if(task_data->suffixes == NULL){
+ /*
+ * Build a list of all the backend dn's
+ */
+ Slapi_Backend *be = NULL;
+ struct suffixlist *list;
+ char *cookie = NULL;
+
+ slapi_log_error(SLAPI_LOG_FATAL, TASK_DES2AES,
+ "Checking for DES passwords to convert to AES...\n");
+ slapi_task_log_notice(task,
+ "Checking for DES passwords to convert to AES...\n");
+
+ be = slapi_get_first_backend(&cookie);
+ while (be){
+ int suffix_idx = 0;
+ int count = slapi_counter_get_value(be->be_suffixcounter);
+
+ list = be->be_suffixlist;
+ for (suffix_idx = 0; list && suffix_idx < count; suffix_idx++) {
+ char *suffix = (char *)slapi_sdn_get_ndn(list->be_suffix);
+ if(charray_inlist(backends, suffix) || strlen(suffix) == 0){
+ list = list->next;
+ continue;
+ }
+ charray_add(&backends, slapi_ch_strdup(suffix));
+ list = list->next;
+ }
+ be = slapi_get_next_backend (cookie);
+ }
+ slapi_ch_free ((void **)&cookie);
+ } else {
+ backends = task_data->suffixes;
+ }
+
+ /*
+ * Search for the password attributes
+ */
+ for (i = 0; attrs && attrs[i]; i++){
+ char *filter = PR_smprintf("%s=*", attrs[i]);
+ /*
+ * Loop over all the backends looking for the password attribute
+ */
+ for(be_idx = 0; backends && backends[be_idx]; be_idx++){
+ pb = slapi_pblock_new();
+ slapi_search_internal_set_pb(pb, backends[be_idx],
+ LDAP_SCOPE_SUBTREE, filter, NULL, 0, NULL, NULL,
+ (void *)plugin_get_default_component_id(),
+ SLAPI_OP_FLAG_IGNORE_UNINDEXED);
+ slapi_search_internal_pb(pb);
+ slapi_pblock_get(pb, SLAPI_PLUGIN_INTOP_RESULT, &result);
+ if (LDAP_SUCCESS != result) {
+ slapi_log_error(SLAPI_LOG_FATAL, "convert_pbe_des_to_aes: ",
+ "Failed to search for password attribute (%s) error (%d), skipping suffix (%s)\n",
+ attrs[i], result, backends[be_idx]);
+ slapi_task_log_notice(task,
+ "Failed to search for password attribute (%s) error (%d), skipping suffix (%s)\n",
+ attrs[i], result, backends[be_idx]);
+ slapi_free_search_results_internal(pb);
+ slapi_pblock_destroy(pb);
+ pb = NULL;
+ continue;
+ }
+ slapi_pblock_get(pb, SLAPI_PLUGIN_INTOP_SEARCH_ENTRIES, &entries);
+ for (ii = 0; entries && entries[ii]; ii++){
+ if((val = slapi_entry_attr_get_charptr(entries[ii], attrs[i]))){
+ if(strlen(val) >= 5 && strncmp(val,"{DES}", 5) == 0){
+ /*
+ * We have a DES encoded password, convert it AES
+ */
+ Slapi_PBlock *mod_pb = NULL;
+ Slapi_Value *sval = NULL;
+ LDAPMod mod_replace;
+ LDAPMod *mods[2];
+ char *replace_val[2];
+ char *passwd = NULL;
+
+ /* Decode the DES password */
+ if(pw_rever_decode(val, &passwd, attrs[i]) == -1){
+ slapi_log_error(SLAPI_LOG_FATAL, TASK_DES2AES,
+ "Failed to decode existing DES password for (%s)\n",
+ slapi_entry_get_dn(entries[ii]));
+ slapi_task_log_notice(task,
+ "Failed to decode existing DES password for (%s)\n",
+ slapi_entry_get_dn(entries[ii]));
+ rc = 1;
+ goto done;
+ }
+
+ /* Encode the password */
+ sval = slapi_value_new_string(passwd);
+ if(pw_rever_encode(&sval, attrs[i]) == -1){
+ slapi_log_error(SLAPI_LOG_FATAL, TASK_DES2AES,
+ "failed to encode AES password for (%s)\n",
+ slapi_entry_get_dn(entries[ii]));
+ slapi_task_log_notice(task,
+ "failed to encode AES password for (%s)\n",
+ slapi_entry_get_dn(entries[ii]));
+ slapi_ch_free_string(&passwd);
+ slapi_value_free(&sval);
+ rc = 1;
+ goto done;
+ }
+
+ /* Replace the attribute in the entry */
+ replace_val[0] = (char *)slapi_value_get_string(sval);
+ replace_val[1] = NULL;
+ mod_replace.mod_op = LDAP_MOD_REPLACE;
+ mod_replace.mod_type = attrs[i];
+ mod_replace.mod_values = replace_val;
+ mods[0] = &mod_replace;
+ mods[1] = 0;
+
+ mod_pb = slapi_pblock_new();
+ slapi_modify_internal_set_pb(mod_pb, slapi_entry_get_dn(entries[ii]),
+ mods, 0, 0, (void *)plugin_get_default_component_id(), 0);
+ slapi_modify_internal_pb(mod_pb);
+
+ slapi_pblock_get(pb, SLAPI_PLUGIN_INTOP_RESULT, &result);
+ if (LDAP_SUCCESS != result) {
+ slapi_log_error(SLAPI_LOG_FATAL, TASK_DES2AES,
+ "Failed to convert password for (%s) error (%d)\n",
+ slapi_entry_get_dn(entries[ii]), result);
+ slapi_task_log_notice(task,
+ "Failed to convert password for (%s) error (%d)\n",
+ slapi_entry_get_dn(entries[ii]), result);
+ rc = 1;
+ } else {
+ slapi_log_error(SLAPI_LOG_FATAL, TASK_DES2AES,
+ "Successfully converted password for (%s)\n",
+ slapi_entry_get_dn(entries[ii]));
+ slapi_task_log_notice(task,
+ "Successfully converted password for (%s)\n",
+ slapi_entry_get_dn(entries[ii]));
+ converted_des_passwd = 1;
+ }
+ slapi_ch_free_string(&passwd);
+ slapi_value_free(&sval);
+ slapi_pblock_destroy(mod_pb);
+ }
+ slapi_ch_free_string(&val);
+ }
+ }
+ slapi_free_search_results_internal(pb);
+ slapi_pblock_destroy(pb);
+ pb = NULL;
+ }
+ slapi_ch_free_string(&filter);
+ }
+ if (!converted_des_passwd){
+ slapi_log_error(SLAPI_LOG_FATAL, TASK_DES2AES,
+ "No DES passwords found to convert.\n");
+ slapi_task_log_notice(task, "No DES passwords found to convert.\n");
+ }
+ } else {
+ /* No AES/DES */
+ if (!have_des){
+ slapi_log_error(SLAPI_LOG_FATAL, TASK_DES2AES,
+ "DES plugin not enabled\n");
+ slapi_task_log_notice(task, "DES plugin not enabled\n");
+ }
+ if (!have_aes){
+ slapi_log_error(SLAPI_LOG_FATAL, TASK_DES2AES,
+ "AES plugin not enabled\n");
+ slapi_task_log_notice(task, "AES plugin not enabled\n");
+ }
+ slapi_log_error(SLAPI_LOG_FATAL, TASK_DES2AES,
+ "Unable to convert passwords\n");
+ slapi_task_log_notice(task, "Unable to convert passwords\n");
+ rc = 1;
+ }
+
+done:
+ charray_free(attrs);
+ charray_free(backends);
+ slapi_free_search_results_internal(pb);
+ slapi_pblock_destroy(pb);
+ slapi_task_finish(task, rc);
+}
+
+static void
+des2aes_task_destructor(Slapi_Task *task)
+{
+ slapi_log_error(SLAPI_LOG_TRACE, TASK_DES2AES,
+ "des2aes_task_destructor -->\n" );
+ if (task) {
+ struct task_des2aes_data *task_data = (struct task_des2aes_data *)slapi_task_get_data(task);
+ while (slapi_task_get_refcount(task) > 0) {
+ /* Yield to wait for the task to finish. */
+ DS_Sleep (PR_MillisecondsToInterval(100));
+ }
+ if (task_data) {
+ slapi_ch_array_free(task_data->suffixes);
+ slapi_ch_free((void **)&task_data);
+ }
+ }
+ slapi_log_error(SLAPI_LOG_TRACE, TASK_DES2AES,
+ "des2aes_task_destructor <--\n" );
+}
+
/* cleanup old tasks that may still be in the DSE from a previous session
* (this can happen if the server crashes [no matter how unlikely we like
* to think that is].)
@@ -2540,6 +2883,7 @@ void task_init(void)
slapi_task_register_handler("upgradedb", task_upgradedb_add);
slapi_task_register_handler("sysconfig reload", task_sysconfig_reload_add);
slapi_task_register_handler("fixup tombstones", task_fixup_tombstones_add);
+ slapi_task_register_handler("des2aes", task_des2aes);
}
/* called when the server is shutting down -- abort all existing tasks */
7 years, 10 months
Branch '389-ds-base-1.3.4' - dirsrvtests/tests ldap/servers
by Mark Reynolds
dirsrvtests/tests/tickets/ticket47462_test.py | 151 +++++++----
ldap/servers/slapd/daemon.c | 195 +++-----------
ldap/servers/slapd/pw.c | 4
ldap/servers/slapd/task.c | 346 +++++++++++++++++++++++++-
4 files changed, 505 insertions(+), 191 deletions(-)
New commits:
commit c0ad918939c40779e463b71c41ab106e7ee890e2
Author: Mark Reynolds <mreynolds(a)redhat.com>
Date: Tue Jun 7 10:02:42 2016 -0400
Ticket 48862 - At startup DES to AES password conversion causes timeout in start script
Bug Description: At server start all the backends are searches for entries that contain
DES password attributes as defined in the plugin. These are typically
unindexed searches, and if there is a very large backend this can cause
the server startup to timeout.
Fix Description: At startup only check "cn=config" for entries with DES password attributes.
A new "conversion" task has been created that can be run after startup
to search all backends(if a suffix is not specified), or specific backends.
dn: cn=convertPasswords, cn=des2aes,cn=tasks,cn=config
objectclass: top
objectclass: extensibleObject
suffix: dc=example,dc=com
suffix: dc=other,dc=suffix
Another bug was discovered in pw_rever_encode() in pw.c where a "for" loop
counter was accidentially reused by a second "for" loop. This could lead
to an infinite loop/hang.
Updated the CI test to perform the conversion task.
https://fedorahosted.org/389/ticket/48862
Reviewed by: nhosoi(Thanks!)
(cherry picked from commit 11f55f3dd2a2c44ddf7b5be54273401add13b1bc)
diff --git a/dirsrvtests/tests/tickets/ticket47462_test.py b/dirsrvtests/tests/tickets/ticket47462_test.py
index f290647..2ac1478 100644
--- a/dirsrvtests/tests/tickets/ticket47462_test.py
+++ b/dirsrvtests/tests/tickets/ticket47462_test.py
@@ -32,6 +32,7 @@ AGMT_DN = ''
USER_DN = 'cn=test_user,' + DEFAULT_SUFFIX
USER1_DN = 'cn=test_user1,' + DEFAULT_SUFFIX
TEST_REPL_DN = 'cn=test repl,' + DEFAULT_SUFFIX
+DES2AES_TASK_DN = 'cn=convert,cn=des2aes,cn=tasks,cn=config'
class TopologyMaster1Master2(object):
@@ -134,6 +135,11 @@ def topology(request):
# clear the tmp directory
master1.clearTmpDir(__file__)
+ def fin():
+ master1.delete()
+ master2.delete()
+ request.addfinalizer(fin)
+
return TopologyMaster1Master2(master1, master2)
@@ -144,11 +150,9 @@ def test_ticket47462(topology):
"""
#
- # First set config as if it's an older version. Set DES to use libdes-plugin,
- # MMR to depend on DES, delete the existing AES plugin, and set a DES password
- # for the replication agreement.
- #
-
+ # First set config as if it's an older version. Set DES to use
+ # libdes-plugin, MMR to depend on DES, delete the existing AES plugin,
+ # and set a DES password for the replication agreement.
#
# Add an extra attribute to the DES plugin args
#
@@ -156,24 +160,29 @@ def test_ticket47462(topology):
topology.master1.modify_s(DES_PLUGIN,
[(ldap.MOD_REPLACE, 'nsslapd-pluginEnabled', 'on')])
except ldap.LDAPError as e:
- log.fatal('Failed to enable DES plugin, error: ' + e.message['desc'])
+ log.fatal('Failed to enable DES plugin, error: ' +
+ e.message['desc'])
assert False
try:
topology.master1.modify_s(DES_PLUGIN,
[(ldap.MOD_ADD, 'nsslapd-pluginarg2', 'description')])
except ldap.LDAPError as e:
- log.fatal('Failed to reset DES plugin, error: ' + e.message['desc'])
+ log.fatal('Failed to reset DES plugin, error: ' +
+ e.message['desc'])
assert False
try:
topology.master1.modify_s(MMR_PLUGIN,
- [(ldap.MOD_DELETE, 'nsslapd-plugin-depends-on-named', 'AES')])
+ [(ldap.MOD_DELETE,
+ 'nsslapd-plugin-depends-on-named',
+ 'AES')])
except ldap.NO_SUCH_ATTRIBUTE:
pass
except ldap.LDAPError as e:
- log.fatal('Failed to reset MMR plugin, error: ' + e.message['desc'])
+ log.fatal('Failed to reset MMR plugin, error: ' +
+ e.message['desc'])
assert False
#
@@ -184,7 +193,8 @@ def test_ticket47462(topology):
except ldap.NO_SUCH_OBJECT:
pass
except ldap.LDAPError as e:
- log.fatal('Failed to delete AES plugin, error: ' + e.message['desc'])
+ log.fatal('Failed to delete AES plugin, error: ' +
+ e.message['desc'])
assert False
# restart the server so we must use DES plugin
@@ -194,7 +204,8 @@ def test_ticket47462(topology):
# Get the agmt dn, and set the password
#
try:
- entry = topology.master1.search_s('cn=config', ldap.SCOPE_SUBTREE, 'objectclass=nsDS5ReplicationAgreement')
+ entry = topology.master1.search_s('cn=config', ldap.SCOPE_SUBTREE,
+ 'objectclass=nsDS5ReplicationAgreement')
if entry:
agmt_dn = entry[0].dn
log.info('Found agmt dn (%s)' % agmt_dn)
@@ -202,12 +213,14 @@ def test_ticket47462(topology):
log.fatal('No replication agreements!')
assert False
except ldap.LDAPError as e:
- log.fatal('Failed to search for replica credentials: ' + e.message['desc'])
+ log.fatal('Failed to search for replica credentials: ' +
+ e.message['desc'])
assert False
try:
properties = {RA_BINDPW: "password"}
- topology.master1.agreement.setProperties(None, agmt_dn, None, properties)
+ topology.master1.agreement.setProperties(None, agmt_dn, None,
+ properties)
log.info('Successfully modified replication agreement')
except ValueError:
log.error('Failed to update replica agreement: ' + AGMT_DN)
@@ -220,12 +233,14 @@ def test_ticket47462(topology):
topology.master1.add_s(Entry((USER1_DN,
{'objectclass': "top person".split(),
'sn': 'sn',
+ 'description': 'DES value to convert',
'cn': 'test_user'})))
loop = 0
ent = None
while loop <= 10:
try:
- ent = topology.master2.getEntry(USER1_DN, ldap.SCOPE_BASE, "(objectclass=*)")
+ ent = topology.master2.getEntry(USER1_DN, ldap.SCOPE_BASE,
+ "(objectclass=*)")
break
except ldap.NO_SUCH_OBJECT:
time.sleep(1)
@@ -259,7 +274,8 @@ def test_ticket47462(topology):
# Check that the restart converted existing DES credentials
#
try:
- entry = topology.master1.search_s('cn=config', ldap.SCOPE_SUBTREE, 'nsDS5ReplicaCredentials=*')
+ entry = topology.master1.search_s('cn=config', ldap.SCOPE_SUBTREE,
+ 'nsDS5ReplicaCredentials=*')
if entry:
val = entry[0].getValue('nsDS5ReplicaCredentials')
if val.startswith('{AES-'):
@@ -268,22 +284,26 @@ def test_ticket47462(topology):
log.fatal('Failed to convert credentials from DES to AES!')
assert False
else:
- log.fatal('Failed to find any entries with nsDS5ReplicaCredentials ')
+ log.fatal('Failed to find entries with nsDS5ReplicaCredentials')
assert False
except ldap.LDAPError as e:
- log.fatal('Failed to search for replica credentials: ' + e.message['desc'])
+ log.fatal('Failed to search for replica credentials: ' +
+ e.message['desc'])
assert False
#
- # Check that the AES plugin exists, and has all the attributes listed in DES plugin.
- # The attributes might not be in the expected order so check all the attributes.
+ # Check that the AES plugin exists, and has all the attributes listed in
+ # DES plugin. The attributes might not be in the expected order so check
+ # all the attributes.
#
try:
- entry = topology.master1.search_s(AES_PLUGIN, ldap.SCOPE_BASE, 'objectclass=*')
+ entry = topology.master1.search_s(AES_PLUGIN, ldap.SCOPE_BASE,
+ 'objectclass=*')
if not entry[0].hasValue('nsslapd-pluginarg0', 'description') and \
not entry[0].hasValue('nsslapd-pluginarg1', 'description') and \
not entry[0].hasValue('nsslapd-pluginarg2', 'description'):
- log.fatal('The AES plugin did not have the DES attribute copied over correctly')
+ log.fatal('The AES plugin did not have the DES attribute copied ' +
+ 'over correctly')
assert False
else:
log.info('The AES plugin was correctly setup')
@@ -295,7 +315,8 @@ def test_ticket47462(topology):
# Check that the MMR plugin was updated
#
try:
- entry = topology.master1.search_s(MMR_PLUGIN, ldap.SCOPE_BASE, 'objectclass=*')
+ entry = topology.master1.search_s(MMR_PLUGIN, ldap.SCOPE_BASE,
+ 'objectclass=*')
if not entry[0].hasValue('nsslapd-plugin-depends-on-named', 'AES'):
log.fatal('The MMR Plugin was not correctly updated')
assert False
@@ -309,7 +330,8 @@ def test_ticket47462(topology):
# Check that the DES plugin was correctly updated
#
try:
- entry = topology.master1.search_s(DES_PLUGIN, ldap.SCOPE_BASE, 'objectclass=*')
+ entry = topology.master1.search_s(DES_PLUGIN, ldap.SCOPE_BASE,
+ 'objectclass=*')
if not entry[0].hasValue('nsslapd-pluginPath', 'libpbe-plugin'):
log.fatal('The DES Plugin was not correctly updated')
assert False
@@ -331,7 +353,8 @@ def test_ticket47462(topology):
ent = None
while loop <= 10:
try:
- ent = topology.master2.getEntry(USER_DN, ldap.SCOPE_BASE, "(objectclass=*)")
+ ent = topology.master2.getEntry(USER_DN, ldap.SCOPE_BASE,
+ "(objectclass=*)")
break
except ldap.NO_SUCH_OBJECT:
time.sleep(1)
@@ -345,30 +368,66 @@ def test_ticket47462(topology):
log.fatal('Failed to add test user: ' + e.message['desc'])
assert False
+ # Check the entry
+ log.info('Entry before running task...')
+ try:
+ entry = topology.master1.search_s(USER1_DN,
+ ldap.SCOPE_BASE,
+ 'objectclass=*')
+ if entry:
+ print(str(entry))
+ else:
+ log.fatal('Failed to find entries')
+ assert False
+ except ldap.LDAPError as e:
+ log.fatal('Failed to search for entries: ' +
+ e.message['desc'])
+ assert False
-def test_ticket47462_final(topology):
- topology.master1.delete()
- topology.master2.delete()
- log.info('Testcase PASSED')
-
-
-def run_isolated():
- '''
- run_isolated is used to run these test cases independently of a test scheduler (xunit, py.test..)
- To run isolated without py.test, you need to
- - edit this file and comment '@pytest.fixture' line before 'topology' function.
- - set the installation prefix
- - run this program
- '''
- global installation1_prefix
- global installation2_prefix
- installation1_prefix = None
- installation2_prefix = None
+ #
+ # Test the DES2AES Task on USER1_DN
+ #
+ try:
+ topology.master1.add_s(Entry((DES2AES_TASK_DN,
+ {'objectclass': ['top',
+ 'extensibleObject'],
+ 'suffix': DEFAULT_SUFFIX,
+ 'cn': 'convert'})))
+ except ldap.LDAPError as e:
+ log.fatal('Failed to add task entry: ' + e.message['desc'])
+ assert False
- topo = topology(True)
- test_ticket47462(topo)
- test_ticket47462_final(topo)
+ # Wait for task
+ task_entry = Entry(DES2AES_TASK_DN)
+ (done, exitCode) = topology.master1.tasks.checkTask(task_entry, True)
+ if exitCode:
+ log.fatal("Error: des2aes task exited with %d" % (exitCode))
+ assert False
+ # Check the entry
+ try:
+ entry = topology.master1.search_s(USER1_DN,
+ ldap.SCOPE_BASE,
+ 'objectclass=*')
+ if entry:
+ val = entry[0].getValue('description')
+ print(str(entry[0]))
+ if val.startswith('{AES-'):
+ log.info('Task: DES credentials have been converted to AES')
+ else:
+ log.fatal('Task: Failed to convert credentials from DES to ' +
+ 'AES! (%s)' % (val))
+ assert False
+ else:
+ log.fatal('Failed to find entries')
+ assert False
+ except ldap.LDAPError as e:
+ log.fatal('Failed to search for entries: ' +
+ e.message['desc'])
+ assert False
if __name__ == '__main__':
- run_isolated()
+ # Run isolated
+ # -s for DEBUG mode
+ CURRENT_FILE = os.path.realpath(__file__)
+ pytest.main("-s %s" % CURRENT_FILE)
diff --git a/ldap/servers/slapd/daemon.c b/ldap/servers/slapd/daemon.c
index d702129..29562ae 100644
--- a/ldap/servers/slapd/daemon.c
+++ b/ldap/servers/slapd/daemon.c
@@ -692,14 +692,12 @@ convert_pbe_des_to_aes()
Slapi_Entry **entries = NULL;
struct slapdplugin *plugin = NULL;
char **attrs = NULL;
- char **backends = NULL;
char *val = NULL;
int converted_des_passwd = 0;
- int disable_des = 1;
int result = -1;
int have_aes = 0;
int have_des = 0;
- int i = 0, ii = 0, be_idx = 0;
+ int i = 0, ii = 0;
/*
* Check that AES plugin is enabled, and grab all the unique
@@ -733,94 +731,56 @@ convert_pbe_des_to_aes()
if(have_aes && have_des){
/*
- * Build a list of all the backend dn's
+ * Find any entries in cn=config that contain DES passwords and convert
+ * them to AES
*/
- Slapi_Backend *be = NULL;
- struct suffixlist *list;
- char *cookie = NULL;
-
- LDAPDebug(LDAP_DEBUG_ANY, "convert_pbe_des_to_aes: "
- "Checking for DES passwords to convert to AES...\n",0,0,0);
-
- be = slapi_get_first_backend(&cookie);
- while (be){
- int suffix_idx = 0;
- int count = slapi_counter_get_value(be->be_suffixcounter);
-
- list = be->be_suffixlist;
- for (suffix_idx = 0; list && suffix_idx < count; suffix_idx++) {
- char *suffix = (char *)slapi_sdn_get_ndn(list->be_suffix);
- if(charray_inlist(backends, suffix) || strlen(suffix) == 0){
- list = list->next;
- continue;
- }
- charray_add(&backends, slapi_ch_strdup(suffix));
- list = list->next;
- }
- be = slapi_get_next_backend (cookie);
- }
- slapi_ch_free ((void **)&cookie);
+ slapi_log_error(SLAPI_LOG_HOUSE, "convert_pbe_des_to_aes",
+ "Converting DES passwords to AES...\n");
- /*
- * Search for the password attributes
- */
for (i = 0; attrs && attrs[i]; i++){
char *filter = PR_smprintf("%s=*", attrs[i]);
- /*
- * Loop over all the backends looking for the password attribute
- */
- for(be_idx = 0; backends && backends[be_idx]; be_idx++){
- pb = slapi_pblock_new();
- slapi_search_internal_set_pb(pb, backends[be_idx],
- LDAP_SCOPE_SUBTREE, filter, NULL, 0, NULL, NULL,
- (void *)plugin_get_default_component_id(),
- SLAPI_OP_FLAG_IGNORE_UNINDEXED);
- slapi_search_internal_pb(pb);
- slapi_pblock_get(pb, SLAPI_PLUGIN_INTOP_RESULT, &result);
- if (LDAP_SUCCESS != result) {
- slapi_log_error(SLAPI_LOG_TRACE, "convert_pbe_des_to_aes: ",
- "Failed to search for password attribute (%s) error (%d), skipping suffix (%s)\n",
- attrs[i], result, backends[be_idx]);
- slapi_free_search_results_internal(pb);
- slapi_pblock_destroy(pb);
- pb = NULL;
- continue;
- }
- slapi_pblock_get(pb, SLAPI_PLUGIN_INTOP_SEARCH_ENTRIES, &entries);
- for (ii = 0; entries && entries[ii]; ii++){
- if((val = slapi_entry_attr_get_charptr(entries[ii], attrs[i]))){
- if(strlen(val) >= 5 && strncmp(val,"{DES}", 5) == 0){
- /*
- * We have a DES encoded password, convert it AES
- */
- Slapi_PBlock *mod_pb = NULL;
- Slapi_Value *sval = NULL;
- LDAPMod mod_replace;
- LDAPMod *mods[2];
- char *replace_val[2];
- char *passwd = NULL;
-
- /* decode the DES password */
- if(pw_rever_decode(val, &passwd, attrs[i]) == -1){
- LDAPDebug(LDAP_DEBUG_ANY,"convert_pbe_des_to_aes: "
- "Failed to decode existing DES password for (%s)\n",
- slapi_entry_get_dn(entries[ii]), 0, 0);
- disable_des = 0;
- goto done;
- }
- /* encode the password */
+ pb = slapi_pblock_new();
+ slapi_search_internal_set_pb(pb, "cn=config",
+ LDAP_SCOPE_SUBTREE, filter, NULL, 0, NULL, NULL,
+ (void *)plugin_get_default_component_id(),
+ SLAPI_OP_FLAG_IGNORE_UNINDEXED);
+ slapi_search_internal_pb(pb);
+ slapi_pblock_get(pb, SLAPI_PLUGIN_INTOP_SEARCH_ENTRIES, &entries);
+ for (ii = 0; entries && entries[ii]; ii++){
+ if((val = slapi_entry_attr_get_charptr(entries[ii], attrs[i]))){
+ if(strlen(val) >= 5 && strncmp(val,"{DES}", 5) == 0){
+ /*
+ * We have a DES encoded password, convert it to AES
+ */
+ Slapi_PBlock *mod_pb = NULL;
+ Slapi_Value *sval = NULL;
+ LDAPMod mod_replace;
+ LDAPMod *mods[2];
+ char *replace_val[2];
+ char *passwd = NULL;
+ int rc = 0;
+
+ /* decode the DES password */
+ if(pw_rever_decode(val, &passwd, attrs[i]) == -1){
+ slapi_log_error(SLAPI_LOG_FATAL ,"convert_pbe_des_to_aes",
+ "Failed to decode existing DES password for (%s)\n",
+ slapi_entry_get_dn(entries[ii]));
+ rc = -1;
+ }
+
+ /* encode the password */
+ if (rc == 0){
sval = slapi_value_new_string(passwd);
if(pw_rever_encode(&sval, attrs[i]) == -1){
- LDAPDebug(LDAP_DEBUG_ANY,"convert_pbe_des_to_aes: "
+ slapi_log_error(SLAPI_LOG_FATAL, "convert_pbe_des_to_aes",
"failed to encode AES password for (%s)\n",
- slapi_entry_get_dn(entries[ii]), 0, 0);
- slapi_ch_free_string(&passwd);
- slapi_value_free(&sval);
- disable_des = 0;
- goto done;
+ slapi_entry_get_dn(entries[ii]));
+ rc = -1;
}
+ }
+ if (rc == 0){
/* replace the attribute in the entry */
replace_val[0] = (char *)slapi_value_get_string(sval);
replace_val[1] = NULL;
@@ -837,83 +797,34 @@ convert_pbe_des_to_aes()
slapi_pblock_get(pb, SLAPI_PLUGIN_INTOP_RESULT, &result);
if (LDAP_SUCCESS != result) {
- LDAPDebug(LDAP_DEBUG_ANY,"convert_pbe_des_to_aes: "
+ slapi_log_error(SLAPI_LOG_FATAL, "convert_pbe_des_to_aes"
"Failed to convert password for (%s) error (%d)\n",
- slapi_entry_get_dn(entries[ii]), result, 0);
- disable_des = 0;
+ slapi_entry_get_dn(entries[ii]), result);
} else {
- LDAPDebug(LDAP_DEBUG_ANY,"convert_pbe_des_to_aes: "
+ slapi_log_error(SLAPI_LOG_HOUSE, "convert_pbe_des_to_aes",
"Successfully converted password for (%s)\n",
- slapi_entry_get_dn(entries[ii]), result, 0);
+ slapi_entry_get_dn(entries[ii]));
converted_des_passwd = 1;
}
- slapi_ch_free_string(&passwd);
- slapi_value_free(&sval);
- slapi_pblock_destroy(mod_pb);
}
- slapi_ch_free_string(&val);
+ slapi_ch_free_string(&passwd);
+ slapi_value_free(&sval);
+ slapi_pblock_destroy(mod_pb);
}
+ slapi_ch_free_string(&val);
}
- slapi_free_search_results_internal(pb);
- slapi_pblock_destroy(pb);
- pb = NULL;
}
+ slapi_free_search_results_internal(pb);
+ slapi_pblock_destroy(pb);
+ pb = NULL;
slapi_ch_free_string(&filter);
}
if (!converted_des_passwd){
- slapi_log_error(SLAPI_LOG_FATAL, "convert_pbe_des_to_aes",
+ slapi_log_error(SLAPI_LOG_HOUSE, "convert_pbe_des_to_aes",
"No DES passwords found to convert.\n");
}
}
-
-done:
charray_free(attrs);
- charray_free(backends);
- slapi_free_search_results_internal(pb);
- slapi_pblock_destroy(pb);
-
- if (have_aes && have_des){
- /*
- * If a conversion attempt did not fail then we can disable the DES plugin
- */
- if(converted_des_passwd && disable_des){
- /*
- * Disable the DES plugin - this also prevents potentially expensive
- * searches at every server startup.
- */
- LDAPMod mod_replace;
- LDAPMod *mods[2];
- char *replace_val[2];
- char *des_dn = "cn=DES,cn=Password Storage Schemes,cn=plugins,cn=config";
-
- replace_val[0] = "off";
- replace_val[1] = NULL;
- mod_replace.mod_op = LDAP_MOD_REPLACE;
- mod_replace.mod_type = "nsslapd-pluginEnabled";
- mod_replace.mod_values = replace_val;
- mods[0] = &mod_replace;
- mods[1] = 0;
-
- pb = slapi_pblock_new();
- slapi_modify_internal_set_pb(pb, des_dn, mods, 0, 0,
- (void *)plugin_get_default_component_id(), 0);
- slapi_modify_internal_pb(pb);
- slapi_pblock_get(pb, SLAPI_PLUGIN_INTOP_RESULT, &result);
- if (LDAP_SUCCESS != result) {
- LDAPDebug(LDAP_DEBUG_ANY,"convert_pbe_des_to_aes: "
- "Failed to disable DES plugin (%s), error (%d)\n",
- des_dn, result, 0);
- } else {
- LDAPDebug(LDAP_DEBUG_ANY,"convert_pbe_des_to_aes: "
- "Successfully disabled DES plugin (%s)\n",
- des_dn, 0, 0);
- }
- slapi_pblock_destroy(pb);
- LDAPDebug(LDAP_DEBUG_ANY,"convert_pbe_des_to_aes: "
- "All DES passwords have been converted to AES.\n",
- 0, 0, 0);
- }
- }
}
#ifdef ENABLE_NUNC_STANS
diff --git a/ldap/servers/slapd/pw.c b/ldap/servers/slapd/pw.c
index 09a0a6b..93e9078 100644
--- a/ldap/servers/slapd/pw.c
+++ b/ldap/servers/slapd/pw.c
@@ -516,10 +516,10 @@ pw_rever_encode(Slapi_Value **vals, char * attr_name)
for ( p = get_plugin_list(PLUGIN_LIST_REVER_PWD_STORAGE_SCHEME); p != NULL; p = p->plg_next )
{
char *L_attr = NULL;
- int i = 0;
+ int i = 0, ii = 0;
/* Get the appropriate encoding function */
- for ( L_attr = p->plg_argv[i]; i<p->plg_argc; L_attr = p->plg_argv[++i] )
+ for ( L_attr = p->plg_argv[ii]; ii<p->plg_argc; L_attr = p->plg_argv[++ii] )
{
if (slapi_attr_types_equivalent(L_attr, attr_name))
{
diff --git a/ldap/servers/slapd/task.c b/ldap/servers/slapd/task.c
index db3c222..405f0bf 100644
--- a/ldap/servers/slapd/task.c
+++ b/ldap/servers/slapd/task.c
@@ -53,6 +53,8 @@ static int shutting_down = 0;
#define TASK_TOMBSTONE_FIXUP_BACKEND "backend"
#define TASK_TOMBSTONE_FIXUP_SUFFIX "suffix"
#define TASK_TOMBSTONE_FIXUP_STRIPCSN "stripcsn"
+#define TASK_DES2AES "des2aes task"
+
#define LOG_BUFFER 256
/* if the cumul. log gets larger than this, it's truncated: */
@@ -83,8 +85,10 @@ static const char *fetch_attr(Slapi_Entry *e, const char *attrname,
const char *default_val);
static Slapi_Entry *get_internal_entry(Slapi_PBlock *pb, char *dn);
static void modify_internal_entry(char *dn, LDAPMod **mods);
-
static void fixup_tombstone_task_destructor(Slapi_Task *task);
+static void task_des2aes_thread(void *arg);
+static void des2aes_task_destructor(Slapi_Task *task);
+
/***********************************
* Public Functions
@@ -2425,6 +2429,345 @@ fixup_tombstone_task_destructor(Slapi_Task *task)
"fixup_tombstone_task_destructor <--\n" );
}
+/*
+ * des2aes Task
+ *
+ * Convert any DES passwords to AES
+ *
+ * dn: cn=convertPasswords, cn=des2aes,cn=tasks,cn=config
+ * objectclass: top
+ * objectclass: extensibleObject
+ * suffix: dc=example,dc=com (If empty all backends are checked)
+ * suffix: dc=other,dc=suffix
+ */
+struct task_des2aes_data
+{
+ char **suffixes;
+ Slapi_Task *task;
+};
+
+static int
+task_des2aes(Slapi_PBlock *pb, Slapi_Entry *e, Slapi_Entry *eAfter,
+ int *returncode, char *returntext, void *arg)
+{
+ struct task_des2aes_data *task_data = NULL;
+ PRThread *thread = NULL;
+ Slapi_Task *task = NULL;
+ char **suffix = NULL;
+ char **bases = NULL;
+ int rc = SLAPI_DSE_CALLBACK_OK;
+
+ /* Get the suffixes */
+ if((suffix = slapi_entry_attr_get_charray(e, "suffix"))){
+ int i;
+ for (i = 0; suffix && suffix[i]; i++){
+ /* Make sure "suffix" is NUL terminated string */
+ char *dn = slapi_create_dn_string("%s", suffix[i]);
+
+ if(dn){
+ if(slapi_dn_syntax_check(pb, dn, 1)){
+ /* invalid suffix name */
+ PR_snprintf(returntext, SLAPI_DSE_RETURNTEXT_SIZE,
+ "Invalid DN syntax (%s) specified for \"suffix\"\n",
+ suffix[i]);
+ *returncode = LDAP_INVALID_DN_SYNTAX;
+ slapi_ch_free_string(&dn);
+ rc = SLAPI_DSE_CALLBACK_ERROR;
+ goto error;
+ } else {
+ slapi_ch_array_add(&bases, dn);
+ }
+ } else{
+ PR_snprintf(returntext, SLAPI_DSE_RETURNTEXT_SIZE,
+ "Invalid DN (%s) specified for \"suffix\"\n", suffix[i]);
+ *returncode = LDAP_INVALID_DN_SYNTAX;
+ rc = SLAPI_DSE_CALLBACK_ERROR;
+ goto error;
+ }
+ }
+ }
+
+ /* Build the task data and fire off a thread to perform the conversion */
+ task = slapi_new_task(slapi_entry_get_ndn(e));
+
+ /* register our destructor for cleaning up our private data */
+ slapi_task_set_destructor_fn(task, des2aes_task_destructor);
+ task_data = (struct task_des2aes_data *)slapi_ch_calloc(1, sizeof(struct task_des2aes_data));
+ task_data->suffixes = bases;
+ task_data->task = task;
+
+ /* Start the conversion thread */
+ thread = PR_CreateThread(PR_USER_THREAD, task_des2aes_thread,
+ (void *)task_data, PR_PRIORITY_NORMAL, PR_GLOBAL_THREAD,
+ PR_UNJOINABLE_THREAD, SLAPD_DEFAULT_THREAD_STACKSIZE);
+ if (thread == NULL) {
+ PR_snprintf(returntext, SLAPI_DSE_RETURNTEXT_SIZE,
+ "unable to create des2aes thread!\n");
+ slapi_log_error(SLAPI_LOG_FATAL, TASK_DES2AES,
+ "unable to create des2aes thread!\n");
+ *returncode = LDAP_OPERATIONS_ERROR;
+ slapi_task_finish(task, *returncode);
+ rc = SLAPI_DSE_CALLBACK_ERROR;
+ }
+
+error:
+ if (rc == SLAPI_DSE_CALLBACK_ERROR){
+ slapi_ch_array_free(bases);
+ slapi_ch_array_free(suffix);
+ slapi_ch_free((void **)&task_data);
+ }
+ return rc;
+}
+
+static void
+task_des2aes_thread(void *arg)
+{
+ struct task_des2aes_data *task_data = arg;
+ Slapi_PBlock *pb = NULL;
+ Slapi_Entry **entries = NULL;
+ Slapi_Task *task = task_data->task;
+ struct slapdplugin *plugin = NULL;
+ char **attrs = NULL;
+ char **backends = NULL;
+ char *val = NULL;
+ int converted_des_passwd = 0;
+ int result = -1;
+ int have_aes = 0;
+ int have_des = 0;
+ int i = 0, ii = 0, be_idx = 0;
+ int rc = 0;
+
+ /*
+ * Check that AES plugin is enabled, and grab all the unique
+ * password attributes.
+ */
+ for ( plugin = get_plugin_list(PLUGIN_LIST_REVER_PWD_STORAGE_SCHEME);
+ plugin != NULL;
+ plugin = plugin->plg_next )
+ {
+ char *plugin_arg = NULL;
+
+ if(plugin->plg_started && strcasecmp(plugin->plg_name, "AES") == 0){
+ /* We have the AES plugin, and its enabled */
+ have_aes = 1;
+ }
+ if(plugin->plg_started && strcasecmp(plugin->plg_name, "DES") == 0){
+ /* We have the DES plugin, and its enabled */
+ have_des = 1;
+ }
+ /* Gather all the unique password attributes from all the PBE plugins */
+ for ( i = 0, plugin_arg = plugin->plg_argv[i];
+ i < plugin->plg_argc;
+ plugin_arg = plugin->plg_argv[++i] )
+ {
+ if(charray_inlist(attrs, plugin_arg)){
+ continue;
+ }
+ charray_add(&attrs, slapi_ch_strdup(plugin_arg));
+ }
+ }
+
+ if(have_aes && have_des){
+ if(task_data->suffixes == NULL){
+ /*
+ * Build a list of all the backend dn's
+ */
+ Slapi_Backend *be = NULL;
+ struct suffixlist *list;
+ char *cookie = NULL;
+
+ slapi_log_error(SLAPI_LOG_FATAL, TASK_DES2AES,
+ "Checking for DES passwords to convert to AES...\n");
+ slapi_task_log_notice(task,
+ "Checking for DES passwords to convert to AES...\n");
+
+ be = slapi_get_first_backend(&cookie);
+ while (be){
+ int suffix_idx = 0;
+ int count = slapi_counter_get_value(be->be_suffixcounter);
+
+ list = be->be_suffixlist;
+ for (suffix_idx = 0; list && suffix_idx < count; suffix_idx++) {
+ char *suffix = (char *)slapi_sdn_get_ndn(list->be_suffix);
+ if(charray_inlist(backends, suffix) || strlen(suffix) == 0){
+ list = list->next;
+ continue;
+ }
+ charray_add(&backends, slapi_ch_strdup(suffix));
+ list = list->next;
+ }
+ be = slapi_get_next_backend (cookie);
+ }
+ slapi_ch_free ((void **)&cookie);
+ } else {
+ backends = task_data->suffixes;
+ }
+
+ /*
+ * Search for the password attributes
+ */
+ for (i = 0; attrs && attrs[i]; i++){
+ char *filter = PR_smprintf("%s=*", attrs[i]);
+ /*
+ * Loop over all the backends looking for the password attribute
+ */
+ for(be_idx = 0; backends && backends[be_idx]; be_idx++){
+ pb = slapi_pblock_new();
+ slapi_search_internal_set_pb(pb, backends[be_idx],
+ LDAP_SCOPE_SUBTREE, filter, NULL, 0, NULL, NULL,
+ (void *)plugin_get_default_component_id(),
+ SLAPI_OP_FLAG_IGNORE_UNINDEXED);
+ slapi_search_internal_pb(pb);
+ slapi_pblock_get(pb, SLAPI_PLUGIN_INTOP_RESULT, &result);
+ if (LDAP_SUCCESS != result) {
+ slapi_log_error(SLAPI_LOG_FATAL, "convert_pbe_des_to_aes: ",
+ "Failed to search for password attribute (%s) error (%d), skipping suffix (%s)\n",
+ attrs[i], result, backends[be_idx]);
+ slapi_task_log_notice(task,
+ "Failed to search for password attribute (%s) error (%d), skipping suffix (%s)\n",
+ attrs[i], result, backends[be_idx]);
+ slapi_free_search_results_internal(pb);
+ slapi_pblock_destroy(pb);
+ pb = NULL;
+ continue;
+ }
+ slapi_pblock_get(pb, SLAPI_PLUGIN_INTOP_SEARCH_ENTRIES, &entries);
+ for (ii = 0; entries && entries[ii]; ii++){
+ if((val = slapi_entry_attr_get_charptr(entries[ii], attrs[i]))){
+ if(strlen(val) >= 5 && strncmp(val,"{DES}", 5) == 0){
+ /*
+ * We have a DES encoded password, convert it AES
+ */
+ Slapi_PBlock *mod_pb = NULL;
+ Slapi_Value *sval = NULL;
+ LDAPMod mod_replace;
+ LDAPMod *mods[2];
+ char *replace_val[2];
+ char *passwd = NULL;
+
+ /* Decode the DES password */
+ if(pw_rever_decode(val, &passwd, attrs[i]) == -1){
+ slapi_log_error(SLAPI_LOG_FATAL, TASK_DES2AES,
+ "Failed to decode existing DES password for (%s)\n",
+ slapi_entry_get_dn(entries[ii]));
+ slapi_task_log_notice(task,
+ "Failed to decode existing DES password for (%s)\n",
+ slapi_entry_get_dn(entries[ii]));
+ rc = 1;
+ goto done;
+ }
+
+ /* Encode the password */
+ sval = slapi_value_new_string(passwd);
+ if(pw_rever_encode(&sval, attrs[i]) == -1){
+ slapi_log_error(SLAPI_LOG_FATAL, TASK_DES2AES,
+ "failed to encode AES password for (%s)\n",
+ slapi_entry_get_dn(entries[ii]));
+ slapi_task_log_notice(task,
+ "failed to encode AES password for (%s)\n",
+ slapi_entry_get_dn(entries[ii]));
+ slapi_ch_free_string(&passwd);
+ slapi_value_free(&sval);
+ rc = 1;
+ goto done;
+ }
+
+ /* Replace the attribute in the entry */
+ replace_val[0] = (char *)slapi_value_get_string(sval);
+ replace_val[1] = NULL;
+ mod_replace.mod_op = LDAP_MOD_REPLACE;
+ mod_replace.mod_type = attrs[i];
+ mod_replace.mod_values = replace_val;
+ mods[0] = &mod_replace;
+ mods[1] = 0;
+
+ mod_pb = slapi_pblock_new();
+ slapi_modify_internal_set_pb(mod_pb, slapi_entry_get_dn(entries[ii]),
+ mods, 0, 0, (void *)plugin_get_default_component_id(), 0);
+ slapi_modify_internal_pb(mod_pb);
+
+ slapi_pblock_get(pb, SLAPI_PLUGIN_INTOP_RESULT, &result);
+ if (LDAP_SUCCESS != result) {
+ slapi_log_error(SLAPI_LOG_FATAL, TASK_DES2AES,
+ "Failed to convert password for (%s) error (%d)\n",
+ slapi_entry_get_dn(entries[ii]), result);
+ slapi_task_log_notice(task,
+ "Failed to convert password for (%s) error (%d)\n",
+ slapi_entry_get_dn(entries[ii]), result);
+ rc = 1;
+ } else {
+ slapi_log_error(SLAPI_LOG_FATAL, TASK_DES2AES,
+ "Successfully converted password for (%s)\n",
+ slapi_entry_get_dn(entries[ii]));
+ slapi_task_log_notice(task,
+ "Successfully converted password for (%s)\n",
+ slapi_entry_get_dn(entries[ii]));
+ converted_des_passwd = 1;
+ }
+ slapi_ch_free_string(&passwd);
+ slapi_value_free(&sval);
+ slapi_pblock_destroy(mod_pb);
+ }
+ slapi_ch_free_string(&val);
+ }
+ }
+ slapi_free_search_results_internal(pb);
+ slapi_pblock_destroy(pb);
+ pb = NULL;
+ }
+ slapi_ch_free_string(&filter);
+ }
+ if (!converted_des_passwd){
+ slapi_log_error(SLAPI_LOG_FATAL, TASK_DES2AES,
+ "No DES passwords found to convert.\n");
+ slapi_task_log_notice(task, "No DES passwords found to convert.\n");
+ }
+ } else {
+ /* No AES/DES */
+ if (!have_des){
+ slapi_log_error(SLAPI_LOG_FATAL, TASK_DES2AES,
+ "DES plugin not enabled\n");
+ slapi_task_log_notice(task, "DES plugin not enabled\n");
+ }
+ if (!have_aes){
+ slapi_log_error(SLAPI_LOG_FATAL, TASK_DES2AES,
+ "AES plugin not enabled\n");
+ slapi_task_log_notice(task, "AES plugin not enabled\n");
+ }
+ slapi_log_error(SLAPI_LOG_FATAL, TASK_DES2AES,
+ "Unable to convert passwords\n");
+ slapi_task_log_notice(task, "Unable to convert passwords\n");
+ rc = 1;
+ }
+
+done:
+ charray_free(attrs);
+ charray_free(backends);
+ slapi_free_search_results_internal(pb);
+ slapi_pblock_destroy(pb);
+ slapi_task_finish(task, rc);
+}
+
+static void
+des2aes_task_destructor(Slapi_Task *task)
+{
+ slapi_log_error(SLAPI_LOG_TRACE, TASK_DES2AES,
+ "des2aes_task_destructor -->\n" );
+ if (task) {
+ struct task_des2aes_data *task_data = (struct task_des2aes_data *)slapi_task_get_data(task);
+ while (slapi_task_get_refcount(task) > 0) {
+ /* Yield to wait for the task to finish. */
+ DS_Sleep (PR_MillisecondsToInterval(100));
+ }
+ if (task_data) {
+ slapi_ch_array_free(task_data->suffixes);
+ slapi_ch_free((void **)&task_data);
+ }
+ }
+ slapi_log_error(SLAPI_LOG_TRACE, TASK_DES2AES,
+ "des2aes_task_destructor <--\n" );
+}
+
/* cleanup old tasks that may still be in the DSE from a previous session
* (this can happen if the server crashes [no matter how unlikely we like
* to think that is].)
@@ -2506,6 +2849,7 @@ void task_init(void)
slapi_task_register_handler("upgradedb", task_upgradedb_add);
slapi_task_register_handler("sysconfig reload", task_sysconfig_reload_add);
slapi_task_register_handler("fixup tombstones", task_fixup_tombstones_add);
+ slapi_task_register_handler("des2aes", task_des2aes);
}
/* called when the server is shutting down -- abort all existing tasks */
7 years, 10 months
dirsrvtests/tests ldap/servers
by Mark Reynolds
dirsrvtests/tests/tickets/ticket47462_test.py | 151 +++++++----
ldap/servers/slapd/daemon.c | 195 +++-----------
ldap/servers/slapd/pw.c | 4
ldap/servers/slapd/task.c | 346 +++++++++++++++++++++++++-
4 files changed, 505 insertions(+), 191 deletions(-)
New commits:
commit 11f55f3dd2a2c44ddf7b5be54273401add13b1bc
Author: Mark Reynolds <mreynolds(a)redhat.com>
Date: Tue Jun 7 10:02:42 2016 -0400
Ticket 48862 - At startup DES to AES password conversion causes timeout in start script
Bug Description: At server start all the backends are searches for entries that contain
DES password attributes as defined in the plugin. These are typically
unindexed searches, and if there is a very large backend this can cause
the server startup to timeout.
Fix Description: At startup only check "cn=config" for entries with DES password attributes.
A new "conversion" task has been created that can be run after startup
to search all backends(if a suffix is not specified), or specific backends.
dn: cn=convertPasswords, cn=des2aes,cn=tasks,cn=config
objectclass: top
objectclass: extensibleObject
suffix: dc=example,dc=com
suffix: dc=other,dc=suffix
Another bug was discovered in pw_rever_encode() in pw.c where a "for" loop
counter was accidentially reused by a second "for" loop. This could lead
to an infinite loop/hang.
Updated the CI test to perform the conversion task.
https://fedorahosted.org/389/ticket/48862
Reviewed by: nhosoi(Thanks!)
diff --git a/dirsrvtests/tests/tickets/ticket47462_test.py b/dirsrvtests/tests/tickets/ticket47462_test.py
index f290647..2ac1478 100644
--- a/dirsrvtests/tests/tickets/ticket47462_test.py
+++ b/dirsrvtests/tests/tickets/ticket47462_test.py
@@ -32,6 +32,7 @@ AGMT_DN = ''
USER_DN = 'cn=test_user,' + DEFAULT_SUFFIX
USER1_DN = 'cn=test_user1,' + DEFAULT_SUFFIX
TEST_REPL_DN = 'cn=test repl,' + DEFAULT_SUFFIX
+DES2AES_TASK_DN = 'cn=convert,cn=des2aes,cn=tasks,cn=config'
class TopologyMaster1Master2(object):
@@ -134,6 +135,11 @@ def topology(request):
# clear the tmp directory
master1.clearTmpDir(__file__)
+ def fin():
+ master1.delete()
+ master2.delete()
+ request.addfinalizer(fin)
+
return TopologyMaster1Master2(master1, master2)
@@ -144,11 +150,9 @@ def test_ticket47462(topology):
"""
#
- # First set config as if it's an older version. Set DES to use libdes-plugin,
- # MMR to depend on DES, delete the existing AES plugin, and set a DES password
- # for the replication agreement.
- #
-
+ # First set config as if it's an older version. Set DES to use
+ # libdes-plugin, MMR to depend on DES, delete the existing AES plugin,
+ # and set a DES password for the replication agreement.
#
# Add an extra attribute to the DES plugin args
#
@@ -156,24 +160,29 @@ def test_ticket47462(topology):
topology.master1.modify_s(DES_PLUGIN,
[(ldap.MOD_REPLACE, 'nsslapd-pluginEnabled', 'on')])
except ldap.LDAPError as e:
- log.fatal('Failed to enable DES plugin, error: ' + e.message['desc'])
+ log.fatal('Failed to enable DES plugin, error: ' +
+ e.message['desc'])
assert False
try:
topology.master1.modify_s(DES_PLUGIN,
[(ldap.MOD_ADD, 'nsslapd-pluginarg2', 'description')])
except ldap.LDAPError as e:
- log.fatal('Failed to reset DES plugin, error: ' + e.message['desc'])
+ log.fatal('Failed to reset DES plugin, error: ' +
+ e.message['desc'])
assert False
try:
topology.master1.modify_s(MMR_PLUGIN,
- [(ldap.MOD_DELETE, 'nsslapd-plugin-depends-on-named', 'AES')])
+ [(ldap.MOD_DELETE,
+ 'nsslapd-plugin-depends-on-named',
+ 'AES')])
except ldap.NO_SUCH_ATTRIBUTE:
pass
except ldap.LDAPError as e:
- log.fatal('Failed to reset MMR plugin, error: ' + e.message['desc'])
+ log.fatal('Failed to reset MMR plugin, error: ' +
+ e.message['desc'])
assert False
#
@@ -184,7 +193,8 @@ def test_ticket47462(topology):
except ldap.NO_SUCH_OBJECT:
pass
except ldap.LDAPError as e:
- log.fatal('Failed to delete AES plugin, error: ' + e.message['desc'])
+ log.fatal('Failed to delete AES plugin, error: ' +
+ e.message['desc'])
assert False
# restart the server so we must use DES plugin
@@ -194,7 +204,8 @@ def test_ticket47462(topology):
# Get the agmt dn, and set the password
#
try:
- entry = topology.master1.search_s('cn=config', ldap.SCOPE_SUBTREE, 'objectclass=nsDS5ReplicationAgreement')
+ entry = topology.master1.search_s('cn=config', ldap.SCOPE_SUBTREE,
+ 'objectclass=nsDS5ReplicationAgreement')
if entry:
agmt_dn = entry[0].dn
log.info('Found agmt dn (%s)' % agmt_dn)
@@ -202,12 +213,14 @@ def test_ticket47462(topology):
log.fatal('No replication agreements!')
assert False
except ldap.LDAPError as e:
- log.fatal('Failed to search for replica credentials: ' + e.message['desc'])
+ log.fatal('Failed to search for replica credentials: ' +
+ e.message['desc'])
assert False
try:
properties = {RA_BINDPW: "password"}
- topology.master1.agreement.setProperties(None, agmt_dn, None, properties)
+ topology.master1.agreement.setProperties(None, agmt_dn, None,
+ properties)
log.info('Successfully modified replication agreement')
except ValueError:
log.error('Failed to update replica agreement: ' + AGMT_DN)
@@ -220,12 +233,14 @@ def test_ticket47462(topology):
topology.master1.add_s(Entry((USER1_DN,
{'objectclass': "top person".split(),
'sn': 'sn',
+ 'description': 'DES value to convert',
'cn': 'test_user'})))
loop = 0
ent = None
while loop <= 10:
try:
- ent = topology.master2.getEntry(USER1_DN, ldap.SCOPE_BASE, "(objectclass=*)")
+ ent = topology.master2.getEntry(USER1_DN, ldap.SCOPE_BASE,
+ "(objectclass=*)")
break
except ldap.NO_SUCH_OBJECT:
time.sleep(1)
@@ -259,7 +274,8 @@ def test_ticket47462(topology):
# Check that the restart converted existing DES credentials
#
try:
- entry = topology.master1.search_s('cn=config', ldap.SCOPE_SUBTREE, 'nsDS5ReplicaCredentials=*')
+ entry = topology.master1.search_s('cn=config', ldap.SCOPE_SUBTREE,
+ 'nsDS5ReplicaCredentials=*')
if entry:
val = entry[0].getValue('nsDS5ReplicaCredentials')
if val.startswith('{AES-'):
@@ -268,22 +284,26 @@ def test_ticket47462(topology):
log.fatal('Failed to convert credentials from DES to AES!')
assert False
else:
- log.fatal('Failed to find any entries with nsDS5ReplicaCredentials ')
+ log.fatal('Failed to find entries with nsDS5ReplicaCredentials')
assert False
except ldap.LDAPError as e:
- log.fatal('Failed to search for replica credentials: ' + e.message['desc'])
+ log.fatal('Failed to search for replica credentials: ' +
+ e.message['desc'])
assert False
#
- # Check that the AES plugin exists, and has all the attributes listed in DES plugin.
- # The attributes might not be in the expected order so check all the attributes.
+ # Check that the AES plugin exists, and has all the attributes listed in
+ # DES plugin. The attributes might not be in the expected order so check
+ # all the attributes.
#
try:
- entry = topology.master1.search_s(AES_PLUGIN, ldap.SCOPE_BASE, 'objectclass=*')
+ entry = topology.master1.search_s(AES_PLUGIN, ldap.SCOPE_BASE,
+ 'objectclass=*')
if not entry[0].hasValue('nsslapd-pluginarg0', 'description') and \
not entry[0].hasValue('nsslapd-pluginarg1', 'description') and \
not entry[0].hasValue('nsslapd-pluginarg2', 'description'):
- log.fatal('The AES plugin did not have the DES attribute copied over correctly')
+ log.fatal('The AES plugin did not have the DES attribute copied ' +
+ 'over correctly')
assert False
else:
log.info('The AES plugin was correctly setup')
@@ -295,7 +315,8 @@ def test_ticket47462(topology):
# Check that the MMR plugin was updated
#
try:
- entry = topology.master1.search_s(MMR_PLUGIN, ldap.SCOPE_BASE, 'objectclass=*')
+ entry = topology.master1.search_s(MMR_PLUGIN, ldap.SCOPE_BASE,
+ 'objectclass=*')
if not entry[0].hasValue('nsslapd-plugin-depends-on-named', 'AES'):
log.fatal('The MMR Plugin was not correctly updated')
assert False
@@ -309,7 +330,8 @@ def test_ticket47462(topology):
# Check that the DES plugin was correctly updated
#
try:
- entry = topology.master1.search_s(DES_PLUGIN, ldap.SCOPE_BASE, 'objectclass=*')
+ entry = topology.master1.search_s(DES_PLUGIN, ldap.SCOPE_BASE,
+ 'objectclass=*')
if not entry[0].hasValue('nsslapd-pluginPath', 'libpbe-plugin'):
log.fatal('The DES Plugin was not correctly updated')
assert False
@@ -331,7 +353,8 @@ def test_ticket47462(topology):
ent = None
while loop <= 10:
try:
- ent = topology.master2.getEntry(USER_DN, ldap.SCOPE_BASE, "(objectclass=*)")
+ ent = topology.master2.getEntry(USER_DN, ldap.SCOPE_BASE,
+ "(objectclass=*)")
break
except ldap.NO_SUCH_OBJECT:
time.sleep(1)
@@ -345,30 +368,66 @@ def test_ticket47462(topology):
log.fatal('Failed to add test user: ' + e.message['desc'])
assert False
+ # Check the entry
+ log.info('Entry before running task...')
+ try:
+ entry = topology.master1.search_s(USER1_DN,
+ ldap.SCOPE_BASE,
+ 'objectclass=*')
+ if entry:
+ print(str(entry))
+ else:
+ log.fatal('Failed to find entries')
+ assert False
+ except ldap.LDAPError as e:
+ log.fatal('Failed to search for entries: ' +
+ e.message['desc'])
+ assert False
-def test_ticket47462_final(topology):
- topology.master1.delete()
- topology.master2.delete()
- log.info('Testcase PASSED')
-
-
-def run_isolated():
- '''
- run_isolated is used to run these test cases independently of a test scheduler (xunit, py.test..)
- To run isolated without py.test, you need to
- - edit this file and comment '@pytest.fixture' line before 'topology' function.
- - set the installation prefix
- - run this program
- '''
- global installation1_prefix
- global installation2_prefix
- installation1_prefix = None
- installation2_prefix = None
+ #
+ # Test the DES2AES Task on USER1_DN
+ #
+ try:
+ topology.master1.add_s(Entry((DES2AES_TASK_DN,
+ {'objectclass': ['top',
+ 'extensibleObject'],
+ 'suffix': DEFAULT_SUFFIX,
+ 'cn': 'convert'})))
+ except ldap.LDAPError as e:
+ log.fatal('Failed to add task entry: ' + e.message['desc'])
+ assert False
- topo = topology(True)
- test_ticket47462(topo)
- test_ticket47462_final(topo)
+ # Wait for task
+ task_entry = Entry(DES2AES_TASK_DN)
+ (done, exitCode) = topology.master1.tasks.checkTask(task_entry, True)
+ if exitCode:
+ log.fatal("Error: des2aes task exited with %d" % (exitCode))
+ assert False
+ # Check the entry
+ try:
+ entry = topology.master1.search_s(USER1_DN,
+ ldap.SCOPE_BASE,
+ 'objectclass=*')
+ if entry:
+ val = entry[0].getValue('description')
+ print(str(entry[0]))
+ if val.startswith('{AES-'):
+ log.info('Task: DES credentials have been converted to AES')
+ else:
+ log.fatal('Task: Failed to convert credentials from DES to ' +
+ 'AES! (%s)' % (val))
+ assert False
+ else:
+ log.fatal('Failed to find entries')
+ assert False
+ except ldap.LDAPError as e:
+ log.fatal('Failed to search for entries: ' +
+ e.message['desc'])
+ assert False
if __name__ == '__main__':
- run_isolated()
+ # Run isolated
+ # -s for DEBUG mode
+ CURRENT_FILE = os.path.realpath(__file__)
+ pytest.main("-s %s" % CURRENT_FILE)
diff --git a/ldap/servers/slapd/daemon.c b/ldap/servers/slapd/daemon.c
index b6113c8..862f6e7 100644
--- a/ldap/servers/slapd/daemon.c
+++ b/ldap/servers/slapd/daemon.c
@@ -703,14 +703,12 @@ convert_pbe_des_to_aes()
Slapi_Entry **entries = NULL;
struct slapdplugin *plugin = NULL;
char **attrs = NULL;
- char **backends = NULL;
char *val = NULL;
int converted_des_passwd = 0;
- int disable_des = 1;
int result = -1;
int have_aes = 0;
int have_des = 0;
- int i = 0, ii = 0, be_idx = 0;
+ int i = 0, ii = 0;
/*
* Check that AES plugin is enabled, and grab all the unique
@@ -744,94 +742,56 @@ convert_pbe_des_to_aes()
if(have_aes && have_des){
/*
- * Build a list of all the backend dn's
+ * Find any entries in cn=config that contain DES passwords and convert
+ * them to AES
*/
- Slapi_Backend *be = NULL;
- struct suffixlist *list;
- char *cookie = NULL;
-
- LDAPDebug(LDAP_DEBUG_ANY, "convert_pbe_des_to_aes: "
- "Checking for DES passwords to convert to AES...\n",0,0,0);
-
- be = slapi_get_first_backend(&cookie);
- while (be){
- int suffix_idx = 0;
- int count = slapi_counter_get_value(be->be_suffixcounter);
-
- list = be->be_suffixlist;
- for (suffix_idx = 0; list && suffix_idx < count; suffix_idx++) {
- char *suffix = (char *)slapi_sdn_get_ndn(list->be_suffix);
- if(charray_inlist(backends, suffix) || strlen(suffix) == 0){
- list = list->next;
- continue;
- }
- charray_add(&backends, slapi_ch_strdup(suffix));
- list = list->next;
- }
- be = slapi_get_next_backend (cookie);
- }
- slapi_ch_free ((void **)&cookie);
+ slapi_log_error(SLAPI_LOG_HOUSE, "convert_pbe_des_to_aes",
+ "Converting DES passwords to AES...\n");
- /*
- * Search for the password attributes
- */
for (i = 0; attrs && attrs[i]; i++){
char *filter = PR_smprintf("%s=*", attrs[i]);
- /*
- * Loop over all the backends looking for the password attribute
- */
- for(be_idx = 0; backends && backends[be_idx]; be_idx++){
- pb = slapi_pblock_new();
- slapi_search_internal_set_pb(pb, backends[be_idx],
- LDAP_SCOPE_SUBTREE, filter, NULL, 0, NULL, NULL,
- (void *)plugin_get_default_component_id(),
- SLAPI_OP_FLAG_IGNORE_UNINDEXED);
- slapi_search_internal_pb(pb);
- slapi_pblock_get(pb, SLAPI_PLUGIN_INTOP_RESULT, &result);
- if (LDAP_SUCCESS != result) {
- slapi_log_error(SLAPI_LOG_TRACE, "convert_pbe_des_to_aes: ",
- "Failed to search for password attribute (%s) error (%d), skipping suffix (%s)\n",
- attrs[i], result, backends[be_idx]);
- slapi_free_search_results_internal(pb);
- slapi_pblock_destroy(pb);
- pb = NULL;
- continue;
- }
- slapi_pblock_get(pb, SLAPI_PLUGIN_INTOP_SEARCH_ENTRIES, &entries);
- for (ii = 0; entries && entries[ii]; ii++){
- if((val = slapi_entry_attr_get_charptr(entries[ii], attrs[i]))){
- if(strlen(val) >= 5 && strncmp(val,"{DES}", 5) == 0){
- /*
- * We have a DES encoded password, convert it AES
- */
- Slapi_PBlock *mod_pb = NULL;
- Slapi_Value *sval = NULL;
- LDAPMod mod_replace;
- LDAPMod *mods[2];
- char *replace_val[2];
- char *passwd = NULL;
-
- /* decode the DES password */
- if(pw_rever_decode(val, &passwd, attrs[i]) == -1){
- LDAPDebug(LDAP_DEBUG_ANY,"convert_pbe_des_to_aes: "
- "Failed to decode existing DES password for (%s)\n",
- slapi_entry_get_dn(entries[ii]), 0, 0);
- disable_des = 0;
- goto done;
- }
- /* encode the password */
+ pb = slapi_pblock_new();
+ slapi_search_internal_set_pb(pb, "cn=config",
+ LDAP_SCOPE_SUBTREE, filter, NULL, 0, NULL, NULL,
+ (void *)plugin_get_default_component_id(),
+ SLAPI_OP_FLAG_IGNORE_UNINDEXED);
+ slapi_search_internal_pb(pb);
+ slapi_pblock_get(pb, SLAPI_PLUGIN_INTOP_SEARCH_ENTRIES, &entries);
+ for (ii = 0; entries && entries[ii]; ii++){
+ if((val = slapi_entry_attr_get_charptr(entries[ii], attrs[i]))){
+ if(strlen(val) >= 5 && strncmp(val,"{DES}", 5) == 0){
+ /*
+ * We have a DES encoded password, convert it to AES
+ */
+ Slapi_PBlock *mod_pb = NULL;
+ Slapi_Value *sval = NULL;
+ LDAPMod mod_replace;
+ LDAPMod *mods[2];
+ char *replace_val[2];
+ char *passwd = NULL;
+ int rc = 0;
+
+ /* decode the DES password */
+ if(pw_rever_decode(val, &passwd, attrs[i]) == -1){
+ slapi_log_error(SLAPI_LOG_FATAL ,"convert_pbe_des_to_aes",
+ "Failed to decode existing DES password for (%s)\n",
+ slapi_entry_get_dn(entries[ii]));
+ rc = -1;
+ }
+
+ /* encode the password */
+ if (rc == 0){
sval = slapi_value_new_string(passwd);
if(pw_rever_encode(&sval, attrs[i]) == -1){
- LDAPDebug(LDAP_DEBUG_ANY,"convert_pbe_des_to_aes: "
+ slapi_log_error(SLAPI_LOG_FATAL, "convert_pbe_des_to_aes",
"failed to encode AES password for (%s)\n",
- slapi_entry_get_dn(entries[ii]), 0, 0);
- slapi_ch_free_string(&passwd);
- slapi_value_free(&sval);
- disable_des = 0;
- goto done;
+ slapi_entry_get_dn(entries[ii]));
+ rc = -1;
}
+ }
+ if (rc == 0){
/* replace the attribute in the entry */
replace_val[0] = (char *)slapi_value_get_string(sval);
replace_val[1] = NULL;
@@ -848,83 +808,34 @@ convert_pbe_des_to_aes()
slapi_pblock_get(pb, SLAPI_PLUGIN_INTOP_RESULT, &result);
if (LDAP_SUCCESS != result) {
- LDAPDebug(LDAP_DEBUG_ANY,"convert_pbe_des_to_aes: "
+ slapi_log_error(SLAPI_LOG_FATAL, "convert_pbe_des_to_aes"
"Failed to convert password for (%s) error (%d)\n",
- slapi_entry_get_dn(entries[ii]), result, 0);
- disable_des = 0;
+ slapi_entry_get_dn(entries[ii]), result);
} else {
- LDAPDebug(LDAP_DEBUG_ANY,"convert_pbe_des_to_aes: "
+ slapi_log_error(SLAPI_LOG_HOUSE, "convert_pbe_des_to_aes",
"Successfully converted password for (%s)\n",
- slapi_entry_get_dn(entries[ii]), result, 0);
+ slapi_entry_get_dn(entries[ii]));
converted_des_passwd = 1;
}
- slapi_ch_free_string(&passwd);
- slapi_value_free(&sval);
- slapi_pblock_destroy(mod_pb);
}
- slapi_ch_free_string(&val);
+ slapi_ch_free_string(&passwd);
+ slapi_value_free(&sval);
+ slapi_pblock_destroy(mod_pb);
}
+ slapi_ch_free_string(&val);
}
- slapi_free_search_results_internal(pb);
- slapi_pblock_destroy(pb);
- pb = NULL;
}
+ slapi_free_search_results_internal(pb);
+ slapi_pblock_destroy(pb);
+ pb = NULL;
slapi_ch_free_string(&filter);
}
if (!converted_des_passwd){
- slapi_log_error(SLAPI_LOG_FATAL, "convert_pbe_des_to_aes",
+ slapi_log_error(SLAPI_LOG_HOUSE, "convert_pbe_des_to_aes",
"No DES passwords found to convert.\n");
}
}
-
-done:
charray_free(attrs);
- charray_free(backends);
- slapi_free_search_results_internal(pb);
- slapi_pblock_destroy(pb);
-
- if (have_aes && have_des){
- /*
- * If a conversion attempt did not fail then we can disable the DES plugin
- */
- if(converted_des_passwd && disable_des){
- /*
- * Disable the DES plugin - this also prevents potentially expensive
- * searches at every server startup.
- */
- LDAPMod mod_replace;
- LDAPMod *mods[2];
- char *replace_val[2];
- char *des_dn = "cn=DES,cn=Password Storage Schemes,cn=plugins,cn=config";
-
- replace_val[0] = "off";
- replace_val[1] = NULL;
- mod_replace.mod_op = LDAP_MOD_REPLACE;
- mod_replace.mod_type = "nsslapd-pluginEnabled";
- mod_replace.mod_values = replace_val;
- mods[0] = &mod_replace;
- mods[1] = 0;
-
- pb = slapi_pblock_new();
- slapi_modify_internal_set_pb(pb, des_dn, mods, 0, 0,
- (void *)plugin_get_default_component_id(), 0);
- slapi_modify_internal_pb(pb);
- slapi_pblock_get(pb, SLAPI_PLUGIN_INTOP_RESULT, &result);
- if (LDAP_SUCCESS != result) {
- LDAPDebug(LDAP_DEBUG_ANY,"convert_pbe_des_to_aes: "
- "Failed to disable DES plugin (%s), error (%d)\n",
- des_dn, result, 0);
- } else {
- LDAPDebug(LDAP_DEBUG_ANY,"convert_pbe_des_to_aes: "
- "Successfully disabled DES plugin (%s)\n",
- des_dn, 0, 0);
- }
- slapi_pblock_destroy(pb);
- LDAPDebug(LDAP_DEBUG_ANY,"convert_pbe_des_to_aes: "
- "All DES passwords have been converted to AES.\n",
- 0, 0, 0);
- }
- }
}
#ifdef ENABLE_NUNC_STANS
diff --git a/ldap/servers/slapd/pw.c b/ldap/servers/slapd/pw.c
index 17938b5..c52fa9b 100644
--- a/ldap/servers/slapd/pw.c
+++ b/ldap/servers/slapd/pw.c
@@ -516,10 +516,10 @@ pw_rever_encode(Slapi_Value **vals, char * attr_name)
for ( p = get_plugin_list(PLUGIN_LIST_REVER_PWD_STORAGE_SCHEME); p != NULL; p = p->plg_next )
{
char *L_attr = NULL;
- int i = 0;
+ int i = 0, ii = 0;
/* Get the appropriate encoding function */
- for ( L_attr = p->plg_argv[i]; i<p->plg_argc; L_attr = p->plg_argv[++i] )
+ for ( L_attr = p->plg_argv[ii]; ii<p->plg_argc; L_attr = p->plg_argv[++ii] )
{
if (slapi_attr_types_equivalent(L_attr, attr_name))
{
diff --git a/ldap/servers/slapd/task.c b/ldap/servers/slapd/task.c
index db3c222..405f0bf 100644
--- a/ldap/servers/slapd/task.c
+++ b/ldap/servers/slapd/task.c
@@ -53,6 +53,8 @@ static int shutting_down = 0;
#define TASK_TOMBSTONE_FIXUP_BACKEND "backend"
#define TASK_TOMBSTONE_FIXUP_SUFFIX "suffix"
#define TASK_TOMBSTONE_FIXUP_STRIPCSN "stripcsn"
+#define TASK_DES2AES "des2aes task"
+
#define LOG_BUFFER 256
/* if the cumul. log gets larger than this, it's truncated: */
@@ -83,8 +85,10 @@ static const char *fetch_attr(Slapi_Entry *e, const char *attrname,
const char *default_val);
static Slapi_Entry *get_internal_entry(Slapi_PBlock *pb, char *dn);
static void modify_internal_entry(char *dn, LDAPMod **mods);
-
static void fixup_tombstone_task_destructor(Slapi_Task *task);
+static void task_des2aes_thread(void *arg);
+static void des2aes_task_destructor(Slapi_Task *task);
+
/***********************************
* Public Functions
@@ -2425,6 +2429,345 @@ fixup_tombstone_task_destructor(Slapi_Task *task)
"fixup_tombstone_task_destructor <--\n" );
}
+/*
+ * des2aes Task
+ *
+ * Convert any DES passwords to AES
+ *
+ * dn: cn=convertPasswords, cn=des2aes,cn=tasks,cn=config
+ * objectclass: top
+ * objectclass: extensibleObject
+ * suffix: dc=example,dc=com (If empty all backends are checked)
+ * suffix: dc=other,dc=suffix
+ */
+struct task_des2aes_data
+{
+ char **suffixes;
+ Slapi_Task *task;
+};
+
+static int
+task_des2aes(Slapi_PBlock *pb, Slapi_Entry *e, Slapi_Entry *eAfter,
+ int *returncode, char *returntext, void *arg)
+{
+ struct task_des2aes_data *task_data = NULL;
+ PRThread *thread = NULL;
+ Slapi_Task *task = NULL;
+ char **suffix = NULL;
+ char **bases = NULL;
+ int rc = SLAPI_DSE_CALLBACK_OK;
+
+ /* Get the suffixes */
+ if((suffix = slapi_entry_attr_get_charray(e, "suffix"))){
+ int i;
+ for (i = 0; suffix && suffix[i]; i++){
+ /* Make sure "suffix" is NUL terminated string */
+ char *dn = slapi_create_dn_string("%s", suffix[i]);
+
+ if(dn){
+ if(slapi_dn_syntax_check(pb, dn, 1)){
+ /* invalid suffix name */
+ PR_snprintf(returntext, SLAPI_DSE_RETURNTEXT_SIZE,
+ "Invalid DN syntax (%s) specified for \"suffix\"\n",
+ suffix[i]);
+ *returncode = LDAP_INVALID_DN_SYNTAX;
+ slapi_ch_free_string(&dn);
+ rc = SLAPI_DSE_CALLBACK_ERROR;
+ goto error;
+ } else {
+ slapi_ch_array_add(&bases, dn);
+ }
+ } else{
+ PR_snprintf(returntext, SLAPI_DSE_RETURNTEXT_SIZE,
+ "Invalid DN (%s) specified for \"suffix\"\n", suffix[i]);
+ *returncode = LDAP_INVALID_DN_SYNTAX;
+ rc = SLAPI_DSE_CALLBACK_ERROR;
+ goto error;
+ }
+ }
+ }
+
+ /* Build the task data and fire off a thread to perform the conversion */
+ task = slapi_new_task(slapi_entry_get_ndn(e));
+
+ /* register our destructor for cleaning up our private data */
+ slapi_task_set_destructor_fn(task, des2aes_task_destructor);
+ task_data = (struct task_des2aes_data *)slapi_ch_calloc(1, sizeof(struct task_des2aes_data));
+ task_data->suffixes = bases;
+ task_data->task = task;
+
+ /* Start the conversion thread */
+ thread = PR_CreateThread(PR_USER_THREAD, task_des2aes_thread,
+ (void *)task_data, PR_PRIORITY_NORMAL, PR_GLOBAL_THREAD,
+ PR_UNJOINABLE_THREAD, SLAPD_DEFAULT_THREAD_STACKSIZE);
+ if (thread == NULL) {
+ PR_snprintf(returntext, SLAPI_DSE_RETURNTEXT_SIZE,
+ "unable to create des2aes thread!\n");
+ slapi_log_error(SLAPI_LOG_FATAL, TASK_DES2AES,
+ "unable to create des2aes thread!\n");
+ *returncode = LDAP_OPERATIONS_ERROR;
+ slapi_task_finish(task, *returncode);
+ rc = SLAPI_DSE_CALLBACK_ERROR;
+ }
+
+error:
+ if (rc == SLAPI_DSE_CALLBACK_ERROR){
+ slapi_ch_array_free(bases);
+ slapi_ch_array_free(suffix);
+ slapi_ch_free((void **)&task_data);
+ }
+ return rc;
+}
+
+static void
+task_des2aes_thread(void *arg)
+{
+ struct task_des2aes_data *task_data = arg;
+ Slapi_PBlock *pb = NULL;
+ Slapi_Entry **entries = NULL;
+ Slapi_Task *task = task_data->task;
+ struct slapdplugin *plugin = NULL;
+ char **attrs = NULL;
+ char **backends = NULL;
+ char *val = NULL;
+ int converted_des_passwd = 0;
+ int result = -1;
+ int have_aes = 0;
+ int have_des = 0;
+ int i = 0, ii = 0, be_idx = 0;
+ int rc = 0;
+
+ /*
+ * Check that AES plugin is enabled, and grab all the unique
+ * password attributes.
+ */
+ for ( plugin = get_plugin_list(PLUGIN_LIST_REVER_PWD_STORAGE_SCHEME);
+ plugin != NULL;
+ plugin = plugin->plg_next )
+ {
+ char *plugin_arg = NULL;
+
+ if(plugin->plg_started && strcasecmp(plugin->plg_name, "AES") == 0){
+ /* We have the AES plugin, and its enabled */
+ have_aes = 1;
+ }
+ if(plugin->plg_started && strcasecmp(plugin->plg_name, "DES") == 0){
+ /* We have the DES plugin, and its enabled */
+ have_des = 1;
+ }
+ /* Gather all the unique password attributes from all the PBE plugins */
+ for ( i = 0, plugin_arg = plugin->plg_argv[i];
+ i < plugin->plg_argc;
+ plugin_arg = plugin->plg_argv[++i] )
+ {
+ if(charray_inlist(attrs, plugin_arg)){
+ continue;
+ }
+ charray_add(&attrs, slapi_ch_strdup(plugin_arg));
+ }
+ }
+
+ if(have_aes && have_des){
+ if(task_data->suffixes == NULL){
+ /*
+ * Build a list of all the backend dn's
+ */
+ Slapi_Backend *be = NULL;
+ struct suffixlist *list;
+ char *cookie = NULL;
+
+ slapi_log_error(SLAPI_LOG_FATAL, TASK_DES2AES,
+ "Checking for DES passwords to convert to AES...\n");
+ slapi_task_log_notice(task,
+ "Checking for DES passwords to convert to AES...\n");
+
+ be = slapi_get_first_backend(&cookie);
+ while (be){
+ int suffix_idx = 0;
+ int count = slapi_counter_get_value(be->be_suffixcounter);
+
+ list = be->be_suffixlist;
+ for (suffix_idx = 0; list && suffix_idx < count; suffix_idx++) {
+ char *suffix = (char *)slapi_sdn_get_ndn(list->be_suffix);
+ if(charray_inlist(backends, suffix) || strlen(suffix) == 0){
+ list = list->next;
+ continue;
+ }
+ charray_add(&backends, slapi_ch_strdup(suffix));
+ list = list->next;
+ }
+ be = slapi_get_next_backend (cookie);
+ }
+ slapi_ch_free ((void **)&cookie);
+ } else {
+ backends = task_data->suffixes;
+ }
+
+ /*
+ * Search for the password attributes
+ */
+ for (i = 0; attrs && attrs[i]; i++){
+ char *filter = PR_smprintf("%s=*", attrs[i]);
+ /*
+ * Loop over all the backends looking for the password attribute
+ */
+ for(be_idx = 0; backends && backends[be_idx]; be_idx++){
+ pb = slapi_pblock_new();
+ slapi_search_internal_set_pb(pb, backends[be_idx],
+ LDAP_SCOPE_SUBTREE, filter, NULL, 0, NULL, NULL,
+ (void *)plugin_get_default_component_id(),
+ SLAPI_OP_FLAG_IGNORE_UNINDEXED);
+ slapi_search_internal_pb(pb);
+ slapi_pblock_get(pb, SLAPI_PLUGIN_INTOP_RESULT, &result);
+ if (LDAP_SUCCESS != result) {
+ slapi_log_error(SLAPI_LOG_FATAL, "convert_pbe_des_to_aes: ",
+ "Failed to search for password attribute (%s) error (%d), skipping suffix (%s)\n",
+ attrs[i], result, backends[be_idx]);
+ slapi_task_log_notice(task,
+ "Failed to search for password attribute (%s) error (%d), skipping suffix (%s)\n",
+ attrs[i], result, backends[be_idx]);
+ slapi_free_search_results_internal(pb);
+ slapi_pblock_destroy(pb);
+ pb = NULL;
+ continue;
+ }
+ slapi_pblock_get(pb, SLAPI_PLUGIN_INTOP_SEARCH_ENTRIES, &entries);
+ for (ii = 0; entries && entries[ii]; ii++){
+ if((val = slapi_entry_attr_get_charptr(entries[ii], attrs[i]))){
+ if(strlen(val) >= 5 && strncmp(val,"{DES}", 5) == 0){
+ /*
+ * We have a DES encoded password, convert it AES
+ */
+ Slapi_PBlock *mod_pb = NULL;
+ Slapi_Value *sval = NULL;
+ LDAPMod mod_replace;
+ LDAPMod *mods[2];
+ char *replace_val[2];
+ char *passwd = NULL;
+
+ /* Decode the DES password */
+ if(pw_rever_decode(val, &passwd, attrs[i]) == -1){
+ slapi_log_error(SLAPI_LOG_FATAL, TASK_DES2AES,
+ "Failed to decode existing DES password for (%s)\n",
+ slapi_entry_get_dn(entries[ii]));
+ slapi_task_log_notice(task,
+ "Failed to decode existing DES password for (%s)\n",
+ slapi_entry_get_dn(entries[ii]));
+ rc = 1;
+ goto done;
+ }
+
+ /* Encode the password */
+ sval = slapi_value_new_string(passwd);
+ if(pw_rever_encode(&sval, attrs[i]) == -1){
+ slapi_log_error(SLAPI_LOG_FATAL, TASK_DES2AES,
+ "failed to encode AES password for (%s)\n",
+ slapi_entry_get_dn(entries[ii]));
+ slapi_task_log_notice(task,
+ "failed to encode AES password for (%s)\n",
+ slapi_entry_get_dn(entries[ii]));
+ slapi_ch_free_string(&passwd);
+ slapi_value_free(&sval);
+ rc = 1;
+ goto done;
+ }
+
+ /* Replace the attribute in the entry */
+ replace_val[0] = (char *)slapi_value_get_string(sval);
+ replace_val[1] = NULL;
+ mod_replace.mod_op = LDAP_MOD_REPLACE;
+ mod_replace.mod_type = attrs[i];
+ mod_replace.mod_values = replace_val;
+ mods[0] = &mod_replace;
+ mods[1] = 0;
+
+ mod_pb = slapi_pblock_new();
+ slapi_modify_internal_set_pb(mod_pb, slapi_entry_get_dn(entries[ii]),
+ mods, 0, 0, (void *)plugin_get_default_component_id(), 0);
+ slapi_modify_internal_pb(mod_pb);
+
+ slapi_pblock_get(pb, SLAPI_PLUGIN_INTOP_RESULT, &result);
+ if (LDAP_SUCCESS != result) {
+ slapi_log_error(SLAPI_LOG_FATAL, TASK_DES2AES,
+ "Failed to convert password for (%s) error (%d)\n",
+ slapi_entry_get_dn(entries[ii]), result);
+ slapi_task_log_notice(task,
+ "Failed to convert password for (%s) error (%d)\n",
+ slapi_entry_get_dn(entries[ii]), result);
+ rc = 1;
+ } else {
+ slapi_log_error(SLAPI_LOG_FATAL, TASK_DES2AES,
+ "Successfully converted password for (%s)\n",
+ slapi_entry_get_dn(entries[ii]));
+ slapi_task_log_notice(task,
+ "Successfully converted password for (%s)\n",
+ slapi_entry_get_dn(entries[ii]));
+ converted_des_passwd = 1;
+ }
+ slapi_ch_free_string(&passwd);
+ slapi_value_free(&sval);
+ slapi_pblock_destroy(mod_pb);
+ }
+ slapi_ch_free_string(&val);
+ }
+ }
+ slapi_free_search_results_internal(pb);
+ slapi_pblock_destroy(pb);
+ pb = NULL;
+ }
+ slapi_ch_free_string(&filter);
+ }
+ if (!converted_des_passwd){
+ slapi_log_error(SLAPI_LOG_FATAL, TASK_DES2AES,
+ "No DES passwords found to convert.\n");
+ slapi_task_log_notice(task, "No DES passwords found to convert.\n");
+ }
+ } else {
+ /* No AES/DES */
+ if (!have_des){
+ slapi_log_error(SLAPI_LOG_FATAL, TASK_DES2AES,
+ "DES plugin not enabled\n");
+ slapi_task_log_notice(task, "DES plugin not enabled\n");
+ }
+ if (!have_aes){
+ slapi_log_error(SLAPI_LOG_FATAL, TASK_DES2AES,
+ "AES plugin not enabled\n");
+ slapi_task_log_notice(task, "AES plugin not enabled\n");
+ }
+ slapi_log_error(SLAPI_LOG_FATAL, TASK_DES2AES,
+ "Unable to convert passwords\n");
+ slapi_task_log_notice(task, "Unable to convert passwords\n");
+ rc = 1;
+ }
+
+done:
+ charray_free(attrs);
+ charray_free(backends);
+ slapi_free_search_results_internal(pb);
+ slapi_pblock_destroy(pb);
+ slapi_task_finish(task, rc);
+}
+
+static void
+des2aes_task_destructor(Slapi_Task *task)
+{
+ slapi_log_error(SLAPI_LOG_TRACE, TASK_DES2AES,
+ "des2aes_task_destructor -->\n" );
+ if (task) {
+ struct task_des2aes_data *task_data = (struct task_des2aes_data *)slapi_task_get_data(task);
+ while (slapi_task_get_refcount(task) > 0) {
+ /* Yield to wait for the task to finish. */
+ DS_Sleep (PR_MillisecondsToInterval(100));
+ }
+ if (task_data) {
+ slapi_ch_array_free(task_data->suffixes);
+ slapi_ch_free((void **)&task_data);
+ }
+ }
+ slapi_log_error(SLAPI_LOG_TRACE, TASK_DES2AES,
+ "des2aes_task_destructor <--\n" );
+}
+
/* cleanup old tasks that may still be in the DSE from a previous session
* (this can happen if the server crashes [no matter how unlikely we like
* to think that is].)
@@ -2506,6 +2849,7 @@ void task_init(void)
slapi_task_register_handler("upgradedb", task_upgradedb_add);
slapi_task_register_handler("sysconfig reload", task_sysconfig_reload_add);
slapi_task_register_handler("fixup tombstones", task_fixup_tombstones_add);
+ slapi_task_register_handler("des2aes", task_des2aes);
}
/* called when the server is shutting down -- abort all existing tasks */
7 years, 10 months
ldap/servers
by Ludwig Krispenz
ldap/servers/slapd/filterentry.c | 208 +++++++++++++++++++++++++--------------
1 file changed, 138 insertions(+), 70 deletions(-)
New commits:
commit 169d0ab5e8d2d61d39585c3a8981a3707578cae8
Author: Ludwig Krispenz <lkrispen(a)redhat.com>
Date: Tue Jun 7 15:38:54 2016 +0200
Ticket 48275 - search returns no entry when OR filter component contains non readable attribute
Bug Description: If an OR filter with more than one components did contain one
component without access the complete filter was set to non matching
even if the component did not contribute to the result of the evaluation
Fix Description: Handle filter components, so that it can be distinguished if
the component did not match aor if there was no access, components
without access are completely ignored in OR filters and evaluation is
only based on components with access
https://fedorahosted.org/389/ticket/48275
Reviewed by: Noriko, Thanks
diff --git a/ldap/servers/slapd/filterentry.c b/ldap/servers/slapd/filterentry.c
index ceeae79..9963c09 100644
--- a/ldap/servers/slapd/filterentry.c
+++ b/ldap/servers/slapd/filterentry.c
@@ -22,7 +22,8 @@
static int test_filter_list();
static int test_extensible_filter();
-static int vattr_test_filter_list();
+static int vattr_test_filter_list_and();
+static int vattr_test_filter_list_or();
static int test_filter_access( Slapi_PBlock *pb, Slapi_Entry*e,
char * attr_type, struct berval *attr_val);
static int slapi_vattr_filter_test_ext_internal( Slapi_PBlock *pb, Slapi_Entry *e,
@@ -762,9 +763,40 @@ slapi_vattr_filter_test(
/*
* vattr_filter_test_ext - full-feature filter test function
*
- * returns 0 filter matched
- * -1 filter did not match
- * >0 an ldap error code
+ * the filter test functions can be run in three different modes:
+ * - verify that the filter matches the entry
+ * - verify that the bound user has access to the attributes used in the filter
+ * - verify that the filter matches and the user has access to the attributes
+ *
+ * A special situation is the case of OR filters, eg
+ * (|(attr1=xxx)(attr2=yyy)(attr3=zzz))
+ * and the case to verify access and filter matching. An or filter is true if any
+ * of the filter components in the or filter is true. So if (attr2=yyy) matches
+ * and the user has access to attr2 the complete filter should match.
+ * But to prevent using a mixture of matching filter components and components with
+ * granted access to guess values of components without access filter evaluation needs
+ * to handle access and matching synchronously.
+ * It is also not sufficient to set a component without access to false because in cases
+ * where this component is part of a NOT filter it would be negated to true.
+ * Filter components withou access to the attribute need to be completely ignored.
+ *
+ * The implementation uses a three valued logic where the result of the evaluation of a
+ * filter component can be:
+ * - true: access to attribute and filter matches
+ * - false: access to attribute and filter does not match
+ * - undefined: no access to the attribute or any error during filter evaluatio
+ *
+ * The rules for complex filters are:
+ * (!(undefined)) --> undefined
+ * (|(undefined)(true)) --> true
+ * (|(undefined)(false)) --> false
+ * (&(undefined)(true)) --> undefined
+ * (&(undefined)(false)) --> false
+ *
+ * This is reflected in the return codes:
+ * 0 filter matched (true)
+ * -1 filter did not match (false)
+ * >0 undefined (or an ldap error code)
*/
int
slapi_vattr_filter_test_ext(
@@ -778,39 +810,11 @@ slapi_vattr_filter_test_ext(
int rc = 0; /* a no op request succeeds */
int access_check_done = 0;
- switch ( f->f_choice ) {
- case LDAP_FILTER_AND:
- case LDAP_FILTER_OR:
- case LDAP_FILTER_NOT:
- /*
- * optimize acl checking by only doing it once it is
- * known that the whole filter passes and so the entry
- * is eligible to be returned.
- * then we check the filter only for access
- *
- * complex filters really benefit from
- * separate stages, filter eval, followed by acl check...
- */
- if(!only_check_access)
- {
- rc = slapi_vattr_filter_test_ext_internal(pb,e,f,0,0, &access_check_done);
- }
-
- if(rc == 0 && verify_access)
- {
- rc = slapi_vattr_filter_test_ext_internal(pb,e,f,-1,-1, &access_check_done);
- }
-
- break;
-
- default:
- /*
- * ...but simple filters are better off doing eval and
- * acl check at once
- */
- rc = slapi_vattr_filter_test_ext_internal(pb,e,f,verify_access,only_check_access, &access_check_done);
- break;
- }
+ /* Fix for ticket 48275
+ * If we want to handle or components which can contain nonmatching components without access propoerly
+ * always filter verification and access check have to be done together for each component
+ */
+ rc = slapi_vattr_filter_test_ext_internal(pb,e,f,verify_access,only_check_access, &access_check_done);
return rc;
}
@@ -925,35 +929,53 @@ slapi_vattr_filter_test_ext_internal(
case LDAP_FILTER_AND:
LDAPDebug( LDAP_DEBUG_FILTER, " AND\n", 0, 0, 0 );
- rc = vattr_test_filter_list( pb, e, f->f_and,
+ rc = vattr_test_filter_list_and( pb, e, f->f_and,
LDAP_FILTER_AND , verify_access, only_check_access, access_check_done);
break;
case LDAP_FILTER_OR:
LDAPDebug( LDAP_DEBUG_FILTER, " OR\n", 0, 0, 0 );
- rc = vattr_test_filter_list( pb, e, f->f_or,
+ rc = vattr_test_filter_list_or( pb, e, f->f_or,
LDAP_FILTER_OR , verify_access, only_check_access, access_check_done);
break;
case LDAP_FILTER_NOT:
LDAPDebug( LDAP_DEBUG_FILTER, " NOT\n", 0, 0, 0 );
rc = slapi_vattr_filter_test_ext_internal( pb, e, f->f_not , verify_access, only_check_access, access_check_done);
- if(!(verify_access && only_check_access)) /* dont play with access control return codes */
+ if(verify_access && only_check_access)
{
- if(verify_access && !rc && !(*access_check_done))
- {
+ /* dont play with access control return codes
+ * do not negate return code */
+ break;
+ }
+ if (rc > 0) {
+ /* an error occurred or access denied, don't negate */
+ break;
+ }
+ if(verify_access)
+ {
+ int rc2;
+ if (!(*access_check_done)) {
/* the filter failed so access control was not checked
* for NOT filters this is significant so we must ensure
* access control is checked
*/
/* check access control only */
- rc = slapi_vattr_filter_test_ext_internal( pb, e, f->f_not , verify_access, -1 /*only_check_access*/, access_check_done);
+ rc2 = slapi_vattr_filter_test_ext_internal( pb, e, f->f_not , verify_access, -1 /*only_check_access*/, access_check_done);
/* preserve error code if any */
- if(!rc)
- rc = !rc;
+ if (rc2) {
+ rc = rc2;
+ } else {
+ rc = (rc==0)?-1:0;
+ }
+ } else {
+ rc = (rc==0)?-1:0;
}
- else
- rc = !rc;
+ }
+ else
+ {
+ /* filter verification only, no error */
+ rc = (rc==0)?-1:0;
}
break;
@@ -987,7 +1009,7 @@ static int test_filter_access( Slapi_PBlock *pb,
}
static int
-vattr_test_filter_list(
+vattr_test_filter_list_and(
Slapi_PBlock *pb,
Slapi_Entry *e,
struct slapi_filter *flist,
@@ -997,37 +1019,83 @@ vattr_test_filter_list(
int *access_check_done
)
{
- int nomatch;
+ int nomatch = -1;
+ int undefined = 0;
+ int rc = 0;
struct slapi_filter *f;
- LDAPDebug( LDAP_DEBUG_FILTER, "=> vattr_test_filter_list\n", 0, 0, 0 );
+ LDAPDebug( LDAP_DEBUG_FILTER, "=> vattr_test_filter_list_and\n", 0, 0, 0 );
- nomatch = 1;
for ( f = flist; f != NULL; f = f->f_next ) {
- if ( slapi_vattr_filter_test_ext_internal( pb, e, f, verify_access, only_check_access, access_check_done ) != 0 ) {
- /* optimize AND evaluation */
- if ( ftype == LDAP_FILTER_AND || verify_access) {
- /* one false is failure
- * for AND all components need to match
- * and for AND and OR access to ALL filter attributes is required
- */
- nomatch = 1;
- break;
- }
+ rc = slapi_vattr_filter_test_ext_internal( pb, e, f, verify_access, only_check_access, access_check_done );
+ if ( rc > 0 ) {
+ undefined = rc;
+ } else if (rc < 0) {
+ undefined = 0;
+ nomatch = -1;
+ break;
} else {
- nomatch = 0;
+ if (!verify_access || (*access_check_done)) {
+ nomatch = 0;
+ } else {
+ /* check access */
+ rc = slapi_vattr_filter_test_ext_internal( pb, e, f, verify_access, 1, access_check_done );
+ if (rc) undefined = rc;
+ }
+ }
+ }
- /* optimize OR evaluation too */
- if ( ftype == LDAP_FILTER_OR && !verify_access) {
- /* access to all atributes needs to be evaluated
- * for filter matching
- * only one needs to be true
- */
- break;
+ LDAPDebug( LDAP_DEBUG_FILTER, "<= test_filter_list_and %d\n", nomatch, 0, 0 );
+ if (undefined) return undefined;
+ return( nomatch );
+}
+
+static int
+vattr_test_filter_list_or(
+ Slapi_PBlock *pb,
+ Slapi_Entry *e,
+ struct slapi_filter *flist,
+ int ftype,
+ int verify_access,
+ int only_check_access,
+ int *access_check_done
+)
+{
+ int nomatch = 1;
+ int undefined = 0;
+ int rc = 0;
+ struct slapi_filter *f;
+
+ LDAPDebug( LDAP_DEBUG_FILTER, "=> vattr_test_filter_list_or\n", 0, 0, 0 );
+
+ for ( f = flist; f != NULL; f = f->f_next ) {
+ if ( verify_access) {
+ /* we do access check first */
+ rc = slapi_vattr_filter_test_ext_internal( pb, e, f, verify_access, -1, access_check_done );
+ if (rc != 0 ) {
+ /* no access to this component, ignore it */
+ undefined = rc;
+ continue;
}
}
+ if (only_check_access) continue;
+ /* now check if filter matches */
+ undefined = 0;
+ rc = slapi_vattr_filter_test_ext_internal( pb, e, f, 0, 0, access_check_done );
+ if ( rc == 0 ) {
+ undefined = 0;
+ nomatch = 0;
+ break;
+ } else if (rc > 0) {
+ undefined = rc;
+ } else {
+ /* filter didn't match, but we have one or component evaluated */
+ nomatch = -1;
+ }
}
- LDAPDebug( LDAP_DEBUG_FILTER, "<= test_filter_list %d\n", nomatch, 0, 0 );
+ LDAPDebug( LDAP_DEBUG_FILTER, "<= test_filter_list_or %d\n", nomatch, 0, 0 );
+
+ if (nomatch == 1) return undefined;
return( nomatch );
}
7 years, 10 months
rpm/389-ds-base.spec.in
by Noriko Hosoi
rpm/389-ds-base.spec.in | 39 ++++++++++++++++++++++++++++++++-------
1 file changed, 32 insertions(+), 7 deletions(-)
New commits:
commit 2252acd6b4b45b890dd2a7d2058696793e0b41b9
Author: Noriko Hosoi <nhosoi(a)redhat.com>
Date: Fri Jun 3 16:00:32 2016 -0700
Ticket #47911 - split out snmp agent into a subpackageTicket 47911
Description:
Base upon the initial spec file patch provided by Peter Robinson <pbrobinson(a)gmail.com>:
https://fedorahosted.org/389/attachment/ticket/47911/389-ds-base-snmp.patch
added 2 changes.
1) separated snmp files into "%files snmp" section.
2) added Obsoetes version setting for the upgrade
https://fedorahosted.org/389/ticket/47911
Reviewed by wibrown(a)redhat.com (Thank you, William!!)
diff --git a/rpm/389-ds-base.spec.in b/rpm/389-ds-base.spec.in
index 02b188d..6cf5fda 100644
--- a/rpm/389-ds-base.spec.in
+++ b/rpm/389-ds-base.spec.in
@@ -156,6 +156,9 @@ Please see http://seclists.org/oss-sec/2016/q1/363 for more information.
the LDAP server and command line utilities for server administration.
%endif
+# upgrade path from monolithic %{name} (including -libs & -devel) to %{name} + %{name}-snmp
+Obsoletes: %{name} <= 1.3.5.4
+
%package libs
Summary: Core libraries for 389 Directory Server (%{variant})
Group: System Environment/Daemons
@@ -207,10 +210,20 @@ Requires: libtevent
%endif
Requires: systemd-libs
-
%description devel
Development Libraries and headers for the 389 Directory Server base package.
+%package snmp
+Summary: SNMP Agent for 389 Directory Server
+Group: System Environment/Daemons
+Requires: %{name} = %{version}-%{release}
+
+%description snmp
+SNMP Agent for the 389 Directory Server base package.
+
+# upgrade path from monolithic %{name} (including -libs & -devel) to %{name} + %{name}-snmp
+Obsoletes: %{name} <= 1.3.5.4
+
%package tests
Summary: The lib389 Continuous Integration Tests
Group: Development/Libraries
@@ -313,13 +326,10 @@ rm -rf $RPM_BUILD_ROOT
%post
output=/dev/null
output2=/dev/null
-%systemd_post %{pkgname}-snmp.service
# reload to pick up any changes to systemd files
/bin/systemctl daemon-reload >$output 2>&1 || :
# reload to pick up any shared lib changes
/sbin/ldconfig
-# restart the snmp subagent if needed
-/bin/systemctl try-restart %{pkgname}-snmp.service > $output 2>&1 || :
# find all instances
instances="" # instances that require a restart after upgrade
ninst=0 # number of instances found in total
@@ -395,8 +405,6 @@ exit 0
%preun
if [ $1 -eq 0 ]; then # Final removal
- # Package removal, not upgrade
- %systemd_preun %{pkgname}-snmp.service %{groupname}
# remove instance specific service files/links
rm -rf %{_sysconfdir}/systemd/system/%{groupname}.wants/* > /dev/null 2>&1 || :
fi
@@ -407,6 +415,15 @@ if [ $1 = 0 ]; then # Final removal
rm -rf /var/run/%{pkgname}
fi
+%post snmp
+%systemd_post %{pkgname}-snmp.service
+
+%preun snmp
+%systemd_preun %{pkgname}-snmp.service %{groupname}
+
+%postun snmp
+%systemd_postun_with_restart %{pkgname}-snmp.service
+
%files
%defattr(-,root,root,-)
%doc LICENSE LICENSE.GPLv3+ LICENSE.openssl
@@ -417,7 +434,6 @@ fi
%dir %{_sysconfdir}/systemd/system/%{groupname}.wants
%config(noreplace)%{_sysconfdir}/%{pkgname}/config/slapd-collations.conf
%config(noreplace)%{_sysconfdir}/%{pkgname}/config/certmap.conf
-%config(noreplace)%{_sysconfdir}/%{pkgname}/config/ldap-agent.conf
%config(noreplace)%{_sysconfdir}/%{pkgname}/config/template-initconfig
%config(noreplace)%{_sysconfdir}/sysconfig/%{pkgname}
%config(noreplace)%{_sysconfdir}/sysconfig/%{pkgname}.systemd
@@ -434,6 +450,8 @@ fi
%ghost %dir %{_localstatedir}/lock/%{pkgname}
%{_mandir}/man1/*
%{_mandir}/man8/*
+%exclude %{_sbindir}/ldap-agent*
+%exclude %{_mandir}/man1/ldap-agent.1.gz
%files devel
%defattr(-,root,root,-)
@@ -455,6 +473,13 @@ fi
%{_libdir}/%{pkgname}/libnunc-stans.so*
%endif
+%files snmp
+%defattr(-,root,root,-)
+%doc LICENSE LICENSE.GPLv3+ LICENSE.openssl README.devel
+%config(noreplace)%{_sysconfdir}/%{pkgname}/config/ldap-agent.conf
+%{_sbindir}/ldap-agent*
+%{_mandir}/man1/ldap-agent.1.gz
+
%files tests
%defattr(-,root,root,-)
%doc LICENSE LICENSE.GPLv3+
7 years, 10 months