dirsrvtests/tickets/ticket48312_test.py | 168 ++++++++++++++++++++++++++++++++
ldap/servers/plugins/mep/mep.c | 18 ++-
ldap/servers/slapd/modrdn.c | 24 ++--
3 files changed, 197 insertions(+), 13 deletions(-)
New commits:
commit 6f8c5550733bd7aad116230813ec85975ae7d657
Author: Mark Reynolds <mreynolds(a)redhat.com>
Date: Mon Oct 19 12:53:53 2015 -0400
Ticket 48312 - Crash when doing modrdn on managed entry
Bug Description: If performing a modrdn on a managed entry, where the new
rdn begins with a "+", the server will crash. This is
caused by the way MEP creates the new managed entry - where
it uses the rdn attribute value from the attribute,
and not the DN. So in this case the DN rdn value escapes
the "+", but it is not escaped in the rdn attribute in
the entry. Then the internal modrdn setup thinks it
is a multivalued RDN, and the first RDN ends up being null.
This actually generates an error, but we don't return an error
from slapi_rename_internal_set_pb_ext(), and the operation
is not set in the pblock. Then slapi_modrdn_internal_pb()
dereferences the operation struct, and we crash.
Fix Description: Instead when updating the managed entry during the modrdn
get the DN value from the DN(which is already escaped), and
not from the attribute value from the entry(which is not escaped).
Also improved the modrdn code to check for NULL operations
in the pblock, report an error, and not dereference them.
https://fedorahosted.org/389/ticket/48312
Reviewed by: nhosoi(Thanks!)
diff --git a/dirsrvtests/tickets/ticket48312_test.py
b/dirsrvtests/tickets/ticket48312_test.py
new file mode 100644
index 0000000..0989279
--- /dev/null
+++ b/dirsrvtests/tickets/ticket48312_test.py
@@ -0,0 +1,168 @@
+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__)
+
+installation1_prefix = None
+
+
+class TopologyStandalone(object):
+ def __init__(self, standalone):
+ standalone.open()
+ self.standalone = standalone
+
+
+(a)pytest.fixture(scope="module")
+def topology(request):
+ global installation1_prefix
+ if installation1_prefix:
+ args_instance[SER_DEPLOYED_DIR] = installation1_prefix
+
+ # Creating standalone instance ...
+ standalone = DirSrv(verbose=False)
+ args_instance[SER_HOST] = HOST_STANDALONE
+ args_instance[SER_PORT] = PORT_STANDALONE
+ args_instance[SER_SERVERID_PROP] = SERVERID_STANDALONE
+ args_instance[SER_CREATION_SUFFIX] = DEFAULT_SUFFIX
+ args_standalone = args_instance.copy()
+ standalone.allocate(args_standalone)
+ instance_standalone = standalone.exists()
+ if instance_standalone:
+ standalone.delete()
+ standalone.create()
+ standalone.open()
+
+ # Delete each instance in the end
+ def fin():
+ standalone.delete()
+
+ request.addfinalizer(fin)
+
+ # Clear out the tmp dir
+ standalone.clearTmpDir(__file__)
+
+ return TopologyStandalone(standalone)
+
+
+def test_ticket48312(topology):
+ """
+ Configure managed entries plugins(tempalte/definition), then perform a
+ modrdn(deleteoldrdn 1), and make sure the server does not crash.
+ """
+
+ GROUP_OU = 'ou=groups,' + DEFAULT_SUFFIX
+ PEOPLE_OU = 'ou=people,' + DEFAULT_SUFFIX
+ USER_DN = 'uid=user1,ou=people,' + DEFAULT_SUFFIX
+ CONFIG_DN = 'cn=config,cn=' + PLUGIN_MANAGED_ENTRY +
',cn=plugins,cn=config'
+ TEMPLATE_DN = 'cn=MEP Template,' + DEFAULT_SUFFIX
+ USER_NEWRDN = 'uid=\+user1'
+
+ #
+ # First enable dynamic plugins
+ #
+ try:
+ topology.standalone.modify_s(DN_CONFIG, [(ldap.MOD_REPLACE,
'nsslapd-dynamic-plugins', 'on')])
+ except ldap.LDAPError as e:
+ ldap.fatal('Failed to enable dynamic plugin!' +
e.message['desc'])
+ assert False
+ topology.standalone.plugins.enable(name=PLUGIN_MANAGED_ENTRY)
+
+ #
+ # Add our org units (they should already exist, but do it just in case)
+ #
+ try:
+ topology.standalone.add_s(Entry((PEOPLE_OU, {
+ 'objectclass': 'top extensibleObject'.split(),
+ 'ou': 'people'})))
+ except ldap.ALREADY_EXISTS:
+ pass
+ except ldap.LDAPError as e:
+ log.fatal('test_mep: Failed to add people org unit: error ' +
e.message['desc'])
+ assert False
+
+ try:
+ topology.standalone.add_s(Entry((GROUP_OU, {
+ 'objectclass': 'top extensibleObject'.split(),
+ 'ou': 'people'})))
+ except ldap.ALREADY_EXISTS:
+ pass
+ except ldap.LDAPError as e:
+ log.fatal('test_mep: Failed to add people org unit: error ' +
e.message['desc'])
+ assert False
+
+ #
+ # Add the template entry
+ #
+ try:
+ topology.standalone.add_s(Entry((TEMPLATE_DN, {
+ 'objectclass': 'top mepTemplateEntry
extensibleObject'.split(),
+ 'cn': 'MEP Template',
+ 'mepRDNAttr': 'cn',
+ 'mepStaticAttr': ['objectclass: posixGroup',
'objectclass: extensibleObject'],
+ 'mepMappedAttr': ['cn: $uid', 'uid: $cn',
'gidNumber: $uidNumber']
+ })))
+ except ldap.LDAPError as e:
+ log.fatal('test_mep: Failed to add template entry: error ' +
e.message['desc'])
+ assert False
+
+ #
+ # Add the definition entry
+ #
+ try:
+ topology.standalone.add_s(Entry((CONFIG_DN, {
+ 'objectclass': 'top extensibleObject'.split(),
+ 'cn': 'config',
+ 'originScope': PEOPLE_OU,
+ 'originFilter': 'objectclass=posixAccount',
+ 'managedBase': GROUP_OU,
+ 'managedTemplate': TEMPLATE_DN
+ })))
+ except ldap.LDAPError as e:
+ log.fatal('test_mep: Failed to add config entry: error ' +
e.message['desc'])
+ assert False
+
+ #
+ # Add an entry that meets the MEP scope
+ #
+ try:
+ topology.standalone.add_s(Entry((USER_DN, {
+ 'objectclass': 'top posixAccount
extensibleObject'.split(),
+ 'uid': 'user1',
+ 'cn': 'user1',
+ 'uidNumber': '1',
+ 'gidNumber': '1',
+ 'homeDirectory': '/home/user1',
+ 'description': 'uiser description'
+ })))
+ except ldap.LDAPError as e:
+ log.fatal('test_mep: Failed to user1: error ' +
e.message['desc'])
+ assert False
+
+ #
+ # Perform a modrdn on USER_DN
+ #
+ try:
+ topology.standalone.rename_s(USER_DN, USER_NEWRDN, delold=1)
+ except ldap.LDAPError as e:
+ log.error('Failed to modrdn: error ' + e.message['desc'])
+ assert False
+
+ 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)
\ No newline at end of file
diff --git a/ldap/servers/plugins/mep/mep.c b/ldap/servers/plugins/mep/mep.c
index 4399851..46acb4b 100644
--- a/ldap/servers/plugins/mep/mep.c
+++ b/ldap/servers/plugins/mep/mep.c
@@ -1309,19 +1309,29 @@ mep_create_managed_entry(struct configEntry *config, Slapi_Entry
*origin)
goto done;
} else {
/* Build the DN and set it in the entry. */
- char *dn = NULL;
+ char **rdn_vals = NULL;
char *rdn_val = NULL;
+ char *dn = NULL;
/* If an origin entry was supplied, the RDN value will be
* the mapped value. If no origin entry was supplied, the
* value will be the mapping rule from the template. */
- rdn_val = slapi_entry_attr_get_charptr(managed_entry, rdn_type);
+ if (origin){
+ const char *origin_dn = slapi_entry_get_dn(origin);
+ rdn_vals = slapi_ldap_explode_dn(origin_dn, 1);
+ rdn_val = rdn_vals[0];
+ } else {
+ rdn_val = slapi_entry_attr_get_charptr(managed_entry, rdn_type);
+ }
/* Create the DN using the mapped RDN value
* and the base specified in the config. */
dn = slapi_ch_smprintf("%s=%s,%s", rdn_type, rdn_val,
config->managed_base);
-
- slapi_ch_free_string(&rdn_val);
+ if(origin){
+ slapi_ldap_value_free(rdn_vals);
+ } else {
+ slapi_ch_free_string(&rdn_val);
+ }
if (dn != NULL) {
slapi_sdn_set_dn_passin(slapi_entry_get_sdn(managed_entry), dn);
diff --git a/ldap/servers/slapd/modrdn.c b/ldap/servers/slapd/modrdn.c
index 798d3d4..e80e1f9 100644
--- a/ldap/servers/slapd/modrdn.c
+++ b/ldap/servers/slapd/modrdn.c
@@ -338,20 +338,26 @@ slapi_rename_internal_set_pb_ext(Slapi_PBlock *pb,
static int rename_internal_pb (Slapi_PBlock *pb)
{
- LDAPControl **controls;
- Operation *op;
- int opresult = 0;
+ LDAPControl **controls;
+ Operation *op;
+ int opresult = 0;
PR_ASSERT (pb != NULL);
slapi_pblock_get(pb, SLAPI_CONTROLS_ARG, &controls);
-
- slapi_pblock_get(pb, SLAPI_OPERATION, &op);
- op->o_handler_data = &opresult;
- op->o_result_handler = internal_getresult_callback;
+ slapi_pblock_get(pb, SLAPI_OPERATION, &op);
+ if (!op) {
+ opresult = 1;
+ slapi_pblock_set(pb, SLAPI_PLUGIN_INTOP_RESULT, &opresult);
+ slapi_log_error(SLAPI_LOG_FATAL, "rename_internal_pb",
+ "Internal error: pblock was not properly initialized\n");
+ return -1;
+ }
+ op->o_handler_data = &opresult;
+ op->o_result_handler = internal_getresult_callback;
slapi_pblock_set(pb, SLAPI_REQCONTROLS, controls);
-
+
/* set parameters common for all internal operations */
set_common_params (pb);
@@ -359,7 +365,7 @@ static int rename_internal_pb (Slapi_PBlock *pb)
set_config_params (pb);
op_shared_rename (pb, 0 /* not passing ownership of args */ );
-
+
slapi_pblock_set(pb, SLAPI_PLUGIN_INTOP_RESULT, &opresult);
return 0;