From d355761f23fae412bb01a1d737cee342c7bd04f9 Mon Sep 17 00:00:00 2001
From: Alexander Bokovoy <abokovoy@redhat.com>
Date: Tue, 29 Aug 2023 12:37:57 +0300
Subject: [PATCH] ipa-client-install: enable SELinux for SSSD

For passkeys (FIDO2) support, SSSD uses libfido2 library which needs
access to USB devices. Add SELinux booleans handling to ipa-client-install
so that correct SELinux booleans can be enabled and disabled during
install and uninstall. Ignore and record a warning when SELinux policy
does not support the boolean.

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

Signed-off-by: Alexander Bokovoy <abokovoy@redhat.com>
Reviewed-By: Francisco Trivino <ftrivino@redhat.com>
Reviewed-By: Rob Crittenden <rcritten@redhat.com>
---
 ipaclient/install/client.py   | 35 ++++++++++++++++++++++++++++++++++-
 ipaplatform/base/constants.py |  3 +++
 2 files changed, 37 insertions(+), 1 deletion(-)

diff --git a/ipaclient/install/client.py b/ipaclient/install/client.py
index 07d62a748..1aaa4ed60 100644
--- a/ipaclient/install/client.py
+++ b/ipaclient/install/client.py
@@ -67,6 +67,7 @@ from ipapython.ipautil import (
 )
 from ipapython.ssh import SSHPublicKey
 from ipapython import version
+from ipapython.errors import SetseboolError
 
 from . import automount, timeconf, sssd
 from ipaclient import discovery
@@ -98,6 +99,7 @@ cli_realm = None
 cli_kdc = None
 client_domain = None
 cli_basedn = None
+selinux_works = None
 # end of global variables
 
 
@@ -2153,6 +2155,7 @@ def install_check(options):
     global cli_kdc
     global client_domain
     global cli_basedn
+    global selinux_works
 
     print("This program will set up IPA client.")
     print("Version {}".format(version.VERSION))
@@ -2166,7 +2169,7 @@ def install_check(options):
             "You must be root to run ipa-client-install.",
             rval=CLIENT_INSTALL_ERROR)
 
-    tasks.check_selinux_status()
+    selinux_works = tasks.check_selinux_status()
 
     if is_ipa_client_configured(on_master=options.on_master):
         logger.error("IPA client is already configured on this system.")
@@ -2674,6 +2677,20 @@ def restore_time_sync(statestore, fstore):
         logger.error('Failed to restore time synchronization service: %s', e)
 
 
+def configure_selinux_for_client(statestore):
+    def backup_state(key, value):
+        statestore.backup_state('selinux', key, value)
+
+    try:
+        tasks.set_selinux_booleans(constants.SELINUX_BOOLEAN_SSSD,
+                                   backup_state)
+    except SetseboolError as e:
+        for c in constants.SELINUX_BOOLEAN_SSSD:
+            if c in e.failed:
+                logger.warning(
+                    "SELinux does not support SSSD boolean %s, ignoring", c)
+
+
 def install(options):
     try:
         _install(options, dict())
@@ -3198,6 +3215,9 @@ def _install(options, tdict):
         logger.info("%s enabled", "SSSD" if options.sssd else "LDAP")
 
         if options.sssd:
+            if selinux_works:
+                configure_selinux_for_client(statestore)
+
             sssd = services.service('sssd', api)
             try:
                 sssd.restart()
@@ -3322,6 +3342,8 @@ def _install(options, tdict):
 
 
 def uninstall_check(options):
+    global selinux_works
+
     if not is_ipa_client_configured():
         if options.on_master:
             rval = SUCCESS
@@ -3337,6 +3359,8 @@ def uninstall_check(options):
         logger.info("Refer to ipa-server-install for uninstallation.")
         raise ScriptError(rval=CLIENT_NOT_CONFIGURED)
 
+    selinux_works = tasks.check_selinux_status()
+
 
 def uninstall(options):
     env = {'PATH': SECURE_PATH}
@@ -3600,6 +3624,15 @@ def uninstall(options):
                 "Failed to disable automatic startup of the SSSD daemon: %s",
                 e)
 
+    if was_sssd_installed and selinux_works:
+        # Restore SELinux boolean states
+        boolean_states = {name: statestore.restore_state('selinux', name)
+                          for name in constants.SELINUX_BOOLEAN_SSSD}
+        try:
+            tasks.set_selinux_booleans(boolean_states)
+        except SetseboolError as e:
+            logger.warning("Unable to reset SELinux variable: %s", str(e))
+
     tasks.restore_hostname(fstore, statestore)
 
     if fstore.has_files():
diff --git a/ipaplatform/base/constants.py b/ipaplatform/base/constants.py
index 61b864376..1689efe52 100644
--- a/ipaplatform/base/constants.py
+++ b/ipaplatform/base/constants.py
@@ -151,6 +151,9 @@ class BaseConstantsNamespace:
             'samba_share_nfs': 'on',
         },
     }
+    SELINUX_BOOLEAN_SSSD = {
+        'sssd_use_usb': 'on',
+    }
     SELINUX_MCS_MAX = 1023
     SELINUX_MCS_REGEX = r"^c(\d+)([.,-]c(\d+))*$"
     SELINUX_MLS_MAX = 15
-- 
2.41.0