"homeDirectory",
"homeDrive",
"lastLogon",
+ "lastLogonTimestamp",
"lastLogoff",
"accountExpires",
"badPwdCount",
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) {
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);
}
}
- 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;
}
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
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))
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"
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",
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()
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)
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 |
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 |
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 |
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 |
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)
# 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({
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 |
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 |
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 |
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)
# 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)
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
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)
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)
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)
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,
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,
badPwdCount=3,
badPasswordTime=badPasswordTime,
lockoutTime=lockoutTime,
+ lastLogon=lastLogon,
+ lastLogonTimestamp=lastLogonTimestamp,
userAccountControl=
dsdb.UF_NORMAL_ACCOUNT,
msDSUserAccountControlComputed=dsdb.UF_LOCKOUT)
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,
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,
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,
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,
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)
badPwdCount=0,
badPasswordTime=badPasswordTime,
lockoutTime=0,
+ lastLogon=lastLogon,
+ lastLogonTimestamp=lastLogonTimestamp,
userAccountControl=
dsdb.UF_NORMAL_ACCOUNT,
msDSUserAccountControlComputed=0)
badPwdCount=1,
badPasswordTime=("greater", badPasswordTime),
lockoutTime=0,
+ lastLogon=lastLogon,
+ lastLogonTimestamp=lastLogonTimestamp,
userAccountControl=
dsdb.UF_NORMAL_ACCOUNT,
msDSUserAccountControlComputed=0)
badPwdCount=2,
badPasswordTime=("greater", badPasswordTime),
lockoutTime=0,
+ lastLogon=lastLogon,
+ lastLogonTimestamp=lastLogonTimestamp,
userAccountControl=
dsdb.UF_NORMAL_ACCOUNT,
msDSUserAccountControlComputed=0)
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,
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
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)
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)
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)
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)
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,
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,
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,
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,
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,
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,
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,
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,
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,
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,
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,
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
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
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")
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)
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)
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,
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,
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)
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")
badPwdCount=1,
badPasswordTime=("greater", badPasswordTime),
lockoutTime=0,
+ lastLogon=lastLogon,
+ lastLogonTimestamp=lastLogonTimestamp,
userAccountControl=
dsdb.UF_NORMAL_ACCOUNT,
msDSUserAccountControlComputed=0)
badPwdCount=2,
badPasswordTime=("greater", badPasswordTime),
lockoutTime=0,
+ lastLogon=lastLogon,
+ lastLogonTimestamp=lastLogonTimestamp,
userAccountControl=
dsdb.UF_NORMAL_ACCOUNT,
msDSUserAccountControlComputed=0)
badPwdCount=2, effective_bad_password_count=0,
badPasswordTime=badPasswordTime,
lockoutTime=0,
+ lastLogon=lastLogon,
+ lastLogonTimestamp=lastLogonTimestamp,
userAccountControl=
dsdb.UF_NORMAL_ACCOUNT,
msDSUserAccountControlComputed=0)
badPwdCount=1,
badPasswordTime=("greater", badPasswordTime),
lockoutTime=0,
+ lastLogon=lastLogon,
+ lastLogonTimestamp=lastLogonTimestamp,
userAccountControl=
dsdb.UF_NORMAL_ACCOUNT,
msDSUserAccountControlComputed=0)
badPwdCount=0,
badPasswordTime=badPasswordTime,
lockoutTime=0,
+ lastLogon=("greater", lastLogon),
+ lastLogonTimestamp=lastLogonTimestamp,
userAccountControl=
dsdb.UF_NORMAL_ACCOUNT,
msDSUserAccountControlComputed=0)
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)