From 3ca0faa6a503da7216e6a66f3b63b359980a47cb Mon Sep 17 00:00:00 2001
From: Alexander Bokovoy <abokovoy@redhat.com>
Date: Tue, 22 Dec 2020 09:11:51 +0200
Subject: [PATCH 1/3] opendnssecinstance: use late binding for UID/GID
 resolution

Move actual resolution of UID/GID values for 'ods' and 'named' entities
to the code that needs them. This prevents failures when uninstalling
IPA server set up without DNS feature. In particular, 'named' group is
created when 'bind' package is installed and if 'bind' package is not
installed, uninstall fails in OpenDNSSEC instance constructor.

We use common pattern for all services during uninstall:

 svc = SVCClass(..)
 if svc.is_configured()
     svc.uninstall()

This requires that the class constructor should not rely on artifacts
that only exist when the service is configured.

Fixes: https://pagure.io/freeipa/issue/8630

Signed-off-by: Alexander Bokovoy <abokovoy@redhat.com>
---
 ipaserver/install/opendnssecinstance.py | 29 ++++++++++++++-----------
 1 file changed, 16 insertions(+), 13 deletions(-)

diff --git a/ipaserver/install/opendnssecinstance.py b/ipaserver/install/opendnssecinstance.py
index 415e7710ed1..ada7da573e2 100644
--- a/ipaserver/install/opendnssecinstance.py
+++ b/ipaserver/install/opendnssecinstance.py
@@ -67,9 +67,9 @@ def __init__(self, fstore=None):
             self, "ods-enforcerd",
             service_desc="OpenDNSSEC enforcer daemon",
         )
-        self.named_gid = constants.NAMED_GROUP.gid
-        self.ods_uid = constants.ODS_USER.uid
-        self.ods_gid = constants.ODS_GROUP.gid
+        self.named_gid = constants.NAMED_GROUP
+        self.ods_uid = constants.ODS_USER
+        self.ods_gid = constants.ODS_GROUP
         self.conf_file_dict = {
             'SOFTHSM_LIB': paths.LIBSOFTHSM2_SO,
             'TOKEN_LABEL': SOFTHSM_DNSSEC_TOKEN_LABEL,
@@ -194,8 +194,8 @@ def __setup_conf_files(self):
                                       quotes=False, separator='=')
 
     def __setup_ownership_file_modes(self):
-        assert self.ods_uid is not None
-        assert self.ods_gid is not None
+        assert self.ods_uid.uid is not None
+        assert self.ods_gid.gid is not None
 
         # workarounds for packaging bugs in opendnssec-1.4.5-2.fc20.x86_64
         # https://bugzilla.redhat.com/show_bug.cgi?id=1098188
@@ -204,24 +204,24 @@ def __setup_ownership_file_modes(self):
                 dir_path = os.path.join(root, directory)
                 os.chmod(dir_path, 0o770)
                 # chown to root:ods
-                os.chown(dir_path, 0, self.ods_gid)
+                os.chown(dir_path, 0, self.ods_gid.gid)
             for filename in files:
                 file_path = os.path.join(root, filename)
                 os.chmod(file_path, 0o660)
                 # chown to root:ods
-                os.chown(file_path, 0, self.ods_gid)
+                os.chown(file_path, 0, self.ods_gid.gid)
 
         for (root, dirs, files) in os.walk(paths.VAR_OPENDNSSEC_DIR):
             for directory in dirs:
                 dir_path = os.path.join(root, directory)
                 os.chmod(dir_path, 0o770)
                 # chown to ods:ods
-                os.chown(dir_path, self.ods_uid, self.ods_gid)
+                os.chown(dir_path, self.ods_uid.uid, self.ods_gid.gid)
             for filename in files:
                 file_path = os.path.join(root, filename)
                 os.chmod(file_path, 0o660)
                 # chown to ods:ods
-                os.chown(file_path, self.ods_uid, self.ods_gid)
+                os.chown(file_path, self.ods_uid.uid, self.ods_gid.gid)
 
     def __generate_master_key(self):
 
@@ -242,11 +242,13 @@ def __generate_master_key(self):
                 for directory in dirs:
                     dir_path = os.path.join(root, directory)
                     os.chmod(dir_path, 0o770 | stat.S_ISGID)
-                    os.chown(dir_path, self.ods_uid, self.named_gid)  # chown to ods:named
+                    # chown to ods:named
+                    os.chown(dir_path, self.ods_uid.uid, self.named_gid.gid)
                 for filename in files:
                     file_path = os.path.join(root, filename)
                     os.chmod(file_path, 0o660 | stat.S_ISGID)
-                    os.chown(file_path, self.ods_uid, self.named_gid)  # chown to ods:named
+                    # chown to ods:named
+                    os.chown(file_path, self.ods_uid.uid, self.named_gid.gid)
 
         finally:
             p11.finalize()
@@ -266,7 +268,8 @@ def __setup_dnssec(self):
             # copy user specified kasp.db to proper location and set proper
             # privileges
             shutil.copy(self.kasp_db_file, paths.OPENDNSSEC_KASP_DB)
-            os.chown(paths.OPENDNSSEC_KASP_DB, self.ods_uid, self.ods_gid)
+            os.chown(paths.OPENDNSSEC_KASP_DB,
+                     self.ods_uid.uid, self.ods_gid.gid)
             os.chmod(paths.OPENDNSSEC_KASP_DB, 0o660)
 
         else:
@@ -290,7 +293,7 @@ def __start(self):
             )
             with open(paths.OPENDNSSEC_ZONELIST_FILE, 'w') as f:
                 f.write(result.output)
-                os.fchown(f.fileno(), self.ods_uid, self.ods_gid)
+                os.fchown(f.fileno(), self.ods_uid.uid, self.ods_gid.gid)
                 os.fchmod(f.fileno(), 0o660)
 
     def uninstall(self):

From d1a6abc47812230b4a267aec461db9b31864378f Mon Sep 17 00:00:00 2001
From: Alexander Bokovoy <abokovoy@redhat.com>
Date: Tue, 22 Dec 2020 09:20:52 +0200
Subject: [PATCH 2/3] dnskeysyncinstance: use late binding for UID/GID
 resolution

Move actual resolution of UID/GID values for 'ods' and 'named' entities
to the code that needs them. This prevents failures when uninstalling
IPA server set up without DNS feature. In particular, 'named' group is
created when 'bind' package is installed and if 'bind' package is not
installed, uninstall fails in OpenDNSSEC instance constructor.

We use common pattern for all services during uninstall:

 svc = SVCClass(..)
 if svc.is_configured()
     svc.uninstall()

This requires that the class constructor should not rely on artifacts
that only exist when the service is configured.

Fixes: https://pagure.io/freeipa/issue/8630

Signed-off-by: Alexander Bokovoy <abokovoy@redhat.com>
---
 ipaserver/install/dnskeysyncinstance.py | 24 ++++++++++++------------
 1 file changed, 12 insertions(+), 12 deletions(-)

diff --git a/ipaserver/install/dnskeysyncinstance.py b/ipaserver/install/dnskeysyncinstance.py
index 16870b73b5c..945ccf76935 100644
--- a/ipaserver/install/dnskeysyncinstance.py
+++ b/ipaserver/install/dnskeysyncinstance.py
@@ -55,10 +55,10 @@ def __init__(self, fstore=None, logger=logger):
             keytab=paths.IPA_DNSKEYSYNCD_KEYTAB
         )
         self.extra_config = [u'dnssecVersion 1', ]  # DNSSEC enabled
-        self.named_uid = constants.NAMED_USER.uid
-        self.named_gid = constants.NAMED_GROUP.gid
-        self.ods_uid = constants.ODS_USER.uid
-        self.ods_gid = constants.ODS_GROUP.gid
+        self.named_uid = constants.NAMED_USER
+        self.named_gid = constants.NAMED_GROUP
+        self.ods_uid = constants.ODS_USER
+        self.ods_gid = constants.ODS_GROUP
 
     suffix = ipautil.dn_attribute_property('_suffix')
 
@@ -78,7 +78,7 @@ def set_dyndb_ldap_workdir_permissions(self):
             else:
                 os.chmod(directory, 0o770)
             # dnssec daemons require to have access into the directory
-            os.chown(directory, self.named_uid, self.named_gid)
+            os.chown(directory, self.named_uid.uid, self.named_gid.gid)
 
     def remove_replica_public_keys(self, replica_fqdn):
         ldap = api.Backend.ldap2
@@ -180,7 +180,7 @@ def setup_named_openssl_conf(self):
             )
             with open(paths.DNSSEC_OPENSSL_CONF, 'w') as f:
                 os.fchmod(f.fileno(), 0o640)
-                os.fchown(f.fileno(), 0, self.named_gid)
+                os.fchown(f.fileno(), 0, self.named_gid.gid)
                 f.write(named_openssl_txt)
 
     def setup_named_sysconfig(self):
@@ -235,7 +235,7 @@ def __setup_softhsm(self):
             os.mkdir(paths.IPA_DNSSEC_DIR)
             os.chmod(paths.IPA_DNSSEC_DIR, 0o770)
             # chown ods:named
-            os.chown(paths.IPA_DNSSEC_DIR, self.ods_uid, self.named_gid)
+            os.chown(paths.IPA_DNSSEC_DIR, self.ods_uid.uid, self.named_gid.gid)
 
         # setup softhsm2 config file
         softhsm_conf_txt = ("# SoftHSM v2 configuration file \n"
@@ -272,7 +272,7 @@ def __setup_softhsm(self):
         os.mkdir(paths.DNSSEC_TOKENS_DIR)
         os.chmod(paths.DNSSEC_TOKENS_DIR, 0o770 | stat.S_ISGID)
         # chown to ods:named
-        os.chown(paths.DNSSEC_TOKENS_DIR, self.ods_uid, self.named_gid)
+        os.chown(paths.DNSSEC_TOKENS_DIR, self.ods_uid.uid, self.named_gid.gid)
 
         # generate PINs for softhsm
         pin_length = 30  # Bind allows max 32 bytes including ending '\0'
@@ -284,7 +284,7 @@ def __setup_softhsm(self):
         logger.debug("Saving user PIN to %s", paths.DNSSEC_SOFTHSM_PIN)
         with open(paths.DNSSEC_SOFTHSM_PIN, 'w') as f:
             # chown to ods:named
-            os.fchown(f.fileno(), self.ods_uid, self.named_gid)
+            os.fchown(f.fileno(), self.ods_uid.uid, self.named_gid.gid)
             os.fchmod(f.fileno(), 0o660)
             f.write(pin)
 
@@ -419,12 +419,12 @@ def __setup_replica_keys(self):
                 dir_path = os.path.join(root, directory)
                 os.chmod(dir_path, 0o770 | stat.S_ISGID)
                 # chown to ods:named
-                os.chown(dir_path, self.ods_uid, self.named_gid)
+                os.chown(dir_path, self.ods_uid.uid, self.named_gid.gid)
             for filename in files:
                 file_path = os.path.join(root, filename)
                 os.chmod(file_path, 0o660 | stat.S_ISGID)
                 # chown to ods:named
-                os.chown(file_path, self.ods_uid, self.named_gid)
+                os.chown(file_path, self.ods_uid.uid, self.named_gid.gid)
 
     def __enable(self):
         try:
@@ -449,7 +449,7 @@ def __setup_principal(self):
             dnssynckey_principal_dn = p
 
         # Make sure access is strictly reserved to the named user
-        os.chown(self.keytab, 0, self.ods_gid)
+        os.chown(self.keytab, 0, self.ods_gid.gid)
         os.chmod(self.keytab, 0o440)
 
         dns_group = DN(('cn', 'DNS Servers'), ('cn', 'privileges'),

From 950fe59f849618eb9e101c613502cca05a7f5d49 Mon Sep 17 00:00:00 2001
From: Alexander Bokovoy <abokovoy@redhat.com>
Date: Tue, 22 Dec 2020 09:25:01 +0200
Subject: [PATCH 3/3] odsexporterinstance: use late binding for UID/GID
 resolution

Move actual resolution of UID/GID values for 'ods' entities to the code
that needs them. This prevents failures when uninstalling IPA server set
up without DNS feature. In particular, 'ods' user and group are created
when 'opendnssec' package is installed and if 'opendnssec' package is
not installed, uninstall fails in OpenDNSSEC Exporter instance
constructor.

We use common pattern of checking the service during uninstall:

 svc = SVCClass()
 if svc.is_configured():
    svc.uninstall()

Thus, service class constructor must not do UID/GID resolution

Fixes: https://pagure.io/freeipa/issue/8630

Signed-off-by: Alexander Bokovoy <abokovoy@redhat.com>
---
 ipaserver/install/odsexporterinstance.py | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)

diff --git a/ipaserver/install/odsexporterinstance.py b/ipaserver/install/odsexporterinstance.py
index d9fa46d0a36..e0359130b90 100644
--- a/ipaserver/install/odsexporterinstance.py
+++ b/ipaserver/install/odsexporterinstance.py
@@ -31,8 +31,8 @@ def __init__(self, fstore=None):
             keytab=paths.IPA_ODS_EXPORTER_KEYTAB,
             service_prefix=u'ipa-ods-exporter'
         )
-        self.ods_uid = constants.ODS_USER.uid
-        self.ods_gid = constants.ODS_GROUP.gid
+        self.ods_uid = constants.ODS_USER
+        self.ods_gid = constants.ODS_GROUP
         self.enable_if_exists = False
 
     suffix = ipautil.dn_attribute_property('_suffix')
@@ -71,7 +71,7 @@ def __setup_key_exporter(self):
                                    quotes=False, separator='=')
 
     def __setup_principal(self):
-        assert self.ods_uid is not None
+        assert self.ods_uid.uid is not None
 
         for f in [paths.IPA_ODS_EXPORTER_CCACHE, self.keytab]:
             try:
@@ -95,7 +95,7 @@ def __setup_principal(self):
 
         # Make sure access is strictly reserved to the ods user
         os.chmod(self.keytab, 0o440)
-        os.chown(self.keytab, 0, self.ods_gid)
+        os.chown(self.keytab, 0, self.ods_gid.gid)
 
         dns_group = DN(('cn', 'DNS Servers'), ('cn', 'privileges'),
                        ('cn', 'pbac'), self.suffix)
