mirror of
https://github.com/samba-team/samba.git
synced 2025-02-23 09:57:40 +03:00
auth: keep track of lastLogon and lastLogonTimestamp
lastLogon is supposed to be updated for every interactive or kerberos login, and (according to testing against Windows2012r2) when the bad password count is non-zero but the lockout time is zero. It is not replicated. lastLogonTimestamp is updated if the old value is more than 14 - random.choice([0, 1, 2, 3, 4, 5]) days old, and it is replicated. The 14 in this calculation is the default, stored as "msDS-LogonTimeSyncInterval", which we offer no interface for changing. The authsam_zero_bad_pwd_count() function is a convenient place to update these values, as it is called upon a successful logon however that logon is performed. That makes the function's name inaccurate, so we rename it authsam_logon_success_accounting(). It also needs to be told whet5her the login is interactive. The password_lockout tests are extended to test lastLogon and lasLogonTimestamp. Signed-off-by: Douglas Bagnall <douglas.bagnall@catalyst.net.nz> Reviewed-by: Garming Sam <garming@catalyst.net.nz> Reviewed-by: Ralph Boehme <slow@samba.org>
This commit is contained in:
parent
909ebe0191
commit
795f4729ca
@ -493,6 +493,7 @@ static NTSTATUS authsam_authenticate(struct auth4_context *auth_context,
|
||||
DATA_BLOB *user_sess_key, DATA_BLOB *lm_sess_key)
|
||||
{
|
||||
NTSTATUS nt_status;
|
||||
bool interactive = (user_info->password_state == AUTH_PASSWORD_HASH);
|
||||
uint16_t acct_flags = samdb_result_acct_flags(msg, NULL);
|
||||
TALLOC_CTX *tmp_ctx = talloc_new(mem_ctx);
|
||||
if (!tmp_ctx) {
|
||||
@ -528,7 +529,9 @@ static NTSTATUS authsam_authenticate(struct auth4_context *auth_context,
|
||||
return nt_status;
|
||||
}
|
||||
|
||||
nt_status = authsam_zero_bad_pwd_count(auth_context->sam_ctx, msg);
|
||||
nt_status = authsam_logon_success_accounting(auth_context->sam_ctx,
|
||||
msg, domain_dn,
|
||||
interactive);
|
||||
if (!NT_STATUS_IS_OK(nt_status)) {
|
||||
TALLOC_FREE(tmp_ctx);
|
||||
return nt_status;
|
||||
|
@ -83,6 +83,7 @@ const char *user_attrs[] = {
|
||||
"homeDirectory",
|
||||
"homeDrive",
|
||||
"lastLogon",
|
||||
"lastLogonTimestamp",
|
||||
"lastLogoff",
|
||||
"accountExpires",
|
||||
"badPwdCount",
|
||||
@ -691,20 +692,132 @@ NTSTATUS authsam_update_bad_pwd_count(struct ldb_context *sam_ctx,
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
NTSTATUS authsam_zero_bad_pwd_count(struct ldb_context *sam_ctx,
|
||||
const struct ldb_message *msg)
|
||||
|
||||
static NTSTATUS authsam_update_lastlogon_timestamp(struct ldb_context *sam_ctx,
|
||||
struct ldb_message *msg_mod,
|
||||
struct ldb_dn *domain_dn,
|
||||
NTTIME old_timestamp,
|
||||
NTTIME now)
|
||||
{
|
||||
/*
|
||||
* We only set lastLogonTimestamp if the current value is older than
|
||||
* now - msDS-LogonTimeSyncInterval days.
|
||||
*
|
||||
* msDS-LogonTimeSyncInterval is an int32_t number of days, while
|
||||
* lastLogonTimestamp is in the 64 bit 100ns NTTIME format.
|
||||
*
|
||||
* The docs say: "the initial update, after the domain functional
|
||||
* level is raised to DS_BEHAVIOR_WIN2003 or higher, is calculated as
|
||||
* 14 days minus a random percentage of 5 days", but we aren't doing
|
||||
* that. The blogosphere seems to think that this randomised update
|
||||
* happens everytime, but [MS-ADA1] doesn't agree.
|
||||
*
|
||||
* Dochelp referred us to the following blog post:
|
||||
* http://blogs.technet.com/b/askds/archive/2009/04/15/the-lastlogontimestamp-attribute-what-it-was-designed-for-and-how-it-works.aspx
|
||||
*
|
||||
* en msDS-LogonTimeSyncInterval is zero, the lastLogonTimestamp is
|
||||
* not changed.
|
||||
*/
|
||||
static const char *attrs[] = { "msDS-LogonTimeSyncInterval",
|
||||
NULL };
|
||||
int ret;
|
||||
struct ldb_result *domain_res = NULL;
|
||||
TALLOC_CTX *mem_ctx = NULL;
|
||||
int32_t sync_interval;
|
||||
NTTIME sync_interval_nt;
|
||||
|
||||
mem_ctx = talloc_new(msg_mod);
|
||||
if (mem_ctx == NULL) {
|
||||
return NT_STATUS_NO_MEMORY;
|
||||
}
|
||||
|
||||
ret = dsdb_search_dn(sam_ctx, mem_ctx, &domain_res, domain_dn, attrs,
|
||||
0);
|
||||
if (ret != LDB_SUCCESS || domain_res->count != 1) {
|
||||
TALLOC_FREE(mem_ctx);
|
||||
return NT_STATUS_INTERNAL_DB_CORRUPTION;
|
||||
}
|
||||
|
||||
sync_interval = ldb_msg_find_attr_as_int(domain_res->msgs[0],
|
||||
"msDS-LogonTimeSyncInterval",
|
||||
14);
|
||||
DEBUG(5, ("sync interval is %d\n", sync_interval));
|
||||
if (sync_interval == 0){
|
||||
/*
|
||||
* Setting msDS-LogonTimeSyncInterval to zero is how you ask
|
||||
* that nothing happens here.
|
||||
*/
|
||||
TALLOC_FREE(mem_ctx);
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
else if (sync_interval >= 5){
|
||||
/*
|
||||
* Subtract "a random percentage of 5" days. Presumably this
|
||||
* percentage is between 0 and 100, and modulus is accurate
|
||||
* enough.
|
||||
*/
|
||||
uint32_t r = generate_random() % 6;
|
||||
sync_interval -= r;
|
||||
DEBUG(5, ("randomised sync interval is %d (-%d)\n", sync_interval, r));
|
||||
}
|
||||
/* In the case where sync_interval < 5 there is no randomisation */
|
||||
|
||||
sync_interval_nt = sync_interval * 24LL * 3600LL * 10000000LL;
|
||||
|
||||
DEBUG(5, ("old timestamp is %lld, threshold %lld, diff %lld\n",
|
||||
(long long int)old_timestamp,
|
||||
(long long int)(now - sync_interval_nt),
|
||||
(long long int)(old_timestamp - now + sync_interval_nt)));
|
||||
|
||||
if (old_timestamp > now){
|
||||
DEBUG(0, ("lastLogonTimestamp is in the future! (%lld > %lld)\n",
|
||||
(long long int)old_timestamp, (long long int)now));
|
||||
/* then what? */
|
||||
|
||||
} else if (old_timestamp < now - sync_interval_nt){
|
||||
DEBUG(5, ("updating lastLogonTimestamp to %lld\n",
|
||||
(long long int)now));
|
||||
|
||||
/* The time has come to update lastLogonTimestamp */
|
||||
ret = samdb_msg_add_int64(sam_ctx, msg_mod, msg_mod,
|
||||
"lastLogonTimestamp", now);
|
||||
|
||||
if (ret != LDB_SUCCESS) {
|
||||
TALLOC_FREE(mem_ctx);
|
||||
return NT_STATUS_NO_MEMORY;
|
||||
}
|
||||
}
|
||||
TALLOC_FREE(mem_ctx);
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* Reset the badPwdCount to zero and update the lastLogon time. */
|
||||
NTSTATUS authsam_logon_success_accounting(struct ldb_context *sam_ctx,
|
||||
const struct ldb_message *msg,
|
||||
struct ldb_dn *domain_dn,
|
||||
bool interactive_or_kerberos)
|
||||
{
|
||||
int ret;
|
||||
NTSTATUS status;
|
||||
int badPwdCount;
|
||||
int64_t lockoutTime;
|
||||
struct ldb_message *msg_mod;
|
||||
TALLOC_CTX *mem_ctx;
|
||||
struct timeval tv_now;
|
||||
NTTIME now;
|
||||
NTTIME lastLogonTimestamp;
|
||||
NTTIME lastLogon;
|
||||
|
||||
lockoutTime = ldb_msg_find_attr_as_int64(msg, "lockoutTime", 0);
|
||||
badPwdCount = ldb_msg_find_attr_as_int(msg, "badPwdCount", 0);
|
||||
if (lockoutTime == 0 && badPwdCount == 0) {
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
lastLogonTimestamp = \
|
||||
ldb_msg_find_attr_as_int64(msg, "lastLogonTimestamp", 0);
|
||||
lastLogon = ldb_msg_find_attr_as_int64(msg, "lastLogon", 0);
|
||||
|
||||
DEBUG(5, ("lastLogonTimestamp is %lld\n",
|
||||
(long long int)lastLogonTimestamp));
|
||||
|
||||
mem_ctx = talloc_new(msg);
|
||||
if (mem_ctx == NULL) {
|
||||
@ -726,7 +839,7 @@ NTSTATUS authsam_zero_bad_pwd_count(struct ldb_context *sam_ctx,
|
||||
TALLOC_FREE(mem_ctx);
|
||||
return NT_STATUS_NO_MEMORY;
|
||||
}
|
||||
} else {
|
||||
} else if (badPwdCount != 0) {
|
||||
ret = samdb_msg_add_int(sam_ctx, msg_mod, msg_mod, "badPwdCount", 0);
|
||||
if (ret != LDB_SUCCESS) {
|
||||
TALLOC_FREE(mem_ctx);
|
||||
@ -734,14 +847,37 @@ NTSTATUS authsam_zero_bad_pwd_count(struct ldb_context *sam_ctx,
|
||||
}
|
||||
}
|
||||
|
||||
ret = dsdb_replace(sam_ctx, msg_mod, 0);
|
||||
if (ret != LDB_SUCCESS) {
|
||||
DEBUG(0, ("Failed to set badPwdCount and lockoutTime to 0 on %s: %s\n",
|
||||
ldb_dn_get_linearized(msg_mod->dn), ldb_errstring(sam_ctx)));
|
||||
tv_now = timeval_current();
|
||||
now = timeval_to_nttime(&tv_now);
|
||||
|
||||
if (interactive_or_kerberos || lastLogon == 0 ||
|
||||
(badPwdCount != 0 && lockoutTime == 0)) {
|
||||
ret = samdb_msg_add_int64(sam_ctx, msg_mod, msg_mod,
|
||||
"lastLogon", now);
|
||||
if (ret != LDB_SUCCESS) {
|
||||
TALLOC_FREE(mem_ctx);
|
||||
return NT_STATUS_NO_MEMORY;
|
||||
}
|
||||
}
|
||||
status = authsam_update_lastlogon_timestamp(sam_ctx, msg_mod, domain_dn,
|
||||
lastLogonTimestamp, now);
|
||||
if (!NT_STATUS_IS_OK(status)) {
|
||||
TALLOC_FREE(mem_ctx);
|
||||
return NT_STATUS_INTERNAL_ERROR;
|
||||
return NT_STATUS_NO_MEMORY;
|
||||
}
|
||||
|
||||
if (msg_mod->num_elements > 0) {
|
||||
ret = dsdb_replace(sam_ctx, msg_mod, 0);
|
||||
if (ret != LDB_SUCCESS) {
|
||||
DEBUG(0, ("Failed to set badPwdCount and lockoutTime "
|
||||
"to 0 and/or lastlogon to now (%lld) "
|
||||
"%s: %s\n", (long long int)now,
|
||||
ldb_dn_get_linearized(msg_mod->dn),
|
||||
ldb_errstring(sam_ctx)));
|
||||
TALLOC_FREE(mem_ctx);
|
||||
return NT_STATUS_INTERNAL_ERROR;
|
||||
}
|
||||
}
|
||||
TALLOC_FREE(mem_ctx);
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
@ -51,10 +51,24 @@ if len(args) < 1:
|
||||
host = args[0]
|
||||
|
||||
lp = sambaopts.get_loadparm()
|
||||
creds = credopts.get_credentials(lp)
|
||||
global_creds = credopts.get_credentials(lp)
|
||||
|
||||
# Force an encrypted connection
|
||||
creds.set_gensec_features(creds.get_gensec_features() | gensec.FEATURE_SEAL)
|
||||
global_creds.set_gensec_features(global_creds.get_gensec_features() |
|
||||
gensec.FEATURE_SEAL)
|
||||
|
||||
def insta_creds(template=global_creds):
|
||||
# get a copy of the global creds or a the passed in creds
|
||||
c = Credentials()
|
||||
c.set_username("testuser")
|
||||
c.set_password("thatsAcomplPASS1")
|
||||
c.set_domain(template.get_domain())
|
||||
c.set_realm(template.get_realm())
|
||||
c.set_workstation(template.get_workstation())
|
||||
c.set_gensec_features(c.get_gensec_features()
|
||||
| gensec.FEATURE_SEAL)
|
||||
c.set_kerberos_state(template.get_kerberos_state())
|
||||
return c
|
||||
|
||||
#
|
||||
# Tests start here
|
||||
@ -129,6 +143,12 @@ userAccountControl: %d
|
||||
if mode == "ignore":
|
||||
return
|
||||
|
||||
if mode == "absent":
|
||||
self.assertFalse(name in res[0],
|
||||
msg="attr[%s] not missing on dn[%s]" %
|
||||
(name, res[0].dn))
|
||||
return
|
||||
|
||||
self.assertTrue(name in res[0],
|
||||
msg="attr[%s] missing on dn[%s]" %
|
||||
(name, res[0].dn))
|
||||
@ -136,39 +156,56 @@ userAccountControl: %d
|
||||
msg="attr[%s]=%r on dn[%s]" %
|
||||
(name, res[0][name], res[0].dn))
|
||||
|
||||
|
||||
print "%s = '%s'" % (name, res[0][name][0])
|
||||
|
||||
if mode == "present":
|
||||
return
|
||||
|
||||
if mode == "equal":
|
||||
self.assertTrue(str(res[0][name][0]) == str(value),
|
||||
msg="attr[%s]=[%s] != [%s] on dn[%s]" %
|
||||
(name, str(res[0][name][0]), str(value), res[0].dn))
|
||||
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.assertTrue(v == value, msg)
|
||||
return
|
||||
|
||||
if mode == "greater":
|
||||
v = int(res[0][name][0])
|
||||
self.assertTrue(v > int(value),
|
||||
msg="attr[%s]=[%s] <= [%s] on dn[%s]" %
|
||||
(name, v, int(value), res[0].dn))
|
||||
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.assertTrue(v < int(value),
|
||||
msg="attr[%s]=[%s] >= [%s] on dn[%s]" %
|
||||
(name, v, int(value), res[0].dn))
|
||||
msg="attr[%s]=[%s] >= [%s] on dn[%s] (diff %d)" %
|
||||
(name, v, int(value), res[0].dn, v - int(value)))
|
||||
return
|
||||
self.assertEqual(mode, not mode, "Invalid Mode[%s]" % mode)
|
||||
|
||||
def _check_account(self, dn,
|
||||
badPwdCount=None,
|
||||
badPasswordTime=None,
|
||||
lastLogon=None,
|
||||
lastLogonTimestamp=None,
|
||||
lockoutTime=None,
|
||||
userAccountControl=None,
|
||||
msDSUserAccountControlComputed=None,
|
||||
effective_bad_password_count=None):
|
||||
|
||||
effective_bad_password_count=None,
|
||||
msg=None):
|
||||
print '-=' * 36
|
||||
if msg is not None:
|
||||
print "\033[01;32m %s \033[00m\n" % msg
|
||||
attrs = [
|
||||
"objectSid",
|
||||
"badPwdCount",
|
||||
"badPasswordTime",
|
||||
"lastLogon",
|
||||
"lastLogonTimestamp",
|
||||
"lockoutTime",
|
||||
"userAccountControl",
|
||||
"msDS-User-Account-Control-Computed"
|
||||
@ -182,6 +219,8 @@ userAccountControl: %d
|
||||
self.assertTrue(len(res) == 1)
|
||||
self._check_attribute(res, "badPwdCount", badPwdCount)
|
||||
self._check_attribute(res, "badPasswordTime", badPasswordTime)
|
||||
self._check_attribute(res, "lastLogon", lastLogon)
|
||||
self._check_attribute(res, "lastLogonTimestamp", lastLogonTimestamp)
|
||||
self._check_attribute(res, "lockoutTime", lockoutTime)
|
||||
self._check_attribute(res, "userAccountControl", userAccountControl)
|
||||
self._check_attribute(res, "msDS-User-Account-Control-Computed",
|
||||
@ -246,7 +285,8 @@ userAccountControl: %d
|
||||
def setUp(self):
|
||||
super(PasswordTests, self).setUp()
|
||||
|
||||
self.ldb = SamDB(url=host_url, session_info=system_session(lp), credentials=creds, lp=lp)
|
||||
self.ldb = SamDB(url=host_url, session_info=system_session(lp),
|
||||
credentials=global_creds, lp=lp)
|
||||
|
||||
# Gets back the basedn
|
||||
base_dn = self.ldb.domain_dn()
|
||||
@ -325,7 +365,7 @@ lockoutThreshold: """ + str(lockoutThreshold) + """
|
||||
self.base_dn = self.ldb.domain_dn()
|
||||
|
||||
self.domain_sid = security.dom_sid(self.ldb.get_domain_sid())
|
||||
self.samr = samr.samr("ncacn_ip_tcp:%s[sign]" % host, lp, creds)
|
||||
self.samr = samr.samr("ncacn_ip_tcp:%s[sign]" % host, lp, 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)
|
||||
|
||||
@ -339,6 +379,8 @@ lockoutThreshold: """ + str(lockoutThreshold) + """
|
||||
res = self._check_account("cn=testuser,cn=users," + self.base_dn,
|
||||
badPwdCount=0,
|
||||
badPasswordTime=0,
|
||||
lastLogon=0,
|
||||
lastLogonTimestamp=('absent', None),
|
||||
userAccountControl=
|
||||
dsdb.UF_NORMAL_ACCOUNT |
|
||||
dsdb.UF_ACCOUNTDISABLE |
|
||||
@ -353,6 +395,8 @@ lockoutThreshold: """ + str(lockoutThreshold) + """
|
||||
res = self._check_account("cn=testuser,cn=users," + self.base_dn,
|
||||
badPwdCount=0,
|
||||
badPasswordTime=0,
|
||||
lastLogon=0,
|
||||
lastLogonTimestamp=('absent', None),
|
||||
userAccountControl=
|
||||
dsdb.UF_NORMAL_ACCOUNT |
|
||||
dsdb.UF_ACCOUNTDISABLE |
|
||||
@ -381,6 +425,8 @@ userPassword: thatsAcomplPASS2
|
||||
res = self._check_account("cn=testuser,cn=users," + self.base_dn,
|
||||
badPwdCount=1,
|
||||
badPasswordTime=("greater", 0),
|
||||
lastLogon=0,
|
||||
lastLogonTimestamp=('absent', None),
|
||||
userAccountControl=
|
||||
dsdb.UF_NORMAL_ACCOUNT |
|
||||
dsdb.UF_ACCOUNTDISABLE |
|
||||
@ -404,6 +450,8 @@ userPassword: thatsAcomplPASS1
|
||||
res = self._check_account("cn=testuser,cn=users," + self.base_dn,
|
||||
badPwdCount=1,
|
||||
badPasswordTime=badPasswordTime,
|
||||
lastLogon=0,
|
||||
lastLogonTimestamp=('absent', None),
|
||||
userAccountControl=
|
||||
dsdb.UF_NORMAL_ACCOUNT |
|
||||
dsdb.UF_ACCOUNTDISABLE |
|
||||
@ -416,6 +464,8 @@ userPassword: thatsAcomplPASS1
|
||||
res = self._check_account("cn=testuser,cn=users," + self.base_dn,
|
||||
badPwdCount=1,
|
||||
badPasswordTime=badPasswordTime,
|
||||
lastLogon=0,
|
||||
lastLogonTimestamp=('absent', None),
|
||||
userAccountControl=
|
||||
dsdb.UF_NORMAL_ACCOUNT,
|
||||
msDSUserAccountControlComputed=0)
|
||||
@ -423,24 +473,22 @@ userPassword: thatsAcomplPASS1
|
||||
# Open a second LDB connection with the user credentials. Use the
|
||||
# command line credentials for informations like the domain, the realm
|
||||
# and the workstation.
|
||||
creds2 = Credentials()
|
||||
creds2.set_username("testuser")
|
||||
creds2.set_password("thatsAcomplPASS1")
|
||||
creds2.set_domain(creds.get_domain())
|
||||
creds2.set_realm(creds.get_realm())
|
||||
creds2.set_workstation(creds.get_workstation())
|
||||
creds2.set_gensec_features(creds2.get_gensec_features()
|
||||
| gensec.FEATURE_SEAL)
|
||||
creds2 = insta_creds()
|
||||
|
||||
self.ldb2 = SamDB(url=host_url, credentials=creds2, lp=lp)
|
||||
|
||||
res = self._check_account("cn=testuser,cn=users," + self.base_dn,
|
||||
badPwdCount=0,
|
||||
badPasswordTime=badPasswordTime,
|
||||
lastLogon=('greater', 0),
|
||||
lastLogonTimestamp=('greater', 0),
|
||||
userAccountControl=
|
||||
dsdb.UF_NORMAL_ACCOUNT,
|
||||
msDSUserAccountControlComputed=0)
|
||||
|
||||
lastLogon = int(res[0]["lastLogon"][0])
|
||||
self.assertGreater(lastLogon, badPasswordTime)
|
||||
|
||||
# (Re)adds the test user "testuser3" with no password atm
|
||||
delete_force(self.ldb, "cn=testuser3,cn=users," + self.base_dn)
|
||||
self.ldb.add({
|
||||
@ -451,6 +499,8 @@ userPassword: thatsAcomplPASS1
|
||||
res = self._check_account("cn=testuser3,cn=users," + self.base_dn,
|
||||
badPwdCount=0,
|
||||
badPasswordTime=0,
|
||||
lastLogon=0,
|
||||
lastLogonTimestamp=('absent', None),
|
||||
userAccountControl=
|
||||
dsdb.UF_NORMAL_ACCOUNT |
|
||||
dsdb.UF_ACCOUNTDISABLE |
|
||||
@ -479,6 +529,8 @@ userPassword: thatsAcomplPASS2
|
||||
res = self._check_account("cn=testuser3,cn=users," + self.base_dn,
|
||||
badPwdCount=1,
|
||||
badPasswordTime=("greater", 0),
|
||||
lastLogon=0,
|
||||
lastLogonTimestamp=('absent', None),
|
||||
userAccountControl=
|
||||
dsdb.UF_NORMAL_ACCOUNT |
|
||||
dsdb.UF_ACCOUNTDISABLE |
|
||||
@ -502,6 +554,8 @@ userPassword: thatsAcomplPASS1
|
||||
res = self._check_account("cn=testuser3,cn=users," + self.base_dn,
|
||||
badPwdCount=1,
|
||||
badPasswordTime=badPasswordTime3,
|
||||
lastLogon=0,
|
||||
lastLogonTimestamp=('absent', None),
|
||||
userAccountControl=
|
||||
dsdb.UF_NORMAL_ACCOUNT |
|
||||
dsdb.UF_ACCOUNTDISABLE |
|
||||
@ -514,6 +568,8 @@ userPassword: thatsAcomplPASS1
|
||||
res = self._check_account("cn=testuser3,cn=users," + self.base_dn,
|
||||
badPwdCount=1,
|
||||
badPasswordTime=badPasswordTime3,
|
||||
lastLogon=0,
|
||||
lastLogonTimestamp=('absent', None),
|
||||
userAccountControl=
|
||||
dsdb.UF_NORMAL_ACCOUNT,
|
||||
msDSUserAccountControlComputed=0)
|
||||
@ -521,19 +577,16 @@ userPassword: thatsAcomplPASS1
|
||||
# Open a second LDB connection with the user credentials. Use the
|
||||
# command line credentials for informations like the domain, the realm
|
||||
# and the workstation.
|
||||
creds3 = Credentials()
|
||||
creds3 = insta_creds()
|
||||
creds3.set_username("testuser3")
|
||||
creds3.set_password("thatsAcomplPASS1")
|
||||
creds3.set_domain(creds.get_domain())
|
||||
creds3.set_realm(creds.get_realm())
|
||||
creds3.set_workstation(creds.get_workstation())
|
||||
creds3.set_gensec_features(creds3.get_gensec_features()
|
||||
| gensec.FEATURE_SEAL)
|
||||
self.ldb3 = SamDB(url=host_url, credentials=creds3, lp=lp)
|
||||
|
||||
res = self._check_account("cn=testuser3,cn=users," + self.base_dn,
|
||||
badPwdCount=0,
|
||||
badPasswordTime=badPasswordTime3,
|
||||
lastLogon=('greater', badPasswordTime3),
|
||||
lastLogonTimestamp=('greater', badPasswordTime3),
|
||||
userAccountControl=
|
||||
dsdb.UF_NORMAL_ACCOUNT,
|
||||
msDSUserAccountControlComputed=0)
|
||||
@ -546,10 +599,14 @@ userPassword: thatsAcomplPASS1
|
||||
res = self._check_account("cn=testuser,cn=users," + self.base_dn,
|
||||
badPwdCount=0,
|
||||
badPasswordTime=("greater", 0),
|
||||
lastLogon=('greater', 0),
|
||||
lastLogonTimestamp=('greater', 0),
|
||||
userAccountControl=
|
||||
dsdb.UF_NORMAL_ACCOUNT,
|
||||
msDSUserAccountControlComputed=0)
|
||||
badPasswordTime = int(res[0]["badPasswordTime"][0])
|
||||
lastLogon = int(res[0]["lastLogon"][0])
|
||||
lastLogonTimestamp = int(res[0]["lastLogonTimestamp"][0])
|
||||
|
||||
# Change password on a connection as another user
|
||||
|
||||
@ -571,6 +628,8 @@ userPassword: thatsAcomplPASS2
|
||||
res = self._check_account("cn=testuser,cn=users," + self.base_dn,
|
||||
badPwdCount=1,
|
||||
badPasswordTime=("greater", badPasswordTime),
|
||||
lastLogon=lastLogon,
|
||||
lastLogonTimestamp=lastLogonTimestamp,
|
||||
userAccountControl=
|
||||
dsdb.UF_NORMAL_ACCOUNT,
|
||||
msDSUserAccountControlComputed=0)
|
||||
@ -589,6 +648,8 @@ userPassword: thatsAcomplPASS2
|
||||
res = self._check_account("cn=testuser,cn=users," + self.base_dn,
|
||||
badPwdCount=1,
|
||||
badPasswordTime=badPasswordTime,
|
||||
lastLogon=lastLogon,
|
||||
lastLogonTimestamp=lastLogonTimestamp,
|
||||
userAccountControl=
|
||||
dsdb.UF_NORMAL_ACCOUNT,
|
||||
msDSUserAccountControlComputed=0)
|
||||
@ -611,6 +672,8 @@ userPassword: thatsAcomplPASS2
|
||||
res = self._check_account("cn=testuser,cn=users," + self.base_dn,
|
||||
badPwdCount=2,
|
||||
badPasswordTime=("greater", badPasswordTime),
|
||||
lastLogon=lastLogon,
|
||||
lastLogonTimestamp=lastLogonTimestamp,
|
||||
userAccountControl=
|
||||
dsdb.UF_NORMAL_ACCOUNT,
|
||||
msDSUserAccountControlComputed=0)
|
||||
@ -636,6 +699,8 @@ userPassword: thatsAcomplPASS2
|
||||
res = self._check_account("cn=testuser,cn=users," + self.base_dn,
|
||||
badPwdCount=3,
|
||||
badPasswordTime=("greater", badPasswordTime),
|
||||
lastLogon=lastLogon,
|
||||
lastLogonTimestamp=lastLogonTimestamp,
|
||||
lockoutTime=("greater", badPasswordTime),
|
||||
userAccountControl=
|
||||
dsdb.UF_NORMAL_ACCOUNT,
|
||||
@ -661,6 +726,8 @@ userPassword: thatsAcomplPASS2
|
||||
res = self._check_account("cn=testuser,cn=users," + self.base_dn,
|
||||
badPwdCount=3,
|
||||
badPasswordTime=badPasswordTime,
|
||||
lastLogon=lastLogon,
|
||||
lastLogonTimestamp=lastLogonTimestamp,
|
||||
lockoutTime=lockoutTime,
|
||||
userAccountControl=
|
||||
dsdb.UF_NORMAL_ACCOUNT,
|
||||
@ -685,6 +752,8 @@ userPassword: thatsAcomplPASS2
|
||||
badPwdCount=3,
|
||||
badPasswordTime=badPasswordTime,
|
||||
lockoutTime=lockoutTime,
|
||||
lastLogon=lastLogon,
|
||||
lastLogonTimestamp=lastLogonTimestamp,
|
||||
userAccountControl=
|
||||
dsdb.UF_NORMAL_ACCOUNT,
|
||||
msDSUserAccountControlComputed=dsdb.UF_LOCKOUT)
|
||||
@ -707,6 +776,8 @@ userPassword: thatsAcomplPASS2x
|
||||
res = self._check_account("cn=testuser,cn=users," + self.base_dn,
|
||||
badPwdCount=3,
|
||||
badPasswordTime=badPasswordTime,
|
||||
lastLogon=lastLogon,
|
||||
lastLogonTimestamp=lastLogonTimestamp,
|
||||
lockoutTime=lockoutTime,
|
||||
userAccountControl=
|
||||
dsdb.UF_NORMAL_ACCOUNT,
|
||||
@ -723,6 +794,8 @@ userPassword: thatsAcomplPASS2
|
||||
res = self._check_account("cn=testuser,cn=users," + self.base_dn,
|
||||
badPwdCount=3,
|
||||
badPasswordTime=badPasswordTime,
|
||||
lastLogon=lastLogon,
|
||||
lastLogonTimestamp=lastLogonTimestamp,
|
||||
lockoutTime=lockoutTime,
|
||||
userAccountControl=
|
||||
dsdb.UF_NORMAL_ACCOUNT,
|
||||
@ -746,6 +819,8 @@ userPassword: thatsAcomplPASS2x
|
||||
res = self._check_account("cn=testuser,cn=users," + self.base_dn,
|
||||
badPwdCount=3,
|
||||
badPasswordTime=badPasswordTime,
|
||||
lastLogon=lastLogon,
|
||||
lastLogonTimestamp=lastLogonTimestamp,
|
||||
lockoutTime=lockoutTime,
|
||||
userAccountControl=
|
||||
dsdb.UF_NORMAL_ACCOUNT,
|
||||
@ -763,6 +838,8 @@ userPassword: thatsAcomplPASS2x
|
||||
res = self._check_account("cn=testuser,cn=users," + self.base_dn,
|
||||
badPwdCount=3,
|
||||
badPasswordTime=badPasswordTime,
|
||||
lastLogon=lastLogon,
|
||||
lastLogonTimestamp=lastLogonTimestamp,
|
||||
lockoutTime=lockoutTime,
|
||||
userAccountControl=
|
||||
dsdb.UF_NORMAL_ACCOUNT,
|
||||
@ -788,16 +865,21 @@ unicodePwd:: """ + base64.b64encode("\"thatsAcomplPASS2x\"".encode('utf-16-le'))
|
||||
badPwdCount=3,
|
||||
badPasswordTime=badPasswordTime,
|
||||
lockoutTime=lockoutTime,
|
||||
lastLogon=lastLogon,
|
||||
lastLogonTimestamp=lastLogonTimestamp,
|
||||
userAccountControl=
|
||||
dsdb.UF_NORMAL_ACCOUNT,
|
||||
msDSUserAccountControlComputed=dsdb.UF_LOCKOUT)
|
||||
|
||||
self._reset_by_method(res, method)
|
||||
|
||||
# Here bad password counts are reset without logon success.
|
||||
res = self._check_account("cn=testuser,cn=users," + self.base_dn,
|
||||
badPwdCount=0,
|
||||
badPasswordTime=badPasswordTime,
|
||||
lockoutTime=0,
|
||||
lastLogon=lastLogon,
|
||||
lastLogonTimestamp=lastLogonTimestamp,
|
||||
userAccountControl=
|
||||
dsdb.UF_NORMAL_ACCOUNT,
|
||||
msDSUserAccountControlComputed=0)
|
||||
@ -817,6 +899,8 @@ unicodePwd:: """ + base64.b64encode("\"thatsAcomplPASS2x\"".encode('utf-16-le'))
|
||||
badPwdCount=0,
|
||||
badPasswordTime=badPasswordTime,
|
||||
lockoutTime=0,
|
||||
lastLogon=lastLogon,
|
||||
lastLogonTimestamp=lastLogonTimestamp,
|
||||
userAccountControl=
|
||||
dsdb.UF_NORMAL_ACCOUNT,
|
||||
msDSUserAccountControlComputed=0)
|
||||
@ -840,6 +924,8 @@ userPassword: thatsAcomplPASS2XYZ
|
||||
badPwdCount=1,
|
||||
badPasswordTime=("greater", badPasswordTime),
|
||||
lockoutTime=0,
|
||||
lastLogon=lastLogon,
|
||||
lastLogonTimestamp=lastLogonTimestamp,
|
||||
userAccountControl=
|
||||
dsdb.UF_NORMAL_ACCOUNT,
|
||||
msDSUserAccountControlComputed=0)
|
||||
@ -864,6 +950,8 @@ userPassword: thatsAcomplPASS2XYZ
|
||||
badPwdCount=2,
|
||||
badPasswordTime=("greater", badPasswordTime),
|
||||
lockoutTime=0,
|
||||
lastLogon=lastLogon,
|
||||
lastLogonTimestamp=lastLogonTimestamp,
|
||||
userAccountControl=
|
||||
dsdb.UF_NORMAL_ACCOUNT,
|
||||
msDSUserAccountControlComputed=0)
|
||||
@ -874,6 +962,8 @@ userPassword: thatsAcomplPASS2XYZ
|
||||
res = self._check_account("cn=testuser,cn=users," + self.base_dn,
|
||||
badPwdCount=0,
|
||||
badPasswordTime=badPasswordTime,
|
||||
lastLogon=lastLogon,
|
||||
lastLogonTimestamp=lastLogonTimestamp,
|
||||
lockoutTime=0,
|
||||
userAccountControl=
|
||||
dsdb.UF_NORMAL_ACCOUNT,
|
||||
@ -895,10 +985,13 @@ userPassword: thatsAcomplPASS2XYZ
|
||||
res = self._check_account("cn=testuser,cn=users," + self.base_dn,
|
||||
badPwdCount=0,
|
||||
badPasswordTime=("greater", 0),
|
||||
lastLogon=("greater", 0),
|
||||
lastLogonTimestamp=("greater", 0),
|
||||
userAccountControl=
|
||||
dsdb.UF_NORMAL_ACCOUNT,
|
||||
msDSUserAccountControlComputed=0)
|
||||
badPasswordTime = int(res[0]["badPasswordTime"][0])
|
||||
lastLogon = int(res[0]["lastLogon"][0])
|
||||
|
||||
# Change password on a connection as another user
|
||||
|
||||
@ -920,6 +1013,8 @@ unicodePwd:: """ + base64.b64encode("\"thatsAcomplPASS2\"".encode('utf-16-le'))
|
||||
res = self._check_account("cn=testuser,cn=users," + self.base_dn,
|
||||
badPwdCount=1,
|
||||
badPasswordTime=("greater", badPasswordTime),
|
||||
lastLogon=lastLogon,
|
||||
lastLogonTimestamp=lastLogon,
|
||||
userAccountControl=
|
||||
dsdb.UF_NORMAL_ACCOUNT,
|
||||
msDSUserAccountControlComputed=0)
|
||||
@ -938,6 +1033,8 @@ unicodePwd:: """ + base64.b64encode("\"thatsAcomplPASS2\"".encode('utf-16-le'))
|
||||
res = self._check_account("cn=testuser,cn=users," + self.base_dn,
|
||||
badPwdCount=1,
|
||||
badPasswordTime=badPasswordTime,
|
||||
lastLogon=lastLogon,
|
||||
lastLogonTimestamp=lastLogon,
|
||||
userAccountControl=
|
||||
dsdb.UF_NORMAL_ACCOUNT,
|
||||
msDSUserAccountControlComputed=0)
|
||||
@ -960,6 +1057,8 @@ unicodePwd:: """ + base64.b64encode("\"thatsAcomplPASS2\"".encode('utf-16-le'))
|
||||
res = self._check_account("cn=testuser,cn=users," + self.base_dn,
|
||||
badPwdCount=2,
|
||||
badPasswordTime=("greater", badPasswordTime),
|
||||
lastLogon=lastLogon,
|
||||
lastLogonTimestamp=lastLogon,
|
||||
userAccountControl=
|
||||
dsdb.UF_NORMAL_ACCOUNT,
|
||||
msDSUserAccountControlComputed=0)
|
||||
@ -973,6 +1072,8 @@ unicodePwd:: """ + base64.b64encode("\"thatsAcomplPASS2\"".encode('utf-16-le'))
|
||||
res = self._check_account("cn=testuser,cn=users," + self.base_dn,
|
||||
badPwdCount=2,
|
||||
badPasswordTime=badPasswordTime,
|
||||
lastLogon=lastLogon,
|
||||
lastLogonTimestamp=lastLogon,
|
||||
userAccountControl=
|
||||
dsdb.UF_NORMAL_ACCOUNT,
|
||||
msDSUserAccountControlComputed=0)
|
||||
@ -998,6 +1099,8 @@ unicodePwd:: """ + base64.b64encode("\"thatsAcomplPASS2\"".encode('utf-16-le'))
|
||||
res = self._check_account("cn=testuser,cn=users," + self.base_dn,
|
||||
badPwdCount=3,
|
||||
badPasswordTime=("greater", badPasswordTime),
|
||||
lastLogon=lastLogon,
|
||||
lastLogonTimestamp=lastLogon,
|
||||
lockoutTime=("greater", badPasswordTime),
|
||||
userAccountControl=
|
||||
dsdb.UF_NORMAL_ACCOUNT,
|
||||
@ -1023,6 +1126,8 @@ unicodePwd:: """ + base64.b64encode("\"thatsAcomplPASS2\"".encode('utf-16-le'))
|
||||
res = self._check_account("cn=testuser,cn=users," + self.base_dn,
|
||||
badPwdCount=3,
|
||||
badPasswordTime=badPasswordTime,
|
||||
lastLogon=lastLogon,
|
||||
lastLogonTimestamp=lastLogon,
|
||||
lockoutTime=lockoutTime,
|
||||
userAccountControl=
|
||||
dsdb.UF_NORMAL_ACCOUNT,
|
||||
@ -1046,6 +1151,8 @@ unicodePwd:: """ + base64.b64encode("\"thatsAcomplPASS2\"".encode('utf-16-le'))
|
||||
res = self._check_account("cn=testuser,cn=users," + self.base_dn,
|
||||
badPwdCount=3,
|
||||
badPasswordTime=badPasswordTime,
|
||||
lastLogon=lastLogon,
|
||||
lastLogonTimestamp=lastLogon,
|
||||
lockoutTime=lockoutTime,
|
||||
userAccountControl=
|
||||
dsdb.UF_NORMAL_ACCOUNT,
|
||||
@ -1069,6 +1176,8 @@ unicodePwd:: """ + base64.b64encode("\"thatsAcomplPASS2x\"".encode('utf-16-le'))
|
||||
res = self._check_account("cn=testuser,cn=users," + self.base_dn,
|
||||
badPwdCount=3,
|
||||
badPasswordTime=badPasswordTime,
|
||||
lastLogon=lastLogon,
|
||||
lastLogonTimestamp=lastLogon,
|
||||
lockoutTime=lockoutTime,
|
||||
userAccountControl=
|
||||
dsdb.UF_NORMAL_ACCOUNT,
|
||||
@ -1080,6 +1189,8 @@ unicodePwd:: """ + base64.b64encode("\"thatsAcomplPASS2x\"".encode('utf-16-le'))
|
||||
res = self._check_account("cn=testuser,cn=users," + self.base_dn,
|
||||
badPwdCount=0,
|
||||
badPasswordTime=badPasswordTime,
|
||||
lastLogon=lastLogon,
|
||||
lastLogonTimestamp=lastLogon,
|
||||
lockoutTime=0,
|
||||
userAccountControl=
|
||||
dsdb.UF_NORMAL_ACCOUNT,
|
||||
@ -1098,6 +1209,8 @@ unicodePwd:: """ + base64.b64encode("\"thatsAcomplPASS2x\"".encode('utf-16-le'))
|
||||
res = self._check_account("cn=testuser,cn=users," + self.base_dn,
|
||||
badPwdCount=0,
|
||||
badPasswordTime=badPasswordTime,
|
||||
lastLogon=lastLogon,
|
||||
lastLogonTimestamp=lastLogon,
|
||||
lockoutTime=0,
|
||||
userAccountControl=
|
||||
dsdb.UF_NORMAL_ACCOUNT,
|
||||
@ -1121,6 +1234,8 @@ unicodePwd:: """ + base64.b64encode("\"thatsAcomplPASS2\"".encode('utf-16-le'))
|
||||
res = self._check_account("cn=testuser,cn=users," + self.base_dn,
|
||||
badPwdCount=1,
|
||||
badPasswordTime=("greater", badPasswordTime),
|
||||
lastLogon=lastLogon,
|
||||
lastLogonTimestamp=lastLogon,
|
||||
lockoutTime=0,
|
||||
userAccountControl=
|
||||
dsdb.UF_NORMAL_ACCOUNT,
|
||||
@ -1145,6 +1260,8 @@ unicodePwd:: """ + base64.b64encode("\"thatsAcomplPASS2\"".encode('utf-16-le'))
|
||||
res = self._check_account("cn=testuser,cn=users," + self.base_dn,
|
||||
badPwdCount=2,
|
||||
badPasswordTime=("greater", badPasswordTime),
|
||||
lastLogon=lastLogon,
|
||||
lastLogonTimestamp=lastLogon,
|
||||
lockoutTime=0,
|
||||
userAccountControl=
|
||||
dsdb.UF_NORMAL_ACCOUNT,
|
||||
@ -1158,6 +1275,8 @@ unicodePwd:: """ + base64.b64encode("\"thatsAcomplPASS2\"".encode('utf-16-le'))
|
||||
res = self._check_account("cn=testuser,cn=users," + self.base_dn,
|
||||
badPwdCount=2,
|
||||
badPasswordTime=badPasswordTime,
|
||||
lastLogon=lastLogon,
|
||||
lastLogonTimestamp=lastLogon,
|
||||
lockoutTime=0,
|
||||
userAccountControl=
|
||||
dsdb.UF_NORMAL_ACCOUNT,
|
||||
@ -1181,6 +1300,8 @@ unicodePwd:: """ + base64.b64encode("\"thatsAcomplPASS2\"".encode('utf-16-le'))
|
||||
res = self._check_account("cn=testuser,cn=users," + self.base_dn,
|
||||
badPwdCount=3,
|
||||
badPasswordTime=("greater", badPasswordTime),
|
||||
lastLogon=lastLogon,
|
||||
lastLogonTimestamp=lastLogon,
|
||||
lockoutTime=("greater", badPasswordTime),
|
||||
userAccountControl=
|
||||
dsdb.UF_NORMAL_ACCOUNT,
|
||||
@ -1193,6 +1314,8 @@ unicodePwd:: """ + base64.b64encode("\"thatsAcomplPASS2\"".encode('utf-16-le'))
|
||||
res = self._check_account("cn=testuser,cn=users," + self.base_dn,
|
||||
badPwdCount=3, effective_bad_password_count=0,
|
||||
badPasswordTime=badPasswordTime,
|
||||
lastLogon=lastLogon,
|
||||
lastLogonTimestamp=lastLogon,
|
||||
lockoutTime=lockoutTime,
|
||||
userAccountControl=
|
||||
dsdb.UF_NORMAL_ACCOUNT,
|
||||
@ -1207,34 +1330,44 @@ unicodePwd:: """ + base64.b64encode("\"thatsAcomplPASS2\"".encode('utf-16-le'))
|
||||
badPwdCount=3, effective_bad_password_count=0,
|
||||
badPasswordTime=badPasswordTime,
|
||||
lockoutTime=lockoutTime,
|
||||
lastLogon=lastLogon,
|
||||
lastLogonTimestamp=lastLogon,
|
||||
userAccountControl=
|
||||
dsdb.UF_NORMAL_ACCOUNT,
|
||||
msDSUserAccountControlComputed=0)
|
||||
|
||||
def _test_login_lockout(self, use_kerberos):
|
||||
# This unlocks by waiting for account_lockout_duration
|
||||
print "Performs a lockout attempt against LDAP using NTLM or Kerberos"
|
||||
if use_kerberos == MUST_USE_KERBEROS:
|
||||
lastlogon_relation = 'greater'
|
||||
print "Performs a lockout attempt against LDAP using Kerberos"
|
||||
else:
|
||||
lastlogon_relation = 'equal'
|
||||
print "Performs a lockout attempt against LDAP using NTLM"
|
||||
|
||||
# Change password on a connection as another user
|
||||
|
||||
res = self._check_account("cn=testuser,cn=users," + self.base_dn,
|
||||
badPwdCount=0,
|
||||
badPasswordTime=("greater", 0),
|
||||
lastLogon=("greater", 0),
|
||||
lastLogonTimestamp=("greater", 0),
|
||||
userAccountControl=
|
||||
dsdb.UF_NORMAL_ACCOUNT,
|
||||
msDSUserAccountControlComputed=0)
|
||||
badPasswordTime = int(res[0]["badPasswordTime"][0])
|
||||
lastLogon = int(res[0]["lastLogon"][0])
|
||||
firstLogon = lastLogon
|
||||
lastLogonTimestamp = int(res[0]["lastLogonTimestamp"][0])
|
||||
print firstLogon
|
||||
print lastLogonTimestamp
|
||||
|
||||
|
||||
self.assertGreater(lastLogon, badPasswordTime)
|
||||
|
||||
# Open a second LDB connection with the user credentials. Use the
|
||||
# command line credentials for informations like the domain, the realm
|
||||
# and the workstation.
|
||||
creds_lockout = Credentials()
|
||||
creds_lockout.set_username("testuser")
|
||||
creds_lockout.set_domain(creds.get_domain())
|
||||
creds_lockout.set_realm(creds.get_realm())
|
||||
creds_lockout.set_workstation(creds.get_workstation())
|
||||
creds_lockout.set_gensec_features(creds_lockout.get_gensec_features()
|
||||
| gensec.FEATURE_SEAL)
|
||||
creds_lockout = insta_creds()
|
||||
creds_lockout.set_kerberos_state(use_kerberos)
|
||||
|
||||
# The wrong password
|
||||
@ -1245,9 +1378,12 @@ unicodePwd:: """ + base64.b64encode("\"thatsAcomplPASS2\"".encode('utf-16-le'))
|
||||
res = self._check_account("cn=testuser,cn=users," + self.base_dn,
|
||||
badPwdCount=1,
|
||||
badPasswordTime=("greater", badPasswordTime),
|
||||
lastLogon=lastLogon,
|
||||
lastLogonTimestamp=lastLogonTimestamp,
|
||||
userAccountControl=
|
||||
dsdb.UF_NORMAL_ACCOUNT,
|
||||
msDSUserAccountControlComputed=0)
|
||||
msDSUserAccountControlComputed=0,
|
||||
msg='lastlogontimestamp with wrong password')
|
||||
badPasswordTime = int(res[0]["badPasswordTime"][0])
|
||||
|
||||
# Correct old password
|
||||
@ -1255,12 +1391,20 @@ unicodePwd:: """ + base64.b64encode("\"thatsAcomplPASS2\"".encode('utf-16-le'))
|
||||
|
||||
ldb_lockout = SamDB(url=host_url, credentials=creds_lockout, lp=lp)
|
||||
|
||||
# lastLogonTimestamp should not change
|
||||
# lastLogon increases if badPwdCount is non-zero (!)
|
||||
res = self._check_account("cn=testuser,cn=users," + self.base_dn,
|
||||
badPwdCount=0,
|
||||
badPasswordTime=badPasswordTime,
|
||||
lastLogon=('greater', lastLogon),
|
||||
lastLogonTimestamp=lastLogonTimestamp,
|
||||
userAccountControl=
|
||||
dsdb.UF_NORMAL_ACCOUNT,
|
||||
msDSUserAccountControlComputed=0)
|
||||
msDSUserAccountControlComputed=0,
|
||||
msg='LLTimestamp is updated to lastlogon')
|
||||
|
||||
lastLogon = int(res[0]["lastLogon"][0])
|
||||
self.assertGreater(lastLogon, badPasswordTime)
|
||||
|
||||
# The wrong password
|
||||
creds_lockout.set_password("thatsAcomplPASS1x")
|
||||
@ -1270,6 +1414,8 @@ unicodePwd:: """ + base64.b64encode("\"thatsAcomplPASS2\"".encode('utf-16-le'))
|
||||
res = self._check_account("cn=testuser,cn=users," + self.base_dn,
|
||||
badPwdCount=1,
|
||||
badPasswordTime=("greater", badPasswordTime),
|
||||
lastLogon=lastLogon,
|
||||
lastLogonTimestamp=lastLogonTimestamp,
|
||||
userAccountControl=
|
||||
dsdb.UF_NORMAL_ACCOUNT,
|
||||
msDSUserAccountControlComputed=0)
|
||||
@ -1288,6 +1434,8 @@ unicodePwd:: """ + base64.b64encode("\"thatsAcomplPASS2\"".encode('utf-16-le'))
|
||||
res = self._check_account("cn=testuser,cn=users," + self.base_dn,
|
||||
badPwdCount=2,
|
||||
badPasswordTime=("greater", badPasswordTime),
|
||||
lastLogon=lastLogon,
|
||||
lastLogonTimestamp=lastLogonTimestamp,
|
||||
userAccountControl=
|
||||
dsdb.UF_NORMAL_ACCOUNT,
|
||||
msDSUserAccountControlComputed=0)
|
||||
@ -1308,6 +1456,8 @@ unicodePwd:: """ + base64.b64encode("\"thatsAcomplPASS2\"".encode('utf-16-le'))
|
||||
res = self._check_account("cn=testuser,cn=users," + self.base_dn,
|
||||
badPwdCount=3,
|
||||
badPasswordTime=("greater", badPasswordTime),
|
||||
lastLogon=lastLogon,
|
||||
lastLogonTimestamp=lastLogonTimestamp,
|
||||
lockoutTime=("greater", badPasswordTime),
|
||||
userAccountControl=
|
||||
dsdb.UF_NORMAL_ACCOUNT,
|
||||
@ -1326,6 +1476,8 @@ unicodePwd:: """ + base64.b64encode("\"thatsAcomplPASS2\"".encode('utf-16-le'))
|
||||
res = self._check_account("cn=testuser,cn=users," + self.base_dn,
|
||||
badPwdCount=3,
|
||||
badPasswordTime=badPasswordTime,
|
||||
lastLogon=lastLogon,
|
||||
lastLogonTimestamp=lastLogonTimestamp,
|
||||
lockoutTime=lockoutTime,
|
||||
userAccountControl=
|
||||
dsdb.UF_NORMAL_ACCOUNT,
|
||||
@ -1342,12 +1494,14 @@ unicodePwd:: """ + base64.b64encode("\"thatsAcomplPASS2\"".encode('utf-16-le'))
|
||||
res = self._check_account("cn=testuser,cn=users," + self.base_dn,
|
||||
badPwdCount=3,
|
||||
badPasswordTime=badPasswordTime,
|
||||
lastLogon=lastLogon,
|
||||
lastLogonTimestamp=lastLogonTimestamp,
|
||||
lockoutTime=lockoutTime,
|
||||
userAccountControl=
|
||||
dsdb.UF_NORMAL_ACCOUNT,
|
||||
msDSUserAccountControlComputed=dsdb.UF_LOCKOUT)
|
||||
|
||||
# The correct password
|
||||
# The correct password, but we are locked out
|
||||
creds_lockout.set_password("thatsAcomplPASS1")
|
||||
try:
|
||||
ldb_lockout = SamDB(url=host_url, credentials=creds_lockout, lp=lp)
|
||||
@ -1358,32 +1512,50 @@ unicodePwd:: """ + base64.b64encode("\"thatsAcomplPASS2\"".encode('utf-16-le'))
|
||||
res = self._check_account("cn=testuser,cn=users," + self.base_dn,
|
||||
badPwdCount=3,
|
||||
badPasswordTime=badPasswordTime,
|
||||
lastLogon=lastLogon,
|
||||
lastLogonTimestamp=lastLogonTimestamp,
|
||||
lockoutTime=lockoutTime,
|
||||
userAccountControl=
|
||||
dsdb.UF_NORMAL_ACCOUNT,
|
||||
msDSUserAccountControlComputed=dsdb.UF_LOCKOUT)
|
||||
|
||||
# wait for the lockout to end
|
||||
time.sleep(self.account_lockout_duration + 1)
|
||||
print self.account_lockout_duration + 1
|
||||
|
||||
res = self._check_account("cn=testuser,cn=users," + self.base_dn,
|
||||
badPwdCount=3, effective_bad_password_count=0,
|
||||
badPasswordTime=badPasswordTime,
|
||||
lockoutTime=lockoutTime,
|
||||
lastLogon=lastLogon,
|
||||
lastLogonTimestamp=lastLogonTimestamp,
|
||||
userAccountControl=
|
||||
dsdb.UF_NORMAL_ACCOUNT,
|
||||
msDSUserAccountControlComputed=0)
|
||||
|
||||
lastLogon = int(res[0]["lastLogon"][0])
|
||||
|
||||
# The correct password after letting the timeout expire
|
||||
|
||||
creds_lockout.set_password("thatsAcomplPASS1")
|
||||
ldb_lockout = SamDB(url=host_url, credentials=creds_lockout, lp=lp)
|
||||
|
||||
creds_lockout2 = insta_creds(creds_lockout)
|
||||
|
||||
ldb_lockout = SamDB(url=host_url, credentials=creds_lockout2, lp=lp)
|
||||
time.sleep(3)
|
||||
|
||||
res = self._check_account("cn=testuser,cn=users," + self.base_dn,
|
||||
badPwdCount=0,
|
||||
badPasswordTime=badPasswordTime,
|
||||
lastLogon=(lastlogon_relation, lastLogon),
|
||||
lastLogonTimestamp=lastLogonTimestamp,
|
||||
lockoutTime=0,
|
||||
userAccountControl=
|
||||
dsdb.UF_NORMAL_ACCOUNT,
|
||||
msDSUserAccountControlComputed=0)
|
||||
msDSUserAccountControlComputed=0,
|
||||
msg="lastLogon is way off")
|
||||
|
||||
lastLogon = int(res[0]["lastLogon"][0])
|
||||
|
||||
# The wrong password
|
||||
creds_lockout.set_password("thatsAcomplPASS1x")
|
||||
@ -1397,6 +1569,8 @@ unicodePwd:: """ + base64.b64encode("\"thatsAcomplPASS2\"".encode('utf-16-le'))
|
||||
badPwdCount=1,
|
||||
badPasswordTime=("greater", badPasswordTime),
|
||||
lockoutTime=0,
|
||||
lastLogon=lastLogon,
|
||||
lastLogonTimestamp=lastLogonTimestamp,
|
||||
userAccountControl=
|
||||
dsdb.UF_NORMAL_ACCOUNT,
|
||||
msDSUserAccountControlComputed=0)
|
||||
@ -1414,6 +1588,8 @@ unicodePwd:: """ + base64.b64encode("\"thatsAcomplPASS2\"".encode('utf-16-le'))
|
||||
badPwdCount=2,
|
||||
badPasswordTime=("greater", badPasswordTime),
|
||||
lockoutTime=0,
|
||||
lastLogon=lastLogon,
|
||||
lastLogonTimestamp=lastLogonTimestamp,
|
||||
userAccountControl=
|
||||
dsdb.UF_NORMAL_ACCOUNT,
|
||||
msDSUserAccountControlComputed=0)
|
||||
@ -1425,6 +1601,8 @@ unicodePwd:: """ + base64.b64encode("\"thatsAcomplPASS2\"".encode('utf-16-le'))
|
||||
badPwdCount=2, effective_bad_password_count=0,
|
||||
badPasswordTime=badPasswordTime,
|
||||
lockoutTime=0,
|
||||
lastLogon=lastLogon,
|
||||
lastLogonTimestamp=lastLogonTimestamp,
|
||||
userAccountControl=
|
||||
dsdb.UF_NORMAL_ACCOUNT,
|
||||
msDSUserAccountControlComputed=0)
|
||||
@ -1441,6 +1619,8 @@ unicodePwd:: """ + base64.b64encode("\"thatsAcomplPASS2\"".encode('utf-16-le'))
|
||||
badPwdCount=1,
|
||||
badPasswordTime=("greater", badPasswordTime),
|
||||
lockoutTime=0,
|
||||
lastLogon=lastLogon,
|
||||
lastLogonTimestamp=lastLogonTimestamp,
|
||||
userAccountControl=
|
||||
dsdb.UF_NORMAL_ACCOUNT,
|
||||
msDSUserAccountControlComputed=0)
|
||||
@ -1454,6 +1634,8 @@ unicodePwd:: """ + base64.b64encode("\"thatsAcomplPASS2\"".encode('utf-16-le'))
|
||||
badPwdCount=0,
|
||||
badPasswordTime=badPasswordTime,
|
||||
lockoutTime=0,
|
||||
lastLogon=("greater", lastLogon),
|
||||
lastLogonTimestamp=lastLogonTimestamp,
|
||||
userAccountControl=
|
||||
dsdb.UF_NORMAL_ACCOUNT,
|
||||
msDSUserAccountControlComputed=0)
|
||||
@ -1464,6 +1646,78 @@ unicodePwd:: """ + base64.b64encode("\"thatsAcomplPASS2\"".encode('utf-16-le'))
|
||||
def test_login_lockout_kerberos(self):
|
||||
self._test_login_lockout(MUST_USE_KERBEROS)
|
||||
|
||||
def _test_multiple_logon(self, use_kerberos):
|
||||
# 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 informations like the domain, the realm
|
||||
# and the workstation.
|
||||
creds2 = insta_creds()
|
||||
creds2.set_kerberos_state(use_kerberos)
|
||||
|
||||
if use_kerberos == MUST_USE_KERBEROS:
|
||||
print "Testing multiple logon with Kerberos"
|
||||
lastlogon_relation = 'greater'
|
||||
else:
|
||||
print "Testing multiple logon with NTLM"
|
||||
lastlogon_relation = 'equal'
|
||||
|
||||
SamDB(url=host_url, credentials=insta_creds(creds2), lp=lp)
|
||||
|
||||
res = self._check_account("cn=testuser,cn=users," + self.base_dn,
|
||||
badPwdCount=0,
|
||||
badPasswordTime=("greater", 0),
|
||||
lastLogon=("greater", 0),
|
||||
lastLogonTimestamp=("greater", 0),
|
||||
userAccountControl=
|
||||
dsdb.UF_NORMAL_ACCOUNT,
|
||||
msDSUserAccountControlComputed=0)
|
||||
badPasswordTime = int(res[0]["badPasswordTime"][0])
|
||||
lastLogon = int(res[0]["lastLogon"][0])
|
||||
lastLogonTimestamp = int(res[0]["lastLogonTimestamp"][0])
|
||||
firstLogon = lastLogon
|
||||
print "last logon is %d" % lastLogon
|
||||
self.assertGreater(lastLogon, badPasswordTime)
|
||||
|
||||
time.sleep(1)
|
||||
SamDB(url=host_url, credentials=insta_creds(creds2), lp=lp)
|
||||
|
||||
res = self._check_account("cn=testuser,cn=users," + self.base_dn,
|
||||
badPwdCount=0,
|
||||
badPasswordTime=badPasswordTime,
|
||||
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=host_url, credentials=insta_creds(creds2), lp=lp)
|
||||
|
||||
res = self._check_account("cn=testuser,cn=users," + self.base_dn,
|
||||
badPwdCount=0,
|
||||
badPasswordTime=badPasswordTime,
|
||||
lastLogon=(lastlogon_relation, lastLogon),
|
||||
lastLogonTimestamp=lastLogonTimestamp,
|
||||
userAccountControl=
|
||||
dsdb.UF_NORMAL_ACCOUNT,
|
||||
msDSUserAccountControlComputed=0)
|
||||
|
||||
def test_multiple_logon_ntlm(self):
|
||||
self._test_multiple_logon(DONT_USE_KERBEROS)
|
||||
|
||||
def test_multiple_logon_kerberos(self):
|
||||
self._test_multiple_logon(MUST_USE_KERBEROS)
|
||||
|
||||
def tearDown(self):
|
||||
super(PasswordTests, self).tearDown()
|
||||
delete_force(self.ldb, "cn=testuser,cn=users," + self.base_dn)
|
||||
|
@ -298,10 +298,13 @@ static krb5_error_code hdb_samba4_auth_status(krb5_context context, HDB *db,
|
||||
struct samba_kdc_db_context);
|
||||
struct samba_kdc_entry *p = talloc_get_type(entry->ctx, struct samba_kdc_entry);
|
||||
|
||||
struct ldb_dn *domain_dn = ldb_get_default_basedn(kdc_db_ctx->samdb);
|
||||
|
||||
if (hdb_auth_status == HDB_AUTH_WRONG_PASSWORD) {
|
||||
authsam_update_bad_pwd_count(kdc_db_ctx->samdb, p->msg, ldb_get_default_basedn(kdc_db_ctx->samdb));
|
||||
authsam_update_bad_pwd_count(kdc_db_ctx->samdb, p->msg, domain_dn);
|
||||
} else if (hdb_auth_status == HDB_AUTH_SUCCESS) {
|
||||
authsam_zero_bad_pwd_count(kdc_db_ctx->samdb, p->msg);
|
||||
authsam_logon_success_accounting(kdc_db_ctx->samdb, p->msg,
|
||||
domain_dn, true);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user