""" KCM Responder Sanity Test Cases :requirement: IDM-SSSD-REQ :: SSSD KCM as default Kerberos CCACHE provider :casecomponent: sssd :subsystemteam: sst_idm_sssd :upstream: yes :status: approved """ import os import re import pytest from pexpect import pxssh from utils_config import set_param from sssd.testlib.common.utils import sssdTools class TestSanityKCM(object): """ KCM Sanity Test cases """ def _kcm_service_op(self, multihost, svc_op): systemd_kcm_op = 'systemctl %s sssd-kcm' % (svc_op) multihost.master[0].run_command(systemd_kcm_op) def _start_kcm(self, multihost): self._kcm_service_op(multihost, 'start') def _stop_kcm(self, multihost): self._kcm_service_op(multihost, 'stop') def _restart_kcm(self, multihost): self._kcm_service_op(multihost, 'restart') def _remove_kcm_log_file(self, multihost): multihost.master[0].run_command('rm -f /var/log/sssd/sssd_kcm.log') def _kcm_log_length(self, multihost): basename = 'sssd_kcm.log' kcm_log_file = '/var/log/sssd/' + basename local_kcm_log_file = '/tmp/kcm.log' try: multihost.master[0].transport.get_file(kcm_log_file, local_kcm_log_file) except FileNotFoundError: return 0 nlines = sum(1 for line in open(local_kcm_log_file)) os.remove(local_kcm_log_file) return nlines def _remove_secret_db(self, multihost): multihost.master[0].run_command( 'rm -f /var/lib/sss/secrets/secrets.ldb') self._restart_kcm(multihost) @pytest.mark.usefixtures("enable_kcm") def test_kinit_kcm(self, multihost): """ :title: kcm: Run kinit with KRB5CCNAME=KCM :id: 245eecf6-04b9-4c9f-8685-681d184fbbcf """ self._start_kcm(multihost) user = 'foo3' cmd = multihost.master[0].run_command( f'su - {user} -c "KRB5CCNAME=KCM:; kinit"', stdin_text='Secret123', raiseonerr=False) assert cmd.returncode == 0, "kinit failed!" cmd2 = multihost.master[0].run_command( f'su - {user} -c "KRB5CCNAME=KCM:; klist"', raiseonerr=False) assert cmd2.returncode == 0, "klist failed!" assert 'Ticket cache: KCM:14583103' in cmd2.stdout_text @staticmethod @pytest.mark.usefixtures("enable_kcm") def test_ssh_login_kcm(multihost): """ :title: kcm: Verify ssh logins are successuful with kcm as default :id: 458ed1e4-b908-40d3-b2fd-392e8d2dcf4b """ # pylint: disable=unused-argument client = sssdTools(multihost.master[0]) ssh0 = client.auth_from_client("foo4", 'Secret123') == 3 if not ssh0: multihost.master[0].run_command( 'journalctl -u sssd -n 50 --no-pager') assert ssh0, "Authentication Failed as user foo4" @pytest.mark.usefixtures("enable_kcm") def test_kcm_debug_level_set(self, multihost): """ :title: kcm: After kcm section with debug level set restaring sssd-kcm service enables kcm debugging :id: 31c74bfc-69d5-46bd-aef8-a5581970832e :description: Test that just adding a [kcm] section and restarting the kcm service enables debugging without having to restart the whole sssd """ # Start from a known-good state where the configuration is refreshed # by the monitor and logging is completely disabled multihost.master[0].service_sssd('stop') self._stop_kcm(multihost) self._remove_kcm_log_file(multihost) set_param(multihost, 'kcm', 'debug_level', '0') multihost.master[0].service_sssd('start') self._start_kcm(multihost) log_lines_pre = self._kcm_log_length(multihost) # Debugging is disabled, kinit and make sure that no debug messages # were produced user = 'foo3' client = sssdTools(multihost.master[0]) ssh0 = client.auth_from_client(user, 'Secret123') == 3 assert ssh0, f"Authentication Failed as user {user}." multihost.master[0].run_command( f'su - {user} -c "kdestroy"', raiseonerr=False) log_lines_nodebug = self._kcm_log_length(multihost) assert log_lines_nodebug == log_lines_pre # Enable debugging, restart only the kcm service, make sure some # debug messages were produced set_param(multihost, 'kcm', 'debug_level', '9') self._restart_kcm(multihost) ssh1 = client.auth_from_client(user, 'Secret123') == 3 assert ssh1, f"Authentication Failed as user {user}." multihost.master[0].run_command( f'su - {user} -c "kdestroy"', raiseonerr=False) log_lines_debug = self._kcm_log_length(multihost) assert log_lines_debug > log_lines_pre + 100 @staticmethod @pytest.mark.usefixtures("enable_kcm") def test_kdestroy_retval(multihost): """ :title: kcm: Test that destroying an empty cache does not return a non-zero return code :id: 2826097f-e6d7-4d99-ac85-3ee081aa681a """ user = 'foo3' client = sssdTools(multihost.master[0]) ssh0 = client.auth_from_client(user, 'Secret123') == 3 assert ssh0, f"Authentication Failed as user {user}." kd1 = multihost.master[0].run_command( f'su -l {user} -c "kdestroy"', raiseonerr=False) assert kd1.returncode == 0, "First kdestroy failed!" # Run the command again in case there was something in the ccache # previously kd2 = multihost.master[0].run_command( f'su -l {user} -c "kdestroy"', raiseonerr=False) assert kd2.returncode == 0, "Second kdestroy failed!" @staticmethod @pytest.mark.usefixtures("enable_kcm") def test_ssh_forward_creds(multihost): """ :title: kcm: Test that SSH can forward credentials with KCM :id: f4b0c785-a895-48a1-a55e-7519cf221393 :ticket: https://github.com/SSSD/sssd/issues/4863 """ ssh = pxssh.pxssh(options={"StrictHostKeyChecking": "no", "UserKnownHostsFile": "/dev/null"}) ssh.force_password = True try: ssh.login(multihost.master[0].sys_hostname, 'foo3', 'Secret123') ssh.sendline('kdestroy -A -q') ssh.prompt(timeout=5) ssh.sendline('kinit foo9') ssh.expect('Password for .*:', timeout=10) ssh.sendline('Secret123') ssh.prompt(timeout=5) ssh.sendline('klist') ssh.prompt(timeout=5) klist = str(ssh.before) ssh.sendline(f'ssh -v -o StrictHostKeyChecking=no -K -l foo9 ' f'{multihost.master[0].sys_hostname} klist') ssh.prompt(timeout=30) ssh_output = str(ssh.before) ssh.logout() except pxssh.ExceptionPxssh as ex: pytest.fail(ex) # Note: The cache is based on uid so for foo3 it is 14583103 and # for foo9 it is 14583109 (see create_posix_usersgroups fixture) assert 'KCM:14583103' in klist, "kinit did not work!" assert 'KCM:14583109' in ssh_output, "Ticket not forwarded!" @staticmethod @pytest.mark.usefixtures("enable_kcm") def test_kvno_display(multihost): """ :title: kcm: Test kvno correctly displays version numbers of principals :id: 7c9178e6-fea5-44a1-b473-76667624cee2 :ticket: https://github.com/SSSD/sssd/issues/4763 """ host_princ = f'host/{multihost.master[0].sys_hostname}@EXAMPLE.TEST' kvno_cmd = f'kvno {host_princ}' client = sssdTools(multihost.master[0]) client.auth_from_client('foo4', 'Secret123') kvno = multihost.master[0].run_command( f'su -l foo4 -c "{kvno_cmd}"', raiseonerr=False) assert kvno.returncode == 0, "kvno failed!" for line in kvno.stdout_text.splitlines(): kvno_check = re.search(r'%s: kvno = (\d+)' % host_princ, line) if kvno_check: print(kvno_check.group()) else: pytest.fail("kvno display was improper") @pytest.mark.usefixtures("enable_kcm", "create_many_user_principals") def test_kcm_peruid_quota(self, multihost): """ :title: kcm: Make sure the quota limits a client, but only that client :id: 3ac8f62e-05e4-4ca7-b588-145fd6258c2a """ # It is easier to keep these tests stable and independent from others # if they start from a clean slate self._remove_secret_db(multihost) client = sssdTools(multihost.master[0]) client.auth_from_client('foo2', 'Secret123') client.auth_from_client('foo3', 'Secret123') # The loop would request 63 users, plus there is foo3 we authenticated # earlier, so this should exactly deplete the quota, but should succeed for i in range(1, 64): username = "user%04d" % i kinit = multihost.master[0].run_command( f'su -l foo3 -c "kinit {username}"', stdin_text='Secret123', raiseonerr=False) assert kinit.returncode == 0 # this kinit should be exactly one over the peruid limit kinit_f = multihost.master[0].run_command( 'su -l foo3 -c "kinit user0064"', stdin_text='Secret123', raiseonerr=False) assert kinit_f.returncode != 0 # Since this is a per-uid limit, another user should be able to kinit # just fine # this kinit should be exactly one over the peruid limit kinit_o = multihost.master[0].run_command( 'su -l foo2 -c "kinit user0064"', stdin_text='Secret123', raiseonerr=False) assert kinit_o.returncode == 0 # kdestroy as the original user, the quota should allow a subsequent # kinit multihost.master[0].run_command( 'su -l foo3 -c "kdestroy -A"', raiseonerr=False) kinit_p = multihost.master[0].run_command( 'su -l foo3 -c "kinit user0064"', stdin_text='Secret123', raiseonerr=False) assert kinit_p.returncode == 0 multihost.master[0].run_command( 'su -l foo2 -c "kdestroy -A"', raiseonerr=False) multihost.master[0].run_command( 'su -l foo3 -c "kdestroy -A"', raiseonerr=False) @pytest.mark.usefixtures("enable_kcm", "create_many_user_principals") def test_kcm_peruid_quota_increase(self, multihost): """ :title: kcm: Quota increase :id: 0b3cab49-befb-4ab2-bb12-b102d94249aa :description: Increasing the peruid quota allows a client to store more data """ # It is easier to keep these tests stable and independent from others # if they start from a clean slate self._remove_secret_db(multihost) user = 'foo3' client = sssdTools(multihost.master[0]) client.auth_from_client(user, 'Secret123') # The loop would request 63 users, plus there is foo3 we authenticated # earlier, so this should exactly deplete the quota, but should succeed for i in range(1, 64): username = "user%04d" % i kinit = multihost.master[0].run_command( f'su -l {user} -c "kinit {username}"', stdin_text='Secret123', raiseonerr=False) assert kinit.returncode == 0 # this kinit should be exactly one over the peruid limit kinit_f = multihost.master[0].run_command( f'su -l {user} -c "kinit user0064"', stdin_text='Secret123', raiseonerr=False) assert kinit_f.returncode != 0 set_param(multihost, 'kcm', 'max_uid_ccaches', '65') self._restart_kcm(multihost) # Now the kinit should work as we increased the limit kinit_p = multihost.master[0].run_command( f'su -l {user} -c "kinit user0064"', stdin_text='Secret123', raiseonerr=False) assert kinit_p.returncode == 0 multihost.master[0].run_command( f'su -l {user} -c "kdestroy -A"', raiseonerr=False) @pytest.mark.usefixtures("enable_kcm") def test_kcm_payload_low_quota(self, multihost): """ :title: kcm: Quota enforcement :id: cb3daadb-c5e7-48f8-b419-11c616f0d602 :description: Set a prohibitive quota for the per-ccache payload limit and make sure it gets enforced """ # It is easier to keep these tests stable and independent from others # if they start from a clean slate self._remove_secret_db(multihost) user = 'foo3' client = sssdTools(multihost.master[0]) client.auth_from_client(user, 'Secret123') multihost.master[0].run_command( f'su -l {user} -c "kdestroy -A"', raiseonerr=False) set_param(multihost, 'kcm', 'max_ccache_size', '1') self._restart_kcm(multihost) # We use kinit to exceed the maximum ccache size as it creates payload # of 1280 bytes by acquiring tgt and also some control credentials. # SSH authentication is not sufficient as it stores only tgt. kv_p = multihost.master[0].run_command( f'su -l foo3 -c "kinit {user}@EXAMPLE.TEST"', stdin_text='Secret123', raiseonerr=False) assert kv_p.returncode != 0