1 from __future__ import print_function
4 from samba.auth import system_session
5 from samba.credentials import Credentials, DONT_USE_KERBEROS, MUST_USE_KERBEROS
6 from ldb import SCOPE_BASE, LdbError
7 from ldb import ERR_CONSTRAINT_VIOLATION
8 from ldb import ERR_INVALID_CREDENTIALS
9 from ldb import Message, MessageElement, Dn
10 from ldb import FLAG_MOD_REPLACE
11 from samba import gensec, dsdb
12 from samba.samdb import SamDB
14 from samba.tests import delete_force
15 from samba.dcerpc import security, samr
16 from samba.ndr import ndr_unpack
17 from samba.tests.password_test import PasswordTestCase
22 class BasePasswordTestCase(PasswordTestCase):
23 def _open_samr_user(self, res):
24 self.assertTrue("objectSid" in res[0])
26 (domain_sid, rid) = ndr_unpack(security.dom_sid, res[0]["objectSid"][0]).split()
27 self.assertEquals(self.domain_sid, domain_sid)
29 return self.samr.OpenUser(self.samr_domain, security.SEC_FLAG_MAXIMUM_ALLOWED, rid)
31 def _check_attribute(self, res, name, value):
33 self.assertTrue(name not in res[0],
34 msg="attr[%s]=%r on dn[%s]" %
35 (name, res[0], res[0].dn))
38 if isinstance(value, tuple):
47 self.assertFalse(name in res[0],
48 msg="attr[%s] not missing on dn[%s]" %
52 self.assertTrue(name in res[0],
53 msg="attr[%s] missing on dn[%s]" %
55 self.assertTrue(len(res[0][name]) == 1,
56 msg="attr[%s]=%r on dn[%s]" %
57 (name, res[0][name], res[0].dn))
59 print("%s = '%s'" % (name, res[0][name][0]))
65 v = int(res[0][name][0])
67 msg = ("attr[%s]=[%s] != [%s] on dn[%s]\n"
68 "(diff %d; actual value is %s than expected)" %
69 (name, v, value, res[0].dn, v - value,
70 ('less' if v < value else 'greater')))
72 self.assertTrue(v == value, msg)
76 v = int(res[0][name][0])
77 self.assertTrue(v > int(value),
78 msg="attr[%s]=[%s] <= [%s] on dn[%s] (diff %d)" %
79 (name, v, int(value), res[0].dn, v - int(value)))
82 v = int(res[0][name][0])
83 self.assertTrue(v < int(value),
84 msg="attr[%s]=[%s] >= [%s] on dn[%s] (diff %d)" %
85 (name, v, int(value), res[0].dn, v - int(value)))
87 self.assertEqual(mode, not mode, "Invalid Mode[%s]" % mode)
89 def _check_account_initial(self, userdn):
90 self._check_account(userdn,
95 lastLogonTimestamp=("absent", None),
96 userAccountControl=dsdb.UF_NORMAL_ACCOUNT,
97 msDSUserAccountControlComputed=0)
99 def _check_account(self, dn,
101 badPasswordTime=None,
104 lastLogonTimestamp=None,
106 userAccountControl=None,
107 msDSUserAccountControlComputed=None,
108 effective_bad_password_count=None,
110 badPwdCountOnly=False):
113 print("\033[01;32m %s \033[00m\n" % msg)
119 "lastLogonTimestamp",
122 "userAccountControl",
123 "msDS-User-Account-Control-Computed"
126 # in order to prevent some time resolution problems we sleep for
130 res = self.ldb.search(dn, scope=SCOPE_BASE, attrs=attrs)
131 self.assertTrue(len(res) == 1)
132 self._check_attribute(res, "badPwdCount", badPwdCount)
133 self._check_attribute(res, "lockoutTime", lockoutTime)
134 self._check_attribute(res, "badPasswordTime", badPasswordTime)
135 if not badPwdCountOnly:
136 self._check_attribute(res, "logonCount", logonCount)
137 self._check_attribute(res, "lastLogon", lastLogon)
138 self._check_attribute(res, "lastLogonTimestamp", lastLogonTimestamp)
139 self._check_attribute(res, "userAccountControl", userAccountControl)
140 self._check_attribute(res, "msDS-User-Account-Control-Computed",
141 msDSUserAccountControlComputed)
143 lastLogon = int(res[0]["lastLogon"][0])
144 logonCount = int(res[0]["logonCount"][0])
146 samr_user = self._open_samr_user(res)
147 uinfo3 = self.samr.QueryUserInfo(samr_user, 3)
148 uinfo5 = self.samr.QueryUserInfo(samr_user, 5)
149 uinfo16 = self.samr.QueryUserInfo(samr_user, 16)
150 uinfo21 = self.samr.QueryUserInfo(samr_user, 21)
151 self.samr.Close(samr_user)
153 expected_acb_info = 0
154 if not badPwdCountOnly:
155 if userAccountControl & dsdb.UF_NORMAL_ACCOUNT:
156 expected_acb_info |= samr.ACB_NORMAL
157 if userAccountControl & dsdb.UF_ACCOUNTDISABLE:
158 expected_acb_info |= samr.ACB_DISABLED
159 if userAccountControl & dsdb.UF_PASSWD_NOTREQD:
160 expected_acb_info |= samr.ACB_PWNOTREQ
161 if msDSUserAccountControlComputed & dsdb.UF_LOCKOUT:
162 expected_acb_info |= samr.ACB_AUTOLOCK
163 if msDSUserAccountControlComputed & dsdb.UF_PASSWORD_EXPIRED:
164 expected_acb_info |= samr.ACB_PW_EXPIRED
166 self.assertEquals(uinfo3.acct_flags, expected_acb_info)
167 self.assertEquals(uinfo3.last_logon, lastLogon)
168 self.assertEquals(uinfo3.logon_count, logonCount)
170 expected_bad_password_count = 0
171 if badPwdCount is not None:
172 expected_bad_password_count = badPwdCount
173 if effective_bad_password_count is None:
174 effective_bad_password_count = expected_bad_password_count
176 self.assertEquals(uinfo3.bad_password_count, expected_bad_password_count)
178 if not badPwdCountOnly:
179 self.assertEquals(uinfo5.acct_flags, expected_acb_info)
180 self.assertEquals(uinfo5.bad_password_count, effective_bad_password_count)
181 self.assertEquals(uinfo5.last_logon, lastLogon)
182 self.assertEquals(uinfo5.logon_count, logonCount)
184 self.assertEquals(uinfo16.acct_flags, expected_acb_info)
186 self.assertEquals(uinfo21.acct_flags, expected_acb_info)
187 self.assertEquals(uinfo21.bad_password_count, effective_bad_password_count)
188 self.assertEquals(uinfo21.last_logon, lastLogon)
189 self.assertEquals(uinfo21.logon_count, logonCount)
191 # check LDAP again and make sure the samr.QueryUserInfo
192 # doesn't have any impact.
193 res2 = self.ldb.search(dn, scope=SCOPE_BASE, attrs=attrs)
194 self.assertEquals(res[0], res2[0])
196 # in order to prevent some time resolution problems we sleep for
201 def update_lockout_settings(self, threshold, duration, observation_window):
202 """Updates the global user lockout settings"""
204 m.dn = Dn(self.ldb, self.base_dn)
205 account_lockout_duration_ticks = -int(duration * (1e7))
206 m["lockoutDuration"] = MessageElement(str(account_lockout_duration_ticks),
207 FLAG_MOD_REPLACE, "lockoutDuration")
208 m["lockoutThreshold"] = MessageElement(str(threshold),
209 FLAG_MOD_REPLACE, "lockoutThreshold")
210 lockout_observation_window_ticks = -int(observation_window * (1e7))
211 m["lockOutObservationWindow"] = MessageElement(str(lockout_observation_window_ticks),
212 FLAG_MOD_REPLACE, "lockOutObservationWindow")
215 def _readd_user(self, creds, lockOutObservationWindow=0):
216 username = creds.get_username()
217 userpass = creds.get_password()
218 userdn = "cn=%s,cn=users,%s" % (username, self.base_dn)
220 delete_force(self.ldb, userdn)
223 "objectclass": "user",
224 "sAMAccountName": username})
226 self.addCleanup(delete_force, self.ldb, userdn)
228 # Sets the initial user password with a "special" password change
229 # I think that this internally is a password set operation and it can
230 # only be performed by someone which has password set privileges on the
231 # account (at least in s4 we do handle it like that).
232 self.ldb.modify_ldif("""
233 dn: """ + userdn + """
237 userPassword: """ + userpass + """
239 # Enables the user account
240 self.ldb.enable_account("(sAMAccountName=%s)" % username)
242 use_kerberos = creds.get_kerberos_state()
243 fail_creds = self.insta_creds(self.template_creds,
245 userpass=userpass + "X",
246 kerberos_state=use_kerberos)
247 self._check_account_initial(userdn)
249 # Fail once to get a badPasswordTime
251 ldb = SamDB(url=self.host_url, credentials=fail_creds, lp=self.lp)
253 except LdbError as e:
255 self.assertEquals(num, ERR_INVALID_CREDENTIALS)
257 # Succeed to reset everything to 0
258 ldb = SamDB(url=self.host_url, credentials=creds, lp=self.lp)
262 def assertLoginFailure(self, url, creds, lp, errno=ERR_INVALID_CREDENTIALS):
264 ldb = SamDB(url=url, credentials=creds, lp=lp)
265 self.fail("Login unexpectedly succeeded")
266 except LdbError as e1:
268 if errno is not None:
269 self.assertEquals(num, errno, ("Login failed in the wrong way"
270 "(got err %d, expected %d)" %
274 super(BasePasswordTestCase, self).setUp()
276 self.global_creds.set_gensec_features(self.global_creds.get_gensec_features() |
279 self.template_creds = Credentials()
280 self.template_creds.set_username("testuser")
281 self.template_creds.set_password("thatsAcomplPASS1")
282 self.template_creds.set_domain(self.global_creds.get_domain())
283 self.template_creds.set_realm(self.global_creds.get_realm())
284 self.template_creds.set_workstation(self.global_creds.get_workstation())
285 self.template_creds.set_gensec_features(self.global_creds.get_gensec_features())
286 self.template_creds.set_kerberos_state(self.global_creds.get_kerberos_state())
288 # Gets back the basedn
289 base_dn = self.ldb.domain_dn()
291 # Gets back the configuration basedn
292 configuration_dn = self.ldb.get_config_basedn().get_linearized()
294 res = self.ldb.search(base_dn,
295 scope=SCOPE_BASE, attrs=["lockoutDuration", "lockOutObservationWindow", "lockoutThreshold"])
297 if "lockoutDuration" in res[0]:
298 lockoutDuration = res[0]["lockoutDuration"][0]
302 if "lockoutObservationWindow" in res[0]:
303 lockoutObservationWindow = res[0]["lockoutObservationWindow"][0]
305 lockoutObservationWindow = 0
307 if "lockoutThreshold" in res[0]:
308 lockoutThreshold = res[0]["lockoutThreshold"][0]
312 self.addCleanup(self.ldb.modify_ldif, """
313 dn: """ + base_dn + """
315 replace: lockoutDuration
316 lockoutDuration: """ + str(lockoutDuration) + """
317 replace: lockoutObservationWindow
318 lockoutObservationWindow: """ + str(lockoutObservationWindow) + """
319 replace: lockoutThreshold
320 lockoutThreshold: """ + str(lockoutThreshold) + """
323 self.base_dn = self.ldb.domain_dn()
326 # Some test cases sleep() for self.account_lockout_duration
327 # so allow it to be controlled via the subclass
329 if not hasattr(self, 'account_lockout_duration'):
330 self.account_lockout_duration = 3
331 if not hasattr(self, 'lockout_observation_window'):
332 self.lockout_observation_window = 3
333 self.update_lockout_settings(threshold=3,
334 duration=self.account_lockout_duration,
335 observation_window=self.lockout_observation_window)
337 # update DC to allow password changes for the duration of this test
338 self.allow_password_changes()
340 self.domain_sid = security.dom_sid(self.ldb.get_domain_sid())
341 self.samr = samr.samr("ncacn_ip_tcp:%s[seal]" % self.host, self.lp, self.global_creds)
342 self.samr_handle = self.samr.Connect2(None, security.SEC_FLAG_MAXIMUM_ALLOWED)
343 self.samr_domain = self.samr.OpenDomain(self.samr_handle, security.SEC_FLAG_MAXIMUM_ALLOWED, self.domain_sid)
345 self.addCleanup(self.delete_ldb_connections)
347 # (Re)adds the test user accounts
348 self.lockout1krb5_creds = self.insta_creds(self.template_creds,
349 username="lockout1krb5",
350 userpass="thatsAcomplPASS0",
351 kerberos_state=MUST_USE_KERBEROS)
352 self.lockout1krb5_ldb = self._readd_user(self.lockout1krb5_creds)
353 self.lockout1ntlm_creds = self.insta_creds(self.template_creds,
354 username="lockout1ntlm",
355 userpass="thatsAcomplPASS0",
356 kerberos_state=DONT_USE_KERBEROS)
357 self.lockout1ntlm_ldb = self._readd_user(self.lockout1ntlm_creds)
359 def delete_ldb_connections(self):
360 del self.lockout1krb5_ldb
361 del self.lockout1ntlm_ldb
365 super(BasePasswordTestCase, self).tearDown()
367 def _test_login_lockout(self, creds, wait_lockout_duration=True):
368 username = creds.get_username()
369 userpass = creds.get_password()
370 userdn = "cn=%s,cn=users,%s" % (username, self.base_dn)
372 use_kerberos = creds.get_kerberos_state()
373 # This unlocks by waiting for account_lockout_duration
374 if use_kerberos == MUST_USE_KERBEROS:
375 logoncount_relation = 'greater'
376 lastlogon_relation = 'greater'
377 print("Performs a lockout attempt against LDAP using Kerberos")
379 logoncount_relation = 'equal'
380 lastlogon_relation = 'equal'
381 print("Performs a lockout attempt against LDAP using NTLM")
383 # Change password on a connection as another user
384 res = self._check_account(userdn,
386 badPasswordTime=("greater", 0),
387 logonCount=(logoncount_relation, 0),
388 lastLogon=("greater", 0),
389 lastLogonTimestamp=("greater", 0),
390 userAccountControl=dsdb.UF_NORMAL_ACCOUNT,
391 msDSUserAccountControlComputed=0)
392 badPasswordTime = int(res[0]["badPasswordTime"][0])
393 logonCount = int(res[0]["logonCount"][0])
394 lastLogon = int(res[0]["lastLogon"][0])
395 firstLogon = lastLogon
396 lastLogonTimestamp = int(res[0]["lastLogonTimestamp"][0])
398 print(lastLogonTimestamp)
400 self.assertGreater(lastLogon, badPasswordTime)
401 self.assertGreaterEqual(lastLogon, lastLogonTimestamp)
403 # Open a second LDB connection with the user credentials. Use the
404 # command line credentials for information like the domain, the realm
405 # and the workstation.
406 creds_lockout = self.insta_creds(creds)
409 creds_lockout.set_password("thatsAcomplPASS1x")
411 self.assertLoginFailure(self.host_url, creds_lockout, self.lp)
413 res = self._check_account(userdn,
415 badPasswordTime=("greater", badPasswordTime),
416 logonCount=logonCount,
418 lastLogonTimestamp=lastLogonTimestamp,
419 userAccountControl=dsdb.UF_NORMAL_ACCOUNT,
420 msDSUserAccountControlComputed=0,
421 msg='lastlogontimestamp with wrong password')
422 badPasswordTime = int(res[0]["badPasswordTime"][0])
424 # Correct old password
425 creds_lockout.set_password(userpass)
427 ldb_lockout = SamDB(url=self.host_url, credentials=creds_lockout, lp=self.lp)
429 # lastLogonTimestamp should not change
430 # lastLogon increases if badPwdCount is non-zero (!)
431 res = self._check_account(userdn,
433 badPasswordTime=badPasswordTime,
434 logonCount=(logoncount_relation, logonCount),
435 lastLogon=('greater', lastLogon),
436 lastLogonTimestamp=lastLogonTimestamp,
437 userAccountControl=dsdb.UF_NORMAL_ACCOUNT,
438 msDSUserAccountControlComputed=0,
439 msg='LLTimestamp is updated to lastlogon')
441 logonCount = int(res[0]["logonCount"][0])
442 lastLogon = int(res[0]["lastLogon"][0])
443 self.assertGreater(lastLogon, badPasswordTime)
444 self.assertGreaterEqual(lastLogon, lastLogonTimestamp)
447 creds_lockout.set_password("thatsAcomplPASS1x")
449 self.assertLoginFailure(self.host_url, creds_lockout, self.lp)
451 res = self._check_account(userdn,
453 badPasswordTime=("greater", badPasswordTime),
454 logonCount=logonCount,
456 lastLogonTimestamp=lastLogonTimestamp,
457 userAccountControl=dsdb.UF_NORMAL_ACCOUNT,
458 msDSUserAccountControlComputed=0)
459 badPasswordTime = int(res[0]["badPasswordTime"][0])
462 creds_lockout.set_password("thatsAcomplPASS1x")
465 ldb_lockout = SamDB(url=self.host_url, credentials=creds_lockout, lp=self.lp)
468 except LdbError as e2:
470 self.assertEquals(num, ERR_INVALID_CREDENTIALS)
472 res = self._check_account(userdn,
474 badPasswordTime=("greater", badPasswordTime),
475 logonCount=logonCount,
477 lastLogonTimestamp=lastLogonTimestamp,
478 userAccountControl=dsdb.UF_NORMAL_ACCOUNT,
479 msDSUserAccountControlComputed=0)
480 badPasswordTime = int(res[0]["badPasswordTime"][0])
482 print("two failed password change")
485 creds_lockout.set_password("thatsAcomplPASS1x")
488 ldb_lockout = SamDB(url=self.host_url, credentials=creds_lockout, lp=self.lp)
491 except LdbError as e3:
493 self.assertEquals(num, ERR_INVALID_CREDENTIALS)
495 res = self._check_account(userdn,
497 badPasswordTime=("greater", badPasswordTime),
498 logonCount=logonCount,
500 lastLogonTimestamp=lastLogonTimestamp,
501 lockoutTime=("greater", badPasswordTime),
502 userAccountControl=dsdb.UF_NORMAL_ACCOUNT,
503 msDSUserAccountControlComputed=dsdb.UF_LOCKOUT)
504 badPasswordTime = int(res[0]["badPasswordTime"][0])
505 lockoutTime = int(res[0]["lockoutTime"][0])
508 creds_lockout.set_password("thatsAcomplPASS1x")
510 ldb_lockout = SamDB(url=self.host_url, credentials=creds_lockout, lp=self.lp)
512 except LdbError as e4:
514 self.assertEquals(num, ERR_INVALID_CREDENTIALS)
516 res = self._check_account(userdn,
518 badPasswordTime=badPasswordTime,
519 logonCount=logonCount,
521 lastLogonTimestamp=lastLogonTimestamp,
522 lockoutTime=lockoutTime,
523 userAccountControl=dsdb.UF_NORMAL_ACCOUNT,
524 msDSUserAccountControlComputed=dsdb.UF_LOCKOUT)
527 creds_lockout.set_password("thatsAcomplPASS1x")
529 ldb_lockout = SamDB(url=self.host_url, credentials=creds_lockout, lp=self.lp)
531 except LdbError as e5:
533 self.assertEquals(num, ERR_INVALID_CREDENTIALS)
535 res = self._check_account(userdn,
537 badPasswordTime=badPasswordTime,
538 logonCount=logonCount,
540 lastLogonTimestamp=lastLogonTimestamp,
541 lockoutTime=lockoutTime,
542 userAccountControl=dsdb.UF_NORMAL_ACCOUNT,
543 msDSUserAccountControlComputed=dsdb.UF_LOCKOUT)
545 # The correct password, but we are locked out
546 creds_lockout.set_password(userpass)
548 ldb_lockout = SamDB(url=self.host_url, credentials=creds_lockout, lp=self.lp)
550 except LdbError as e6:
552 self.assertEquals(num, ERR_INVALID_CREDENTIALS)
554 res = self._check_account(userdn,
556 badPasswordTime=badPasswordTime,
557 logonCount=logonCount,
559 lastLogonTimestamp=lastLogonTimestamp,
560 lockoutTime=lockoutTime,
561 userAccountControl=dsdb.UF_NORMAL_ACCOUNT,
562 msDSUserAccountControlComputed=dsdb.UF_LOCKOUT)
564 # if we're just checking the user gets locked out, we can stop here
565 if not wait_lockout_duration:
568 # wait for the lockout to end
569 time.sleep(self.account_lockout_duration + 1)
570 print(self.account_lockout_duration + 1)
572 res = self._check_account(userdn,
573 badPwdCount=3, effective_bad_password_count=0,
574 badPasswordTime=badPasswordTime,
575 logonCount=logonCount,
576 lockoutTime=lockoutTime,
578 lastLogonTimestamp=lastLogonTimestamp,
579 userAccountControl=dsdb.UF_NORMAL_ACCOUNT,
580 msDSUserAccountControlComputed=0)
582 # The correct password after letting the timeout expire
584 creds_lockout.set_password(userpass)
586 creds_lockout2 = self.insta_creds(creds_lockout)
588 ldb_lockout = SamDB(url=self.host_url, credentials=creds_lockout2, lp=self.lp)
591 res = self._check_account(userdn,
593 badPasswordTime=badPasswordTime,
594 logonCount=(logoncount_relation, logonCount),
595 lastLogon=(lastlogon_relation, lastLogon),
596 lastLogonTimestamp=lastLogonTimestamp,
598 userAccountControl=dsdb.UF_NORMAL_ACCOUNT,
599 msDSUserAccountControlComputed=0,
600 msg="lastLogon is way off")
602 logonCount = int(res[0]["logonCount"][0])
603 lastLogon = int(res[0]["lastLogon"][0])
606 creds_lockout.set_password("thatsAcomplPASS1x")
608 ldb_lockout = SamDB(url=self.host_url, credentials=creds_lockout, lp=self.lp)
610 except LdbError as e7:
612 self.assertEquals(num, ERR_INVALID_CREDENTIALS)
614 res = self._check_account(userdn,
616 badPasswordTime=("greater", badPasswordTime),
617 logonCount=logonCount,
620 lastLogonTimestamp=lastLogonTimestamp,
621 userAccountControl=dsdb.UF_NORMAL_ACCOUNT,
622 msDSUserAccountControlComputed=0)
623 badPasswordTime = int(res[0]["badPasswordTime"][0])
626 creds_lockout.set_password("thatsAcomplPASS1x")
628 ldb_lockout = SamDB(url=self.host_url, credentials=creds_lockout, lp=self.lp)
630 except LdbError as e8:
632 self.assertEquals(num, ERR_INVALID_CREDENTIALS)
634 res = self._check_account(userdn,
636 badPasswordTime=("greater", badPasswordTime),
637 logonCount=logonCount,
640 lastLogonTimestamp=lastLogonTimestamp,
641 userAccountControl=dsdb.UF_NORMAL_ACCOUNT,
642 msDSUserAccountControlComputed=0)
643 badPasswordTime = int(res[0]["badPasswordTime"][0])
645 time.sleep(self.lockout_observation_window + 1)
647 res = self._check_account(userdn,
648 badPwdCount=2, effective_bad_password_count=0,
649 badPasswordTime=badPasswordTime,
650 logonCount=logonCount,
653 lastLogonTimestamp=lastLogonTimestamp,
654 userAccountControl=dsdb.UF_NORMAL_ACCOUNT,
655 msDSUserAccountControlComputed=0)
658 creds_lockout.set_password("thatsAcomplPASS1x")
660 ldb_lockout = SamDB(url=self.host_url, credentials=creds_lockout, lp=self.lp)
662 except LdbError as e9:
664 self.assertEquals(num, ERR_INVALID_CREDENTIALS)
666 res = self._check_account(userdn,
668 badPasswordTime=("greater", badPasswordTime),
669 logonCount=logonCount,
672 lastLogonTimestamp=lastLogonTimestamp,
673 userAccountControl=dsdb.UF_NORMAL_ACCOUNT,
674 msDSUserAccountControlComputed=0)
675 badPasswordTime = int(res[0]["badPasswordTime"][0])
677 # The correct password without letting the timeout expire
678 creds_lockout.set_password(userpass)
679 ldb_lockout = SamDB(url=self.host_url, credentials=creds_lockout, lp=self.lp)
681 res = self._check_account(userdn,
683 badPasswordTime=badPasswordTime,
684 logonCount=(logoncount_relation, logonCount),
686 lastLogon=("greater", lastLogon),
687 lastLogonTimestamp=lastLogonTimestamp,
688 userAccountControl=dsdb.UF_NORMAL_ACCOUNT,
689 msDSUserAccountControlComputed=0)
691 def _test_multiple_logon(self, creds):
692 # Test the happy case in which a user logs on correctly, then
693 # logs on correctly again, so that the bad password and
694 # lockout times are both zero the second time. The lastlogon
695 # time should increase.
697 # Open a second LDB connection with the user credentials. Use the
698 # command line credentials for information like the domain, the realm
699 # and the workstation.
700 username = creds.get_username()
701 userdn = "cn=%s,cn=users,%s" % (username, self.base_dn)
703 use_kerberos = creds.get_kerberos_state()
704 if use_kerberos == MUST_USE_KERBEROS:
705 print("Testing multiple logon with Kerberos")
706 logoncount_relation = 'greater'
707 lastlogon_relation = 'greater'
709 print("Testing multiple logon with NTLM")
710 logoncount_relation = 'equal'
711 lastlogon_relation = 'equal'
713 SamDB(url=self.host_url, credentials=self.insta_creds(creds), lp=self.lp)
715 res = self._check_account(userdn,
717 badPasswordTime=("greater", 0),
718 logonCount=(logoncount_relation, 0),
719 lastLogon=("greater", 0),
720 lastLogonTimestamp=("greater", 0),
721 userAccountControl=dsdb.UF_NORMAL_ACCOUNT,
722 msDSUserAccountControlComputed=0)
723 badPasswordTime = int(res[0]["badPasswordTime"][0])
724 logonCount = int(res[0]["logonCount"][0])
725 lastLogon = int(res[0]["lastLogon"][0])
726 lastLogonTimestamp = int(res[0]["lastLogonTimestamp"][0])
727 firstLogon = lastLogon
728 print("last logon is %d" % lastLogon)
729 self.assertGreater(lastLogon, badPasswordTime)
730 self.assertGreaterEqual(lastLogon, lastLogonTimestamp)
733 SamDB(url=self.host_url, credentials=self.insta_creds(creds), lp=self.lp)
735 res = self._check_account(userdn,
737 badPasswordTime=badPasswordTime,
738 logonCount=(logoncount_relation, logonCount),
739 lastLogon=(lastlogon_relation, lastLogon),
740 lastLogonTimestamp=lastLogonTimestamp,
741 userAccountControl=dsdb.UF_NORMAL_ACCOUNT,
742 msDSUserAccountControlComputed=0,
743 msg=("second logon, firstlogon was %s" %
746 lastLogon = int(res[0]["lastLogon"][0])
750 SamDB(url=self.host_url, credentials=self.insta_creds(creds), lp=self.lp)
752 res = self._check_account(userdn,
754 badPasswordTime=badPasswordTime,
755 logonCount=(logoncount_relation, logonCount),
756 lastLogon=(lastlogon_relation, lastLogon),
757 lastLogonTimestamp=lastLogonTimestamp,
758 userAccountControl=dsdb.UF_NORMAL_ACCOUNT,
759 msDSUserAccountControlComputed=0)