[SCM] Samba Shared Repository - branch master updated

Garming Sam garming at samba.org
Tue Dec 15 07:39:50 UTC 2015


The branch, master has been updated
       via  ab1ebb1 password_lockout: test creds.get_kerberos_state()
       via  795f472 auth: keep track of lastLogon and lastLogonTimestamp
       via  909ebe0 password_lockout tests: add assertLoginFailure()
       via  d097e81 auth: increase resolution for password grace period calculation
       via  8b26559 pycredentials: add get_kerberos_state() method
      from  203f023 s4:torture/winbind: add more debug output to samba4.winbind.struct.domain_info

https://git.samba.org/?p=samba.git;a=shortlog;h=master


- Log -----------------------------------------------------------------
commit ab1ebb1d1c650396841e4ba4a18b3c08689d4f52
Author: Douglas Bagnall <douglas.bagnall at catalyst.net.nz>
Date:   Tue Dec 1 13:48:59 2015 +1300

    password_lockout: test creds.get_kerberos_state()
    
    Signed-off-by: Douglas Bagnall <douglas.bagnall at catalyst.net.nz>
    Reviewed-by: Garming Sam <garming at catalyst.net.nz>
    Reviewed-by: Ralph Boehme <slow at samba.org>
    
    Autobuild-User(master): Garming Sam <garming at samba.org>
    Autobuild-Date(master): Tue Dec 15 03:17:52 CET 2015 on sn-devel-104

commit 795f4729ca94029fcee750fbebbe9bc3ea43a214
Author: Douglas Bagnall <douglas.bagnall at catalyst.net.nz>
Date:   Fri Oct 23 16:57:56 2015 +1300

    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 at catalyst.net.nz>
    Reviewed-by: Garming Sam <garming at catalyst.net.nz>
    Reviewed-by: Ralph Boehme <slow at samba.org>

commit 909ebe0191a409c107904df658dc9111dd5de669
Author: Douglas Bagnall <douglas.bagnall at catalyst.net.nz>
Date:   Thu Oct 22 16:54:19 2015 +1300

    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 at catalyst.net.nz>
    Reviewed-by: Garming Sam <garming at catalyst.net.nz>
    Reviewed-by: Ralph Boehme <slow at samba.org>

commit d097e813fff3aaed261a18d8066e6bd11f12abad
Author: Douglas Bagnall <douglas.bagnall at catalyst.net.nz>
Date:   Thu Oct 22 09:45:26 2015 +1300

    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 at catalyst.net.nz>
    Reviewed-by: Garming Sam <garming at catalyst.net.nz>
    Reviewed-by: Ralph Boehme <slow at samba.org>

commit 8b26559aeb6d1c2c12e2ea374c30e4082ece7ec3
Author: Douglas Bagnall <douglas.bagnall at catalyst.net.nz>
Date:   Tue Dec 1 13:17:18 2015 +1300

    pycredentials: add get_kerberos_state() method
    
    Signed-off-by: Douglas Bagnall <douglas.bagnall at catalyst.net.nz>
    Reviewed-by: Garming Sam <garming at catalyst.net.nz>
    Reviewed-by: Ralph Boehme <slow at samba.org>

-----------------------------------------------------------------------

Summary of changes:
 auth/credentials/pycredentials.c              |   8 +
 source4/auth/ntlm/auth_sam.c                  |   9 +-
 source4/auth/sam.c                            | 158 +++++++++++-
 source4/dsdb/tests/python/password_lockout.py | 359 ++++++++++++++++++++++----
 source4/kdc/hdb-samba4.c                      |   7 +-
 5 files changed, 475 insertions(+), 66 deletions(-)


Changeset truncated at 500 lines:

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,
diff --git a/source4/auth/ntlm/auth_sam.c b/source4/auth/ntlm/auth_sam.c
index 17f3cfc..43c7a3d 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) {
 			/*
@@ -491,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) {
@@ -526,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 133b40b..e6badbc 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",
@@ -233,10 +272,21 @@ 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()
 
-        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()
@@ -315,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)
 
@@ -329,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 |
@@ -343,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 |
@@ -371,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 |
@@ -394,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 |
@@ -406,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)
@@ -413,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({
@@ -441,6 +499,8 @@ userPassword: thatsAcomplPASS1
         res = self._check_account("cn=testuser3,cn=users," + self.base_dn,
                                   badPwdCount=0,
                                   badPasswordTime=0,


-- 
Samba Shared Repository



More information about the samba-cvs mailing list