2 # -*- coding: utf-8 -*-
3 # This tests the password lockout behavior for AD implementations
5 # Copyright Matthias Dieter Wallnoefer 2010
6 # Copyright Andrew Bartlett 2013
7 # Copyright Stefan Metzmacher 2014
15 sys.path.insert(0, "bin/python")
18 from samba.tests.subunitrun import TestProgram, SubunitOptions
20 import samba.getopt as options
22 from samba.auth import system_session
23 from samba.credentials import Credentials, DONT_USE_KERBEROS, MUST_USE_KERBEROS
24 from ldb import SCOPE_BASE, LdbError
25 from ldb import ERR_CONSTRAINT_VIOLATION
26 from ldb import ERR_INVALID_CREDENTIALS
27 from ldb import Message, MessageElement, Dn
28 from ldb import FLAG_MOD_REPLACE
29 from samba import gensec, dsdb
30 from samba.samdb import SamDB
32 from samba.tests import delete_force
33 from samba.dcerpc import security, samr
34 from samba.ndr import ndr_unpack
36 parser = optparse.OptionParser("password_lockout.py [options] <host>")
37 sambaopts = options.SambaOptions(parser)
38 parser.add_option_group(sambaopts)
39 parser.add_option_group(options.VersionOptions(parser))
40 # use command line creds if available
41 credopts = options.CredentialsOptions(parser)
42 parser.add_option_group(credopts)
43 subunitopts = SubunitOptions(parser)
44 parser.add_option_group(subunitopts)
45 opts, args = parser.parse_args()
53 lp = sambaopts.get_loadparm()
54 global_creds = credopts.get_credentials(lp)
56 # Force an encrypted connection
57 global_creds.set_gensec_features(global_creds.get_gensec_features() |
60 def insta_creds(template=global_creds, username="testuser", userpass="thatsAcomplPASS1"):
61 # get a copy of the global creds or a the passed in creds
63 c.set_username(username)
64 c.set_password(userpass)
65 c.set_domain(template.get_domain())
66 c.set_realm(template.get_realm())
67 c.set_workstation(template.get_workstation())
68 c.set_gensec_features(c.get_gensec_features()
69 | gensec.FEATURE_SEAL)
70 c.set_kerberos_state(template.get_kerberos_state())
77 class PasswordTests(samba.tests.TestCase):
79 def _open_samr_user(self, res):
80 self.assertTrue("objectSid" in res[0])
82 (domain_sid, rid) = ndr_unpack(security.dom_sid, res[0]["objectSid"][0]).split()
83 self.assertEquals(self.domain_sid, domain_sid)
85 return self.samr.OpenUser(self.samr_domain, security.SEC_FLAG_MAXIMUM_ALLOWED, rid)
87 def _reset_samr(self, res):
89 # Now reset the lockout, by removing ACB_AUTOLOCK (which removes the lock, despite being a generated attribute)
90 samr_user = self._open_samr_user(res)
91 acb_info = self.samr.QueryUserInfo(samr_user, 16)
92 acb_info.acct_flags &= ~samr.ACB_AUTOLOCK
93 self.samr.SetUserInfo(samr_user, 16, acb_info)
94 self.samr.Close(samr_user)
96 def _reset_ldap_lockoutTime(self, res):
97 self.ldb.modify_ldif("""
98 dn: """ + str(res[0].dn) + """
104 def _reset_ldap_userAccountControl(self, res):
105 self.assertTrue("userAccountControl" in res[0])
106 self.assertTrue("msDS-User-Account-Control-Computed" in res[0])
108 uac = int(res[0]["userAccountControl"][0])
109 uacc = int(res[0]["msDS-User-Account-Control-Computed"][0])
112 uac = uac & ~dsdb.UF_LOCKOUT
114 self.ldb.modify_ldif("""
115 dn: """ + str(res[0].dn) + """
117 replace: userAccountControl
118 userAccountControl: %d
121 def _reset_by_method(self, res, method):
122 if method is "ldap_userAccountControl":
123 self._reset_ldap_userAccountControl(res)
124 elif method is "ldap_lockoutTime":
125 self._reset_ldap_lockoutTime(res)
126 elif method is "samr":
127 self._reset_samr(res)
129 self.assertTrue(False, msg="Invalid reset method[%s]" % method)
131 def _check_attribute(self, res, name, value):
133 self.assertTrue(name not in res[0],
134 msg="attr[%s]=%r on dn[%s]" %
135 (name, res[0], res[0].dn))
138 if isinstance(value, tuple):
139 (mode, value) = value
147 self.assertFalse(name in res[0],
148 msg="attr[%s] not missing on dn[%s]" %
152 self.assertTrue(name in res[0],
153 msg="attr[%s] missing on dn[%s]" %
155 self.assertTrue(len(res[0][name]) == 1,
156 msg="attr[%s]=%r on dn[%s]" %
157 (name, res[0][name], res[0].dn))
160 print "%s = '%s'" % (name, res[0][name][0])
162 if mode == "present":
166 v = int(res[0][name][0])
168 msg = ("attr[%s]=[%s] != [%s] on dn[%s]\n"
169 "(diff %d; actual value is %s than expected)" %
170 (name, v, value, res[0].dn, v - value,
171 ('less' if v < value else 'greater')))
173 self.assertTrue(v == value, msg)
176 if mode == "greater":
177 v = int(res[0][name][0])
178 self.assertTrue(v > int(value),
179 msg="attr[%s]=[%s] <= [%s] on dn[%s] (diff %d)" %
180 (name, v, int(value), res[0].dn, v - int(value)))
183 v = int(res[0][name][0])
184 self.assertTrue(v < int(value),
185 msg="attr[%s]=[%s] >= [%s] on dn[%s] (diff %d)" %
186 (name, v, int(value), res[0].dn, v - int(value)))
188 self.assertEqual(mode, not mode, "Invalid Mode[%s]" % mode)
190 def _check_account(self, dn,
192 badPasswordTime=None,
194 lastLogonTimestamp=None,
196 userAccountControl=None,
197 msDSUserAccountControlComputed=None,
198 effective_bad_password_count=None,
202 print "\033[01;32m %s \033[00m\n" % msg
208 "lastLogonTimestamp",
210 "userAccountControl",
211 "msDS-User-Account-Control-Computed"
214 # in order to prevent some time resolution problems we sleep for
218 res = self.ldb.search(dn, scope=SCOPE_BASE, attrs=attrs)
219 self.assertTrue(len(res) == 1)
220 self._check_attribute(res, "badPwdCount", badPwdCount)
221 self._check_attribute(res, "badPasswordTime", badPasswordTime)
222 self._check_attribute(res, "lastLogon", lastLogon)
223 self._check_attribute(res, "lastLogonTimestamp", lastLogonTimestamp)
224 self._check_attribute(res, "lockoutTime", lockoutTime)
225 self._check_attribute(res, "userAccountControl", userAccountControl)
226 self._check_attribute(res, "msDS-User-Account-Control-Computed",
227 msDSUserAccountControlComputed)
229 lastLogon = int(res[0]["lastLogon"][0])
231 samr_user = self._open_samr_user(res)
232 uinfo3 = self.samr.QueryUserInfo(samr_user, 3)
233 uinfo5 = self.samr.QueryUserInfo(samr_user, 5)
234 uinfo16 = self.samr.QueryUserInfo(samr_user, 16)
235 uinfo21 = self.samr.QueryUserInfo(samr_user, 21)
236 self.samr.Close(samr_user)
238 expected_acb_info = 0
239 if userAccountControl & dsdb.UF_NORMAL_ACCOUNT:
240 expected_acb_info |= samr.ACB_NORMAL
241 if userAccountControl & dsdb.UF_ACCOUNTDISABLE:
242 expected_acb_info |= samr.ACB_DISABLED
243 if userAccountControl & dsdb.UF_PASSWD_NOTREQD:
244 expected_acb_info |= samr.ACB_PWNOTREQ
245 if msDSUserAccountControlComputed & dsdb.UF_LOCKOUT:
246 expected_acb_info |= samr.ACB_AUTOLOCK
247 if msDSUserAccountControlComputed & dsdb.UF_PASSWORD_EXPIRED:
248 expected_acb_info |= samr.ACB_PW_EXPIRED
250 expected_bad_password_count = 0
251 if badPwdCount is not None:
252 expected_bad_password_count = badPwdCount
253 if effective_bad_password_count is None:
254 effective_bad_password_count = expected_bad_password_count
256 self.assertEquals(uinfo3.acct_flags, expected_acb_info)
257 self.assertEquals(uinfo3.bad_password_count, expected_bad_password_count)
258 self.assertEquals(uinfo3.last_logon, lastLogon)
260 self.assertEquals(uinfo5.acct_flags, expected_acb_info)
261 self.assertEquals(uinfo5.bad_password_count, effective_bad_password_count)
262 self.assertEquals(uinfo5.last_logon, lastLogon)
264 self.assertEquals(uinfo16.acct_flags, expected_acb_info)
266 self.assertEquals(uinfo21.acct_flags, expected_acb_info)
267 self.assertEquals(uinfo21.bad_password_count, effective_bad_password_count)
268 self.assertEquals(uinfo21.last_logon, lastLogon)
270 # check LDAP again and make sure the samr.QueryUserInfo
271 # doesn't have any impact.
272 res2 = self.ldb.search(dn, scope=SCOPE_BASE, attrs=attrs)
273 self.assertEquals(res[0], res2[0])
275 # in order to prevent some time resolution problems we sleep for
280 def _readd_user(self, creds):
281 username = creds.get_username()
282 userpass = creds.get_password()
283 userdn = "cn=%s,cn=users,%s" % (username, self.base_dn)
285 # (Re)adds the test user "testuser" with no password atm
286 delete_force(self.ldb, userdn)
289 "objectclass": "user",
290 "sAMAccountName": username})
292 self.addCleanup(delete_force, self.ldb, userdn)
294 res = self._check_account(userdn,
298 lastLogonTimestamp=('absent', None),
300 dsdb.UF_NORMAL_ACCOUNT |
301 dsdb.UF_ACCOUNTDISABLE |
302 dsdb.UF_PASSWD_NOTREQD,
303 msDSUserAccountControlComputed=
304 dsdb.UF_PASSWORD_EXPIRED)
306 # SAMR doesn't have any impact if dsdb.UF_LOCKOUT isn't present.
307 # It doesn't create "lockoutTime" = 0.
308 self._reset_samr(res)
310 res = self._check_account(userdn,
314 lastLogonTimestamp=('absent', None),
316 dsdb.UF_NORMAL_ACCOUNT |
317 dsdb.UF_ACCOUNTDISABLE |
318 dsdb.UF_PASSWD_NOTREQD,
319 msDSUserAccountControlComputed=
320 dsdb.UF_PASSWORD_EXPIRED)
322 # Tests a password change when we don't have any password yet with a
325 self.ldb.modify_ldif("""
326 dn: """ + userdn + """
329 userPassword: noPassword
331 userPassword: thatsAcomplPASS2
334 except LdbError, (num, msg):
335 self.assertEquals(num, ERR_CONSTRAINT_VIOLATION)
336 # Windows (2008 at least) seems to have some small bug here: it
337 # returns "0000056A" on longer (always wrong) previous passwords.
338 self.assertTrue('00000056' in msg, msg)
340 res = self._check_account(userdn,
342 badPasswordTime=("greater", 0),
344 lastLogonTimestamp=('absent', None),
346 dsdb.UF_NORMAL_ACCOUNT |
347 dsdb.UF_ACCOUNTDISABLE |
348 dsdb.UF_PASSWD_NOTREQD,
349 msDSUserAccountControlComputed=
350 dsdb.UF_PASSWORD_EXPIRED)
351 badPasswordTime = int(res[0]["badPasswordTime"][0])
353 # Sets the initial user password with a "special" password change
354 # I think that this internally is a password set operation and it can
355 # only be performed by someone which has password set privileges on the
356 # account (at least in s4 we do handle it like that).
357 self.ldb.modify_ldif("""
358 dn: """ + userdn + """
362 userPassword: """ + userpass + """
365 res = self._check_account(userdn,
367 badPasswordTime=badPasswordTime,
369 lastLogonTimestamp=('absent', None),
371 dsdb.UF_NORMAL_ACCOUNT |
372 dsdb.UF_ACCOUNTDISABLE |
373 dsdb.UF_PASSWD_NOTREQD,
374 msDSUserAccountControlComputed=0)
376 # Enables the user account
377 self.ldb.enable_account("(sAMAccountName=%s)" % username)
379 res = self._check_account(userdn,
381 badPasswordTime=badPasswordTime,
383 lastLogonTimestamp=('absent', None),
385 dsdb.UF_NORMAL_ACCOUNT,
386 msDSUserAccountControlComputed=0)
388 # Open a second LDB connection with the user credentials. Use the
389 # command line credentials for informations like the domain, the realm
390 # and the workstation.
392 ldb = SamDB(url=host_url, credentials=creds, lp=lp)
394 res = self._check_account(userdn,
396 badPasswordTime=badPasswordTime,
397 lastLogon=('greater', 0),
398 lastLogonTimestamp=('greater', 0),
400 dsdb.UF_NORMAL_ACCOUNT,
401 msDSUserAccountControlComputed=0)
403 lastLogon = int(res[0]["lastLogon"][0])
404 self.assertGreater(lastLogon, badPasswordTime)
407 def assertLoginFailure(self, url, creds, lp, errno=ERR_INVALID_CREDENTIALS):
409 ldb = SamDB(url=url, credentials=creds, lp=lp)
410 self.fail("Login unexpectedly succeeded")
411 except LdbError, (num, msg):
412 if errno is not None:
413 self.assertEquals(num, errno, ("Login failed in the wrong way"
414 "(got err %d, expected %d)" %
418 super(PasswordTests, self).setUp()
420 self.ldb = SamDB(url=host_url, session_info=system_session(lp),
421 credentials=global_creds, lp=lp)
423 # Gets back the basedn
424 base_dn = self.ldb.domain_dn()
426 # Gets back the configuration basedn
427 configuration_dn = self.ldb.get_config_basedn().get_linearized()
429 # Get the old "dSHeuristics" if it was set
430 dsheuristics = self.ldb.get_dsheuristics()
432 # Reset the "dSHeuristics" as they were before
433 self.addCleanup(self.ldb.set_dsheuristics, dsheuristics)
435 res = self.ldb.search(base_dn,
436 scope=SCOPE_BASE, attrs=["lockoutDuration", "lockOutObservationWindow", "lockoutThreshold"])
438 if "lockoutDuration" in res[0]:
439 lockoutDuration = res[0]["lockoutDuration"][0]
443 if "lockoutObservationWindow" in res[0]:
444 lockoutObservationWindow = res[0]["lockoutObservationWindow"][0]
446 lockoutObservationWindow = 0
448 if "lockoutThreshold" in res[0]:
449 lockoutThreshold = res[0]["lockoutThreshold"][0]
453 self.addCleanup(self.ldb.modify_ldif, """
454 dn: """ + base_dn + """
456 replace: lockoutDuration
457 lockoutDuration: """ + str(lockoutDuration) + """
458 replace: lockoutObservationWindow
459 lockoutObservationWindow: """ + str(lockoutObservationWindow) + """
460 replace: lockoutThreshold
461 lockoutThreshold: """ + str(lockoutThreshold) + """
465 m.dn = Dn(self.ldb, base_dn)
467 self.account_lockout_duration = 2
468 account_lockout_duration_ticks = -int(self.account_lockout_duration * (1e7))
470 m["lockoutDuration"] = MessageElement(str(account_lockout_duration_ticks),
471 FLAG_MOD_REPLACE, "lockoutDuration")
473 account_lockout_threshold = 3
474 m["lockoutThreshold"] = MessageElement(str(account_lockout_threshold),
475 FLAG_MOD_REPLACE, "lockoutThreshold")
477 self.lockout_observation_window = 2
478 lockout_observation_window_ticks = -int(self.lockout_observation_window * (1e7))
480 m["lockOutObservationWindow"] = MessageElement(str(lockout_observation_window_ticks),
481 FLAG_MOD_REPLACE, "lockOutObservationWindow")
485 # Set the "dSHeuristics" to activate the correct "userPassword" behaviour
486 self.ldb.set_dsheuristics("000000001")
488 # Get the old "minPwdAge"
489 minPwdAge = self.ldb.get_minPwdAge()
491 # Reset the "minPwdAge" as it was before
492 self.addCleanup(self.ldb.set_minPwdAge, minPwdAge)
494 # Set it temporarely to "0"
495 self.ldb.set_minPwdAge("0")
497 self.base_dn = self.ldb.domain_dn()
499 self.domain_sid = security.dom_sid(self.ldb.get_domain_sid())
500 self.samr = samr.samr("ncacn_ip_tcp:%s[seal]" % host, lp, global_creds)
501 self.samr_handle = self.samr.Connect2(None, security.SEC_FLAG_MAXIMUM_ALLOWED)
502 self.samr_domain = self.samr.OpenDomain(self.samr_handle, security.SEC_FLAG_MAXIMUM_ALLOWED, self.domain_sid)
504 self.creds2 = insta_creds()
505 self.ldb2 = self._readd_user(self.creds2)
507 self.creds3 = insta_creds(username="testuser3", userpass="thatsAcomplPASS1")
508 self.ldb3 = self._readd_user(self.creds3)
510 def _test_userPassword_lockout_with_clear_change(self, creds, other_ldb, method):
511 print "Performs a password cleartext change operation on 'userPassword'"
512 # Notice: This works only against Windows if "dSHeuristics" has been set
514 username = creds.get_username()
515 userpass = creds.get_password()
516 userdn = "cn=%s,cn=users,%s" % (username, self.base_dn)
518 res = self._check_account(userdn,
520 badPasswordTime=("greater", 0),
521 lastLogon=('greater', 0),
522 lastLogonTimestamp=('greater', 0),
524 dsdb.UF_NORMAL_ACCOUNT,
525 msDSUserAccountControlComputed=0)
526 badPasswordTime = int(res[0]["badPasswordTime"][0])
527 lastLogon = int(res[0]["lastLogon"][0])
528 lastLogonTimestamp = int(res[0]["lastLogonTimestamp"][0])
530 # Change password on a connection as another user
534 other_ldb.modify_ldif("""
535 dn: """ + userdn + """
538 userPassword: thatsAcomplPASS1x
540 userPassword: thatsAcomplPASS2
543 except LdbError, (num, msg):
544 self.assertEquals(num, ERR_CONSTRAINT_VIOLATION)
545 self.assertTrue('00000056' in msg, msg)
547 res = self._check_account(userdn,
549 badPasswordTime=("greater", badPasswordTime),
551 lastLogonTimestamp=lastLogonTimestamp,
553 dsdb.UF_NORMAL_ACCOUNT,
554 msDSUserAccountControlComputed=0)
555 badPasswordTime = int(res[0]["badPasswordTime"][0])
557 # Correct old password
558 other_ldb.modify_ldif("""
559 dn: """ + userdn + """
562 userPassword: """ + userpass + """
564 userPassword: thatsAcomplPASS2
567 res = self._check_account(userdn,
569 badPasswordTime=badPasswordTime,
571 lastLogonTimestamp=lastLogonTimestamp,
573 dsdb.UF_NORMAL_ACCOUNT,
574 msDSUserAccountControlComputed=0)
578 other_ldb.modify_ldif("""
579 dn: """ + userdn + """
582 userPassword: thatsAcomplPASS1x
584 userPassword: thatsAcomplPASS2
587 except LdbError, (num, msg):
588 self.assertEquals(num, ERR_CONSTRAINT_VIOLATION)
589 self.assertTrue('00000056' in msg, msg)
591 res = self._check_account(userdn,
593 badPasswordTime=("greater", badPasswordTime),
595 lastLogonTimestamp=lastLogonTimestamp,
597 dsdb.UF_NORMAL_ACCOUNT,
598 msDSUserAccountControlComputed=0)
599 badPasswordTime = int(res[0]["badPasswordTime"][0])
601 print "two failed password change"
605 other_ldb.modify_ldif("""
606 dn: """ + userdn + """
609 userPassword: thatsAcomplPASS1x
611 userPassword: thatsAcomplPASS2
614 except LdbError, (num, msg):
615 self.assertEquals(num, ERR_CONSTRAINT_VIOLATION)
616 self.assertTrue('00000056' in msg, msg)
618 res = self._check_account(userdn,
620 badPasswordTime=("greater", badPasswordTime),
622 lastLogonTimestamp=lastLogonTimestamp,
623 lockoutTime=("greater", badPasswordTime),
625 dsdb.UF_NORMAL_ACCOUNT,
626 msDSUserAccountControlComputed=dsdb.UF_LOCKOUT)
627 badPasswordTime = int(res[0]["badPasswordTime"][0])
628 lockoutTime = int(res[0]["lockoutTime"][0])
632 other_ldb.modify_ldif("""
633 dn: """ + userdn + """
636 userPassword: thatsAcomplPASS1x
638 userPassword: thatsAcomplPASS2
641 except LdbError, (num, msg):
642 self.assertEquals(num, ERR_CONSTRAINT_VIOLATION)
643 self.assertTrue('00000775' in msg, msg)
645 res = self._check_account(userdn,
647 badPasswordTime=badPasswordTime,
649 lastLogonTimestamp=lastLogonTimestamp,
650 lockoutTime=lockoutTime,
652 dsdb.UF_NORMAL_ACCOUNT,
653 msDSUserAccountControlComputed=dsdb.UF_LOCKOUT)
657 other_ldb.modify_ldif("""
658 dn: """ + userdn + """
661 userPassword: thatsAcomplPASS1x
663 userPassword: thatsAcomplPASS2
666 except LdbError, (num, msg):
667 self.assertEquals(num, ERR_CONSTRAINT_VIOLATION)
668 self.assertTrue('00000775' in msg, msg)
670 res = self._check_account(userdn,
672 badPasswordTime=badPasswordTime,
673 lockoutTime=lockoutTime,
675 lastLogonTimestamp=lastLogonTimestamp,
677 dsdb.UF_NORMAL_ACCOUNT,
678 msDSUserAccountControlComputed=dsdb.UF_LOCKOUT)
681 # Correct old password
682 other_ldb.modify_ldif("""
683 dn: """ + userdn + """
686 userPassword: thatsAcomplPASS2
688 userPassword: thatsAcomplPASS2x
691 except LdbError, (num, msg):
692 self.assertEquals(num, ERR_CONSTRAINT_VIOLATION)
693 self.assertTrue('00000775' in msg, msg)
695 res = self._check_account(userdn,
697 badPasswordTime=badPasswordTime,
699 lastLogonTimestamp=lastLogonTimestamp,
700 lockoutTime=lockoutTime,
702 dsdb.UF_NORMAL_ACCOUNT,
703 msDSUserAccountControlComputed=dsdb.UF_LOCKOUT)
705 # Now reset the password, which does NOT change the lockout!
706 self.ldb.modify_ldif("""
707 dn: """ + userdn + """
709 replace: userPassword
710 userPassword: thatsAcomplPASS2
713 res = self._check_account(userdn,
715 badPasswordTime=badPasswordTime,
717 lastLogonTimestamp=lastLogonTimestamp,
718 lockoutTime=lockoutTime,
720 dsdb.UF_NORMAL_ACCOUNT,
721 msDSUserAccountControlComputed=dsdb.UF_LOCKOUT)
724 # Correct old password
725 other_ldb.modify_ldif("""
726 dn: """ + userdn + """
729 userPassword: thatsAcomplPASS2
731 userPassword: thatsAcomplPASS2x
734 except LdbError, (num, msg):
735 self.assertEquals(num, ERR_CONSTRAINT_VIOLATION)
736 self.assertTrue('00000775' in msg, msg)
738 res = self._check_account(userdn,
740 badPasswordTime=badPasswordTime,
742 lastLogonTimestamp=lastLogonTimestamp,
743 lockoutTime=lockoutTime,
745 dsdb.UF_NORMAL_ACCOUNT,
746 msDSUserAccountControlComputed=dsdb.UF_LOCKOUT)
749 m.dn = Dn(self.ldb, userdn)
750 m["userAccountControl"] = MessageElement(
751 str(dsdb.UF_LOCKOUT),
752 FLAG_MOD_REPLACE, "userAccountControl")
756 # This shows that setting the UF_LOCKOUT flag alone makes no difference
757 res = self._check_account(userdn,
759 badPasswordTime=badPasswordTime,
761 lastLogonTimestamp=lastLogonTimestamp,
762 lockoutTime=lockoutTime,
764 dsdb.UF_NORMAL_ACCOUNT,
765 msDSUserAccountControlComputed=dsdb.UF_LOCKOUT)
767 # This shows that setting the UF_LOCKOUT flag makes no difference
769 # Correct old password
770 other_ldb.modify_ldif("""
771 dn: """ + userdn + """
774 unicodePwd:: """ + base64.b64encode("\"thatsAcomplPASS2\"".encode('utf-16-le')) + """
776 unicodePwd:: """ + base64.b64encode("\"thatsAcomplPASS2x\"".encode('utf-16-le')) + """
779 except LdbError, (num, msg):
780 self.assertEquals(num, ERR_CONSTRAINT_VIOLATION)
781 self.assertTrue('00000775' in msg, msg)
783 res = self._check_account(userdn,
785 badPasswordTime=badPasswordTime,
786 lockoutTime=lockoutTime,
788 lastLogonTimestamp=lastLogonTimestamp,
790 dsdb.UF_NORMAL_ACCOUNT,
791 msDSUserAccountControlComputed=dsdb.UF_LOCKOUT)
793 self._reset_by_method(res, method)
795 # Here bad password counts are reset without logon success.
796 res = self._check_account(userdn,
798 badPasswordTime=badPasswordTime,
801 lastLogonTimestamp=lastLogonTimestamp,
803 dsdb.UF_NORMAL_ACCOUNT,
804 msDSUserAccountControlComputed=0)
806 # The correct password after doing the unlock
808 other_ldb.modify_ldif("""
809 dn: """ + userdn + """
812 unicodePwd:: """ + base64.b64encode("\"thatsAcomplPASS2\"".encode('utf-16-le')) + """
814 unicodePwd:: """ + base64.b64encode("\"thatsAcomplPASS2x\"".encode('utf-16-le')) + """
816 userpass = "thatsAcomplPASS2x"
817 creds.set_password(userpass)
819 res = self._check_account(userdn,
821 badPasswordTime=badPasswordTime,
824 lastLogonTimestamp=lastLogonTimestamp,
826 dsdb.UF_NORMAL_ACCOUNT,
827 msDSUserAccountControlComputed=0)
831 other_ldb.modify_ldif("""
832 dn: """ + userdn + """
835 userPassword: thatsAcomplPASS1xyz
837 userPassword: thatsAcomplPASS2XYZ
840 except LdbError, (num, msg):
841 self.assertEquals(num, ERR_CONSTRAINT_VIOLATION)
842 self.assertTrue('00000056' in msg, msg)
844 res = self._check_account(userdn,
846 badPasswordTime=("greater", badPasswordTime),
849 lastLogonTimestamp=lastLogonTimestamp,
851 dsdb.UF_NORMAL_ACCOUNT,
852 msDSUserAccountControlComputed=0)
853 badPasswordTime = int(res[0]["badPasswordTime"][0])
857 other_ldb.modify_ldif("""
858 dn: """ + userdn + """
861 userPassword: thatsAcomplPASS1xyz
863 userPassword: thatsAcomplPASS2XYZ
866 except LdbError, (num, msg):
867 self.assertEquals(num, ERR_CONSTRAINT_VIOLATION)
868 self.assertTrue('00000056' in msg, msg)
870 res = self._check_account(userdn,
872 badPasswordTime=("greater", badPasswordTime),
875 lastLogonTimestamp=lastLogonTimestamp,
877 dsdb.UF_NORMAL_ACCOUNT,
878 msDSUserAccountControlComputed=0)
879 badPasswordTime = int(res[0]["badPasswordTime"][0])
881 self._reset_ldap_lockoutTime(res)
883 res = self._check_account(userdn,
885 badPasswordTime=badPasswordTime,
887 lastLogonTimestamp=lastLogonTimestamp,
890 dsdb.UF_NORMAL_ACCOUNT,
891 msDSUserAccountControlComputed=0)
893 def test_userPassword_lockout_with_clear_change_ldap_userAccountControl(self):
894 self._test_userPassword_lockout_with_clear_change(self.creds2,
896 "ldap_userAccountControl")
898 def test_userPassword_lockout_with_clear_change_ldap_lockoutTime(self):
899 self._test_userPassword_lockout_with_clear_change(self.creds2,
903 def test_userPassword_lockout_with_clear_change_samr(self):
904 self._test_userPassword_lockout_with_clear_change(self.creds2,
908 def _test_unicodePwd_lockout_with_clear_change(self, creds, other_ldb):
909 print "Performs a password cleartext change operation on 'unicodePwd'"
910 username = creds.get_username()
911 userpass = creds.get_password()
912 userdn = "cn=%s,cn=users,%s" % (username, self.base_dn)
914 res = self._check_account(userdn,
916 badPasswordTime=("greater", 0),
917 lastLogon=("greater", 0),
918 lastLogonTimestamp=("greater", 0),
920 dsdb.UF_NORMAL_ACCOUNT,
921 msDSUserAccountControlComputed=0)
922 badPasswordTime = int(res[0]["badPasswordTime"][0])
923 lastLogon = int(res[0]["lastLogon"][0])
925 # Change password on a connection as another user
929 other_ldb.modify_ldif("""
930 dn: """ + userdn + """
933 unicodePwd:: """ + base64.b64encode("\"thatsAcomplPASS1x\"".encode('utf-16-le')) + """
935 unicodePwd:: """ + base64.b64encode("\"thatsAcomplPASS2\"".encode('utf-16-le')) + """
938 except LdbError, (num, msg):
939 self.assertEquals(num, ERR_CONSTRAINT_VIOLATION)
940 self.assertTrue('00000056' in msg, msg)
942 res = self._check_account(userdn,
944 badPasswordTime=("greater", badPasswordTime),
946 lastLogonTimestamp=lastLogon,
948 dsdb.UF_NORMAL_ACCOUNT,
949 msDSUserAccountControlComputed=0)
950 badPasswordTime = int(res[0]["badPasswordTime"][0])
952 # Correct old password
953 old_utf16 = ("\"%s\"" % userpass).encode('utf-16-le')
954 invalid_utf16 = "\"thatsAcomplPASSX\"".encode('utf-16-le')
955 userpass = "thatsAcomplPASS2"
956 creds.set_password(userpass)
957 new_utf16 = ("\"%s\"" % userpass).encode('utf-16-le')
959 other_ldb.modify_ldif("""
960 dn: """ + userdn + """
963 unicodePwd:: """ + base64.b64encode(old_utf16) + """
965 unicodePwd:: """ + base64.b64encode(new_utf16) + """
968 res = self._check_account(userdn,
970 badPasswordTime=badPasswordTime,
972 lastLogonTimestamp=lastLogon,
974 dsdb.UF_NORMAL_ACCOUNT,
975 msDSUserAccountControlComputed=0)
979 other_ldb.modify_ldif("""
980 dn: """ + userdn + """
983 unicodePwd:: """ + base64.b64encode(old_utf16) + """
985 unicodePwd:: """ + base64.b64encode(new_utf16) + """
988 except LdbError, (num, msg):
989 self.assertEquals(num, ERR_CONSTRAINT_VIOLATION)
990 self.assertTrue('00000056' in msg, msg)
992 res = self._check_account(userdn,
994 badPasswordTime=("greater", badPasswordTime),
996 lastLogonTimestamp=lastLogon,
998 dsdb.UF_NORMAL_ACCOUNT,
999 msDSUserAccountControlComputed=0)
1000 badPasswordTime = int(res[0]["badPasswordTime"][0])
1002 # SAMR doesn't have any impact if dsdb.UF_LOCKOUT isn't present.
1003 # It doesn't create "lockoutTime" = 0 and doesn't
1004 # reset "badPwdCount" = 0.
1005 self._reset_samr(res)
1007 res = self._check_account(userdn,
1009 badPasswordTime=badPasswordTime,
1010 lastLogon=lastLogon,
1011 lastLogonTimestamp=lastLogon,
1013 dsdb.UF_NORMAL_ACCOUNT,
1014 msDSUserAccountControlComputed=0)
1016 print "two failed password change"
1018 # Wrong old password
1020 other_ldb.modify_ldif("""
1021 dn: """ + userdn + """
1024 unicodePwd:: """ + base64.b64encode(invalid_utf16) + """
1026 unicodePwd:: """ + base64.b64encode(new_utf16) + """
1029 except LdbError, (num, msg):
1030 self.assertEquals(num, ERR_CONSTRAINT_VIOLATION)
1031 self.assertTrue('00000056' in msg, msg)
1033 # this is strange, why do we have lockoutTime=badPasswordTime here?
1034 res = self._check_account(userdn,
1036 badPasswordTime=("greater", badPasswordTime),
1037 lastLogon=lastLogon,
1038 lastLogonTimestamp=lastLogon,
1039 lockoutTime=("greater", badPasswordTime),
1041 dsdb.UF_NORMAL_ACCOUNT,
1042 msDSUserAccountControlComputed=dsdb.UF_LOCKOUT)
1043 badPasswordTime = int(res[0]["badPasswordTime"][0])
1044 lockoutTime = int(res[0]["lockoutTime"][0])
1046 # Wrong old password
1048 other_ldb.modify_ldif("""
1049 dn: """ + userdn + """
1052 unicodePwd:: """ + base64.b64encode(invalid_utf16) + """
1054 unicodePwd:: """ + base64.b64encode(new_utf16) + """
1057 except LdbError, (num, msg):
1058 self.assertEquals(num, ERR_CONSTRAINT_VIOLATION)
1059 self.assertTrue('00000775' in msg, msg)
1061 res = self._check_account(userdn,
1063 badPasswordTime=badPasswordTime,
1064 lastLogon=lastLogon,
1065 lastLogonTimestamp=lastLogon,
1066 lockoutTime=lockoutTime,
1068 dsdb.UF_NORMAL_ACCOUNT,
1069 msDSUserAccountControlComputed=dsdb.UF_LOCKOUT)
1071 # Wrong old password
1073 other_ldb.modify_ldif("""
1074 dn: """ + userdn + """
1077 unicodePwd:: """ + base64.b64encode(invalid_utf16) + """
1079 unicodePwd:: """ + base64.b64encode(new_utf16) + """
1082 except LdbError, (num, msg):
1083 self.assertEquals(num, ERR_CONSTRAINT_VIOLATION)
1084 self.assertTrue('00000775' in msg, msg)
1086 res = self._check_account(userdn,
1088 badPasswordTime=badPasswordTime,
1089 lastLogon=lastLogon,
1090 lastLogonTimestamp=lastLogon,
1091 lockoutTime=lockoutTime,
1093 dsdb.UF_NORMAL_ACCOUNT,
1094 msDSUserAccountControlComputed=dsdb.UF_LOCKOUT)
1097 # Correct old password
1098 other_ldb.modify_ldif("""
1099 dn: """ + userdn + """
1102 unicodePwd:: """ + base64.b64encode(new_utf16) + """
1104 unicodePwd:: """ + base64.b64encode(invalid_utf16) + """
1107 except LdbError, (num, msg):
1108 self.assertEquals(num, ERR_CONSTRAINT_VIOLATION)
1109 self.assertTrue('00000775' in msg, msg)
1111 res = self._check_account(userdn,
1113 badPasswordTime=badPasswordTime,
1114 lastLogon=lastLogon,
1115 lastLogonTimestamp=lastLogon,
1116 lockoutTime=lockoutTime,
1118 dsdb.UF_NORMAL_ACCOUNT,
1119 msDSUserAccountControlComputed=dsdb.UF_LOCKOUT)
1121 # Now reset the lockout, by removing ACB_AUTOLOCK (which removes the lock, despite being a generated attribute)
1122 self._reset_samr(res);
1124 res = self._check_account(userdn,
1126 badPasswordTime=badPasswordTime,
1127 lastLogon=lastLogon,
1128 lastLogonTimestamp=lastLogon,
1131 dsdb.UF_NORMAL_ACCOUNT,
1132 msDSUserAccountControlComputed=0)
1134 # Correct old password
1135 old_utf16 = ("\"%s\"" % userpass).encode('utf-16-le')
1136 invalid_utf16 = "\"thatsAcomplPASSiX\"".encode('utf-16-le')
1137 userpass = "thatsAcomplPASS2x"
1138 creds.set_password(userpass)
1139 new_utf16 = ("\"%s\"" % userpass).encode('utf-16-le')
1141 other_ldb.modify_ldif("""
1142 dn: """ + userdn + """
1145 unicodePwd:: """ + base64.b64encode(old_utf16) + """
1147 unicodePwd:: """ + base64.b64encode(new_utf16) + """
1150 res = self._check_account(userdn,
1152 badPasswordTime=badPasswordTime,
1153 lastLogon=lastLogon,
1154 lastLogonTimestamp=lastLogon,
1157 dsdb.UF_NORMAL_ACCOUNT,
1158 msDSUserAccountControlComputed=0)
1160 # Wrong old password
1162 other_ldb.modify_ldif("""
1163 dn: """ + userdn + """
1166 unicodePwd:: """ + base64.b64encode(invalid_utf16) + """
1168 unicodePwd:: """ + base64.b64encode(new_utf16) + """
1171 except LdbError, (num, msg):
1172 self.assertEquals(num, ERR_CONSTRAINT_VIOLATION)
1173 self.assertTrue('00000056' in msg, msg)
1175 res = self._check_account(userdn,
1177 badPasswordTime=("greater", badPasswordTime),
1178 lastLogon=lastLogon,
1179 lastLogonTimestamp=lastLogon,
1182 dsdb.UF_NORMAL_ACCOUNT,
1183 msDSUserAccountControlComputed=0)
1184 badPasswordTime = int(res[0]["badPasswordTime"][0])
1186 # Wrong old password
1188 other_ldb.modify_ldif("""
1189 dn: """ + userdn + """
1192 unicodePwd:: """ + base64.b64encode(invalid_utf16) + """
1194 unicodePwd:: """ + base64.b64encode(new_utf16) + """
1197 except LdbError, (num, msg):
1198 self.assertEquals(num, ERR_CONSTRAINT_VIOLATION)
1199 self.assertTrue('00000056' in msg, msg)
1201 res = self._check_account(userdn,
1203 badPasswordTime=("greater", badPasswordTime),
1204 lastLogon=lastLogon,
1205 lastLogonTimestamp=lastLogon,
1208 dsdb.UF_NORMAL_ACCOUNT,
1209 msDSUserAccountControlComputed=0)
1210 badPasswordTime = int(res[0]["badPasswordTime"][0])
1212 # SAMR doesn't have any impact if dsdb.UF_LOCKOUT isn't present.
1213 # It doesn't reset "badPwdCount" = 0.
1214 self._reset_samr(res)
1216 res = self._check_account(userdn,
1218 badPasswordTime=badPasswordTime,
1219 lastLogon=lastLogon,
1220 lastLogonTimestamp=lastLogon,
1223 dsdb.UF_NORMAL_ACCOUNT,
1224 msDSUserAccountControlComputed=0)
1226 # Wrong old password
1228 other_ldb.modify_ldif("""
1229 dn: """ + userdn + """
1232 unicodePwd:: """ + base64.b64encode(invalid_utf16) + """
1234 unicodePwd:: """ + base64.b64encode(new_utf16) + """
1237 except LdbError, (num, msg):
1238 self.assertEquals(num, ERR_CONSTRAINT_VIOLATION)
1239 self.assertTrue('00000056' in msg, msg)
1241 res = self._check_account(userdn,
1243 badPasswordTime=("greater", badPasswordTime),
1244 lastLogon=lastLogon,
1245 lastLogonTimestamp=lastLogon,
1246 lockoutTime=("greater", badPasswordTime),
1248 dsdb.UF_NORMAL_ACCOUNT,
1249 msDSUserAccountControlComputed=dsdb.UF_LOCKOUT)
1250 badPasswordTime = int(res[0]["badPasswordTime"][0])
1251 lockoutTime = int(res[0]["lockoutTime"][0])
1253 time.sleep(self.account_lockout_duration + 1)
1255 res = self._check_account(userdn,
1256 badPwdCount=3, effective_bad_password_count=0,
1257 badPasswordTime=badPasswordTime,
1258 lastLogon=lastLogon,
1259 lastLogonTimestamp=lastLogon,
1260 lockoutTime=lockoutTime,
1262 dsdb.UF_NORMAL_ACCOUNT,
1263 msDSUserAccountControlComputed=0)
1265 # SAMR doesn't have any impact if dsdb.UF_LOCKOUT isn't present.
1266 # It doesn't reset "lockoutTime" = 0 and doesn't
1267 # reset "badPwdCount" = 0.
1268 self._reset_samr(res)
1270 res = self._check_account(userdn,
1271 badPwdCount=3, effective_bad_password_count=0,
1272 badPasswordTime=badPasswordTime,
1273 lockoutTime=lockoutTime,
1274 lastLogon=lastLogon,
1275 lastLogonTimestamp=lastLogon,
1277 dsdb.UF_NORMAL_ACCOUNT,
1278 msDSUserAccountControlComputed=0)
1280 def test_unicodePwd_lockout_with_clear_change(self):
1281 return self._test_unicodePwd_lockout_with_clear_change(self.creds2, self.ldb3)
1283 def _test_login_lockout(self, creds, use_kerberos):
1284 username = creds.get_username()
1285 userpass = creds.get_password()
1286 userdn = "cn=%s,cn=users,%s" % (username, self.base_dn)
1288 # This unlocks by waiting for account_lockout_duration
1289 if use_kerberos == MUST_USE_KERBEROS:
1290 lastlogon_relation = 'greater'
1291 print "Performs a lockout attempt against LDAP using Kerberos"
1293 lastlogon_relation = 'equal'
1294 print "Performs a lockout attempt against LDAP using NTLM"
1296 # Change password on a connection as another user
1297 res = self._check_account(userdn,
1299 badPasswordTime=("greater", 0),
1300 lastLogon=("greater", 0),
1301 lastLogonTimestamp=("greater", 0),
1303 dsdb.UF_NORMAL_ACCOUNT,
1304 msDSUserAccountControlComputed=0)
1305 badPasswordTime = int(res[0]["badPasswordTime"][0])
1306 lastLogon = int(res[0]["lastLogon"][0])
1307 firstLogon = lastLogon
1308 lastLogonTimestamp = int(res[0]["lastLogonTimestamp"][0])
1310 print lastLogonTimestamp
1313 self.assertGreater(lastLogon, badPasswordTime)
1315 # Open a second LDB connection with the user credentials. Use the
1316 # command line credentials for informations like the domain, the realm
1317 # and the workstation.
1318 creds_lockout = insta_creds(template=creds)
1319 creds_lockout.set_kerberos_state(use_kerberos)
1321 # The wrong password
1322 creds_lockout.set_password("thatsAcomplPASS1x")
1324 self.assertLoginFailure(host_url, creds_lockout, lp)
1326 res = self._check_account(userdn,
1328 badPasswordTime=("greater", badPasswordTime),
1329 lastLogon=lastLogon,
1330 lastLogonTimestamp=lastLogonTimestamp,
1332 dsdb.UF_NORMAL_ACCOUNT,
1333 msDSUserAccountControlComputed=0,
1334 msg='lastlogontimestamp with wrong password')
1335 badPasswordTime = int(res[0]["badPasswordTime"][0])
1337 # Correct old password
1338 creds_lockout.set_password(userpass)
1340 ldb_lockout = SamDB(url=host_url, credentials=creds_lockout, lp=lp)
1342 # lastLogonTimestamp should not change
1343 # lastLogon increases if badPwdCount is non-zero (!)
1344 res = self._check_account(userdn,
1346 badPasswordTime=badPasswordTime,
1347 lastLogon=('greater', lastLogon),
1348 lastLogonTimestamp=lastLogonTimestamp,
1350 dsdb.UF_NORMAL_ACCOUNT,
1351 msDSUserAccountControlComputed=0,
1352 msg='LLTimestamp is updated to lastlogon')
1354 lastLogon = int(res[0]["lastLogon"][0])
1355 self.assertGreater(lastLogon, badPasswordTime)
1357 # The wrong password
1358 creds_lockout.set_password("thatsAcomplPASS1x")
1360 self.assertLoginFailure(host_url, creds_lockout, lp)
1362 res = self._check_account(userdn,
1364 badPasswordTime=("greater", badPasswordTime),
1365 lastLogon=lastLogon,
1366 lastLogonTimestamp=lastLogonTimestamp,
1368 dsdb.UF_NORMAL_ACCOUNT,
1369 msDSUserAccountControlComputed=0)
1370 badPasswordTime = int(res[0]["badPasswordTime"][0])
1372 # The wrong password
1373 creds_lockout.set_password("thatsAcomplPASS1x")
1376 ldb_lockout = SamDB(url=host_url, credentials=creds_lockout, lp=lp)
1379 except LdbError, (num, msg):
1380 self.assertEquals(num, ERR_INVALID_CREDENTIALS)
1382 res = self._check_account(userdn,
1384 badPasswordTime=("greater", badPasswordTime),
1385 lastLogon=lastLogon,
1386 lastLogonTimestamp=lastLogonTimestamp,
1388 dsdb.UF_NORMAL_ACCOUNT,
1389 msDSUserAccountControlComputed=0)
1390 badPasswordTime = int(res[0]["badPasswordTime"][0])
1392 print "two failed password change"
1394 # The wrong password
1395 creds_lockout.set_password("thatsAcomplPASS1x")
1398 ldb_lockout = SamDB(url=host_url, credentials=creds_lockout, lp=lp)
1401 except LdbError, (num, msg):
1402 self.assertEquals(num, ERR_INVALID_CREDENTIALS)
1404 res = self._check_account(userdn,
1406 badPasswordTime=("greater", badPasswordTime),
1407 lastLogon=lastLogon,
1408 lastLogonTimestamp=lastLogonTimestamp,
1409 lockoutTime=("greater", badPasswordTime),
1411 dsdb.UF_NORMAL_ACCOUNT,
1412 msDSUserAccountControlComputed=dsdb.UF_LOCKOUT)
1413 badPasswordTime = int(res[0]["badPasswordTime"][0])
1414 lockoutTime = int(res[0]["lockoutTime"][0])
1416 # The wrong password
1417 creds_lockout.set_password("thatsAcomplPASS1x")
1419 ldb_lockout = SamDB(url=host_url, credentials=creds_lockout, lp=lp)
1421 except LdbError, (num, msg):
1422 self.assertEquals(num, ERR_INVALID_CREDENTIALS)
1424 res = self._check_account(userdn,
1426 badPasswordTime=badPasswordTime,
1427 lastLogon=lastLogon,
1428 lastLogonTimestamp=lastLogonTimestamp,
1429 lockoutTime=lockoutTime,
1431 dsdb.UF_NORMAL_ACCOUNT,
1432 msDSUserAccountControlComputed=dsdb.UF_LOCKOUT)
1434 # The wrong password
1435 creds_lockout.set_password("thatsAcomplPASS1x")
1437 ldb_lockout = SamDB(url=host_url, credentials=creds_lockout, lp=lp)
1439 except LdbError, (num, msg):
1440 self.assertEquals(num, ERR_INVALID_CREDENTIALS)
1442 res = self._check_account(userdn,
1444 badPasswordTime=badPasswordTime,
1445 lastLogon=lastLogon,
1446 lastLogonTimestamp=lastLogonTimestamp,
1447 lockoutTime=lockoutTime,
1449 dsdb.UF_NORMAL_ACCOUNT,
1450 msDSUserAccountControlComputed=dsdb.UF_LOCKOUT)
1452 # The correct password, but we are locked out
1453 creds_lockout.set_password(userpass)
1455 ldb_lockout = SamDB(url=host_url, credentials=creds_lockout, lp=lp)
1457 except LdbError, (num, msg):
1458 self.assertEquals(num, ERR_INVALID_CREDENTIALS)
1460 res = self._check_account(userdn,
1462 badPasswordTime=badPasswordTime,
1463 lastLogon=lastLogon,
1464 lastLogonTimestamp=lastLogonTimestamp,
1465 lockoutTime=lockoutTime,
1467 dsdb.UF_NORMAL_ACCOUNT,
1468 msDSUserAccountControlComputed=dsdb.UF_LOCKOUT)
1470 # wait for the lockout to end
1471 time.sleep(self.account_lockout_duration + 1)
1472 print self.account_lockout_duration + 1
1474 res = self._check_account(userdn,
1475 badPwdCount=3, effective_bad_password_count=0,
1476 badPasswordTime=badPasswordTime,
1477 lockoutTime=lockoutTime,
1478 lastLogon=lastLogon,
1479 lastLogonTimestamp=lastLogonTimestamp,
1481 dsdb.UF_NORMAL_ACCOUNT,
1482 msDSUserAccountControlComputed=0)
1484 lastLogon = int(res[0]["lastLogon"][0])
1486 # The correct password after letting the timeout expire
1488 creds_lockout.set_password(userpass)
1490 creds_lockout2 = insta_creds(creds_lockout)
1492 ldb_lockout = SamDB(url=host_url, credentials=creds_lockout2, lp=lp)
1495 res = self._check_account(userdn,
1497 badPasswordTime=badPasswordTime,
1498 lastLogon=(lastlogon_relation, lastLogon),
1499 lastLogonTimestamp=lastLogonTimestamp,
1502 dsdb.UF_NORMAL_ACCOUNT,
1503 msDSUserAccountControlComputed=0,
1504 msg="lastLogon is way off")
1506 lastLogon = int(res[0]["lastLogon"][0])
1508 # The wrong password
1509 creds_lockout.set_password("thatsAcomplPASS1x")
1511 ldb_lockout = SamDB(url=host_url, credentials=creds_lockout, lp=lp)
1513 except LdbError, (num, msg):
1514 self.assertEquals(num, ERR_INVALID_CREDENTIALS)
1516 res = self._check_account(userdn,
1518 badPasswordTime=("greater", badPasswordTime),
1520 lastLogon=lastLogon,
1521 lastLogonTimestamp=lastLogonTimestamp,
1523 dsdb.UF_NORMAL_ACCOUNT,
1524 msDSUserAccountControlComputed=0)
1525 badPasswordTime = int(res[0]["badPasswordTime"][0])
1527 # The wrong password
1528 creds_lockout.set_password("thatsAcomplPASS1x")
1530 ldb_lockout = SamDB(url=host_url, credentials=creds_lockout, lp=lp)
1532 except LdbError, (num, msg):
1533 self.assertEquals(num, ERR_INVALID_CREDENTIALS)
1535 res = self._check_account(userdn,
1537 badPasswordTime=("greater", badPasswordTime),
1539 lastLogon=lastLogon,
1540 lastLogonTimestamp=lastLogonTimestamp,
1542 dsdb.UF_NORMAL_ACCOUNT,
1543 msDSUserAccountControlComputed=0)
1544 badPasswordTime = int(res[0]["badPasswordTime"][0])
1546 time.sleep(self.lockout_observation_window + 1)
1548 res = self._check_account(userdn,
1549 badPwdCount=2, effective_bad_password_count=0,
1550 badPasswordTime=badPasswordTime,
1552 lastLogon=lastLogon,
1553 lastLogonTimestamp=lastLogonTimestamp,
1555 dsdb.UF_NORMAL_ACCOUNT,
1556 msDSUserAccountControlComputed=0)
1558 # The wrong password
1559 creds_lockout.set_password("thatsAcomplPASS1x")
1561 ldb_lockout = SamDB(url=host_url, credentials=creds_lockout, lp=lp)
1563 except LdbError, (num, msg):
1564 self.assertEquals(num, ERR_INVALID_CREDENTIALS)
1566 res = self._check_account(userdn,
1568 badPasswordTime=("greater", badPasswordTime),
1570 lastLogon=lastLogon,
1571 lastLogonTimestamp=lastLogonTimestamp,
1573 dsdb.UF_NORMAL_ACCOUNT,
1574 msDSUserAccountControlComputed=0)
1575 badPasswordTime = int(res[0]["badPasswordTime"][0])
1577 # The correct password without letting the timeout expire
1578 creds_lockout.set_password(userpass)
1579 ldb_lockout = SamDB(url=host_url, credentials=creds_lockout, lp=lp)
1581 res = self._check_account(userdn,
1583 badPasswordTime=badPasswordTime,
1585 lastLogon=("greater", lastLogon),
1586 lastLogonTimestamp=lastLogonTimestamp,
1588 dsdb.UF_NORMAL_ACCOUNT,
1589 msDSUserAccountControlComputed=0)
1591 def test_login_lockout_ntlm(self):
1592 self._test_login_lockout(self.creds2, DONT_USE_KERBEROS)
1594 def test_login_lockout_kerberos(self):
1595 self._test_login_lockout(self.creds2, MUST_USE_KERBEROS)
1597 def _test_multiple_logon(self, creds, use_kerberos):
1598 # Test the happy case in which a user logs on correctly, then
1599 # logs on correctly again, so that the bad password and
1600 # lockout times are both zero the second time. The lastlogon
1601 # time should increase.
1603 # Open a second LDB connection with the user credentials. Use the
1604 # command line credentials for informations like the domain, the realm
1605 # and the workstation.
1606 username = creds.get_username()
1607 userdn = "cn=%s,cn=users,%s" % (username, self.base_dn)
1608 creds2 = insta_creds(template=creds)
1609 creds2.set_kerberos_state(use_kerberos)
1610 self.assertEqual(creds2.get_kerberos_state(), use_kerberos)
1612 if use_kerberos == MUST_USE_KERBEROS:
1613 print "Testing multiple logon with Kerberos"
1614 lastlogon_relation = 'greater'
1616 print "Testing multiple logon with NTLM"
1617 lastlogon_relation = 'equal'
1619 SamDB(url=host_url, credentials=insta_creds(creds2), lp=lp)
1621 res = self._check_account(userdn,
1623 badPasswordTime=("greater", 0),
1624 lastLogon=("greater", 0),
1625 lastLogonTimestamp=("greater", 0),
1627 dsdb.UF_NORMAL_ACCOUNT,
1628 msDSUserAccountControlComputed=0)
1629 badPasswordTime = int(res[0]["badPasswordTime"][0])
1630 lastLogon = int(res[0]["lastLogon"][0])
1631 lastLogonTimestamp = int(res[0]["lastLogonTimestamp"][0])
1632 firstLogon = lastLogon
1633 print "last logon is %d" % lastLogon
1634 self.assertGreater(lastLogon, badPasswordTime)
1637 SamDB(url=host_url, credentials=insta_creds(creds2), lp=lp)
1639 res = self._check_account(userdn,
1641 badPasswordTime=badPasswordTime,
1642 lastLogon=(lastlogon_relation, lastLogon),
1643 lastLogonTimestamp=lastLogonTimestamp,
1645 dsdb.UF_NORMAL_ACCOUNT,
1646 msDSUserAccountControlComputed=0,
1647 msg=("second logon, firstlogon was %s" %
1651 lastLogon = int(res[0]["lastLogon"][0])
1655 SamDB(url=host_url, credentials=insta_creds(creds2), lp=lp)
1657 res = self._check_account(userdn,
1659 badPasswordTime=badPasswordTime,
1660 lastLogon=(lastlogon_relation, lastLogon),
1661 lastLogonTimestamp=lastLogonTimestamp,
1663 dsdb.UF_NORMAL_ACCOUNT,
1664 msDSUserAccountControlComputed=0)
1666 def test_multiple_logon_ntlm(self):
1667 self._test_multiple_logon(self.creds2, DONT_USE_KERBEROS)
1669 def test_multiple_logon_kerberos(self):
1670 self._test_multiple_logon(self.creds2, MUST_USE_KERBEROS)
1673 super(PasswordTests, self).tearDown()
1675 host_url = "ldap://%s" % host
1677 TestProgram(module=__name__, opts=subunitopts)