mirror of
https://salsa.debian.org/freeipa-team/freeipa.git
synced 2024-12-23 15:40:01 -06:00
517ae594e1
SPAKE is based on the crypto primitives which are not FIPS compliant yet. This means that in FIPS mode use of 'hardened' authentication indicator is not possible. Skip corresponding tests in FIPS mode. Related: https://pagure.io/freeipa/issue/9119 Signed-off-by: Alexander Bokovoy <abokovoy@redhat.com> Reviewed-By: Francisco Trivino <ftrivino@redhat.com>
317 lines
12 KiB
Python
317 lines
12 KiB
Python
#
|
|
# Copyright (C) 2019,2020 FreeIPA Contributors see COPYING for license
|
|
#
|
|
|
|
"""
|
|
Module provides tests for Kerberos ticket policy options
|
|
"""
|
|
|
|
from __future__ import absolute_import
|
|
|
|
import pytest
|
|
import time
|
|
from datetime import datetime
|
|
|
|
from ipalib.constants import IPAAPI_USER
|
|
from ipaplatform.paths import paths
|
|
|
|
from ipatests.test_integration.base import IntegrationTest
|
|
from ipatests.test_integration.test_otp import add_otptoken, del_otptoken
|
|
from ipatests.pytest_ipa.integration import tasks
|
|
|
|
PASSWORD = "Secret123"
|
|
USER1 = "testuser1"
|
|
USER2 = "testuser2"
|
|
MAXLIFE = 86400
|
|
|
|
|
|
def maxlife_within_policy(input, maxlife, slush=3600):
|
|
"""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/...
|
|
|
|
slush defaults to 1 * 60 * 60 matching the jitter window.
|
|
"""
|
|
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
|
|
|
|
|
|
@pytest.fixture
|
|
def reset_to_default_policy():
|
|
"""Reset default user authentication and user authentication type"""
|
|
|
|
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']
|
|
tasks.kinit_admin(host)
|
|
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])
|
|
|
|
|
|
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")
|
|
assert maxlife_within_policy(result.stdout_text, MAXLIFE) is True
|
|
|
|
|
|
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)
|
|
|
|
@pytest.fixture(autouse=True, scope="function")
|
|
def with_admin(self):
|
|
tasks.kinit_admin(self.master)
|
|
yield
|
|
tasks.kdestroy_all(self.master)
|
|
|
|
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
|
|
|
|
def test_krbtpolicy_password_and_hardended(self):
|
|
"""Test a pwd and hardened kerberos ticket policy with 10min tickets"""
|
|
if self.master.is_fips_mode:
|
|
pytest.skip("SPAKE pre-auth is not compatible with FIPS mode")
|
|
|
|
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')
|
|
assert maxlife_within_policy(result.stdout_text, 600,
|
|
slush=600) 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
|
|
|
|
def test_krbtpolicy_hardended(self):
|
|
"""Test a hardened kerberos ticket policy with 30min tickets"""
|
|
if self.master.is_fips_mode:
|
|
pytest.skip("SPAKE pre-auth is not compatible with FIPS mode")
|
|
|
|
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
|
|
|
|
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')
|
|
assert maxlife_within_policy(result.stdout_text, 1200,
|
|
slush=1200) is True
|
|
|
|
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
|
|
|
|
def test_krbtpolicy_otp(self, reset_to_default_policy):
|
|
"""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")
|
|
reset_to_default_policy(master, USER1)
|
|
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='])
|
|
|
|
def test_krbtpolicy_jitter(self):
|
|
"""Test jitter lifetime with no auth indicators"""
|
|
kinit_check_life(self.master, USER1)
|
|
|
|
def test_krbtpolicy_jitter_otp(self, reset_to_default_policy):
|
|
"""Test jitter lifetime with OTP"""
|
|
reset_to_default_policy(self.master, USER1)
|
|
self.master.run_command(["ipa", "user-mod", USER1,
|
|
"--user-auth-type", "otp"])
|
|
kinit_check_life(self.master, USER1)
|
|
|
|
def test_ccache_sweep_expired(self, reset_to_default_policy):
|
|
"""Test that the ccache sweeper works on expired ccaches
|
|
|
|
- Force wipe all existing ccaches
|
|
- Set the ticket policy to a short value, 20 seconds.
|
|
- Do a series of kinit, ipa command, kdestroy to generate ccaches
|
|
- sleep() for expiration
|
|
- Run the sweeper
|
|
- Verify that all expired ccaches are gone
|
|
"""
|
|
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'])
|
|
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
|
|
time.sleep(MAXLIFE)
|
|
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()) == 0
|
|
|
|
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
|