Blame SOURCES/0014-Issue-49192-Deleting-suffix-can-hang-server.patch

6f51e1
From 353955ba9b4c487e30315d39d1880b6b784817d2 Mon Sep 17 00:00:00 2001
6f51e1
From: Mark Reynolds <mreynolds@redhat.com>
6f51e1
Date: Mon, 27 Mar 2017 10:59:40 -0400
6f51e1
Subject: [PATCH] Issue 49192 - Deleting suffix can hang server
6f51e1
6f51e1
Description: If you attempt to bind as an inactive user the backend rwlock
6f51e1
             is not unlocked.  Regression introduced from issue 49051.
6f51e1
6f51e1
https://pagure.io/389-ds-base/issue/49192
6f51e1
6f51e1
Reviewed by: nhosoi(Thanks!)
6f51e1
---
6f51e1
 dirsrvtests/tests/tickets/ticket49192_test.py | 177 ++++++++++++++++++++++++++
6f51e1
 ldap/servers/slapd/bind.c                     |   3 -
6f51e1
 ldap/servers/slapd/pw_verify.c                |   8 +-
6f51e1
 3 files changed, 179 insertions(+), 9 deletions(-)
6f51e1
 create mode 100644 dirsrvtests/tests/tickets/ticket49192_test.py
6f51e1
6f51e1
diff --git a/dirsrvtests/tests/tickets/ticket49192_test.py b/dirsrvtests/tests/tickets/ticket49192_test.py
6f51e1
new file mode 100644
6f51e1
index 0000000..f770ba7
6f51e1
--- /dev/null
6f51e1
+++ b/dirsrvtests/tests/tickets/ticket49192_test.py
6f51e1
@@ -0,0 +1,177 @@
6f51e1
+import time
6f51e1
+import ldap
6f51e1
+import logging
6f51e1
+import pytest
6f51e1
+from lib389 import Entry
6f51e1
+from lib389._constants import *
6f51e1
+from lib389.properties import *
6f51e1
+from lib389.tasks import *
6f51e1
+from lib389.utils import *
6f51e1
+from lib389.topologies import topology_st as topo
6f51e1
+
6f51e1
+DEBUGGING = os.getenv("DEBUGGING", default=False)
6f51e1
+if DEBUGGING:
6f51e1
+    logging.getLogger(__name__).setLevel(logging.DEBUG)
6f51e1
+else:
6f51e1
+    logging.getLogger(__name__).setLevel(logging.INFO)
6f51e1
+log = logging.getLogger(__name__)
6f51e1
+
6f51e1
+INDEX_DN = 'cn=index,cn=Second_Backend,cn=ldbm database,cn=plugins,cn=config'
6f51e1
+SUFFIX_DN = 'cn=Second_Backend,cn=ldbm database,cn=plugins,cn=config'
6f51e1
+MY_SUFFIX = "o=hang.com"
6f51e1
+USER_DN = 'uid=user,' + MY_SUFFIX
6f51e1
+
6f51e1
+
6f51e1
+def test_ticket49192(topo):
6f51e1
+    """Trigger deadlock when removing suffix
6f51e1
+    """
6f51e1
+
6f51e1
+    #
6f51e1
+    # Create a second suffix/backend
6f51e1
+    #
6f51e1
+    log.info('Creating second backend...')
6f51e1
+    topo.standalone.backends.create(None, properties={
6f51e1
+        BACKEND_NAME: "Second_Backend",
6f51e1
+        'suffix': "o=hang.com",
6f51e1
+        })
6f51e1
+    try:
6f51e1
+        topo.standalone.add_s(Entry(("o=hang.com", {
6f51e1
+            'objectclass': 'top organization'.split(),
6f51e1
+            'o': 'hang.com'})))
6f51e1
+    except ldap.LDAPError as e:
6f51e1
+        log.fatal('Failed to create 2nd suffix: error ' + e.message['desc'])
6f51e1
+        assert False
6f51e1
+
6f51e1
+    #
6f51e1
+    # Add roles
6f51e1
+    #
6f51e1
+    log.info('Adding roles...')
6f51e1
+    try:
6f51e1
+        topo.standalone.add_s(Entry(('cn=nsManagedDisabledRole,' + MY_SUFFIX, {
6f51e1
+            'objectclass': ['top', 'LdapSubEntry',
6f51e1
+                            'nsRoleDefinition',
6f51e1
+                            'nsSimpleRoleDefinition',
6f51e1
+                            'nsManagedRoleDefinition'],
6f51e1
+            'cn': 'nsManagedDisabledRole'})))
6f51e1
+    except ldap.LDAPError as e:
6f51e1
+        log.fatal('Failed to add managed role: error ' + e.message['desc'])
6f51e1
+        assert False
6f51e1
+
6f51e1
+    try:
6f51e1
+        topo.standalone.add_s(Entry(('cn=nsDisabledRole,' + MY_SUFFIX, {
6f51e1
+            'objectclass': ['top', 'LdapSubEntry',
6f51e1
+                            'nsRoleDefinition',
6f51e1
+                            'nsComplexRoleDefinition',
6f51e1
+                            'nsNestedRoleDefinition'],
6f51e1
+            'cn': 'nsDisabledRole',
6f51e1
+            'nsRoledn': 'cn=nsManagedDisabledRole,' + MY_SUFFIX})))
6f51e1
+    except ldap.LDAPError as e:
6f51e1
+        log.fatal('Failed to add nested role: error ' + e.message['desc'])
6f51e1
+        assert False
6f51e1
+
6f51e1
+    try:
6f51e1
+        topo.standalone.add_s(Entry(('cn=nsAccountInactivationTmp,' + MY_SUFFIX, {
6f51e1
+            'objectclass': ['top', 'nsContainer'],
6f51e1
+            'cn': 'nsAccountInactivationTmp'})))
6f51e1
+    except ldap.LDAPError as e:
6f51e1
+        log.fatal('Failed to add container: error ' + e.message['desc'])
6f51e1
+        assert False
6f51e1
+
6f51e1
+    try:
6f51e1
+        topo.standalone.add_s(Entry(('cn=\"cn=nsDisabledRole,' + MY_SUFFIX + '\",cn=nsAccountInactivationTmp,'  + MY_SUFFIX, {
6f51e1
+            'objectclass': ['top', 'extensibleObject', 'costemplate',
6f51e1
+                            'ldapsubentry'],
6f51e1
+            'nsAccountLock': 'true'})))
6f51e1
+    except ldap.LDAPError as e:
6f51e1
+        log.fatal('Failed to add cos1: error ' + e.message['desc'])
6f51e1
+        assert False
6f51e1
+
6f51e1
+    try:
6f51e1
+        topo.standalone.add_s(Entry(('cn=nsAccountInactivation_cos,' + MY_SUFFIX, {
6f51e1
+            'objectclass': ['top', 'LdapSubEntry', 'cosSuperDefinition',
6f51e1
+                            'cosClassicDefinition'],
6f51e1
+            'cn': 'nsAccountInactivation_cos',
6f51e1
+            'cosTemplateDn': 'cn=nsAccountInactivationTmp,' + MY_SUFFIX,
6f51e1
+            'cosSpecifier': 'nsRole',
6f51e1
+            'cosAttribute': 'nsAccountLock operational'})))
6f51e1
+    except ldap.LDAPError as e:
6f51e1
+        log.fatal('Failed to add cos2 : error ' + e.message['desc'])
6f51e1
+        assert False
6f51e1
+
6f51e1
+    #
6f51e1
+    # Add test entry
6f51e1
+    #
6f51e1
+    try:
6f51e1
+        topo.standalone.add_s(Entry((USER_DN, {
6f51e1
+            'objectclass': 'top extensibleObject'.split(),
6f51e1
+            'uid': 'user',
6f51e1
+            'userpassword': 'password',
6f51e1
+        })))
6f51e1
+    except ldap.LDAPError as e:
6f51e1
+        log.fatal('Failed to add user: error ' + e.message['desc'])
6f51e1
+        assert False
6f51e1
+
6f51e1
+    #
6f51e1
+    # Inactivate the user account
6f51e1
+    #
6f51e1
+    try:
6f51e1
+        topo.standalone.modify_s(USER_DN,
6f51e1
+                                [(ldap.MOD_ADD,
6f51e1
+                                  'nsRoleDN',
6f51e1
+                                  'cn=nsManagedDisabledRole,' + MY_SUFFIX)])
6f51e1
+    except ldap.LDAPError as e:
6f51e1
+        log.fatal('Failed to disable user: error ' + e.message['desc'])
6f51e1
+        assert False
6f51e1
+
6f51e1
+    time.sleep(1)
6f51e1
+
6f51e1
+    # Bind as user (should fail)
6f51e1
+    try:
6f51e1
+        topo.standalone.simple_bind_s(USER_DN, 'password')
6f51e1
+        log.error("Bind incorrectly worked")
6f51e1
+        assert False
6f51e1
+    except ldap.UNWILLING_TO_PERFORM:
6f51e1
+        log.info('Got error 53 as expected')
6f51e1
+    except ldap.LDAPError as e:
6f51e1
+        log.fatal('Bind has unexpected error ' + e.message['desc'])
6f51e1
+        assert False
6f51e1
+
6f51e1
+    # Bind as root DN
6f51e1
+    try:
6f51e1
+        topo.standalone.simple_bind_s(DN_DM, PASSWORD)
6f51e1
+    except ldap.LDAPError as e:
6f51e1
+        log.fatal('RootDN Bind has unexpected error ' + e.message['desc'])
6f51e1
+        assert False
6f51e1
+
6f51e1
+    #
6f51e1
+    # Delete suffix
6f51e1
+    #
6f51e1
+    log.info('Delete the suffix and children...')
6f51e1
+    try:
6f51e1
+        index_entries = topo.standalone.search_s(
6f51e1
+            SUFFIX_DN, ldap.SCOPE_SUBTREE, 'objectclass=top')
6f51e1
+    except ldap.LDAPError as e:
6f51e1
+            log.error('Failed to search: %s - error %s' % (SUFFIX_DN, str(e)))
6f51e1
+
6f51e1
+    for entry in reversed(index_entries):
6f51e1
+        try:
6f51e1
+            log.info("Deleting: " + entry.dn)
6f51e1
+            if entry.dn != SUFFIX_DN and entry.dn != INDEX_DN:
6f51e1
+                topo.standalone.search_s(entry.dn,
6f51e1
+                                         ldap.SCOPE_ONELEVEL,
6f51e1
+                                         'objectclass=top')
6f51e1
+            topo.standalone.delete_s(entry.dn)
6f51e1
+        except ldap.LDAPError as e:
6f51e1
+            log.fatal('Failed to delete entry: %s - error %s' %
6f51e1
+                      (entry.dn, str(e)))
6f51e1
+            assert False
6f51e1
+
6f51e1
+    log.info("Test Passed")
6f51e1
+
6f51e1
+
6f51e1
+if __name__ == '__main__':
6f51e1
+    # Run isolated
6f51e1
+    # -s for DEBUG mode
6f51e1
+    CURRENT_FILE = os.path.realpath(__file__)
6f51e1
+    pytest.main("-s %s" % CURRENT_FILE)
6f51e1
+
6f51e1
diff --git a/ldap/servers/slapd/bind.c b/ldap/servers/slapd/bind.c
6f51e1
index 5c4fada..f83df7d 100644
6f51e1
--- a/ldap/servers/slapd/bind.c
6f51e1
+++ b/ldap/servers/slapd/bind.c
6f51e1
@@ -771,9 +771,6 @@ do_bind( Slapi_PBlock *pb )
6f51e1
                     /* need_new_pw failed; need_new_pw already send_ldap_result in it. */
6f51e1
                     goto free_and_return;
6f51e1
                 } 
6f51e1
-                if (be) {
6f51e1
-                    slapi_be_Unlock(be);
6f51e1
-                }
6f51e1
             } else {	/* anonymous */
6f51e1
                 /* set bind creds here so anonymous limits are set */
6f51e1
                 bind_credentials_set(pb->pb_conn, authtype, NULL, NULL, NULL, NULL, NULL);
6f51e1
diff --git a/ldap/servers/slapd/pw_verify.c b/ldap/servers/slapd/pw_verify.c
6f51e1
index a9fd9ec..852b027 100644
6f51e1
--- a/ldap/servers/slapd/pw_verify.c
6f51e1
+++ b/ldap/servers/slapd/pw_verify.c
6f51e1
@@ -50,8 +50,6 @@ pw_verify_root_dn(const char *dn, const Slapi_Value *cred)
6f51e1
  *
6f51e1
  * In the future, this will use the credentials and do mfa.
6f51e1
  *
6f51e1
- * If you get SLAPI_BIND_SUCCESS or SLAPI_BIND_ANONYMOUS you need to unlock
6f51e1
- * the backend.
6f51e1
  * All other results, it's already released.
6f51e1
  */
6f51e1
 int
6f51e1
@@ -81,10 +79,8 @@ pw_verify_be_dn(Slapi_PBlock *pb, Slapi_Entry **referral)
6f51e1
     set_db_default_result_handlers(pb);
6f51e1
     /* now take the dn, and check it */
6f51e1
     rc = (*be->be_bind)(pb);
6f51e1
-    /* now attempt the bind. */
6f51e1
-    if (rc != SLAPI_BIND_SUCCESS && rc != SLAPI_BIND_ANONYMOUS) {
6f51e1
-        slapi_be_Unlock(be);
6f51e1
-    }
6f51e1
+    slapi_be_Unlock(be);
6f51e1
+
6f51e1
     return rc;
6f51e1
 }
6f51e1
 
6f51e1
-- 
6f51e1
2.9.3
6f51e1