The Samba-Bugzilla – Attachment 11754 Details for
Bug 11659
lastLogon and lastLogonTimestamp are not updated
Home
|
New
|
Browse
|
Search
|
[?]
|
Reports
|
Requests
|
Help
|
New Account
|
Log In
[x]
|
Forgot Password
Login:
[x]
[patch]
Patch from master with cherry-pick information (for v4-3-test)
bug11659-v43.patch (text/plain), 67.21 KB, created by
Karolin Seeger
on 2016-01-04 11:41:41 UTC
(
hide
)
Description:
Patch from master with cherry-pick information (for v4-3-test)
Filename:
MIME Type:
Creator:
Karolin Seeger
Created:
2016-01-04 11:41:41 UTC
Size:
67.21 KB
patch
obsolete
>From 716c4ebb2df5f3efb3e64b8cced4daa71f7233b8 Mon Sep 17 00:00:00 2001 >From: Douglas Bagnall <douglas.bagnall@catalyst.net.nz> >Date: Tue, 1 Dec 2015 13:17:18 +1300 >Subject: [PATCH 1/5] pycredentials: add get_kerberos_state() method > >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> >(cherry picked from commit 8b26559aeb6d1c2c12e2ea374c30e4082ece7ec3) > >BUG: https://bugzilla.samba.org/show_bug.cgi?id=11659 >lastLogon and lastLogonTimestamp are not updated >--- > auth/credentials/pycredentials.c | 8 ++++++++ > 1 file changed, 8 insertions(+) > >diff --git a/auth/credentials/pycredentials.c b/auth/credentials/pycredentials.c >index e32d9a9..5fc2a70 100644 >--- a/auth/credentials/pycredentials.c >+++ b/auth/credentials/pycredentials.c >@@ -209,6 +209,12 @@ static PyObject *py_creds_get_nt_hash(pytalloc_Object *self) > return PyString_FromStringAndSize(discard_const_p(char, ntpw->hash), 16); > } > >+static PyObject *py_creds_get_kerberos_state(pytalloc_Object *self) >+{ >+ int state = cli_credentials_get_kerberos_state(PyCredentials_AsCliCredentials(self)); >+ return PyInt_FromLong(state); >+} >+ > static PyObject *py_creds_set_kerberos_state(pytalloc_Object *self, PyObject *args) > { > int state; >@@ -452,6 +458,8 @@ static PyMethodDef py_creds_methods[] = { > "Parse credentials string." }, > { "get_nt_hash", (PyCFunction)py_creds_get_nt_hash, METH_NOARGS, > NULL }, >+ { "get_kerberos_state", (PyCFunction)py_creds_get_kerberos_state, METH_NOARGS, >+ NULL }, > { "set_kerberos_state", (PyCFunction)py_creds_set_kerberos_state, METH_VARARGS, > NULL }, > { "set_krb_forwardable", (PyCFunction)py_creds_set_krb_forwardable, METH_VARARGS, >-- >1.9.1 > > >From bc5a1d837710f70dbc337fda04c7c9f5e57cd580 Mon Sep 17 00:00:00 2001 >From: Douglas Bagnall <douglas.bagnall@catalyst.net.nz> >Date: Thu, 22 Oct 2015 09:45:26 +1300 >Subject: [PATCH 2/5] auth: increase resolution for password grace period > calculation > >This changes the resolution of "now" from 1s to 100ns. > >It should have little effect in practice, unless users are in the >habit of playing chicken with the grace period. > >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> >(cherry picked from commit d097e813fff3aaed261a18d8066e6bd11f12abad) > >BUG: https://bugzilla.samba.org/show_bug.cgi?id=11659 >lastLogon and lastLogonTimestamp are not updated >--- > source4/auth/ntlm/auth_sam.c | 4 +++- > 1 file changed, 3 insertions(+), 1 deletion(-) > >diff --git a/source4/auth/ntlm/auth_sam.c b/source4/auth/ntlm/auth_sam.c >index 17f3cfc..6da25a0 100644 >--- a/source4/auth/ntlm/auth_sam.c >+++ b/source4/auth/ntlm/auth_sam.c >@@ -294,6 +294,7 @@ static NTSTATUS authsam_password_check_and_record(struct auth4_context *auth_con > struct samr_Password *nt_history_pwd = NULL; > struct samr_Password *lm_history_pwd = NULL; > NTTIME pwdLastSet; >+ struct timeval tv_now; > NTTIME now; > int allowed_period_mins; > NTTIME allowed_period; >@@ -414,7 +415,8 @@ static NTSTATUS authsam_password_check_and_record(struct auth4_context *auth_con > */ > allowed_period = allowed_period_mins * 60 * 1000*1000*10; > pwdLastSet = samdb_result_nttime(msg, "pwdLastSet", 0); >- unix_to_nt_time(&now, time(NULL)); >+ tv_now = timeval_current(); >+ now = timeval_to_nttime(&tv_now); > > if (now < pwdLastSet) { > /* >-- >1.9.1 > > >From 57d17a0b503a9036447d7367c5bb1b120a9a6d2e Mon Sep 17 00:00:00 2001 >From: Douglas Bagnall <douglas.bagnall@catalyst.net.nz> >Date: Thu, 22 Oct 2015 16:54:19 +1300 >Subject: [PATCH 3/5] password_lockout tests: add assertLoginFailure() > >In a few places where a login should fail in a particular way, an >actual login success would not have triggered a test failure -- only >the wrong kind of login failure was caught. > >This makes a helper function to deal with them all. > >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> >(cherry picked from commit 909ebe0191a409c107904df658dc9111dd5de669) > >BUG: https://bugzilla.samba.org/show_bug.cgi?id=11659 >lastLogon and lastLogonTimestamp are not updated >--- > source4/dsdb/tests/python/password_lockout.py | 22 ++++++++++++---------- > 1 file changed, 12 insertions(+), 10 deletions(-) > >diff --git a/source4/dsdb/tests/python/password_lockout.py b/source4/dsdb/tests/python/password_lockout.py >index 133b40b..fddbb8f 100755 >--- a/source4/dsdb/tests/python/password_lockout.py >+++ b/source4/dsdb/tests/python/password_lockout.py >@@ -233,6 +233,16 @@ userAccountControl: %d > time.sleep(0.01) > return res > >+ def assertLoginFailure(self, url, creds, lp, errno=ERR_INVALID_CREDENTIALS): >+ try: >+ ldb = SamDB(url=url, credentials=creds, lp=lp) >+ self.fail("Login unexpectedly succeeded") >+ except LdbError, (num, msg): >+ if errno is not None: >+ self.assertEquals(num, errno, ("Login failed in the wrong way" >+ "(got err %d, expected %d)" % >+ (num, errno))) >+ > def setUp(self): > super(PasswordTests, self).setUp() > >@@ -1230,11 +1240,7 @@ unicodePwd:: """ + base64.b64encode("\"thatsAcomplPASS2\"".encode('utf-16-le')) > # The wrong password > creds_lockout.set_password("thatsAcomplPASS1x") > >- try: >- ldb_lockout = SamDB(url=host_url, credentials=creds_lockout, lp=lp) >- >- except LdbError, (num, msg): >- self.assertEquals(num, ERR_INVALID_CREDENTIALS) >+ self.assertLoginFailure(host_url, creds_lockout, lp) > > res = self._check_account("cn=testuser,cn=users," + self.base_dn, > badPwdCount=1, >@@ -1259,11 +1265,7 @@ unicodePwd:: """ + base64.b64encode("\"thatsAcomplPASS2\"".encode('utf-16-le')) > # The wrong password > creds_lockout.set_password("thatsAcomplPASS1x") > >- try: >- ldb_lockout = SamDB(url=host_url, credentials=creds_lockout, lp=lp) >- >- except LdbError, (num, msg): >- self.assertEquals(num, ERR_INVALID_CREDENTIALS) >+ self.assertLoginFailure(host_url, creds_lockout, lp) > > res = self._check_account("cn=testuser,cn=users," + self.base_dn, > badPwdCount=1, >-- >1.9.1 > > >From 1ed99211cfffde2025e42c57a1848a1720a575ff Mon Sep 17 00:00:00 2001 >From: Douglas Bagnall <douglas.bagnall@catalyst.net.nz> >Date: Fri, 23 Oct 2015 16:57:56 +1300 >Subject: [PATCH 4/5] 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> >(cherry picked from commit 795f4729ca94029fcee750fbebbe9bc3ea43a214) > >BUG: https://bugzilla.samba.org/show_bug.cgi?id=11659 >lastLogon and lastLogonTimestamp are not updated >--- > source4/auth/ntlm/auth_sam.c | 5 +- > source4/auth/sam.c | 158 +++++++++++- > source4/dsdb/tests/python/password_lockout.py | 336 ++++++++++++++++++++++---- > source4/kdc/hdb-samba4.c | 7 +- > 4 files changed, 451 insertions(+), 55 deletions(-) > >diff --git a/source4/auth/ntlm/auth_sam.c b/source4/auth/ntlm/auth_sam.c >index 6da25a0..43c7a3d 100644 >--- a/source4/auth/ntlm/auth_sam.c >+++ b/source4/auth/ntlm/auth_sam.c >@@ -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; >diff --git a/source4/auth/sam.c b/source4/auth/sam.c >index f7bc693..cdfe8dd 100644 >--- a/source4/auth/sam.c >+++ b/source4/auth/sam.c >@@ -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; > } >diff --git a/source4/dsdb/tests/python/password_lockout.py b/source4/dsdb/tests/python/password_lockout.py >index fddbb8f..e669532 100755 >--- a/source4/dsdb/tests/python/password_lockout.py >+++ b/source4/dsdb/tests/python/password_lockout.py >@@ -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) >diff --git a/source4/kdc/hdb-samba4.c b/source4/kdc/hdb-samba4.c >index 77f6a90..c4a4bb4 100644 >--- a/source4/kdc/hdb-samba4.c >+++ b/source4/kdc/hdb-samba4.c >@@ -184,10 +184,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; > } >-- >1.9.1 > > >From 7f545ac6385c1774d0b90ab5fe1ab694613a02dd Mon Sep 17 00:00:00 2001 >From: Douglas Bagnall <douglas.bagnall@catalyst.net.nz> >Date: Tue, 1 Dec 2015 13:48:59 +1300 >Subject: [PATCH 5/5] password_lockout: test creds.get_kerberos_state() > >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> > >Autobuild-User(master): Garming Sam <garming@samba.org> >Autobuild-Date(master): Tue Dec 15 03:17:52 CET 2015 on sn-devel-104 > >(cherry picked from commit ab1ebb1d1c650396841e4ba4a18b3c08689d4f52) > >BUG: https://bugzilla.samba.org/show_bug.cgi?id=11659 >lastLogon and lastLogonTimestamp are not updated >--- > source4/dsdb/tests/python/password_lockout.py | 1 + > 1 file changed, 1 insertion(+) > >diff --git a/source4/dsdb/tests/python/password_lockout.py b/source4/dsdb/tests/python/password_lockout.py >index e669532..e6badbc 100755 >--- a/source4/dsdb/tests/python/password_lockout.py >+++ b/source4/dsdb/tests/python/password_lockout.py >@@ -1657,6 +1657,7 @@ unicodePwd:: """ + base64.b64encode("\"thatsAcomplPASS2\"".encode('utf-16-le')) > # and the workstation. > creds2 = insta_creds() > creds2.set_kerberos_state(use_kerberos) >+ self.assertEqual(creds2.get_kerberos_state(), use_kerberos) > > if use_kerberos == MUST_USE_KERBEROS: > print "Testing multiple logon with Kerberos" >-- >1.9.1 >
You cannot view the attachment while viewing its details because your browser does not support IFRAMEs.
View the attachment on a separate page
.
View Attachment As Raw
Flags:
metze
:
review+
kseeger
:
review?
(
abartlet
)
kseeger
:
review?
(
slow
)
Actions:
View
Attachments on
bug 11659
: 11754