1
0
mirror of https://github.com/samba-team/samba.git synced 2024-12-23 17:34:34 +03:00
samba-mirror/source4/dsdb/tests/python/password_lockout_base.py
Joseph Sutton 2d1d3b7314 pytest/password_lockout: Remove unused variables
Signed-off-by: Joseph Sutton <josephsutton@catalyst.net.nz>
Reviewed-by: Andrew Bartlett <abartlet@samba.org>
2023-05-16 23:29:32 +00:00

786 lines
34 KiB
Python

from samba.credentials import Credentials, DONT_USE_KERBEROS, MUST_USE_KERBEROS
from ldb import SCOPE_BASE, LdbError
from ldb import ERR_INVALID_CREDENTIALS
from ldb import SUCCESS as LDB_SUCCESS
from ldb import Message, MessageElement, Dn
from ldb import FLAG_MOD_REPLACE
from samba import gensec, dsdb
from samba.samdb import SamDB
from samba.tests import delete_force
from samba.dcerpc import security, samr
from samba.ndr import ndr_unpack
from samba.tests.password_test import PasswordTestCase
import time
class BasePasswordTestCase(PasswordTestCase):
if False:
debug = print
else:
def debug(self, *args, **kwargs):
pass
def _open_samr_user(self, res):
self.assertIn("objectSid", res[0])
(domain_sid, rid) = ndr_unpack(security.dom_sid, res[0]["objectSid"][0]).split()
self.assertEqual(self.domain_sid, domain_sid)
return self.samr.OpenUser(self.samr_domain, security.SEC_FLAG_MAXIMUM_ALLOWED, rid)
def _check_attribute(self, res, name, value):
if value is None:
self.assertNotIn(name, res[0],
msg="attr[%s]=%r on dn[%s]" %
(name, res[0], res[0].dn))
return
if isinstance(value, tuple):
(mode, value) = value
else:
mode = "equal"
if mode == "ignore":
return
if mode == "absent":
self.assertNotIn(name, res[0],
msg="attr[%s] not missing on dn[%s]" %
(name, res[0].dn))
return
self.assertIn(name, res[0],
msg="attr[%s] missing on dn[%s]" %
(name, res[0].dn))
self.assertEqual(1, len(res[0][name]),
msg="attr[%s]=%r on dn[%s]" %
(name, res[0][name], res[0].dn))
self.debug("%s = '%s'" % (name, res[0][name][0]))
if mode == "present":
return
if mode == "equal":
v = int(res[0][name][0])
value = int(value)
msg = ("attr[%s]=[%s] != [%s] on dn[%s]\n"
"(diff %d; actual value is %s than expected)" %
(name, v, value, res[0].dn, v - value,
('less' if v < value else 'greater')))
self.assertEqual(v, value, msg)
return
if mode == "greater":
v = int(res[0][name][0])
self.assertGreater(v, int(value),
msg="attr[%s]=[%s] <= [%s] on dn[%s] (diff %d)" %
(name, v, int(value), res[0].dn, v - int(value)))
return
if mode == "less":
v = int(res[0][name][0])
self.assertLess(v, int(value),
msg="attr[%s]=[%s] >= [%s] on dn[%s] (diff %d)" %
(name, v, int(value), res[0].dn, v - int(value)))
return
self.fail("Invalid Mode[%s]" % mode)
def _check_account_initial(self, userdn):
self._check_account(userdn,
badPwdCount=0,
badPasswordTime=0,
logonCount=0,
lastLogon=0,
lastLogonTimestamp=("absent", None),
userAccountControl=dsdb.UF_NORMAL_ACCOUNT,
msDSUserAccountControlComputed=0)
def _check_account(self, dn,
badPwdCount=None,
badPasswordTime=None,
logonCount=None,
lastLogon=None,
lastLogonTimestamp=None,
lockoutTime=None,
userAccountControl=None,
msDSUserAccountControlComputed=None,
effective_bad_password_count=None,
msg=None,
badPwdCountOnly=False):
self.debug('-=' * 36)
if msg is not None:
self.debug("\033[01;32m %s \033[00m\n" % msg)
attrs = [
"objectSid",
"sAMAccountName",
"badPwdCount",
"badPasswordTime",
"lastLogon",
"lastLogonTimestamp",
"logonCount",
"lockoutTime",
"userAccountControl",
"msDS-User-Account-Control-Computed"
]
# in order to prevent some time resolution problems we sleep for
# 10 micro second
time.sleep(0.01)
res = self.ldb.search(dn, scope=SCOPE_BASE, attrs=attrs)
self.assertEqual(1, len(res))
self._check_attribute(res, "badPwdCount", badPwdCount)
self._check_attribute(res, "lockoutTime", lockoutTime)
self._check_attribute(res, "badPasswordTime", badPasswordTime)
if not badPwdCountOnly:
self._check_attribute(res, "logonCount", logonCount)
self._check_attribute(res, "lastLogon", lastLogon)
self._check_attribute(res, "lastLogonTimestamp", lastLogonTimestamp)
self._check_attribute(res, "userAccountControl", userAccountControl)
self._check_attribute(res, "msDS-User-Account-Control-Computed",
msDSUserAccountControlComputed)
lastLogon = int(res[0]["lastLogon"][0])
logonCount = int(res[0]["logonCount"][0])
samr_user = self._open_samr_user(res)
uinfo3 = self.samr.QueryUserInfo(samr_user, 3)
uinfo5 = self.samr.QueryUserInfo(samr_user, 5)
uinfo16 = self.samr.QueryUserInfo(samr_user, 16)
uinfo21 = self.samr.QueryUserInfo(samr_user, 21)
self.samr.Close(samr_user)
expected_acb_info = 0
if not badPwdCountOnly:
if userAccountControl & dsdb.UF_NORMAL_ACCOUNT:
expected_acb_info |= samr.ACB_NORMAL
if userAccountControl & dsdb.UF_ACCOUNTDISABLE:
expected_acb_info |= samr.ACB_DISABLED
if userAccountControl & dsdb.UF_PASSWD_NOTREQD:
expected_acb_info |= samr.ACB_PWNOTREQ
if msDSUserAccountControlComputed & dsdb.UF_LOCKOUT:
expected_acb_info |= samr.ACB_AUTOLOCK
if msDSUserAccountControlComputed & dsdb.UF_PASSWORD_EXPIRED:
expected_acb_info |= samr.ACB_PW_EXPIRED
self.assertEqual(uinfo3.acct_flags, expected_acb_info)
self.assertEqual(uinfo3.last_logon, lastLogon)
self.assertEqual(uinfo3.logon_count, logonCount)
expected_bad_password_count = 0
if badPwdCount is not None:
expected_bad_password_count = badPwdCount
if effective_bad_password_count is None:
effective_bad_password_count = expected_bad_password_count
self.assertEqual(uinfo3.bad_password_count, expected_bad_password_count)
if not badPwdCountOnly:
self.assertEqual(uinfo5.acct_flags, expected_acb_info)
self.assertEqual(uinfo5.bad_password_count, effective_bad_password_count)
self.assertEqual(uinfo5.last_logon, lastLogon)
self.assertEqual(uinfo5.logon_count, logonCount)
self.assertEqual(uinfo16.acct_flags, expected_acb_info)
self.assertEqual(uinfo21.acct_flags, expected_acb_info)
self.assertEqual(uinfo21.bad_password_count, effective_bad_password_count)
self.assertEqual(uinfo21.last_logon, lastLogon)
self.assertEqual(uinfo21.logon_count, logonCount)
# check LDAP again and make sure the samr.QueryUserInfo
# doesn't have any impact.
res2 = self.ldb.search(dn, scope=SCOPE_BASE, attrs=attrs)
self.assertEqual(res[0], res2[0])
# in order to prevent some time resolution problems we sleep for
# 10 micro second
time.sleep(0.01)
return res
def update_lockout_settings(self, threshold, duration, observation_window):
"""Updates the global user lockout settings"""
m = Message()
m.dn = Dn(self.ldb, self.base_dn)
account_lockout_duration_ticks = -int(duration * (1e7))
m["lockoutDuration"] = MessageElement(str(account_lockout_duration_ticks),
FLAG_MOD_REPLACE, "lockoutDuration")
m["lockoutThreshold"] = MessageElement(str(threshold),
FLAG_MOD_REPLACE, "lockoutThreshold")
lockout_observation_window_ticks = -int(observation_window * (1e7))
m["lockOutObservationWindow"] = MessageElement(str(lockout_observation_window_ticks),
FLAG_MOD_REPLACE, "lockOutObservationWindow")
self.ldb.modify(m)
def _readd_user(self, creds, lockOutObservationWindow=0, simple=False):
username = creds.get_username()
userpass = creds.get_password()
userdn = "cn=%s,cn=users,%s" % (username, self.base_dn)
if simple:
creds.set_bind_dn(userdn)
ldap_url = self.host_url_ldaps
else:
ldap_url = self.host_url
delete_force(self.ldb, userdn)
self.ldb.add({
"dn": userdn,
"objectclass": "user",
"sAMAccountName": username})
self.addCleanup(delete_force, self.ldb, userdn)
# Sets the initial user password with a "special" password change
# I think that this internally is a password set operation and it can
# only be performed by someone which has password set privileges on the
# account (at least in s4 we do handle it like that).
self.ldb.modify_ldif("""
dn: """ + userdn + """
changetype: modify
delete: userPassword
add: userPassword
userPassword: """ + userpass + """
""")
# Enables the user account
self.ldb.enable_account("(sAMAccountName=%s)" % username)
use_kerberos = creds.get_kerberos_state()
fail_creds = self.insta_creds(self.template_creds,
username=username,
userpass=userpass + "X",
kerberos_state=use_kerberos)
if simple:
fail_creds.set_bind_dn(userdn)
self._check_account_initial(userdn)
# Fail once to get a badPasswordTime
self.assertLoginFailure(ldap_url, fail_creds, self.lp)
# Always reset with Simple bind or Kerberos, allows testing without NTLM
if simple or use_kerberos == MUST_USE_KERBEROS:
success_creds = creds
else:
success_creds = self.insta_creds(self.template_creds,
username=username,
userpass=userpass)
success_creds.set_bind_dn(userdn)
ldap_url = self.host_url_ldaps
# Succeed to reset everything to 0
self.assertLoginSuccess(ldap_url, success_creds, self.lp)
def assertLoginFailure(self, url, creds, lp, errno=ERR_INVALID_CREDENTIALS):
try:
SamDB(url=url, credentials=creds, lp=lp)
self.fail("Login unexpectedly succeeded")
except LdbError as e1:
(num, msg) = e1.args
if errno is not None:
self.assertEqual(num, errno, ("Login failed in the wrong way"
"(got err %d, expected %d)" %
(num, errno)))
def assertLoginSuccess(self, url, creds, lp):
try:
ldb = SamDB(url=url, credentials=creds, lp=lp)
return ldb
except LdbError as e1:
(num, msg) = e1.args
self.assertEqual(num, LDB_SUCCESS,
("Login failed - %d - %s" % (
num, msg)))
def setUp(self):
super(BasePasswordTestCase, self).setUp()
self.global_creds.set_gensec_features(self.global_creds.get_gensec_features() |
gensec.FEATURE_SEAL)
self.template_creds = Credentials()
self.template_creds.set_username("testuser")
self.template_creds.set_password("thatsAcomplPASS1")
self.template_creds.set_domain(self.global_creds.get_domain())
self.template_creds.set_realm(self.global_creds.get_realm())
self.template_creds.set_workstation(self.global_creds.get_workstation())
self.template_creds.set_gensec_features(self.global_creds.get_gensec_features())
self.template_creds.set_kerberos_state(self.global_creds.get_kerberos_state())
# Gets back the basedn
base_dn = self.ldb.domain_dn()
res = self.ldb.search(base_dn,
scope=SCOPE_BASE, attrs=["lockoutDuration", "lockOutObservationWindow", "lockoutThreshold"])
if "lockoutDuration" in res[0]:
lockoutDuration = res[0]["lockoutDuration"][0]
else:
lockoutDuration = 0
if "lockoutObservationWindow" in res[0]:
lockoutObservationWindow = res[0]["lockoutObservationWindow"][0]
else:
lockoutObservationWindow = 0
if "lockoutThreshold" in res[0]:
lockoutThreshold = res[0]["lockoutThreshold"][0]
else:
lockoutThreshold = 0
self.addCleanup(self.ldb.modify_ldif, """
dn: """ + base_dn + """
changetype: modify
replace: lockoutDuration
lockoutDuration: """ + str(lockoutDuration) + """
replace: lockoutObservationWindow
lockoutObservationWindow: """ + str(lockoutObservationWindow) + """
replace: lockoutThreshold
lockoutThreshold: """ + str(lockoutThreshold) + """
""")
self.base_dn = self.ldb.domain_dn()
#
# Some test cases sleep() for self.account_lockout_duration
# so allow it to be controlled via the subclass
#
if not hasattr(self, 'account_lockout_duration'):
self.account_lockout_duration = 3
if not hasattr(self, 'lockout_observation_window'):
self.lockout_observation_window = 3
self.update_lockout_settings(threshold=3,
duration=self.account_lockout_duration,
observation_window=self.lockout_observation_window)
# update DC to allow password changes for the duration of this test
self.allow_password_changes()
self.domain_sid = security.dom_sid(self.ldb.get_domain_sid())
self.samr = samr.samr("ncacn_ip_tcp:%s[seal]" % self.host, self.lp, self.global_creds)
self.samr_handle = self.samr.Connect2(None, security.SEC_FLAG_MAXIMUM_ALLOWED)
self.samr_domain = self.samr.OpenDomain(self.samr_handle, security.SEC_FLAG_MAXIMUM_ALLOWED, self.domain_sid)
self.addCleanup(self.delete_ldb_connections)
# (Re)adds the test user accounts
self.lockout1krb5_creds = self.insta_creds(self.template_creds,
username="lockout1krb5",
userpass="thatsAcomplPASS0",
kerberos_state=MUST_USE_KERBEROS)
self._readd_user(self.lockout1krb5_creds)
self.lockout1ntlm_creds = self.insta_creds(self.template_creds,
username="lockout1ntlm",
userpass="thatsAcomplPASS0",
kerberos_state=DONT_USE_KERBEROS)
self._readd_user(self.lockout1ntlm_creds)
self.lockout1simple_creds = self.insta_creds(self.template_creds,
username="lockout1simple",
userpass="thatsAcomplPASS0",
kerberos_state=DONT_USE_KERBEROS)
self._readd_user(self.lockout1simple_creds,
simple=True)
def delete_ldb_connections(self):
del self.ldb
def tearDown(self):
super(BasePasswordTestCase, self).tearDown()
def _test_login_lockout(self, creds, wait_lockout_duration=True):
username = creds.get_username()
userpass = creds.get_password()
userdn = "cn=%s,cn=users,%s" % (username, self.base_dn)
use_kerberos = creds.get_kerberos_state()
# This unlocks by waiting for account_lockout_duration
if use_kerberos == MUST_USE_KERBEROS:
logoncount_relation = 'greater'
lastlogon_relation = 'greater'
self.debug("Performs a lockout attempt against LDAP using Kerberos")
else:
logoncount_relation = 'equal'
lastlogon_relation = 'equal'
self.debug("Performs a lockout attempt against LDAP using NTLM")
# Change password on a connection as another user
res = self._check_account(userdn,
badPwdCount=0,
badPasswordTime=("greater", 0),
logonCount=(logoncount_relation, 0),
lastLogon=("greater", 0),
lastLogonTimestamp=("greater", 0),
userAccountControl=dsdb.UF_NORMAL_ACCOUNT,
msDSUserAccountControlComputed=0)
badPasswordTime = int(res[0]["badPasswordTime"][0])
logonCount = int(res[0]["logonCount"][0])
lastLogon = int(res[0]["lastLogon"][0])
firstLogon = lastLogon
lastLogonTimestamp = int(res[0]["lastLogonTimestamp"][0])
self.debug(firstLogon)
self.debug(lastLogonTimestamp)
self.assertGreater(lastLogon, badPasswordTime)
self.assertGreaterEqual(lastLogon, lastLogonTimestamp)
# Open a second LDB connection with the user credentials. Use the
# command line credentials for information like the domain, the realm
# and the workstation.
creds_lockout = self.insta_creds(creds)
# The wrong password
creds_lockout.set_password("thatsAcomplPASS1x")
self.assertLoginFailure(self.host_url, creds_lockout, self.lp)
res = self._check_account(userdn,
badPwdCount=1,
badPasswordTime=("greater", badPasswordTime),
logonCount=logonCount,
lastLogon=lastLogon,
lastLogonTimestamp=lastLogonTimestamp,
userAccountControl=dsdb.UF_NORMAL_ACCOUNT,
msDSUserAccountControlComputed=0,
msg='lastlogontimestamp with wrong password')
badPasswordTime = int(res[0]["badPasswordTime"][0])
# Correct old password
creds_lockout.set_password(userpass)
SamDB(url=self.host_url, credentials=creds_lockout, lp=self.lp)
# lastLogonTimestamp should not change
# lastLogon increases if badPwdCount is non-zero (!)
res = self._check_account(userdn,
badPwdCount=0,
badPasswordTime=badPasswordTime,
logonCount=(logoncount_relation, logonCount),
lastLogon=('greater', lastLogon),
lastLogonTimestamp=lastLogonTimestamp,
userAccountControl=dsdb.UF_NORMAL_ACCOUNT,
msDSUserAccountControlComputed=0,
msg='LLTimestamp is updated to lastlogon')
logonCount = int(res[0]["logonCount"][0])
lastLogon = int(res[0]["lastLogon"][0])
self.assertGreater(lastLogon, badPasswordTime)
self.assertGreaterEqual(lastLogon, lastLogonTimestamp)
# The wrong password
creds_lockout.set_password("thatsAcomplPASS1x")
self.assertLoginFailure(self.host_url, creds_lockout, self.lp)
res = self._check_account(userdn,
badPwdCount=1,
badPasswordTime=("greater", badPasswordTime),
logonCount=logonCount,
lastLogon=lastLogon,
lastLogonTimestamp=lastLogonTimestamp,
userAccountControl=dsdb.UF_NORMAL_ACCOUNT,
msDSUserAccountControlComputed=0)
badPasswordTime = int(res[0]["badPasswordTime"][0])
# The wrong password
creds_lockout.set_password("thatsAcomplPASS1x")
try:
SamDB(url=self.host_url, credentials=creds_lockout, lp=self.lp)
self.fail()
except LdbError as e2:
(num, msg) = e2.args
self.assertEqual(num, ERR_INVALID_CREDENTIALS)
res = self._check_account(userdn,
badPwdCount=2,
badPasswordTime=("greater", badPasswordTime),
logonCount=logonCount,
lastLogon=lastLogon,
lastLogonTimestamp=lastLogonTimestamp,
userAccountControl=dsdb.UF_NORMAL_ACCOUNT,
msDSUserAccountControlComputed=0)
badPasswordTime = int(res[0]["badPasswordTime"][0])
self.debug("two failed password change")
# The wrong password
creds_lockout.set_password("thatsAcomplPASS1x")
try:
SamDB(url=self.host_url, credentials=creds_lockout, lp=self.lp)
self.fail()
except LdbError as e3:
(num, msg) = e3.args
self.assertEqual(num, ERR_INVALID_CREDENTIALS)
res = self._check_account(userdn,
badPwdCount=3,
badPasswordTime=("greater", badPasswordTime),
logonCount=logonCount,
lastLogon=lastLogon,
lastLogonTimestamp=lastLogonTimestamp,
lockoutTime=("greater", badPasswordTime),
userAccountControl=dsdb.UF_NORMAL_ACCOUNT,
msDSUserAccountControlComputed=dsdb.UF_LOCKOUT)
badPasswordTime = int(res[0]["badPasswordTime"][0])
lockoutTime = int(res[0]["lockoutTime"][0])
# The wrong password
creds_lockout.set_password("thatsAcomplPASS1x")
try:
SamDB(url=self.host_url, credentials=creds_lockout, lp=self.lp)
self.fail()
except LdbError as e4:
(num, msg) = e4.args
self.assertEqual(num, ERR_INVALID_CREDENTIALS)
res = self._check_account(userdn,
badPwdCount=3,
badPasswordTime=badPasswordTime,
logonCount=logonCount,
lastLogon=lastLogon,
lastLogonTimestamp=lastLogonTimestamp,
lockoutTime=lockoutTime,
userAccountControl=dsdb.UF_NORMAL_ACCOUNT,
msDSUserAccountControlComputed=dsdb.UF_LOCKOUT)
# The wrong password
creds_lockout.set_password("thatsAcomplPASS1x")
try:
SamDB(url=self.host_url, credentials=creds_lockout, lp=self.lp)
self.fail()
except LdbError as e5:
(num, msg) = e5.args
self.assertEqual(num, ERR_INVALID_CREDENTIALS)
res = self._check_account(userdn,
badPwdCount=3,
badPasswordTime=badPasswordTime,
logonCount=logonCount,
lastLogon=lastLogon,
lastLogonTimestamp=lastLogonTimestamp,
lockoutTime=lockoutTime,
userAccountControl=dsdb.UF_NORMAL_ACCOUNT,
msDSUserAccountControlComputed=dsdb.UF_LOCKOUT)
# The correct password, but we are locked out
creds_lockout.set_password(userpass)
try:
SamDB(url=self.host_url, credentials=creds_lockout, lp=self.lp)
self.fail()
except LdbError as e6:
(num, msg) = e6.args
self.assertEqual(num, ERR_INVALID_CREDENTIALS)
res = self._check_account(userdn,
badPwdCount=3,
badPasswordTime=badPasswordTime,
logonCount=logonCount,
lastLogon=lastLogon,
lastLogonTimestamp=lastLogonTimestamp,
lockoutTime=lockoutTime,
userAccountControl=dsdb.UF_NORMAL_ACCOUNT,
msDSUserAccountControlComputed=dsdb.UF_LOCKOUT)
# if we're just checking the user gets locked out, we can stop here
if not wait_lockout_duration:
return
# wait for the lockout to end
time.sleep(self.account_lockout_duration + 1)
self.debug(self.account_lockout_duration + 1)
res = self._check_account(userdn,
badPwdCount=3, effective_bad_password_count=0,
badPasswordTime=badPasswordTime,
logonCount=logonCount,
lockoutTime=lockoutTime,
lastLogon=lastLogon,
lastLogonTimestamp=lastLogonTimestamp,
userAccountControl=dsdb.UF_NORMAL_ACCOUNT,
msDSUserAccountControlComputed=0)
# The correct password after letting the timeout expire
creds_lockout.set_password(userpass)
creds_lockout2 = self.insta_creds(creds_lockout)
SamDB(url=self.host_url, credentials=creds_lockout2, lp=self.lp)
time.sleep(3)
res = self._check_account(userdn,
badPwdCount=0,
badPasswordTime=badPasswordTime,
logonCount=(logoncount_relation, logonCount),
lastLogon=(lastlogon_relation, lastLogon),
lastLogonTimestamp=lastLogonTimestamp,
lockoutTime=0,
userAccountControl=dsdb.UF_NORMAL_ACCOUNT,
msDSUserAccountControlComputed=0,
msg="lastLogon is way off")
logonCount = int(res[0]["logonCount"][0])
lastLogon = int(res[0]["lastLogon"][0])
# The wrong password
creds_lockout.set_password("thatsAcomplPASS1x")
try:
SamDB(url=self.host_url, credentials=creds_lockout, lp=self.lp)
self.fail()
except LdbError as e7:
(num, msg) = e7.args
self.assertEqual(num, ERR_INVALID_CREDENTIALS)
res = self._check_account(userdn,
badPwdCount=1,
badPasswordTime=("greater", badPasswordTime),
logonCount=logonCount,
lockoutTime=0,
lastLogon=lastLogon,
lastLogonTimestamp=lastLogonTimestamp,
userAccountControl=dsdb.UF_NORMAL_ACCOUNT,
msDSUserAccountControlComputed=0)
badPasswordTime = int(res[0]["badPasswordTime"][0])
# The wrong password
creds_lockout.set_password("thatsAcomplPASS1x")
try:
SamDB(url=self.host_url, credentials=creds_lockout, lp=self.lp)
self.fail()
except LdbError as e8:
(num, msg) = e8.args
self.assertEqual(num, ERR_INVALID_CREDENTIALS)
res = self._check_account(userdn,
badPwdCount=2,
badPasswordTime=("greater", badPasswordTime),
logonCount=logonCount,
lockoutTime=0,
lastLogon=lastLogon,
lastLogonTimestamp=lastLogonTimestamp,
userAccountControl=dsdb.UF_NORMAL_ACCOUNT,
msDSUserAccountControlComputed=0)
badPasswordTime = int(res[0]["badPasswordTime"][0])
time.sleep(self.lockout_observation_window + 1)
res = self._check_account(userdn,
badPwdCount=2, effective_bad_password_count=0,
badPasswordTime=badPasswordTime,
logonCount=logonCount,
lockoutTime=0,
lastLogon=lastLogon,
lastLogonTimestamp=lastLogonTimestamp,
userAccountControl=dsdb.UF_NORMAL_ACCOUNT,
msDSUserAccountControlComputed=0)
# The wrong password
creds_lockout.set_password("thatsAcomplPASS1x")
try:
SamDB(url=self.host_url, credentials=creds_lockout, lp=self.lp)
self.fail()
except LdbError as e9:
(num, msg) = e9.args
self.assertEqual(num, ERR_INVALID_CREDENTIALS)
res = self._check_account(userdn,
badPwdCount=1,
badPasswordTime=("greater", badPasswordTime),
logonCount=logonCount,
lockoutTime=0,
lastLogon=lastLogon,
lastLogonTimestamp=lastLogonTimestamp,
userAccountControl=dsdb.UF_NORMAL_ACCOUNT,
msDSUserAccountControlComputed=0)
badPasswordTime = int(res[0]["badPasswordTime"][0])
# The correct password without letting the timeout expire
creds_lockout.set_password(userpass)
SamDB(url=self.host_url, credentials=creds_lockout, lp=self.lp)
res = self._check_account(userdn,
badPwdCount=0,
badPasswordTime=badPasswordTime,
logonCount=(logoncount_relation, logonCount),
lockoutTime=0,
lastLogon=("greater", lastLogon),
lastLogonTimestamp=lastLogonTimestamp,
userAccountControl=dsdb.UF_NORMAL_ACCOUNT,
msDSUserAccountControlComputed=0)
def _test_multiple_logon(self, creds):
# Test the happy case in which a user logs on correctly, then
# logs on correctly again, so that the bad password and
# lockout times are both zero the second time. The lastlogon
# time should increase.
# Open a second LDB connection with the user credentials. Use the
# command line credentials for information like the domain, the realm
# and the workstation.
username = creds.get_username()
userdn = "cn=%s,cn=users,%s" % (username, self.base_dn)
use_kerberos = creds.get_kerberos_state()
if use_kerberos == MUST_USE_KERBEROS:
self.debug("Testing multiple logon with Kerberos")
logoncount_relation = 'greater'
lastlogon_relation = 'greater'
else:
self.debug("Testing multiple logon with NTLM")
logoncount_relation = 'equal'
lastlogon_relation = 'equal'
SamDB(url=self.host_url, credentials=self.insta_creds(creds), lp=self.lp)
res = self._check_account(userdn,
badPwdCount=0,
badPasswordTime=("greater", 0),
logonCount=(logoncount_relation, 0),
lastLogon=("greater", 0),
lastLogonTimestamp=("greater", 0),
userAccountControl=dsdb.UF_NORMAL_ACCOUNT,
msDSUserAccountControlComputed=0)
badPasswordTime = int(res[0]["badPasswordTime"][0])
logonCount = int(res[0]["logonCount"][0])
lastLogon = int(res[0]["lastLogon"][0])
lastLogonTimestamp = int(res[0]["lastLogonTimestamp"][0])
firstLogon = lastLogon
self.debug("last logon is %d" % lastLogon)
self.assertGreater(lastLogon, badPasswordTime)
self.assertGreaterEqual(lastLogon, lastLogonTimestamp)
time.sleep(1)
SamDB(url=self.host_url, credentials=self.insta_creds(creds), lp=self.lp)
res = self._check_account(userdn,
badPwdCount=0,
badPasswordTime=badPasswordTime,
logonCount=(logoncount_relation, logonCount),
lastLogon=(lastlogon_relation, lastLogon),
lastLogonTimestamp=lastLogonTimestamp,
userAccountControl=dsdb.UF_NORMAL_ACCOUNT,
msDSUserAccountControlComputed=0,
msg=("second logon, firstlogon was %s" %
firstLogon))
lastLogon = int(res[0]["lastLogon"][0])
time.sleep(1)
SamDB(url=self.host_url, credentials=self.insta_creds(creds), lp=self.lp)
res = self._check_account(userdn,
badPwdCount=0,
badPasswordTime=badPasswordTime,
logonCount=(logoncount_relation, logonCount),
lastLogon=(lastlogon_relation, lastLogon),
lastLogonTimestamp=lastLogonTimestamp,
userAccountControl=dsdb.UF_NORMAL_ACCOUNT,
msDSUserAccountControlComputed=0)