From fc67606f3a93378283e0f15b94d4f463f0ad8167 Mon Sep 17 00:00:00 2001
From: Rob Crittenden <rcritten@redhat.com>
Date: Tue, 29 May 2018 14:19:16 -0400
Subject: [PATCH] Disable Schema Compat plugin during server upgrade

If this is enabled it can cause a deadlock with SSSD trying
to look up entries and it trying to get data on AD users
from SSSD.

When reading the entry from LDIF try to get the camel-case
nsslapd-pluginEnabled and fall back to the all lower-case
nsslapd-pluginenabled if that is not found. It would be nice
if the fetch function was case sensitive but this is likely
overkill as it is, but better safe than blowing up.

Upon restoring it will always write the camel-case version.

https://pagure.io/freeipa/issue/6721

Signed-off-by: Rob Crittenden <rcritten@redhat.com>
---
 ipaserver/install/upgradeinstance.py | 43 ++++++++++++++++++++++++++++++++++++
 1 file changed, 43 insertions(+)

diff --git a/ipaserver/install/upgradeinstance.py b/ipaserver/install/upgradeinstance.py
index df0777ec5d..a9e28a87b7 100644
--- a/ipaserver/install/upgradeinstance.py
+++ b/ipaserver/install/upgradeinstance.py
@@ -37,6 +37,7 @@
 logger = logging.getLogger(__name__)
 
 DSE = 'dse.ldif'
+COMPAT_DN = "cn=Schema Compatibility,cn=plugins,cn=config"
 
 
 class GetEntryFromLDIF(ldif.LDIFParser):
@@ -113,6 +114,7 @@ def create_instance(self):
         self.step("saving configuration", self.__save_config)
         self.step("disabling listeners", self.__disable_listeners)
         self.step("enabling DS global lock", self.__enable_ds_global_write_lock)
+        self.step("disabling Schema Compat", self.__disable_schema_compat)
         self.step("starting directory server", self.__start)
         if self.schema_files:
             self.step("updating schema", self.__update_schema)
@@ -161,6 +163,29 @@ def __save_config(self):
             else:
                 self.backup_state('nsslapd-global-backend-lock', global_lock)
 
+        with open(self.filename, "r") as in_file:
+            parser = GetEntryFromLDIF(in_file, entries_dn=[COMPAT_DN])
+            parser.parse()
+
+            try:
+                compat_entry = parser.get_results()[COMPAT_DN]
+            except KeyError:
+                pass
+            else:
+                try:
+                    schema_compat_enabled = compat_entry[
+                        'nsslapd-pluginEnabled'][0].decode('utf-8')
+                    self.backup_state('schema_compat_enabled',
+                                      schema_compat_enabled)
+                except KeyError:
+                    try:
+                        schema_compat_enabled = compat_entry[
+                            'nsslapd-pluginenabled'][0].decode('utf-8')
+                        self.backup_state('schema_compat_enabled',
+                                          schema_compat_enabled)
+                    except KeyError:
+                        pass
+
     def __enable_ds_global_write_lock(self):
         ldif_outfile = "%s.modified.out" % self.filename
         with open(ldif_outfile, "w") as out_file:
@@ -177,6 +202,7 @@ def __restore_config(self):
         port = self.restore_state('nsslapd-port')
         security = self.restore_state('nsslapd-security')
         global_lock = self.restore_state('nsslapd-global-backend-lock')
+        schema_compat_enabled = self.restore_state('schema_compat_enabled')
 
         ldif_outfile = "%s.modified.out" % self.filename
         with open(ldif_outfile, "w") as out_file:
@@ -195,6 +221,10 @@ def __restore_config(self):
                 if global_lock is not None:
                     parser.add_value("cn=config", "nsslapd-global-backend-lock",
                                      [global_lock.encode('utf-8')])
+                if schema_compat_enabled is not None:
+                    parser.replace_value(
+                        COMPAT_DN, "nsslapd-pluginEnabled",
+                        [schema_compat_enabled.encode('utf-8')])
 
                 parser.parse()
 
@@ -212,6 +242,19 @@ def __disable_listeners(self):
 
         shutil.copy2(ldif_outfile, self.filename)
 
+    def __disable_schema_compat(self):
+        ldif_outfile = "%s.modified.out" % self.filename
+        with open(ldif_outfile, "w") as out_file:
+            with open(self.filename, "r") as in_file:
+                parser = installutils.ModifyLDIF(in_file, out_file)
+                parser.remove_value(COMPAT_DN, "nsslapd-pluginEnabled")
+                parser.remove_value(COMPAT_DN, "nsslapd-pluginenabled")
+                parser.add_value(COMPAT_DN, "nsslapd-pluginEnabled",
+                                 [b"off"])
+                parser.parse()
+
+        shutil.copy2(ldif_outfile, self.filename)
+
     def __update_schema(self):
         self.modified = schemaupdate.update_schema(
             self.schema_files,
