2019-11-19 17:33:23 -06:00
|
|
|
#
|
2020-11-10 13:07:47 -06:00
|
|
|
# Copyright (C) 2019,2020 FreeIPA Contributors see COPYING for license
|
2019-11-19 17:33:23 -06:00
|
|
|
#
|
|
|
|
|
|
|
|
"""
|
|
|
|
Module provides tests for Kerberos ticket policy options
|
|
|
|
"""
|
|
|
|
|
|
|
|
from __future__ import absolute_import
|
|
|
|
|
2020-11-10 13:07:47 -06:00
|
|
|
import pytest
|
2019-12-19 06:30:13 -06:00
|
|
|
import time
|
2019-11-19 17:33:23 -06:00
|
|
|
from datetime import datetime
|
|
|
|
|
2021-03-01 08:44:31 -06:00
|
|
|
from ipalib.constants import IPAAPI_USER
|
2020-11-20 09:09:16 -06:00
|
|
|
from ipaplatform.paths import paths
|
2020-11-20 09:07:27 -06:00
|
|
|
|
2019-11-19 17:33:23 -06:00
|
|
|
from ipatests.test_integration.base import IntegrationTest
|
2019-12-19 06:30:13 -06:00
|
|
|
from ipatests.test_integration.test_otp import add_otptoken, del_otptoken
|
2019-11-19 17:33:23 -06:00
|
|
|
from ipatests.pytest_ipa.integration import tasks
|
|
|
|
|
|
|
|
PASSWORD = "Secret123"
|
|
|
|
USER1 = "testuser1"
|
|
|
|
USER2 = "testuser2"
|
|
|
|
MAXLIFE = 86400
|
|
|
|
|
|
|
|
|
2020-11-19 11:06:21 -06:00
|
|
|
def maxlife_within_policy(input, maxlife, slush=3600):
|
2019-11-19 17:33:23 -06:00
|
|
|
"""Given klist output of the TGT verify that it is within policy
|
|
|
|
|
|
|
|
Ensure that the validity period is somewhere within the
|
|
|
|
absolute maxlife and a slush value, maxlife - slush.
|
|
|
|
|
|
|
|
Returns True if within policy.
|
|
|
|
|
|
|
|
Input should be a string like:
|
|
|
|
11/19/2019 16:37:40 11/20/2019 16:37:39 krbtgt/...
|
2020-11-19 11:06:21 -06:00
|
|
|
|
|
|
|
slush defaults to 1 * 60 * 60 matching the jitter window.
|
2019-11-19 17:33:23 -06:00
|
|
|
"""
|
|
|
|
data = input.split()
|
|
|
|
start = datetime.strptime(data[0] + ' ' + data[1], '%m/%d/%Y %H:%M:%S')
|
|
|
|
end = datetime.strptime(data[2] + ' ' + data[3], '%m/%d/%Y %H:%M:%S')
|
|
|
|
diff = int((end - start).total_seconds())
|
|
|
|
|
|
|
|
return maxlife >= diff >= maxlife - slush
|
|
|
|
|
|
|
|
|
2020-11-20 09:07:27 -06:00
|
|
|
@pytest.fixture
|
|
|
|
def reset_to_default_policy():
|
2019-12-19 06:30:13 -06:00
|
|
|
"""Reset default user authentication and user authentication type"""
|
2020-11-20 09:07:27 -06:00
|
|
|
|
|
|
|
state = dict()
|
|
|
|
|
|
|
|
def _reset_to_default_policy(host, user=None):
|
|
|
|
state['host'] = host
|
|
|
|
state['user'] = user
|
|
|
|
|
|
|
|
yield _reset_to_default_policy
|
|
|
|
|
|
|
|
host = state['host']
|
|
|
|
user = state['user']
|
2019-12-19 06:30:13 -06:00
|
|
|
tasks.kinit_admin(host)
|
2020-11-20 09:07:27 -06:00
|
|
|
host.run_command(['ipa', 'krbtpolicy-reset'])
|
|
|
|
if user:
|
|
|
|
host.run_command(['ipa', 'user-mod', user, '--user-auth-type='])
|
|
|
|
host.run_command(['ipa', 'krbtpolicy-reset', user])
|
2019-12-19 06:30:13 -06:00
|
|
|
|
|
|
|
|
2020-11-10 13:07:47 -06:00
|
|
|
def kinit_check_life(master, user):
|
|
|
|
"""Acquire a TGT and check if it's within the lifetime window"""
|
|
|
|
master.run_command(["kinit", user], stdin_text=f"{PASSWORD}\n")
|
|
|
|
result = master.run_command("klist | grep krbtgt")
|
2020-11-19 11:06:21 -06:00
|
|
|
assert maxlife_within_policy(result.stdout_text, MAXLIFE) is True
|
2020-11-10 13:07:47 -06:00
|
|
|
|
|
|
|
|
2019-11-19 17:33:23 -06:00
|
|
|
class TestPWPolicy(IntegrationTest):
|
|
|
|
"""Tests password custom and default password policies.
|
|
|
|
"""
|
|
|
|
num_replicas = 0
|
|
|
|
|
|
|
|
@classmethod
|
|
|
|
def install(cls, mh):
|
|
|
|
tasks.install_master(cls.master)
|
|
|
|
tasks.create_active_user(cls.master, USER1, PASSWORD)
|
|
|
|
tasks.create_active_user(cls.master, USER2, PASSWORD)
|
|
|
|
|
2020-11-19 11:06:21 -06:00
|
|
|
@pytest.fixture(autouse=True, scope="function")
|
2020-11-10 13:07:47 -06:00
|
|
|
def with_admin(self):
|
|
|
|
tasks.kinit_admin(self.master)
|
|
|
|
yield
|
|
|
|
tasks.kdestroy_all(self.master)
|
|
|
|
|
2019-11-19 17:33:23 -06:00
|
|
|
def test_krbtpolicy_default(self):
|
|
|
|
"""Test the default kerberos ticket policy 24-hr tickets"""
|
|
|
|
master = self.master
|
|
|
|
master.run_command(['ipa', 'krbtpolicy-mod', USER1,
|
|
|
|
'--maxlife', str(MAXLIFE)])
|
|
|
|
tasks.kdestroy_all(master)
|
|
|
|
|
|
|
|
master.run_command(['kinit', USER1],
|
|
|
|
stdin_text=PASSWORD + '\n')
|
|
|
|
result = master.run_command('klist | grep krbtgt')
|
|
|
|
assert maxlife_within_policy(result.stdout_text, MAXLIFE) is True
|
|
|
|
|
2022-02-01 12:38:29 -06:00
|
|
|
def test_krbtpolicy_password_and_hardended(self):
|
|
|
|
"""Test a pwd and hardened kerberos ticket policy with 10min tickets"""
|
2022-03-15 04:39:46 -05:00
|
|
|
if self.master.is_fips_mode:
|
|
|
|
pytest.skip("SPAKE pre-auth is not compatible with FIPS mode")
|
|
|
|
|
2019-11-19 17:33:23 -06:00
|
|
|
master = self.master
|
|
|
|
master.run_command(['ipa', 'user-mod', USER1,
|
|
|
|
'--user-auth-type', 'password',
|
|
|
|
'--user-auth-type', 'hardened'])
|
|
|
|
master.run_command(['ipa', 'config-mod',
|
|
|
|
'--user-auth-type', 'password',
|
|
|
|
'--user-auth-type', 'hardened'])
|
|
|
|
master.run_command(['ipa', 'krbtpolicy-mod', USER1,
|
|
|
|
'--hardened-maxlife', '600'])
|
|
|
|
|
|
|
|
tasks.kdestroy_all(master)
|
|
|
|
|
|
|
|
master.run_command(['kinit', USER1],
|
|
|
|
stdin_text=PASSWORD + '\n')
|
|
|
|
result = master.run_command('klist | grep krbtgt')
|
2020-11-19 11:06:21 -06:00
|
|
|
assert maxlife_within_policy(result.stdout_text, 600,
|
|
|
|
slush=600) is True
|
2019-11-19 17:33:23 -06:00
|
|
|
|
|
|
|
tasks.kdestroy_all(master)
|
|
|
|
|
|
|
|
# Verify that the short policy only applies to USER1
|
|
|
|
master.run_command(['kinit', USER2],
|
|
|
|
stdin_text=PASSWORD + '\n')
|
|
|
|
result = master.run_command('klist | grep krbtgt')
|
|
|
|
assert maxlife_within_policy(result.stdout_text, MAXLIFE) is True
|
|
|
|
|
2022-02-01 12:38:29 -06:00
|
|
|
def test_krbtpolicy_hardended(self):
|
|
|
|
"""Test a hardened kerberos ticket policy with 30min tickets"""
|
2022-03-15 04:39:46 -05:00
|
|
|
if self.master.is_fips_mode:
|
|
|
|
pytest.skip("SPAKE pre-auth is not compatible with FIPS mode")
|
|
|
|
|
2022-02-01 12:38:29 -06:00
|
|
|
master = self.master
|
|
|
|
master.run_command(['ipa', 'user-mod', USER1,
|
|
|
|
'--user-auth-type', 'hardened'])
|
|
|
|
master.run_command(['ipa', 'config-mod',
|
|
|
|
'--user-auth-type', 'hardened'])
|
|
|
|
master.run_command(['ipa', 'krbtpolicy-mod', USER1,
|
|
|
|
'--hardened-maxlife', '1800'])
|
|
|
|
|
|
|
|
tasks.kdestroy_all(master)
|
|
|
|
|
|
|
|
master.run_command(['kinit', USER1],
|
|
|
|
stdin_text=PASSWORD + '\n')
|
|
|
|
result = master.run_command('klist | grep krbtgt')
|
|
|
|
assert maxlife_within_policy(result.stdout_text, 1800,
|
|
|
|
slush=1800) is True
|
|
|
|
|
|
|
|
tasks.kdestroy_all(master)
|
|
|
|
|
|
|
|
# Verify that the short policy only applies to USER1
|
|
|
|
master.run_command(['kinit', USER2],
|
|
|
|
stdin_text=PASSWORD + '\n')
|
|
|
|
result = master.run_command('klist | grep krbtgt')
|
|
|
|
assert maxlife_within_policy(result.stdout_text, MAXLIFE) is True
|
|
|
|
|
2019-11-19 17:33:23 -06:00
|
|
|
def test_krbtpolicy_password(self):
|
|
|
|
"""Test the kerberos ticket policy which issues 20 min tickets"""
|
|
|
|
master = self.master
|
|
|
|
master.run_command(['ipa', 'krbtpolicy-mod', USER2,
|
|
|
|
'--maxlife', '1200'])
|
|
|
|
|
|
|
|
tasks.kdestroy_all(master)
|
|
|
|
|
|
|
|
master.run_command(['kinit', USER2],
|
|
|
|
stdin_text=PASSWORD + '\n')
|
|
|
|
result = master.run_command('klist | grep krbtgt')
|
2020-11-19 11:06:21 -06:00
|
|
|
assert maxlife_within_policy(result.stdout_text, 1200,
|
|
|
|
slush=1200) is True
|
2019-11-19 17:33:23 -06:00
|
|
|
|
2019-12-18 04:08:59 -06:00
|
|
|
def test_krbtpolicy_reset(self):
|
|
|
|
"""Test a hardened kerberos ticket policy reset"""
|
|
|
|
master = self.master
|
|
|
|
master.run_command(['ipa', 'krbtpolicy-reset', USER2])
|
|
|
|
master.run_command(['kinit', USER2],
|
|
|
|
stdin_text=PASSWORD + '\n')
|
|
|
|
result = master.run_command('klist | grep krbtgt')
|
|
|
|
assert maxlife_within_policy(result.stdout_text, MAXLIFE) is True
|
|
|
|
|
2020-11-20 09:07:27 -06:00
|
|
|
def test_krbtpolicy_otp(self, reset_to_default_policy):
|
2019-12-19 06:30:13 -06:00
|
|
|
"""Test otp ticket policy"""
|
|
|
|
master = self.master
|
|
|
|
master.run_command(['ipa', 'user-mod', USER1,
|
|
|
|
'--user-auth-type', 'otp'])
|
|
|
|
master.run_command(['ipa', 'config-mod',
|
|
|
|
'--user-auth-type', 'otp'])
|
|
|
|
master.run_command(['ipa', 'krbtpolicy-mod', USER1,
|
|
|
|
'--otp-maxrenew=90', '--otp-maxlife=60'])
|
|
|
|
armor = tasks.create_temp_file(self.master, create_file=False)
|
|
|
|
otpuid, totp = add_otptoken(master, USER1, otptype="totp")
|
|
|
|
otpvalue = totp.generate(int(time.time())).decode("ascii")
|
2020-11-20 09:07:27 -06:00
|
|
|
reset_to_default_policy(master, USER1)
|
2019-12-19 06:30:13 -06:00
|
|
|
try:
|
|
|
|
tasks.kdestroy_all(master)
|
|
|
|
# create armor for FAST
|
|
|
|
master.run_command(['kinit', '-n', '-c', armor])
|
|
|
|
# expect ticket expire in otp-maxlife=60 seconds
|
|
|
|
master.run_command(
|
|
|
|
['kinit', '-T', armor, USER1, '-r', '90'],
|
|
|
|
stdin_text='{0}{1}\n'.format(PASSWORD, otpvalue))
|
|
|
|
master.run_command(['ipa', 'user-find', USER1])
|
|
|
|
time.sleep(30)
|
|
|
|
# when user kerberos ticket expired but still within renew time,
|
|
|
|
# kinit -R should give user new life
|
|
|
|
master.run_command(['kinit', '-R', USER1])
|
|
|
|
master.run_command(['ipa', 'user-find', USER1])
|
|
|
|
time.sleep(60)
|
|
|
|
# when renew time expires, kinit -R should fail
|
|
|
|
result1 = master.run_command(['kinit', '-R', USER1],
|
|
|
|
raiseonerr=False)
|
|
|
|
tasks.assert_error(
|
|
|
|
result1,
|
|
|
|
"kinit: Ticket expired while renewing credentials", 1)
|
|
|
|
master.run_command(['ipa', 'user-find', USER1],
|
|
|
|
ok_returncode=1)
|
|
|
|
finally:
|
|
|
|
del_otptoken(master, otpuid)
|
|
|
|
self.master.run_command(['rm', '-f', armor])
|
|
|
|
master.run_command(['ipa', 'config-mod', '--user-auth-type='])
|
2020-11-10 13:07:47 -06:00
|
|
|
|
|
|
|
def test_krbtpolicy_jitter(self):
|
|
|
|
"""Test jitter lifetime with no auth indicators"""
|
|
|
|
kinit_check_life(self.master, USER1)
|
|
|
|
|
2020-11-20 09:09:16 -06:00
|
|
|
def test_krbtpolicy_jitter_otp(self, reset_to_default_policy):
|
2020-11-10 13:07:47 -06:00
|
|
|
"""Test jitter lifetime with OTP"""
|
2020-11-20 09:09:16 -06:00
|
|
|
reset_to_default_policy(self.master, USER1)
|
2020-11-10 13:07:47 -06:00
|
|
|
self.master.run_command(["ipa", "user-mod", USER1,
|
|
|
|
"--user-auth-type", "otp"])
|
|
|
|
kinit_check_life(self.master, USER1)
|
2020-11-20 09:09:16 -06:00
|
|
|
|
2021-03-01 08:44:31 -06:00
|
|
|
def test_ccache_sweep_expired(self, reset_to_default_policy):
|
|
|
|
"""Test that the ccache sweeper works on expired ccaches
|
2020-11-20 09:09:16 -06:00
|
|
|
|
|
|
|
- Force wipe all existing ccaches
|
2021-03-01 08:44:31 -06:00
|
|
|
- Set the ticket policy to a short value, 20 seconds.
|
2020-11-20 09:09:16 -06:00
|
|
|
- Do a series of kinit, ipa command, kdestroy to generate ccaches
|
2021-03-01 08:44:31 -06:00
|
|
|
- sleep() for expiration
|
2020-11-20 09:09:16 -06:00
|
|
|
- Run the sweeper
|
2021-03-01 08:44:31 -06:00
|
|
|
- Verify that all expired ccaches are gone
|
2020-11-20 09:09:16 -06:00
|
|
|
"""
|
|
|
|
MAXLIFE = 20
|
|
|
|
reset_to_default_policy(self.master) # this will reset at END of test
|
|
|
|
tasks.kinit_admin(self.master)
|
|
|
|
self.master.run_command(
|
|
|
|
['ipa', 'krbtpolicy-mod', '--maxlife', str(MAXLIFE)]
|
|
|
|
)
|
|
|
|
tasks.kdestroy_all(self.master)
|
|
|
|
self.master.run_command(
|
|
|
|
['find', paths.IPA_CCACHES, '-type', 'f', '-delete']
|
|
|
|
)
|
|
|
|
for _i in range(5):
|
|
|
|
tasks.kinit_admin(self.master)
|
|
|
|
self.master.run_command(['ipa', 'user-show', 'admin'])
|
2021-03-01 08:44:31 -06:00
|
|
|
tasks.kdestroy_all(self.master)
|
|
|
|
|
|
|
|
result = self.master.run_command(
|
|
|
|
"ls -1 {0} | wc -l".format(paths.IPA_CCACHES)
|
|
|
|
)
|
|
|
|
assert int(result.stdout_text.strip()) == 5
|
|
|
|
|
|
|
|
# let ccache expire
|
2020-11-20 09:09:16 -06:00
|
|
|
time.sleep(MAXLIFE)
|
2021-03-01 08:44:31 -06:00
|
|
|
ccache_sweep_cmd = ["/usr/libexec/ipa/ipa-ccache-sweeper", "-m", "0"]
|
|
|
|
|
|
|
|
# should be run as ipaapi for GSSProxy
|
2020-11-20 09:09:16 -06:00
|
|
|
self.master.run_command(
|
2021-03-01 08:44:31 -06:00
|
|
|
["runuser", "-u", IPAAPI_USER, "--"] + ccache_sweep_cmd
|
2020-11-20 09:09:16 -06:00
|
|
|
)
|
2021-03-01 08:44:31 -06:00
|
|
|
|
2020-11-20 09:09:16 -06:00
|
|
|
result = self.master.run_command(
|
|
|
|
"ls -1 {0} | wc -l".format(paths.IPA_CCACHES)
|
|
|
|
)
|
|
|
|
assert int(result.stdout_text.strip()) == 0
|
2021-03-01 08:44:31 -06:00
|
|
|
|
|
|
|
def test_ccache_sweep_valid(self):
|
|
|
|
"""Test that the ccache sweeper doesn't remove valid ccaches
|
|
|
|
- Force wipe all existing ccaches
|
|
|
|
- Run the sweeper
|
|
|
|
- Verify that all valid ccaches weren't removed
|
|
|
|
Note: assumed that ccache expiration doesn't happen during test
|
|
|
|
"""
|
|
|
|
tasks.kdestroy_all(self.master)
|
|
|
|
self.master.run_command(
|
|
|
|
["find", paths.IPA_CCACHES, "-type", "f", "-delete"]
|
|
|
|
)
|
|
|
|
|
|
|
|
for _i in range(5):
|
|
|
|
tasks.kinit_admin(self.master)
|
|
|
|
self.master.run_command(["ipa", "user-show", "admin"])
|
|
|
|
tasks.kdestroy_all(self.master)
|
|
|
|
|
|
|
|
result = self.master.run_command(
|
|
|
|
"ls -1 {0} | wc -l".format(paths.IPA_CCACHES)
|
|
|
|
)
|
|
|
|
assert int(result.stdout_text.strip()) == 5
|
|
|
|
|
|
|
|
ccache_sweep_cmd = ["/usr/libexec/ipa/ipa-ccache-sweeper", "-m", "0"]
|
|
|
|
|
|
|
|
# should be run as ipaapi for GSSProxy
|
|
|
|
self.master.run_command(
|
|
|
|
["runuser", "-u", IPAAPI_USER, "--"] + ccache_sweep_cmd
|
|
|
|
)
|
|
|
|
result = self.master.run_command(
|
|
|
|
"ls -1 {0} | wc -l".format(paths.IPA_CCACHES)
|
|
|
|
)
|
|
|
|
assert int(result.stdout_text.strip()) == 5
|