Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
192 changes: 192 additions & 0 deletions src/tests/system/tests/test_ldap_krb5.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@
is set to ``krb5``. They use ``KnownTopology.LDAP_KRB5`` (client + LDAP + KDC,
host keytab provisioned by the topology controller).

Misc krb cases ported from sssd-qe krb_misc are included in this module.

:requirement: SSSD - Kerberos
"""

Expand All @@ -18,6 +20,20 @@
from sssd_test_framework.roles.kdc import KDC
from sssd_test_framework.topology import KnownTopology

# BZ 798655 helper: ``setuid((uid_t)-2)``. Linux ``INVALID_UID`` is ``(uid_t)-1``
# (https://github.com/torvalds/linux/blob/master/include/linux/uidgid.h); ``setuid(-1)``
# fails ``EINVAL``. ``(uid_t)-2`` is not that sentinel, so ``setuid(-2)`` succeed.

NOBODY_C_SOURCE = (
"#include <unistd.h>\n"
"int main(void) {\n"
" (void)setuid((uid_t)-2);\n"
" for (;;) {\n"
" (void)sleep(60);\n"
" }\n"
"}\n"
)


@pytest.mark.importance("high")
@pytest.mark.authentication
Expand Down Expand Up @@ -209,3 +225,179 @@ def test_ldap_krb5__keytab_selects_correct_principal_with_multiple_realms(
assert (
selected_ok in log_content
), f"SSSD should select {correct_pattern}; log missing line after wait: {log_content[:500]!r}"


@pytest.mark.importance("high")
@pytest.mark.authentication
@pytest.mark.ticket(bz=847039)
@pytest.mark.topology(KnownTopology.LDAP_KRB5)
def test_ldap_krb5__auth_succeeds_when_kpasswd_unresolvable(client: Client, provider: GenericProvider, kdc: KDC):
"""
:title: Auth succeeds when krb5_kpasswd is unresolvable

BZ 847039: login works when krb5_kpasswd is unresolvable (kpasswd not needed for auth)

:setup:
1. Add user puser1 to LDAP and KDC
2. Configure SSSD with LDAP+KRB5
3. Set krb5_kpasswd to an unresolvable hostname
4. Restart SSSD and clear cache
:steps:
1. Run id for puser1 so NSS resolution goes through SSSD before SSH login
2. Authenticate puser1 with SSH password
:expectedresults:
1. id succeeds for puser1
2. SSH password authentication succeeds despite unresolvable kpasswd
:customerscenario: True
"""
provider.user("puser1").add(uid=50001, gid=50001, password="12345678")
kdc.principal("puser1").add(password="12345678")

client.sssd.common.krb5_auth(kdc)
client.sssd.domain["krb5_realm"] = kdc.realm
client.sssd.domain["krb5_server"] = kdc.host.hostname
client.sssd.domain["krb5_kpasswd"] = "invalid.cannotresolve.invalid"

client.sssd.restart(clean=True)

assert client.tools.id("puser1"), "id failed for puser1!"

assert client.auth.ssh.password("puser1", "12345678"), "Auth failed when krb5_kpasswd is unresolvable!"


@pytest.mark.importance("high")
@pytest.mark.authentication
@pytest.mark.ticket(bz=798655)
@pytest.mark.topology(KnownTopology.LDAP_KRB5)
def test_ldap_krb5__auth_and_logs_clean_with_setuid_minus_two_helper(
client: Client, provider: GenericProvider, kdc: KDC
):
"""
:title: auth and logs stay clean while setuid((uid_t)-2) helper runs

BZ 798655 / ``strtol`` regression: helper calls ``setuid((uid_t)-2)``. Linux treats
``(uid_t)-1`` as ``INVALID_UID`` (``setuid(-1)`` → ``EINVAL``); ``(uid_t)-2`` is not,
so ``setuid(-2)`` may succeed when permitted — see comment above ``NOBODY_C_SOURCE``.
Built with ``gcc`` on the client (skipped if missing or compile fails).

:setup:
1. Add user puser1 to LDAP and KDC
2. Configure SSSD with LDAP+KRB5
3. Restart SSSD and clear cache
4. Verify auth succeeds for puser1
5. Build and start the helper under fixed paths under ``/tmp``
:steps:
1. Run id for puser1 while the helper process is running (NSS via SSSD before SSH)
2. Authenticate puser1 over SSH while the helper process is still running
3. Check the SSSD domain log for ``strtol failed [Numerical result out of range]``
:expectedresults:
1. id succeeds for puser1
2. SSH password authentication succeeds
3. That ``strtol`` message does not appear in the log
:customerscenario: True
"""
provider.user("puser1").add(uid=50001, gid=50001, password="12345678")
kdc.principal("puser1").add(password="12345678")

client.sssd.common.krb5_auth(kdc)
client.sssd.domain["krb5_realm"] = kdc.realm
client.sssd.domain["krb5_server"] = kdc.host.hostname
client.sssd.domain["krb5_kpasswd"] = kdc.host.hostname

Comment thread
spoore1 marked this conversation as resolved.
client.sssd.restart(clean=True)

assert client.auth.ssh.password("puser1", "12345678"), "Auth failed before starting setuid helper!"

result = client.host.conn.run("which gcc", raise_on_error=False)
if result.rc != 0:
pytest.skip("gcc not available")

nobody_src = "/tmp/sssd_test_bz798655_nobody.c"
nobody_bin = "/tmp/sssd_test_bz798655_nobody"
client.fs.write(nobody_src, NOBODY_C_SOURCE)
result = client.host.conn.run(
f"gcc -o {nobody_bin} {nobody_src}",
raise_on_error=False,
)
if result.rc != 0:
pytest.skip(f"Failed to compile nobody.c: {result.stderr}")

helper_pid = ""
proc = client.host.conn.run(
f"nohup {nobody_bin} </dev/null >/dev/null 2>&1 & echo $!",
raise_on_error=False,
)
if proc.stdout:
helper_pid = proc.stdout.strip()

try:
assert client.tools.id("puser1"), "id failed while setuid helper is running!"
assert client.auth.ssh.password("puser1", "12345678"), "Auth failed while setuid((uid_t)-2) helper is running!"
Comment thread
aplopez marked this conversation as resolved.

domain = client.sssd.default_domain
log_content = client.fs.read(f"/var/log/sssd/sssd_{domain}.log")
assert (
"strtol failed [Numerical result out of range]" not in log_content
), "strtol error found in SSSD log while helper was running!"
finally:
if helper_pid.isdigit():
client.host.conn.run(f"kill {helper_pid}", raise_on_error=False)
client.host.conn.run(
f"rm -f {nobody_bin} {nobody_src}",
raise_on_error=False,
)


@pytest.mark.importance("high")
@pytest.mark.authentication
@pytest.mark.topology(KnownTopology.LDAP_KRB5)
def test_ldap_krb5__password_change_via_ssh(client: Client, provider: GenericProvider, kdc: KDC):
"""
:title: Password change via SSH triggers krb5_child initial auth

GH 677: SSH passwd with chpass_provider=krb5 logs initial auth in krb5_child.log

:setup:
1. Add user puser1 to LDAP and KDC
2. Configure SSSD with LDAP+KRB5, chpass_provider=krb5
3. Restart SSSD and clear cache
:steps:
1. Run id for puser1 so NSS resolution goes through SSSD before SSH login
2. Change puser1 password via SSH passwd
3. Check ``krb5_child.log`` for the initial-auth line for password change
4. Authenticate over SSH using the new password
:expectedresults:
1. id and initial SSH login succeed
2. Password change succeeds
3. krb5_child.log contains 'Initial authentication for change password'
4. SSH login with the new password succeeds
:customerscenario: True
"""
provider.user("puser1").add(uid=50001, gid=50001, password="12345678")
kdc.principal("puser1").add(password="12345678")

client.sssd.common.krb5_auth(kdc)
client.sssd.domain["krb5_realm"] = kdc.realm
client.sssd.domain["krb5_server"] = kdc.host.hostname
client.sssd.domain["krb5_kpasswd"] = kdc.host.hostname
client.sssd.domain["chpass_provider"] = "krb5"

Comment thread
spoore1 marked this conversation as resolved.
client.sssd.restart(clean=True)

client.tools.id("puser1")

assert client.auth.ssh.password("puser1", "12345678"), "Auth failed before password change!"

new_password = "NewSecret123!"

assert client.auth.ssh.passwd.password(
"puser1",
"12345678",
new_password,
), "Password change via SSH failed!"
log_content = client.fs.read("/var/log/sssd/krb5_child.log")
assert (
"Initial authentication for change password" in log_content
), f"krb5_child initial auth message not found: {log_content[:500]}!"

assert client.auth.ssh.password("puser1", new_password), "Auth with new password failed after password change!"