mirror of
https://github.com/samba-team/samba.git
synced 2024-12-22 13:34:15 +03:00
python/samba/krb5: Add test for password rotation on UF_SMARCARD_REQUIRED accounts
This demonstrates behaviour against a server presumed to be in FL 2016 what the impact of the msDS-ExpirePasswordsOnSmartCardOnlyAccounts attribute is. Signed-off-by: Andrew Bartlett <abartlet@samba.org> Reviewed-by: Jo Sutton <josutton@catalyst.net.nz>
This commit is contained in:
parent
a85f4c661b
commit
48bff4b95f
@ -67,6 +67,23 @@ SidType = RawKerberosTest.SidType
|
|||||||
global_asn1_print = False
|
global_asn1_print = False
|
||||||
global_hexdump = False
|
global_hexdump = False
|
||||||
|
|
||||||
|
def set_ExpirePasswordsOnSmartCardOnlyAccounts(samdb, val):
|
||||||
|
msg = ldb.Message()
|
||||||
|
msg.dn = samdb.get_default_basedn()
|
||||||
|
|
||||||
|
# Allow val to be True, False, strings or message elements
|
||||||
|
if val is True:
|
||||||
|
val = "TRUE"
|
||||||
|
elif val is False:
|
||||||
|
val = "FALSE"
|
||||||
|
elif val is None:
|
||||||
|
val = []
|
||||||
|
|
||||||
|
msg["msDS-ExpirePasswordsOnSmartCardOnlyAccounts"] \
|
||||||
|
= ldb.MessageElement(val,
|
||||||
|
ldb.FLAG_MOD_REPLACE,
|
||||||
|
"msDS-ExpirePasswordsOnSmartCardOnlyAccounts")
|
||||||
|
samdb.modify(msg)
|
||||||
|
|
||||||
class PkInitTests(KDCBaseTest):
|
class PkInitTests(KDCBaseTest):
|
||||||
@classmethod
|
@classmethod
|
||||||
@ -589,6 +606,59 @@ class PkInitTests(KDCBaseTest):
|
|||||||
logon_type=netlogon.NetlogonNetworkInformation,
|
logon_type=netlogon.NetlogonNetworkInformation,
|
||||||
expect_error=ntstatus.NT_STATUS_WRONG_PASSWORD)
|
expect_error=ntstatus.NT_STATUS_WRONG_PASSWORD)
|
||||||
|
|
||||||
|
def _test_samlogon_smartcard_required_expired(self, smartcard_pw_expire):
|
||||||
|
"""Test SamLogon with an account set to smartcard login required. No actual PK-INIT in this test."""
|
||||||
|
samdb = self.get_samdb()
|
||||||
|
msgs = samdb.search(base=samdb.get_default_basedn(),
|
||||||
|
scope=ldb.SCOPE_BASE,
|
||||||
|
attrs=["msDS-ExpirePasswordsOnSmartCardOnlyAccounts"])
|
||||||
|
msg = msgs[0]
|
||||||
|
|
||||||
|
try:
|
||||||
|
old_ExpirePasswordsOnSmartCardOnlyAccounts = msg["msDS-ExpirePasswordsOnSmartCardOnlyAccounts"]
|
||||||
|
except KeyError:
|
||||||
|
old_ExpirePasswordsOnSmartCardOnlyAccounts = None
|
||||||
|
|
||||||
|
self.addCleanup(set_ExpirePasswordsOnSmartCardOnlyAccounts,
|
||||||
|
samdb, old_ExpirePasswordsOnSmartCardOnlyAccounts)
|
||||||
|
|
||||||
|
# Enable auto-rotation for this test
|
||||||
|
set_ExpirePasswordsOnSmartCardOnlyAccounts(samdb, smartcard_pw_expire)
|
||||||
|
|
||||||
|
client_creds = self._get_creds(smartcard_required=True)
|
||||||
|
|
||||||
|
client_creds.set_kerberos_state(credentials.AUTO_USE_KERBEROS)
|
||||||
|
|
||||||
|
msg = ldb.Message()
|
||||||
|
msg.dn = client_creds.get_dn()
|
||||||
|
|
||||||
|
# Ideally we would set this to a time just long enough for the
|
||||||
|
# password to expire, but we are unable to do that.
|
||||||
|
#
|
||||||
|
# 0 means "must change on first login"
|
||||||
|
msg["pwdLastSet"] = \
|
||||||
|
ldb.MessageElement(str(0),
|
||||||
|
ldb.FLAG_MOD_REPLACE,
|
||||||
|
"pwdLastSet")
|
||||||
|
samdb.modify(msg)
|
||||||
|
|
||||||
|
# This shows that the magic rotation behaviour is not
|
||||||
|
# triggered in SamLogon
|
||||||
|
self._test_samlogon(
|
||||||
|
creds=client_creds,
|
||||||
|
logon_type=netlogon.NetlogonInteractiveInformation,
|
||||||
|
expect_error=ntstatus.NT_STATUS_SMARTCARD_LOGON_REQUIRED)
|
||||||
|
|
||||||
|
self._test_samlogon(creds=client_creds,
|
||||||
|
logon_type=netlogon.NetlogonNetworkInformation,
|
||||||
|
expect_error=ntstatus.NT_STATUS_WRONG_PASSWORD)
|
||||||
|
|
||||||
|
def test_samlogon_smartcard_required_expired(self):
|
||||||
|
self._test_samlogon_smartcard_required_expired(True)
|
||||||
|
|
||||||
|
def test_samlogon_smartcard_required_expired_disabled(self):
|
||||||
|
self._test_samlogon_smartcard_required_expired(False)
|
||||||
|
|
||||||
def test_pkinit_ntlm_from_pac(self):
|
def test_pkinit_ntlm_from_pac(self):
|
||||||
"""Test public-key PK-INIT to get an NT hash and confirm NTLM
|
"""Test public-key PK-INIT to get an NT hash and confirm NTLM
|
||||||
authentication is possible with it."""
|
authentication is possible with it."""
|
||||||
@ -630,8 +700,11 @@ class PkInitTests(KDCBaseTest):
|
|||||||
|
|
||||||
freshness_token = self.create_freshness_token()
|
freshness_token = self.create_freshness_token()
|
||||||
|
|
||||||
|
# The hash will not match as UF_SMARTCARD_REQUIRED at creation
|
||||||
|
# time make the password random
|
||||||
kdc_exchange_dict = self._pkinit_req(client_creds, krbtgt_creds,
|
kdc_exchange_dict = self._pkinit_req(client_creds, krbtgt_creds,
|
||||||
freshness_token=freshness_token)
|
freshness_token=freshness_token,
|
||||||
|
expect_matching_nt_hash_in_pac=False)
|
||||||
nt_hash_from_pac = kdc_exchange_dict['nt_hash_from_pac']
|
nt_hash_from_pac = kdc_exchange_dict['nt_hash_from_pac']
|
||||||
|
|
||||||
client_creds.set_nt_hash(nt_hash_from_pac,
|
client_creds.set_nt_hash(nt_hash_from_pac,
|
||||||
@ -659,22 +732,23 @@ class PkInitTests(KDCBaseTest):
|
|||||||
def test_pkinit_ntlm_from_pac_must_change_now(self):
|
def test_pkinit_ntlm_from_pac_must_change_now(self):
|
||||||
"""Test public-key PK-INIT to get an NT hash and confirm NTLM
|
"""Test public-key PK-INIT to get an NT hash and confirm NTLM
|
||||||
authentication is possible with it."""
|
authentication is possible with it."""
|
||||||
|
samdb = self.get_samdb()
|
||||||
|
|
||||||
client_creds = self._get_creds()
|
client_creds = self._get_creds()
|
||||||
client_creds.set_kerberos_state(credentials.AUTO_USE_KERBEROS)
|
client_creds.set_kerberos_state(credentials.AUTO_USE_KERBEROS)
|
||||||
|
|
||||||
msg = ldb.Message()
|
mod_msg = ldb.Message()
|
||||||
msg.dn = client_creds.get_dn()
|
mod_msg.dn = client_creds.get_dn()
|
||||||
|
|
||||||
# Ideally we would set this to a time just long enough for the
|
# Ideally we would set this to a time just long enough for the
|
||||||
# password to expire, but we are unable to do that.
|
# password to expire, but this is good enough
|
||||||
#
|
#
|
||||||
# 0 means "must change on first login"
|
# 0 means "must change on first login"
|
||||||
msg["pwdLastSet"] = \
|
mod_msg["pwdLastSet"] = \
|
||||||
ldb.MessageElement(str(0),
|
ldb.MessageElement(str(0),
|
||||||
ldb.FLAG_MOD_REPLACE,
|
ldb.FLAG_MOD_REPLACE,
|
||||||
"pwdLastSet")
|
"pwdLastSet")
|
||||||
samdb = self.get_samdb()
|
samdb.modify(mod_msg)
|
||||||
samdb.modify(msg)
|
|
||||||
|
|
||||||
krbtgt_creds = self.get_krbtgt_creds()
|
krbtgt_creds = self.get_krbtgt_creds()
|
||||||
|
|
||||||
@ -707,9 +781,29 @@ class PkInitTests(KDCBaseTest):
|
|||||||
logon_type=netlogon.NetlogonNetworkInformation,
|
logon_type=netlogon.NetlogonNetworkInformation,
|
||||||
expect_error=ntstatus.NT_STATUS_PASSWORD_MUST_CHANGE)
|
expect_error=ntstatus.NT_STATUS_PASSWORD_MUST_CHANGE)
|
||||||
|
|
||||||
def test_pkinit_ntlm_from_pac_smartcard_required_must_change_now(self):
|
def _test_pkinit_ntlm_from_pac_smartcard_required_must_change_now(self, smartcard_pw_expire):
|
||||||
"""Test public-key PK-INIT to get the user's NT hash for an account
|
"""Test public-key PK-INIT to get the user's NT hash for an account
|
||||||
that is restricted by UF_SMARTCARD_REQUIRED."""
|
that is restricted by UF_SMARTCARD_REQUIRED.
|
||||||
|
|
||||||
|
We test with both modes for the 2016FL msDS-ExpirePasswordsOnSmartCardOnlyAccounts behaviour"""
|
||||||
|
|
||||||
|
samdb = self.get_samdb()
|
||||||
|
msgs = samdb.search(base=samdb.get_default_basedn(),
|
||||||
|
scope=ldb.SCOPE_BASE,
|
||||||
|
attrs=["msDS-ExpirePasswordsOnSmartCardOnlyAccounts"])
|
||||||
|
msg = msgs[0]
|
||||||
|
|
||||||
|
try:
|
||||||
|
old_ExpirePasswordsOnSmartCardOnlyAccounts = msg["msDS-ExpirePasswordsOnSmartCardOnlyAccounts"]
|
||||||
|
except KeyError:
|
||||||
|
old_ExpirePasswordsOnSmartCardOnlyAccounts = None
|
||||||
|
|
||||||
|
self.addCleanup(set_ExpirePasswordsOnSmartCardOnlyAccounts,
|
||||||
|
samdb, old_ExpirePasswordsOnSmartCardOnlyAccounts)
|
||||||
|
|
||||||
|
# Enable auto-rotation for this test
|
||||||
|
set_ExpirePasswordsOnSmartCardOnlyAccounts(samdb, smartcard_pw_expire)
|
||||||
|
|
||||||
client_creds = self._get_creds(smartcard_required=True)
|
client_creds = self._get_creds(smartcard_required=True)
|
||||||
client_creds.set_kerberos_state(credentials.AUTO_USE_KERBEROS)
|
client_creds.set_kerberos_state(credentials.AUTO_USE_KERBEROS)
|
||||||
|
|
||||||
@ -717,42 +811,129 @@ class PkInitTests(KDCBaseTest):
|
|||||||
|
|
||||||
freshness_token = self.create_freshness_token()
|
freshness_token = self.create_freshness_token()
|
||||||
|
|
||||||
samdb = self.get_samdb()
|
# The hash will not match as UF_SMARTCARD_REQUIRED at creation
|
||||||
|
# time make the password random
|
||||||
kdc_exchange_dict = self._pkinit_req(client_creds, krbtgt_creds,
|
kdc_exchange_dict = self._pkinit_req(client_creds, krbtgt_creds,
|
||||||
freshness_token=freshness_token)
|
freshness_token=freshness_token,
|
||||||
|
expect_matching_nt_hash_in_pac=False)
|
||||||
nt_hash_from_pac = kdc_exchange_dict['nt_hash_from_pac']
|
nt_hash_from_pac = kdc_exchange_dict['nt_hash_from_pac']
|
||||||
|
|
||||||
msg = ldb.Message()
|
client_creds.set_nt_hash(nt_hash_from_pac,
|
||||||
msg.dn = client_creds.get_dn()
|
credentials.SPECIFIED)
|
||||||
|
|
||||||
|
mod_msg = ldb.Message()
|
||||||
|
mod_msg.dn = client_creds.get_dn()
|
||||||
|
|
||||||
# Ideally we would set this to a time just long enough for the
|
# Ideally we would set this to a time just long enough for the
|
||||||
# password to expire, but we are unable to do that.
|
# password to expire, but this is good enough
|
||||||
#
|
#
|
||||||
# 0 means "must change on first login"
|
# 0 means "must change on first login"
|
||||||
msg["pwdLastSet"] = \
|
mod_msg["pwdLastSet"] = \
|
||||||
ldb.MessageElement(str(0),
|
ldb.MessageElement(str(0),
|
||||||
ldb.FLAG_MOD_REPLACE,
|
ldb.FLAG_MOD_REPLACE,
|
||||||
"pwdLastSet")
|
"pwdLastSet")
|
||||||
samdb.modify(msg)
|
samdb.modify(mod_msg)
|
||||||
|
|
||||||
kdc_exchange_dict = self._pkinit_req(client_creds, krbtgt_creds,
|
# pwdLastSet has magic set properties, but this still sticks
|
||||||
freshness_token=freshness_token)
|
# to zero. We assert this so that we can be sure of the
|
||||||
nt_hash_from_pac2 = kdc_exchange_dict['nt_hash_from_pac']
|
# remaining checks
|
||||||
|
|
||||||
self.assertNotEqual(nt_hash_from_pac.hash, nt_hash_from_pac2.hash)
|
|
||||||
|
|
||||||
# The password should have changed as it was expired and the
|
|
||||||
# DC is set up to change expired passwords to keep the
|
|
||||||
# smart-card logins working and the keys fresh
|
|
||||||
res = samdb.search(base=client_creds.get_dn(),
|
res = samdb.search(base=client_creds.get_dn(),
|
||||||
scope=ldb.SCOPE_BASE,
|
scope=ldb.SCOPE_BASE,
|
||||||
attrs=["pwdLastSet"])
|
attrs=["pwdLastSet"])
|
||||||
self.assertNotEqual(res[0]["pwdLastSet"], 0)
|
self.assertEqual(int(res[0]["pwdLastSet"][0]), 0)
|
||||||
|
|
||||||
|
# Interactive SamLogon will fail, but with
|
||||||
|
# SMARTCARD_LOGON_REQUIRED not password expired
|
||||||
|
self._test_samlogon(
|
||||||
|
creds=client_creds,
|
||||||
|
logon_type=netlogon.NetlogonInteractiveInformation,
|
||||||
|
expect_error=ntstatus.NT_STATUS_SMARTCARD_LOGON_REQUIRED)
|
||||||
|
|
||||||
|
# The password should not have changed yet as we have not
|
||||||
|
# touched the KDC so far
|
||||||
|
res = samdb.search(base=client_creds.get_dn(),
|
||||||
|
scope=ldb.SCOPE_BASE,
|
||||||
|
attrs=["pwdLastSet"])
|
||||||
|
self.assertEqual(int(res[0]["pwdLastSet"][0]), 0)
|
||||||
|
|
||||||
|
if smartcard_pw_expire:
|
||||||
|
# msDS-ExpirePasswordsOnSmartCardOnlyAccounts=TRUE
|
||||||
|
#
|
||||||
|
# Try NTLM (Network SamLogon), this show that password expiry
|
||||||
|
# is enforced for UF_SMARTCARD_REQUIRED
|
||||||
|
self._test_samlogon(creds=client_creds,
|
||||||
|
logon_type=netlogon.NetlogonNetworkInformation,
|
||||||
|
expect_error=ntstatus.NT_STATUS_PASSWORD_MUST_CHANGE)
|
||||||
|
else:
|
||||||
|
# msDS-ExpirePasswordsOnSmartCardOnlyAccounts=FALSE
|
||||||
|
#
|
||||||
|
# Try NTLM (Network SamLogon), this show that password expiry
|
||||||
|
# is not enforced for UF_SMARTCARD_REQUIRED
|
||||||
|
self._test_samlogon(creds=client_creds,
|
||||||
|
logon_type=netlogon.NetlogonNetworkInformation)
|
||||||
|
|
||||||
|
# The password should not have changed yet as we have not
|
||||||
|
# touched the KDC so far
|
||||||
|
res = samdb.search(base=client_creds.get_dn(),
|
||||||
|
scope=ldb.SCOPE_BASE,
|
||||||
|
attrs=["pwdLastSet"])
|
||||||
|
self.assertEqual(int(res[0]["pwdLastSet"][0]), 0)
|
||||||
|
|
||||||
|
# password-based AS-REQ will fail, but with
|
||||||
|
# SMARTCARD_LOGON_REQUIRED not password expired.
|
||||||
|
#
|
||||||
|
# But it will rotate the PW.
|
||||||
|
self._as_req(client_creds,
|
||||||
|
krbtgt_creds,
|
||||||
|
expect_error=KDC_ERR_POLICY,
|
||||||
|
expect_edata=True,
|
||||||
|
expect_status=True,
|
||||||
|
expected_status=ntstatus.NT_STATUS_SMARTCARD_LOGON_REQUIRED,
|
||||||
|
send_enc_ts=True)
|
||||||
|
|
||||||
|
res = samdb.search(base=client_creds.get_dn(),
|
||||||
|
scope=ldb.SCOPE_BASE,
|
||||||
|
attrs=["pwdLastSet"])
|
||||||
|
if smartcard_pw_expire:
|
||||||
|
# The password should have changed as it was expired and the
|
||||||
|
# KDC is set up to change expired passwords to keep the
|
||||||
|
# smart-card logins working and the keys fresh
|
||||||
|
self.assertGreater(int(res[0]["pwdLastSet"][0]), 0)
|
||||||
|
else:
|
||||||
|
self.assertEqual(int(res[0]["pwdLastSet"][0]), 0)
|
||||||
|
|
||||||
|
kdc_exchange_dict = self._pkinit_req(client_creds, krbtgt_creds,
|
||||||
|
freshness_token=freshness_token,
|
||||||
|
expect_matching_nt_hash_in_pac=not smartcard_pw_expire)
|
||||||
|
nt_hash_from_pac2 = kdc_exchange_dict['nt_hash_from_pac']
|
||||||
|
|
||||||
|
if smartcard_pw_expire:
|
||||||
|
self.assertNotEqual(nt_hash_from_pac.hash, nt_hash_from_pac2.hash)
|
||||||
|
else:
|
||||||
|
self.assertEqual(nt_hash_from_pac.hash, nt_hash_from_pac2.hash)
|
||||||
|
|
||||||
|
# The password will not have further changed, the not-PKINIT
|
||||||
|
# request will have triggered the rotation.
|
||||||
|
res2 = samdb.search(base=client_creds.get_dn(),
|
||||||
|
scope=ldb.SCOPE_BASE,
|
||||||
|
attrs=["pwdLastSet"])
|
||||||
|
self.assertEqual(res[0]["pwdLastSet"], res2[0]["pwdLastSet"])
|
||||||
|
|
||||||
client_creds.set_nt_hash(nt_hash_from_pac2,
|
client_creds.set_nt_hash(nt_hash_from_pac2,
|
||||||
credentials.SPECIFIED)
|
credentials.SPECIFIED)
|
||||||
|
|
||||||
|
# Password has not changed again, so we will continue to get the same NT hash
|
||||||
|
kdc_exchange_dict = self._pkinit_req(client_creds, krbtgt_creds,
|
||||||
|
freshness_token=freshness_token,
|
||||||
|
expect_matching_nt_hash_in_pac=True)
|
||||||
|
|
||||||
|
# The password will not have further changed, the earlier
|
||||||
|
# not-PKINIT request will have triggered the rotation.
|
||||||
|
res3 = samdb.search(base=client_creds.get_dn(),
|
||||||
|
scope=ldb.SCOPE_BASE,
|
||||||
|
attrs=["pwdLastSet"])
|
||||||
|
self.assertEqual(res[0]["pwdLastSet"], res3[0]["pwdLastSet"])
|
||||||
|
|
||||||
# password-based AS-REQ will fail
|
# password-based AS-REQ will fail
|
||||||
self._as_req(client_creds,
|
self._as_req(client_creds,
|
||||||
krbtgt_creds,
|
krbtgt_creds,
|
||||||
@ -773,6 +954,114 @@ class PkInitTests(KDCBaseTest):
|
|||||||
self._test_samlogon(creds=client_creds,
|
self._test_samlogon(creds=client_creds,
|
||||||
logon_type=netlogon.NetlogonNetworkInformation)
|
logon_type=netlogon.NetlogonNetworkInformation)
|
||||||
|
|
||||||
|
def test_pkinit_ntlm_from_pac_smartcard_required_must_change_now(self):
|
||||||
|
"""Test public-key PK-INIT to get the user's NT hash for an account
|
||||||
|
that is restricted by UF_SMARTCARD_REQUIRED but is expired.
|
||||||
|
|
||||||
|
Verify that NT hash with SamLogon requests
|
||||||
|
|
||||||
|
This variant sets the enabling attribute for auto-rotation."""
|
||||||
|
self._test_pkinit_ntlm_from_pac_smartcard_required_must_change_now(True)
|
||||||
|
|
||||||
|
def test_pkinit_ntlm_from_pac_smartcard_required_must_change_now_rotate_disabled(self):
|
||||||
|
"""Test public-key PK-INIT to get the user's NT hash for an account
|
||||||
|
that is restricted by UF_SMARTCARD_REQUIRED but is expired.
|
||||||
|
|
||||||
|
Verify that NT hash with SamLogon requests
|
||||||
|
|
||||||
|
This variant DISABLES the enabling attribute for auto-rotation."""
|
||||||
|
self._test_pkinit_ntlm_from_pac_smartcard_required_must_change_now(False)
|
||||||
|
|
||||||
|
def _test_pkinit_smartcard_required_must_change_now(self, smartcard_pw_expire):
|
||||||
|
"""Test public-key PK-INIT to get the user's NT hash for an account
|
||||||
|
that is restricted by UF_SMARTCARD_REQUIRED.
|
||||||
|
|
||||||
|
We test with both modes for the 2016FL msDS-ExpirePasswordsOnSmartCardOnlyAccounts behaviour"""
|
||||||
|
|
||||||
|
samdb = self.get_samdb()
|
||||||
|
msgs = samdb.search(base=samdb.get_default_basedn(),
|
||||||
|
scope=ldb.SCOPE_BASE,
|
||||||
|
attrs=["msDS-ExpirePasswordsOnSmartCardOnlyAccounts"])
|
||||||
|
msg = msgs[0]
|
||||||
|
|
||||||
|
try:
|
||||||
|
old_ExpirePasswordsOnSmartCardOnlyAccounts = msg["msDS-ExpirePasswordsOnSmartCardOnlyAccounts"]
|
||||||
|
except KeyError:
|
||||||
|
old_ExpirePasswordsOnSmartCardOnlyAccounts = None
|
||||||
|
|
||||||
|
self.addCleanup(set_ExpirePasswordsOnSmartCardOnlyAccounts,
|
||||||
|
samdb, old_ExpirePasswordsOnSmartCardOnlyAccounts)
|
||||||
|
|
||||||
|
# Enable auto-rotation for this test
|
||||||
|
set_ExpirePasswordsOnSmartCardOnlyAccounts(samdb, smartcard_pw_expire)
|
||||||
|
|
||||||
|
client_creds = self._get_creds(smartcard_required=True)
|
||||||
|
client_creds.set_kerberos_state(credentials.AUTO_USE_KERBEROS)
|
||||||
|
|
||||||
|
krbtgt_creds = self.get_krbtgt_creds()
|
||||||
|
|
||||||
|
freshness_token = self.create_freshness_token()
|
||||||
|
|
||||||
|
kdc_exchange_dict = self._pkinit_req(client_creds, krbtgt_creds,
|
||||||
|
freshness_token=freshness_token,
|
||||||
|
expect_matching_nt_hash_in_pac=False)
|
||||||
|
nt_hash_from_pac = kdc_exchange_dict['nt_hash_from_pac']
|
||||||
|
|
||||||
|
mod_msg = ldb.Message()
|
||||||
|
mod_msg.dn = client_creds.get_dn()
|
||||||
|
|
||||||
|
# Ideally we would set this to a time just long enough for the
|
||||||
|
# password to expire, but this is good enough
|
||||||
|
#
|
||||||
|
# 0 means "must change on first login"
|
||||||
|
mod_msg["pwdLastSet"] = \
|
||||||
|
ldb.MessageElement(str(0),
|
||||||
|
ldb.FLAG_MOD_REPLACE,
|
||||||
|
"pwdLastSet")
|
||||||
|
samdb.modify(mod_msg)
|
||||||
|
|
||||||
|
# pwdLastSet has magic set properties, but this still sticks
|
||||||
|
# to zero. We assert this so that we can be sure of the
|
||||||
|
# remaining checks
|
||||||
|
res = samdb.search(base=client_creds.get_dn(),
|
||||||
|
scope=ldb.SCOPE_BASE,
|
||||||
|
attrs=["pwdLastSet"])
|
||||||
|
self.assertEqual(int(res[0]["pwdLastSet"][0]), 0)
|
||||||
|
|
||||||
|
kdc_exchange_dict = self._pkinit_req(client_creds, krbtgt_creds,
|
||||||
|
freshness_token=freshness_token,
|
||||||
|
expect_matching_nt_hash_in_pac=False)
|
||||||
|
nt_hash_from_pac2 = kdc_exchange_dict['nt_hash_from_pac']
|
||||||
|
|
||||||
|
if smartcard_pw_expire:
|
||||||
|
self.assertNotEqual(nt_hash_from_pac.hash, nt_hash_from_pac2.hash)
|
||||||
|
else:
|
||||||
|
self.assertEqual(nt_hash_from_pac.hash, nt_hash_from_pac2.hash)
|
||||||
|
|
||||||
|
# If expiry/rotation enabled, the password will have changed, the PKINIT
|
||||||
|
# request will have triggered the rotation.
|
||||||
|
res2 = samdb.search(base=client_creds.get_dn(),
|
||||||
|
scope=ldb.SCOPE_BASE,
|
||||||
|
attrs=["pwdLastSet"])
|
||||||
|
if smartcard_pw_expire:
|
||||||
|
self.assertGreater(int(res2[0]["pwdLastSet"][0]), 0)
|
||||||
|
else:
|
||||||
|
self.assertEqual(int(res2[0]["pwdLastSet"][0]), 0)
|
||||||
|
|
||||||
|
def test_pkinit_smartcard_required_must_change_now(self):
|
||||||
|
"""Test public-key PK-INIT to get the user's NT hash for an account
|
||||||
|
that is restricted by UF_SMARTCARD_REQUIRED but is expired.
|
||||||
|
|
||||||
|
This variant sets the enabling attribute for auto-rotation."""
|
||||||
|
self._test_pkinit_smartcard_required_must_change_now(True)
|
||||||
|
|
||||||
|
def test_pkinit_smartcard_required_must_change_now_rotate_disabled(self):
|
||||||
|
"""Test public-key PK-INIT to get the user's NT hash for an account
|
||||||
|
that is restricted by UF_SMARTCARD_REQUIRED but is expired.
|
||||||
|
|
||||||
|
This variant DISABLES the enabling attribute for auto-rotation."""
|
||||||
|
self._test_pkinit_smartcard_required_must_change_now(False)
|
||||||
|
|
||||||
def test_pkinit_kpasswd_change(self):
|
def test_pkinit_kpasswd_change(self):
|
||||||
"""Test public-key PK-INIT to get an initial ticket to change the user's own password."""
|
"""Test public-key PK-INIT to get an initial ticket to change the user's own password."""
|
||||||
client_creds = self._get_creds()
|
client_creds = self._get_creds()
|
||||||
@ -1199,6 +1488,7 @@ class PkInitTests(KDCBaseTest):
|
|||||||
certificate_signature=None,
|
certificate_signature=None,
|
||||||
freshness_token=None,
|
freshness_token=None,
|
||||||
win2k_variant=False,
|
win2k_variant=False,
|
||||||
|
expect_matching_nt_hash_in_pac=True,
|
||||||
target_sname=None
|
target_sname=None
|
||||||
):
|
):
|
||||||
self.assertIsNot(using_pkinit, PkInit.NOT_USED)
|
self.assertIsNot(using_pkinit, PkInit.NOT_USED)
|
||||||
@ -1460,7 +1750,8 @@ class PkInitTests(KDCBaseTest):
|
|||||||
kdc_options=str(kdc_options),
|
kdc_options=str(kdc_options),
|
||||||
using_pkinit=using_pkinit,
|
using_pkinit=using_pkinit,
|
||||||
pk_nonce=pk_nonce,
|
pk_nonce=pk_nonce,
|
||||||
expect_edata=expect_edata)
|
expect_edata=expect_edata,
|
||||||
|
expect_matching_nt_hash_in_pac=expect_matching_nt_hash_in_pac)
|
||||||
|
|
||||||
till = self.get_KerberosTime(offset=36000)
|
till = self.get_KerberosTime(offset=36000)
|
||||||
|
|
||||||
|
@ -3108,6 +3108,7 @@ class RawKerberosTest(TestCase):
|
|||||||
expect_resource_groups_flag=None,
|
expect_resource_groups_flag=None,
|
||||||
expected_device_groups=None,
|
expected_device_groups=None,
|
||||||
expected_extra_pac_buffers=None,
|
expected_extra_pac_buffers=None,
|
||||||
|
expect_matching_nt_hash_in_pac=None,
|
||||||
to_rodc=False):
|
to_rodc=False):
|
||||||
if expected_error_mode == 0:
|
if expected_error_mode == 0:
|
||||||
expected_error_mode = ()
|
expected_error_mode = ()
|
||||||
@ -3188,6 +3189,7 @@ class RawKerberosTest(TestCase):
|
|||||||
'expect_resource_groups_flag': expect_resource_groups_flag,
|
'expect_resource_groups_flag': expect_resource_groups_flag,
|
||||||
'expected_device_groups': expected_device_groups,
|
'expected_device_groups': expected_device_groups,
|
||||||
'expected_extra_pac_buffers': expected_extra_pac_buffers,
|
'expected_extra_pac_buffers': expected_extra_pac_buffers,
|
||||||
|
'expect_matching_nt_hash_in_pac': expect_matching_nt_hash_in_pac,
|
||||||
'to_rodc': to_rodc
|
'to_rodc': to_rodc
|
||||||
}
|
}
|
||||||
if callback_dict is None:
|
if callback_dict is None:
|
||||||
@ -4781,10 +4783,10 @@ class RawKerberosTest(TestCase):
|
|||||||
|
|
||||||
creds = kdc_exchange_dict['creds']
|
creds = kdc_exchange_dict['creds']
|
||||||
nt_password = bytes(ntlm_package.nt_password.hash)
|
nt_password = bytes(ntlm_package.nt_password.hash)
|
||||||
if creds.user_account_control & UF_SMARTCARD_REQUIRED:
|
if kdc_exchange_dict['expect_matching_nt_hash_in_pac']:
|
||||||
self.assertNotEqual(creds.get_nt_hash(), nt_password)
|
|
||||||
else:
|
|
||||||
self.assertEqual(creds.get_nt_hash(), nt_password)
|
self.assertEqual(creds.get_nt_hash(), nt_password)
|
||||||
|
else:
|
||||||
|
self.assertNotEqual(creds.get_nt_hash(), nt_password)
|
||||||
|
|
||||||
kdc_exchange_dict['nt_hash_from_pac'] = ntlm_package.nt_password
|
kdc_exchange_dict['nt_hash_from_pac'] = ntlm_package.nt_password
|
||||||
|
|
||||||
|
@ -75,6 +75,7 @@
|
|||||||
^samba.tests.krb5.pkinit_tests.samba.tests.krb5.pkinit_tests.PkInitTests.test_pkinit_ntlm_from_pac_must_change_now
|
^samba.tests.krb5.pkinit_tests.samba.tests.krb5.pkinit_tests.PkInitTests.test_pkinit_ntlm_from_pac_must_change_now
|
||||||
^samba.tests.krb5.pkinit_tests.samba.tests.krb5.pkinit_tests.PkInitTests.test_pkinit_ntlm_from_pac_smartcard_required
|
^samba.tests.krb5.pkinit_tests.samba.tests.krb5.pkinit_tests.PkInitTests.test_pkinit_ntlm_from_pac_smartcard_required
|
||||||
^samba.tests.krb5.pkinit_tests.samba.tests.krb5.pkinit_tests.PkInitTests.test_samlogon_smartcard_required
|
^samba.tests.krb5.pkinit_tests.samba.tests.krb5.pkinit_tests.PkInitTests.test_samlogon_smartcard_required
|
||||||
|
^samba.tests.krb5.pkinit_tests.samba.tests.krb5.pkinit_tests.PkInitTests.test_pkinit_smartcard_required_must_change_now
|
||||||
#
|
#
|
||||||
# Windows 2000 PK-INIT tests
|
# Windows 2000 PK-INIT tests
|
||||||
#
|
#
|
||||||
|
@ -36,6 +36,7 @@
|
|||||||
^samba.tests.krb5.pkinit_tests.samba.tests.krb5.pkinit_tests.PkInitTests.test_pkinit_ntlm_from_pac
|
^samba.tests.krb5.pkinit_tests.samba.tests.krb5.pkinit_tests.PkInitTests.test_pkinit_ntlm_from_pac
|
||||||
^samba.tests.krb5.pkinit_tests.samba.tests.krb5.pkinit_tests.PkInitTests.test_pkinit_kpasswd_change
|
^samba.tests.krb5.pkinit_tests.samba.tests.krb5.pkinit_tests.PkInitTests.test_pkinit_kpasswd_change
|
||||||
^samba.tests.krb5.pkinit_tests.samba.tests.krb5.pkinit_tests.PkInitTests.test_samlogon_smartcard_required
|
^samba.tests.krb5.pkinit_tests.samba.tests.krb5.pkinit_tests.PkInitTests.test_samlogon_smartcard_required
|
||||||
|
^samba.tests.krb5.pkinit_tests.samba.tests.krb5.pkinit_tests.PkInitTests.test_pkinit_smartcard_required_must_change_now
|
||||||
#
|
#
|
||||||
# PK-INIT Freshness tests
|
# PK-INIT Freshness tests
|
||||||
#
|
#
|
||||||
|
Loading…
Reference in New Issue
Block a user