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
10 from __future__
import print_function
16 sys
.path
.insert(0, "bin/python")
19 from samba
.tests
.subunitrun
import TestProgram
, SubunitOptions
21 import samba
.getopt
as options
23 from samba
.auth
import system_session
24 from samba
.credentials
import Credentials
, DONT_USE_KERBEROS
, MUST_USE_KERBEROS
25 from ldb
import SCOPE_BASE
, LdbError
26 from ldb
import ERR_CONSTRAINT_VIOLATION
27 from ldb
import ERR_INVALID_CREDENTIALS
28 from ldb
import Message
, MessageElement
, Dn
29 from ldb
import FLAG_MOD_REPLACE
30 from samba
import gensec
, dsdb
31 from samba
.samdb
import SamDB
33 from samba
.tests
import delete_force
34 from samba
.dcerpc
import security
, samr
35 from samba
.ndr
import ndr_unpack
36 from samba
.tests
.pso
import PasswordSettings
37 from samba
.net
import Net
38 from samba
import NTSTATUSError
, ntstatus
41 parser
= optparse
.OptionParser("password_lockout.py [options] <host>")
42 sambaopts
= options
.SambaOptions(parser
)
43 parser
.add_option_group(sambaopts
)
44 parser
.add_option_group(options
.VersionOptions(parser
))
45 # use command line creds if available
46 credopts
= options
.CredentialsOptions(parser
)
47 parser
.add_option_group(credopts
)
48 subunitopts
= SubunitOptions(parser
)
49 parser
.add_option_group(subunitopts
)
50 opts
, args
= parser
.parse_args()
58 lp
= sambaopts
.get_loadparm()
59 global_creds
= credopts
.get_credentials(lp
)
61 import password_lockout_base
67 class PasswordTests(password_lockout_base
.BasePasswordTestCase
):
70 self
.host_url
= host_url
72 self
.global_creds
= global_creds
73 self
.ldb
= SamDB(url
=self
.host_url
, session_info
=system_session(self
.lp
),
74 credentials
=self
.global_creds
, lp
=self
.lp
)
75 super(PasswordTests
, self
).setUp()
77 self
.lockout2krb5_creds
= self
.insta_creds(self
.template_creds
,
78 username
="lockout2krb5",
79 userpass
="thatsAcomplPASS0",
80 kerberos_state
=MUST_USE_KERBEROS
)
81 self
.lockout2krb5_ldb
= self
._readd
_user
(self
.lockout2krb5_creds
,
82 lockOutObservationWindow
=self
.lockout_observation_window
)
84 self
.lockout2ntlm_creds
= self
.insta_creds(self
.template_creds
,
85 username
="lockout2ntlm",
86 userpass
="thatsAcomplPASS0",
87 kerberos_state
=DONT_USE_KERBEROS
)
88 self
.lockout2ntlm_ldb
= self
._readd
_user
(self
.lockout2ntlm_creds
,
89 lockOutObservationWindow
=self
.lockout_observation_window
)
92 def use_pso_lockout_settings(self
, creds
):
94 # create a PSO with the lockout settings the test cases normally expect
96 # Some test cases sleep() for self.account_lockout_duration
97 pso
= PasswordSettings("lockout-PSO", self
.ldb
, lockout_attempts
=3,
98 lockout_duration
=self
.account_lockout_duration
)
99 self
.addCleanup(self
.ldb
.delete
, pso
.dn
)
101 userdn
= "cn=%s,cn=users,%s" % (creds
.get_username(), self
.base_dn
)
104 # update the global lockout settings to be wildly different to what
105 # the test cases normally expect
106 self
.update_lockout_settings(threshold
=10, duration
=600,
107 observation_window
=600)
109 def _reset_samr(self
, res
):
111 # Now reset the lockout, by removing ACB_AUTOLOCK (which removes the lock, despite being a generated attribute)
112 samr_user
= self
._open
_samr
_user
(res
)
113 acb_info
= self
.samr
.QueryUserInfo(samr_user
, 16)
114 acb_info
.acct_flags
&= ~samr
.ACB_AUTOLOCK
115 self
.samr
.SetUserInfo(samr_user
, 16, acb_info
)
116 self
.samr
.Close(samr_user
)
119 class PasswordTestsWithoutSleep(PasswordTests
):
121 # The tests in this class do not sleep, so we can have a
122 # longer window and not flap on slower hosts
123 self
.account_lockout_duration
= 30
124 self
.lockout_observation_window
= 30
125 super(PasswordTestsWithoutSleep
, self
).setUp()
127 def _reset_ldap_lockoutTime(self
, res
):
128 self
.ldb
.modify_ldif("""
129 dn: """ + str(res
[0].dn
) + """
135 def _reset_ldap_userAccountControl(self
, res
):
136 self
.assertTrue("userAccountControl" in res
[0])
137 self
.assertTrue("msDS-User-Account-Control-Computed" in res
[0])
139 uac
= int(res
[0]["userAccountControl"][0])
140 uacc
= int(res
[0]["msDS-User-Account-Control-Computed"][0])
143 uac
= uac
& ~dsdb
.UF_LOCKOUT
145 self
.ldb
.modify_ldif("""
146 dn: """ + str(res
[0].dn
) + """
148 replace: userAccountControl
149 userAccountControl: %d
152 def _reset_by_method(self
, res
, method
):
153 if method
is "ldap_userAccountControl":
154 self
._reset
_ldap
_userAccountControl
(res
)
155 elif method
is "ldap_lockoutTime":
156 self
._reset
_ldap
_lockoutTime
(res
)
157 elif method
is "samr":
158 self
._reset
_samr
(res
)
160 self
.assertTrue(False, msg
="Invalid reset method[%s]" % method
)
162 def _test_userPassword_lockout_with_clear_change(self
, creds
, other_ldb
, method
,
163 initial_lastlogon_relation
=None):
165 Tests user lockout behaviour when we try to change the user's password
166 but specify an incorrect old-password. The method parameter specifies
167 how to reset the locked out account (e.g. by resetting lockoutTime)
169 # Notice: This works only against Windows if "dSHeuristics" has been set
171 username
= creds
.get_username()
172 userpass
= creds
.get_password()
173 userdn
= "cn=%s,cn=users,%s" % (username
, self
.base_dn
)
175 use_kerberos
= creds
.get_kerberos_state()
176 if use_kerberos
== MUST_USE_KERBEROS
:
177 logoncount_relation
= 'greater'
178 lastlogon_relation
= 'greater'
179 print("Performs a password cleartext change operation on 'userPassword' using Kerberos")
181 logoncount_relation
= 'equal'
182 lastlogon_relation
= 'equal'
183 print("Performs a password cleartext change operation on 'userPassword' using NTLMSSP")
185 if initial_lastlogon_relation
is not None:
186 lastlogon_relation
= initial_lastlogon_relation
188 res
= self
._check
_account
(userdn
,
190 badPasswordTime
=("greater", 0),
191 logonCount
=(logoncount_relation
, 0),
192 lastLogon
=(lastlogon_relation
, 0),
193 lastLogonTimestamp
=('greater', 0),
195 dsdb
.UF_NORMAL_ACCOUNT
,
196 msDSUserAccountControlComputed
=0)
197 badPasswordTime
= int(res
[0]["badPasswordTime"][0])
198 logonCount
= int(res
[0]["logonCount"][0])
199 lastLogon
= int(res
[0]["lastLogon"][0])
200 lastLogonTimestamp
= int(res
[0]["lastLogonTimestamp"][0])
201 if lastlogon_relation
== 'greater':
202 self
.assertGreater(lastLogon
, badPasswordTime
)
203 self
.assertGreaterEqual(lastLogon
, lastLogonTimestamp
)
205 # Change password on a connection as another user
209 other_ldb
.modify_ldif("""
210 dn: """ + userdn
+ """
213 userPassword: thatsAcomplPASS1x
215 userPassword: thatsAcomplPASS2
218 except LdbError
as e
:
220 self
.assertEquals(num
, ERR_CONSTRAINT_VIOLATION
)
221 self
.assertTrue('00000056' in msg
, msg
)
223 res
= self
._check
_account
(userdn
,
225 badPasswordTime
=("greater", badPasswordTime
),
226 logonCount
=logonCount
,
228 lastLogonTimestamp
=lastLogonTimestamp
,
230 dsdb
.UF_NORMAL_ACCOUNT
,
231 msDSUserAccountControlComputed
=0)
232 badPasswordTime
= int(res
[0]["badPasswordTime"][0])
234 # Correct old password
235 other_ldb
.modify_ldif("""
236 dn: """ + userdn
+ """
239 userPassword: """ + userpass
+ """
241 userPassword: thatsAcomplPASS2
244 res
= self
._check
_account
(userdn
,
246 badPasswordTime
=badPasswordTime
,
247 logonCount
=logonCount
,
249 lastLogonTimestamp
=lastLogonTimestamp
,
251 dsdb
.UF_NORMAL_ACCOUNT
,
252 msDSUserAccountControlComputed
=0)
256 other_ldb
.modify_ldif("""
257 dn: """ + userdn
+ """
260 userPassword: thatsAcomplPASS1x
262 userPassword: thatsAcomplPASS2
265 except LdbError
as e1
:
267 self
.assertEquals(num
, ERR_CONSTRAINT_VIOLATION
)
268 self
.assertTrue('00000056' in msg
, msg
)
270 res
= self
._check
_account
(userdn
,
272 badPasswordTime
=("greater", badPasswordTime
),
273 logonCount
=logonCount
,
275 lastLogonTimestamp
=lastLogonTimestamp
,
277 dsdb
.UF_NORMAL_ACCOUNT
,
278 msDSUserAccountControlComputed
=0)
279 badPasswordTime
= int(res
[0]["badPasswordTime"][0])
281 print("two failed password change")
285 other_ldb
.modify_ldif("""
286 dn: """ + userdn
+ """
289 userPassword: thatsAcomplPASS1x
291 userPassword: thatsAcomplPASS2
294 except LdbError
as e2
:
296 self
.assertEquals(num
, ERR_CONSTRAINT_VIOLATION
)
297 self
.assertTrue('00000056' in msg
, msg
)
299 res
= self
._check
_account
(userdn
,
301 badPasswordTime
=("greater", badPasswordTime
),
302 logonCount
=logonCount
,
304 lastLogonTimestamp
=lastLogonTimestamp
,
305 lockoutTime
=("greater", badPasswordTime
),
307 dsdb
.UF_NORMAL_ACCOUNT
,
308 msDSUserAccountControlComputed
=dsdb
.UF_LOCKOUT
)
309 badPasswordTime
= int(res
[0]["badPasswordTime"][0])
310 lockoutTime
= int(res
[0]["lockoutTime"][0])
314 other_ldb
.modify_ldif("""
315 dn: """ + userdn
+ """
318 userPassword: thatsAcomplPASS1x
320 userPassword: thatsAcomplPASS2
323 except LdbError
as e3
:
325 self
.assertEquals(num
, ERR_CONSTRAINT_VIOLATION
)
326 self
.assertTrue('00000775' in msg
, msg
)
328 res
= self
._check
_account
(userdn
,
330 badPasswordTime
=badPasswordTime
,
331 logonCount
=logonCount
,
333 lastLogonTimestamp
=lastLogonTimestamp
,
334 lockoutTime
=lockoutTime
,
336 dsdb
.UF_NORMAL_ACCOUNT
,
337 msDSUserAccountControlComputed
=dsdb
.UF_LOCKOUT
)
341 other_ldb
.modify_ldif("""
342 dn: """ + userdn
+ """
345 userPassword: thatsAcomplPASS1x
347 userPassword: thatsAcomplPASS2
350 except LdbError
as e4
:
352 self
.assertEquals(num
, ERR_CONSTRAINT_VIOLATION
)
353 self
.assertTrue('00000775' in msg
, msg
)
355 res
= self
._check
_account
(userdn
,
357 badPasswordTime
=badPasswordTime
,
358 logonCount
=logonCount
,
359 lockoutTime
=lockoutTime
,
361 lastLogonTimestamp
=lastLogonTimestamp
,
363 dsdb
.UF_NORMAL_ACCOUNT
,
364 msDSUserAccountControlComputed
=dsdb
.UF_LOCKOUT
)
367 # Correct old password
368 other_ldb
.modify_ldif("""
369 dn: """ + userdn
+ """
372 userPassword: thatsAcomplPASS2
374 userPassword: thatsAcomplPASS2x
377 except LdbError
as e5
:
379 self
.assertEquals(num
, ERR_CONSTRAINT_VIOLATION
)
380 self
.assertTrue('00000775' in msg
, msg
)
382 res
= self
._check
_account
(userdn
,
384 badPasswordTime
=badPasswordTime
,
385 logonCount
=logonCount
,
387 lastLogonTimestamp
=lastLogonTimestamp
,
388 lockoutTime
=lockoutTime
,
390 dsdb
.UF_NORMAL_ACCOUNT
,
391 msDSUserAccountControlComputed
=dsdb
.UF_LOCKOUT
)
393 # Now reset the password, which does NOT change the lockout!
394 self
.ldb
.modify_ldif("""
395 dn: """ + userdn
+ """
397 replace: userPassword
398 userPassword: thatsAcomplPASS2
401 res
= self
._check
_account
(userdn
,
403 badPasswordTime
=badPasswordTime
,
404 logonCount
=logonCount
,
406 lastLogonTimestamp
=lastLogonTimestamp
,
407 lockoutTime
=lockoutTime
,
409 dsdb
.UF_NORMAL_ACCOUNT
,
410 msDSUserAccountControlComputed
=dsdb
.UF_LOCKOUT
)
413 # Correct old password
414 other_ldb
.modify_ldif("""
415 dn: """ + userdn
+ """
418 userPassword: thatsAcomplPASS2
420 userPassword: thatsAcomplPASS2x
423 except LdbError
as e6
:
425 self
.assertEquals(num
, ERR_CONSTRAINT_VIOLATION
)
426 self
.assertTrue('00000775' in msg
, msg
)
428 res
= self
._check
_account
(userdn
,
430 badPasswordTime
=badPasswordTime
,
431 logonCount
=logonCount
,
433 lastLogonTimestamp
=lastLogonTimestamp
,
434 lockoutTime
=lockoutTime
,
436 dsdb
.UF_NORMAL_ACCOUNT
,
437 msDSUserAccountControlComputed
=dsdb
.UF_LOCKOUT
)
440 m
.dn
= Dn(self
.ldb
, userdn
)
441 m
["userAccountControl"] = MessageElement(
442 str(dsdb
.UF_LOCKOUT
),
443 FLAG_MOD_REPLACE
, "userAccountControl")
447 # This shows that setting the UF_LOCKOUT flag alone makes no difference
448 res
= self
._check
_account
(userdn
,
450 badPasswordTime
=badPasswordTime
,
451 logonCount
=logonCount
,
453 lastLogonTimestamp
=lastLogonTimestamp
,
454 lockoutTime
=lockoutTime
,
456 dsdb
.UF_NORMAL_ACCOUNT
,
457 msDSUserAccountControlComputed
=dsdb
.UF_LOCKOUT
)
459 # This shows that setting the UF_LOCKOUT flag makes no difference
461 # Correct old password
462 other_ldb
.modify_ldif("""
463 dn: """ + userdn
+ """
466 unicodePwd:: """ + base64
.b64encode("\"thatsAcomplPASS2\"".encode('utf-16-le')).decode('utf8') + """
468 unicodePwd:: """ + base64
.b64encode("\"thatsAcomplPASS2x\"".encode('utf-16-le')).decode('utf8') + """
471 except LdbError
as e7
:
473 self
.assertEquals(num
, ERR_CONSTRAINT_VIOLATION
)
474 self
.assertTrue('00000775' in msg
, msg
)
476 res
= self
._check
_account
(userdn
,
478 badPasswordTime
=badPasswordTime
,
479 logonCount
=logonCount
,
480 lockoutTime
=lockoutTime
,
482 lastLogonTimestamp
=lastLogonTimestamp
,
484 dsdb
.UF_NORMAL_ACCOUNT
,
485 msDSUserAccountControlComputed
=dsdb
.UF_LOCKOUT
)
487 self
._reset
_by
_method
(res
, method
)
489 # Here bad password counts are reset without logon success.
490 res
= self
._check
_account
(userdn
,
492 badPasswordTime
=badPasswordTime
,
493 logonCount
=logonCount
,
496 lastLogonTimestamp
=lastLogonTimestamp
,
498 dsdb
.UF_NORMAL_ACCOUNT
,
499 msDSUserAccountControlComputed
=0)
501 # The correct password after doing the unlock
503 other_ldb
.modify_ldif("""
504 dn: """ + userdn
+ """
507 unicodePwd:: """ + base64
.b64encode("\"thatsAcomplPASS2\"".encode('utf-16-le')).decode('utf8') + """
509 unicodePwd:: """ + base64
.b64encode("\"thatsAcomplPASS2x\"".encode('utf-16-le')).decode('utf8') + """
511 userpass
= "thatsAcomplPASS2x"
512 creds
.set_password(userpass
)
514 res
= self
._check
_account
(userdn
,
516 badPasswordTime
=badPasswordTime
,
517 logonCount
=logonCount
,
520 lastLogonTimestamp
=lastLogonTimestamp
,
522 dsdb
.UF_NORMAL_ACCOUNT
,
523 msDSUserAccountControlComputed
=0)
527 other_ldb
.modify_ldif("""
528 dn: """ + userdn
+ """
531 userPassword: thatsAcomplPASS1xyz
533 userPassword: thatsAcomplPASS2XYZ
536 except LdbError
as e8
:
538 self
.assertEquals(num
, ERR_CONSTRAINT_VIOLATION
)
539 self
.assertTrue('00000056' in msg
, msg
)
541 res
= self
._check
_account
(userdn
,
543 badPasswordTime
=("greater", badPasswordTime
),
544 logonCount
=logonCount
,
547 lastLogonTimestamp
=lastLogonTimestamp
,
549 dsdb
.UF_NORMAL_ACCOUNT
,
550 msDSUserAccountControlComputed
=0)
551 badPasswordTime
= int(res
[0]["badPasswordTime"][0])
555 other_ldb
.modify_ldif("""
556 dn: """ + userdn
+ """
559 userPassword: thatsAcomplPASS1xyz
561 userPassword: thatsAcomplPASS2XYZ
564 except LdbError
as e9
:
566 self
.assertEquals(num
, ERR_CONSTRAINT_VIOLATION
)
567 self
.assertTrue('00000056' in msg
, msg
)
569 res
= self
._check
_account
(userdn
,
571 badPasswordTime
=("greater", badPasswordTime
),
572 logonCount
=logonCount
,
575 lastLogonTimestamp
=lastLogonTimestamp
,
577 dsdb
.UF_NORMAL_ACCOUNT
,
578 msDSUserAccountControlComputed
=0)
579 badPasswordTime
= int(res
[0]["badPasswordTime"][0])
581 self
._reset
_ldap
_lockoutTime
(res
)
583 res
= self
._check
_account
(userdn
,
585 badPasswordTime
=badPasswordTime
,
586 logonCount
=logonCount
,
588 lastLogonTimestamp
=lastLogonTimestamp
,
591 dsdb
.UF_NORMAL_ACCOUNT
,
592 msDSUserAccountControlComputed
=0)
594 # The following test lockout behaviour when modifying a user's password
595 # and specifying an invalid old password. There are variants for both
596 # NTLM and kerberos user authentication. As well as that, there are 3 ways
597 # to reset the locked out account: by clearing the lockout bit for
598 # userAccountControl (via LDAP), resetting it via SAMR, and by resetting
600 def test_userPassword_lockout_with_clear_change_krb5_ldap_userAccountControl(self
):
601 self
._test
_userPassword
_lockout
_with
_clear
_change
(self
.lockout1krb5_creds
,
602 self
.lockout2krb5_ldb
,
603 "ldap_userAccountControl")
605 def test_userPassword_lockout_with_clear_change_krb5_ldap_lockoutTime(self
):
606 self
._test
_userPassword
_lockout
_with
_clear
_change
(self
.lockout1krb5_creds
,
607 self
.lockout2krb5_ldb
,
610 def test_userPassword_lockout_with_clear_change_krb5_samr(self
):
611 self
._test
_userPassword
_lockout
_with
_clear
_change
(self
.lockout1krb5_creds
,
612 self
.lockout2krb5_ldb
,
615 def test_userPassword_lockout_with_clear_change_ntlm_ldap_userAccountControl(self
):
616 self
._test
_userPassword
_lockout
_with
_clear
_change
(self
.lockout1ntlm_creds
,
617 self
.lockout2ntlm_ldb
,
618 "ldap_userAccountControl",
619 initial_lastlogon_relation
='greater')
621 def test_userPassword_lockout_with_clear_change_ntlm_ldap_lockoutTime(self
):
622 self
._test
_userPassword
_lockout
_with
_clear
_change
(self
.lockout1ntlm_creds
,
623 self
.lockout2ntlm_ldb
,
625 initial_lastlogon_relation
='greater')
627 def test_userPassword_lockout_with_clear_change_ntlm_samr(self
):
628 self
._test
_userPassword
_lockout
_with
_clear
_change
(self
.lockout1ntlm_creds
,
629 self
.lockout2ntlm_ldb
,
631 initial_lastlogon_relation
='greater')
633 # For PSOs, just test a selection of the above combinations
634 def test_pso_userPassword_lockout_with_clear_change_krb5_ldap_userAccountControl(self
):
635 self
.use_pso_lockout_settings(self
.lockout1krb5_creds
)
636 self
._test
_userPassword
_lockout
_with
_clear
_change
(self
.lockout1krb5_creds
,
637 self
.lockout2krb5_ldb
,
638 "ldap_userAccountControl")
640 def test_pso_userPassword_lockout_with_clear_change_ntlm_ldap_lockoutTime(self
):
641 self
.use_pso_lockout_settings(self
.lockout1ntlm_creds
)
642 self
._test
_userPassword
_lockout
_with
_clear
_change
(self
.lockout1ntlm_creds
,
643 self
.lockout2ntlm_ldb
,
645 initial_lastlogon_relation
='greater')
647 def test_pso_userPassword_lockout_with_clear_change_ntlm_samr(self
):
648 self
.use_pso_lockout_settings(self
.lockout1ntlm_creds
)
649 self
._test
_userPassword
_lockout
_with
_clear
_change
(self
.lockout1ntlm_creds
,
650 self
.lockout2ntlm_ldb
,
652 initial_lastlogon_relation
='greater')
654 def test_multiple_logon_krb5(self
):
655 self
._test
_multiple
_logon
(self
.lockout1krb5_creds
)
657 def test_multiple_logon_ntlm(self
):
658 self
._test
_multiple
_logon
(self
.lockout1ntlm_creds
)
660 def _test_samr_password_change(self
, creds
, other_creds
, lockout_threshold
=3):
661 """Tests user lockout by using bad password in SAMR password_change"""
663 # create a connection for SAMR using another user's credentials
664 lp
= self
.get_loadparm()
665 net
= Net(other_creds
, lp
, server
=self
.host
)
667 # work out the initial account values for this user
668 username
= creds
.get_username()
669 userdn
= "cn=%s,cn=users,%s" % (username
, self
.base_dn
)
670 res
= self
._check
_account
(userdn
,
672 badPasswordTime
=("greater", 0),
673 badPwdCountOnly
=True)
674 badPasswordTime
= int(res
[0]["badPasswordTime"][0])
675 logonCount
= int(res
[0]["logonCount"][0])
676 lastLogon
= int(res
[0]["lastLogon"][0])
677 lastLogonTimestamp
= int(res
[0]["lastLogonTimestamp"][0])
679 # prove we can change the user password (using the correct password)
680 new_password
= "thatsAcomplPASS2"
681 net
.change_password(newpassword
=new_password
.encode('utf-8'),
683 oldpassword
=creds
.get_password())
684 creds
.set_password(new_password
)
686 # try entering 'x' many bad passwords in a row to lock the user out
687 new_password
= "thatsAcomplPASS3"
688 for i
in range(lockout_threshold
):
691 print("Trying bad password, attempt #%u" % badPwdCount
)
692 net
.change_password(newpassword
=new_password
.encode('utf-8'),
693 username
=creds
.get_username(),
694 oldpassword
="bad-password")
695 self
.fail("Invalid SAMR change_password accepted")
696 except NTSTATUSError
as e
:
697 enum
= ctypes
.c_uint32(e
[0]).value
698 self
.assertEquals(enum
, ntstatus
.NT_STATUS_WRONG_PASSWORD
)
700 # check the status of the account is updated after each bad attempt
703 if badPwdCount
>= lockout_threshold
:
704 account_flags
= dsdb
.UF_LOCKOUT
705 lockoutTime
= ("greater", badPasswordTime
)
707 res
= self
._check
_account
(userdn
,
708 badPwdCount
=badPwdCount
,
709 badPasswordTime
=("greater", badPasswordTime
),
710 logonCount
=logonCount
,
712 lastLogonTimestamp
=lastLogonTimestamp
,
713 lockoutTime
=lockoutTime
,
714 userAccountControl
=dsdb
.UF_NORMAL_ACCOUNT
,
715 msDSUserAccountControlComputed
=account_flags
)
716 badPasswordTime
= int(res
[0]["badPasswordTime"][0])
718 # the user is now locked out
719 lockoutTime
= int(res
[0]["lockoutTime"][0])
721 # check the user remains locked out regardless of whether they use a
722 # good or a bad password now
723 for password
in (creds
.get_password(), "bad-password"):
725 print("Trying password %s" % password
)
726 net
.change_password(newpassword
=new_password
.encode('utf-8'),
727 username
=creds
.get_username(),
728 oldpassword
=password
)
729 self
.fail("Invalid SAMR change_password accepted")
730 except NTSTATUSError
as e
:
731 enum
= ctypes
.c_uint32(e
[0]).value
732 self
.assertEquals(enum
, ntstatus
.NT_STATUS_ACCOUNT_LOCKED_OUT
)
734 res
= self
._check
_account
(userdn
,
735 badPwdCount
=lockout_threshold
,
736 badPasswordTime
=badPasswordTime
,
737 logonCount
=logonCount
,
739 lastLogonTimestamp
=lastLogonTimestamp
,
740 lockoutTime
=lockoutTime
,
741 userAccountControl
=dsdb
.UF_NORMAL_ACCOUNT
,
742 msDSUserAccountControlComputed
=dsdb
.UF_LOCKOUT
)
744 # reset the user account lockout
745 self
._reset
_samr
(res
)
747 # check bad password counts are reset
748 res
= self
._check
_account
(userdn
,
750 badPasswordTime
=badPasswordTime
,
751 logonCount
=logonCount
,
754 lastLogonTimestamp
=lastLogonTimestamp
,
755 userAccountControl
=dsdb
.UF_NORMAL_ACCOUNT
,
756 msDSUserAccountControlComputed
=0)
758 # check we can change the user password successfully now
759 net
.change_password(newpassword
=new_password
.encode('utf-8'),
761 oldpassword
=creds
.get_password())
762 creds
.set_password(new_password
)
764 def test_samr_change_password(self
):
765 self
._test
_samr
_password
_change
(self
.lockout1ntlm_creds
,
766 other_creds
=self
.lockout2ntlm_creds
)
768 # same as above, but use a PSO to enforce the lockout
769 def test_pso_samr_change_password(self
):
770 self
.use_pso_lockout_settings(self
.lockout1ntlm_creds
)
771 self
._test
_samr
_password
_change
(self
.lockout1ntlm_creds
,
772 other_creds
=self
.lockout2ntlm_creds
)
775 class PasswordTestsWithSleep(PasswordTests
):
777 super(PasswordTestsWithSleep
, self
).setUp()
779 def _test_unicodePwd_lockout_with_clear_change(self
, creds
, other_ldb
,
780 initial_logoncount_relation
=None):
781 print("Performs a password cleartext change operation on 'unicodePwd'")
782 username
= creds
.get_username()
783 userpass
= creds
.get_password()
784 userdn
= "cn=%s,cn=users,%s" % (username
, self
.base_dn
)
785 if initial_logoncount_relation
is not None:
786 logoncount_relation
= initial_logoncount_relation
788 logoncount_relation
= "greater"
790 res
= self
._check
_account
(userdn
,
792 badPasswordTime
=("greater", 0),
793 logonCount
=(logoncount_relation
, 0),
794 lastLogon
=("greater", 0),
795 lastLogonTimestamp
=("greater", 0),
797 dsdb
.UF_NORMAL_ACCOUNT
,
798 msDSUserAccountControlComputed
=0)
799 badPasswordTime
= int(res
[0]["badPasswordTime"][0])
800 logonCount
= int(res
[0]["logonCount"][0])
801 lastLogon
= int(res
[0]["lastLogon"][0])
802 lastLogonTimestamp
= int(res
[0]["lastLogonTimestamp"][0])
803 self
.assertGreater(lastLogonTimestamp
, badPasswordTime
)
804 self
.assertGreaterEqual(lastLogon
, lastLogonTimestamp
)
806 # Change password on a connection as another user
810 other_ldb
.modify_ldif("""
811 dn: """ + userdn
+ """
814 unicodePwd:: """ + base64
.b64encode("\"thatsAcomplPASS1x\"".encode('utf-16-le')).decode('utf8') + """
816 unicodePwd:: """ + base64
.b64encode("\"thatsAcomplPASS2\"".encode('utf-16-le')).decode('utf8') + """
819 except LdbError
as e10
:
820 (num
, msg
) = e10
.args
821 self
.assertEquals(num
, ERR_CONSTRAINT_VIOLATION
)
822 self
.assertTrue('00000056' in msg
, msg
)
824 res
= self
._check
_account
(userdn
,
826 badPasswordTime
=("greater", badPasswordTime
),
827 logonCount
=logonCount
,
829 lastLogonTimestamp
=lastLogonTimestamp
,
831 dsdb
.UF_NORMAL_ACCOUNT
,
832 msDSUserAccountControlComputed
=0)
833 badPasswordTime
= int(res
[0]["badPasswordTime"][0])
835 # Correct old password
836 old_utf16
= ("\"%s\"" % userpass
).encode('utf-16-le')
837 invalid_utf16
= "\"thatsAcomplPASSX\"".encode('utf-16-le')
838 userpass
= "thatsAcomplPASS2"
839 creds
.set_password(userpass
)
840 new_utf16
= ("\"%s\"" % userpass
).encode('utf-16-le')
842 other_ldb
.modify_ldif("""
843 dn: """ + userdn
+ """
846 unicodePwd:: """ + base64
.b64encode(old_utf16
).decode('utf8') + """
848 unicodePwd:: """ + base64
.b64encode(new_utf16
).decode('utf8') + """
851 res
= self
._check
_account
(userdn
,
853 badPasswordTime
=badPasswordTime
,
854 logonCount
=logonCount
,
856 lastLogonTimestamp
=lastLogonTimestamp
,
858 dsdb
.UF_NORMAL_ACCOUNT
,
859 msDSUserAccountControlComputed
=0)
863 other_ldb
.modify_ldif("""
864 dn: """ + userdn
+ """
867 unicodePwd:: """ + base64
.b64encode(old_utf16
).decode('utf8') + """
869 unicodePwd:: """ + base64
.b64encode(new_utf16
).decode('utf8') + """
872 except LdbError
as e11
:
873 (num
, msg
) = e11
.args
874 self
.assertEquals(num
, ERR_CONSTRAINT_VIOLATION
)
875 self
.assertTrue('00000056' in msg
, msg
)
877 res
= self
._check
_account
(userdn
,
879 badPasswordTime
=("greater", badPasswordTime
),
880 logonCount
=logonCount
,
882 lastLogonTimestamp
=lastLogonTimestamp
,
884 dsdb
.UF_NORMAL_ACCOUNT
,
885 msDSUserAccountControlComputed
=0)
886 badPasswordTime
= int(res
[0]["badPasswordTime"][0])
888 # SAMR doesn't have any impact if dsdb.UF_LOCKOUT isn't present.
889 # It doesn't create "lockoutTime" = 0 and doesn't
890 # reset "badPwdCount" = 0.
891 self
._reset
_samr
(res
)
893 res
= self
._check
_account
(userdn
,
895 badPasswordTime
=badPasswordTime
,
896 logonCount
=logonCount
,
898 lastLogonTimestamp
=lastLogonTimestamp
,
900 dsdb
.UF_NORMAL_ACCOUNT
,
901 msDSUserAccountControlComputed
=0)
903 print("two failed password change")
907 other_ldb
.modify_ldif("""
908 dn: """ + userdn
+ """
911 unicodePwd:: """ + base64
.b64encode(invalid_utf16
).decode('utf8') + """
913 unicodePwd:: """ + base64
.b64encode(new_utf16
).decode('utf8') + """
916 except LdbError
as e12
:
917 (num
, msg
) = e12
.args
918 self
.assertEquals(num
, ERR_CONSTRAINT_VIOLATION
)
919 self
.assertTrue('00000056' in msg
, msg
)
921 # this is strange, why do we have lockoutTime=badPasswordTime here?
922 res
= self
._check
_account
(userdn
,
924 badPasswordTime
=("greater", badPasswordTime
),
925 logonCount
=logonCount
,
927 lastLogonTimestamp
=lastLogonTimestamp
,
928 lockoutTime
=("greater", badPasswordTime
),
930 dsdb
.UF_NORMAL_ACCOUNT
,
931 msDSUserAccountControlComputed
=dsdb
.UF_LOCKOUT
)
932 badPasswordTime
= int(res
[0]["badPasswordTime"][0])
933 lockoutTime
= int(res
[0]["lockoutTime"][0])
937 other_ldb
.modify_ldif("""
938 dn: """ + userdn
+ """
941 unicodePwd:: """ + base64
.b64encode(invalid_utf16
).decode('utf8') + """
943 unicodePwd:: """ + base64
.b64encode(new_utf16
).decode('utf8') + """
946 except LdbError
as e13
:
947 (num
, msg
) = e13
.args
948 self
.assertEquals(num
, ERR_CONSTRAINT_VIOLATION
)
949 self
.assertTrue('00000775' in msg
, msg
)
951 res
= self
._check
_account
(userdn
,
953 badPasswordTime
=badPasswordTime
,
954 logonCount
=logonCount
,
956 lastLogonTimestamp
=lastLogonTimestamp
,
957 lockoutTime
=lockoutTime
,
959 dsdb
.UF_NORMAL_ACCOUNT
,
960 msDSUserAccountControlComputed
=dsdb
.UF_LOCKOUT
)
964 other_ldb
.modify_ldif("""
965 dn: """ + userdn
+ """
968 unicodePwd:: """ + base64
.b64encode(invalid_utf16
).decode('utf8') + """
970 unicodePwd:: """ + base64
.b64encode(new_utf16
).decode('utf8') + """
973 except LdbError
as e14
:
974 (num
, msg
) = e14
.args
975 self
.assertEquals(num
, ERR_CONSTRAINT_VIOLATION
)
976 self
.assertTrue('00000775' in msg
, msg
)
978 res
= self
._check
_account
(userdn
,
980 badPasswordTime
=badPasswordTime
,
981 logonCount
=logonCount
,
983 lastLogonTimestamp
=lastLogonTimestamp
,
984 lockoutTime
=lockoutTime
,
986 dsdb
.UF_NORMAL_ACCOUNT
,
987 msDSUserAccountControlComputed
=dsdb
.UF_LOCKOUT
)
990 # Correct old password
991 other_ldb
.modify_ldif("""
992 dn: """ + userdn
+ """
995 unicodePwd:: """ + base64
.b64encode(new_utf16
).decode('utf8') + """
997 unicodePwd:: """ + base64
.b64encode(invalid_utf16
).decode('utf8') + """
1000 except LdbError
as e15
:
1001 (num
, msg
) = e15
.args
1002 self
.assertEquals(num
, ERR_CONSTRAINT_VIOLATION
)
1003 self
.assertTrue('00000775' in msg
, msg
)
1005 res
= self
._check
_account
(userdn
,
1007 badPasswordTime
=badPasswordTime
,
1008 logonCount
=logonCount
,
1009 lastLogon
=lastLogon
,
1010 lastLogonTimestamp
=lastLogonTimestamp
,
1011 lockoutTime
=lockoutTime
,
1013 dsdb
.UF_NORMAL_ACCOUNT
,
1014 msDSUserAccountControlComputed
=dsdb
.UF_LOCKOUT
)
1016 # Now reset the lockout, by removing ACB_AUTOLOCK (which removes the lock, despite being a generated attribute)
1017 self
._reset
_samr
(res
);
1019 res
= self
._check
_account
(userdn
,
1021 badPasswordTime
=badPasswordTime
,
1022 logonCount
=logonCount
,
1023 lastLogon
=lastLogon
,
1024 lastLogonTimestamp
=lastLogonTimestamp
,
1027 dsdb
.UF_NORMAL_ACCOUNT
,
1028 msDSUserAccountControlComputed
=0)
1030 # Correct old password
1031 old_utf16
= ("\"%s\"" % userpass
).encode('utf-16-le')
1032 invalid_utf16
= "\"thatsAcomplPASSiX\"".encode('utf-16-le')
1033 userpass
= "thatsAcomplPASS2x"
1034 creds
.set_password(userpass
)
1035 new_utf16
= ("\"%s\"" % userpass
).encode('utf-16-le')
1037 other_ldb
.modify_ldif("""
1038 dn: """ + userdn
+ """
1041 unicodePwd:: """ + base64
.b64encode(old_utf16
).decode('utf8') + """
1043 unicodePwd:: """ + base64
.b64encode(new_utf16
).decode('utf8') + """
1046 res
= self
._check
_account
(userdn
,
1048 badPasswordTime
=badPasswordTime
,
1049 logonCount
=logonCount
,
1050 lastLogon
=lastLogon
,
1051 lastLogonTimestamp
=lastLogonTimestamp
,
1054 dsdb
.UF_NORMAL_ACCOUNT
,
1055 msDSUserAccountControlComputed
=0)
1057 # Wrong old password
1059 other_ldb
.modify_ldif("""
1060 dn: """ + userdn
+ """
1063 unicodePwd:: """ + base64
.b64encode(invalid_utf16
).decode('utf8') + """
1065 unicodePwd:: """ + base64
.b64encode(new_utf16
).decode('utf8') + """
1068 except LdbError
as e16
:
1069 (num
, msg
) = e16
.args
1070 self
.assertEquals(num
, ERR_CONSTRAINT_VIOLATION
)
1071 self
.assertTrue('00000056' in msg
, msg
)
1073 res
= self
._check
_account
(userdn
,
1075 badPasswordTime
=("greater", badPasswordTime
),
1076 logonCount
=logonCount
,
1077 lastLogon
=lastLogon
,
1078 lastLogonTimestamp
=lastLogonTimestamp
,
1081 dsdb
.UF_NORMAL_ACCOUNT
,
1082 msDSUserAccountControlComputed
=0)
1083 badPasswordTime
= int(res
[0]["badPasswordTime"][0])
1085 # Wrong old password
1087 other_ldb
.modify_ldif("""
1088 dn: """ + userdn
+ """
1091 unicodePwd:: """ + base64
.b64encode(invalid_utf16
).decode('utf8') + """
1093 unicodePwd:: """ + base64
.b64encode(new_utf16
).decode('utf8') + """
1096 except LdbError
as e17
:
1097 (num
, msg
) = e17
.args
1098 self
.assertEquals(num
, ERR_CONSTRAINT_VIOLATION
)
1099 self
.assertTrue('00000056' in msg
, msg
)
1101 res
= self
._check
_account
(userdn
,
1103 badPasswordTime
=("greater", badPasswordTime
),
1104 logonCount
=logonCount
,
1105 lastLogon
=lastLogon
,
1106 lastLogonTimestamp
=lastLogonTimestamp
,
1109 dsdb
.UF_NORMAL_ACCOUNT
,
1110 msDSUserAccountControlComputed
=0)
1111 badPasswordTime
= int(res
[0]["badPasswordTime"][0])
1113 # SAMR doesn't have any impact if dsdb.UF_LOCKOUT isn't present.
1114 # It doesn't reset "badPwdCount" = 0.
1115 self
._reset
_samr
(res
)
1117 res
= self
._check
_account
(userdn
,
1119 badPasswordTime
=badPasswordTime
,
1120 logonCount
=logonCount
,
1121 lastLogon
=lastLogon
,
1122 lastLogonTimestamp
=lastLogonTimestamp
,
1125 dsdb
.UF_NORMAL_ACCOUNT
,
1126 msDSUserAccountControlComputed
=0)
1128 # Wrong old password
1130 other_ldb
.modify_ldif("""
1131 dn: """ + userdn
+ """
1134 unicodePwd:: """ + base64
.b64encode(invalid_utf16
).decode('utf8') + """
1136 unicodePwd:: """ + base64
.b64encode(new_utf16
).decode('utf8') + """
1139 except LdbError
as e18
:
1140 (num
, msg
) = e18
.args
1141 self
.assertEquals(num
, ERR_CONSTRAINT_VIOLATION
)
1142 self
.assertTrue('00000056' in msg
, msg
)
1144 res
= self
._check
_account
(userdn
,
1146 badPasswordTime
=("greater", badPasswordTime
),
1147 logonCount
=logonCount
,
1148 lastLogon
=lastLogon
,
1149 lastLogonTimestamp
=lastLogonTimestamp
,
1150 lockoutTime
=("greater", badPasswordTime
),
1152 dsdb
.UF_NORMAL_ACCOUNT
,
1153 msDSUserAccountControlComputed
=dsdb
.UF_LOCKOUT
)
1154 badPasswordTime
= int(res
[0]["badPasswordTime"][0])
1155 lockoutTime
= int(res
[0]["lockoutTime"][0])
1157 time
.sleep(self
.account_lockout_duration
+ 1)
1159 res
= self
._check
_account
(userdn
,
1160 badPwdCount
=3, effective_bad_password_count
=0,
1161 badPasswordTime
=badPasswordTime
,
1162 logonCount
=logonCount
,
1163 lastLogon
=lastLogon
,
1164 lastLogonTimestamp
=lastLogonTimestamp
,
1165 lockoutTime
=lockoutTime
,
1167 dsdb
.UF_NORMAL_ACCOUNT
,
1168 msDSUserAccountControlComputed
=0)
1170 # SAMR doesn't have any impact if dsdb.UF_LOCKOUT isn't present.
1171 # It doesn't reset "lockoutTime" = 0 and doesn't
1172 # reset "badPwdCount" = 0.
1173 self
._reset
_samr
(res
)
1175 res
= self
._check
_account
(userdn
,
1176 badPwdCount
=3, effective_bad_password_count
=0,
1177 badPasswordTime
=badPasswordTime
,
1178 logonCount
=logonCount
,
1179 lockoutTime
=lockoutTime
,
1180 lastLogon
=lastLogon
,
1181 lastLogonTimestamp
=lastLogonTimestamp
,
1183 dsdb
.UF_NORMAL_ACCOUNT
,
1184 msDSUserAccountControlComputed
=0)
1186 def test_unicodePwd_lockout_with_clear_change_krb5(self
):
1187 self
._test
_unicodePwd
_lockout
_with
_clear
_change
(self
.lockout1krb5_creds
,
1188 self
.lockout2krb5_ldb
)
1190 def test_unicodePwd_lockout_with_clear_change_ntlm(self
):
1191 self
._test
_unicodePwd
_lockout
_with
_clear
_change
(self
.lockout1ntlm_creds
,
1192 self
.lockout2ntlm_ldb
,
1193 initial_logoncount_relation
="equal")
1195 def test_login_lockout_krb5(self
):
1196 self
._test
_login
_lockout
(self
.lockout1krb5_creds
)
1198 def test_login_lockout_ntlm(self
):
1199 self
._test
_login
_lockout
(self
.lockout1ntlm_creds
)
1201 # Repeat the login lockout tests using PSOs
1202 def test_pso_login_lockout_krb5(self
):
1203 """Check the PSO lockout settings get applied to the user correctly"""
1204 self
.use_pso_lockout_settings(self
.lockout1krb5_creds
)
1205 self
._test
_login
_lockout
(self
.lockout1krb5_creds
)
1207 def test_pso_login_lockout_ntlm(self
):
1208 """Check the PSO lockout settings get applied to the user correctly"""
1209 self
.use_pso_lockout_settings(self
.lockout1ntlm_creds
)
1210 self
._test
_login
_lockout
(self
.lockout1ntlm_creds
)
1212 def _testing_add_user(self
, creds
, lockOutObservationWindow
=0):
1213 username
= creds
.get_username()
1214 userpass
= creds
.get_password()
1215 userdn
= "cn=%s,cn=users,%s" % (username
, self
.base_dn
)
1217 use_kerberos
= creds
.get_kerberos_state()
1218 if use_kerberos
== MUST_USE_KERBEROS
:
1219 logoncount_relation
= 'greater'
1220 lastlogon_relation
= 'greater'
1222 logoncount_relation
= 'equal'
1223 if lockOutObservationWindow
== 0:
1224 lastlogon_relation
= 'greater'
1226 lastlogon_relation
= 'equal'
1228 delete_force(self
.ldb
, userdn
)
1231 "objectclass": "user",
1232 "sAMAccountName": username
})
1234 self
.addCleanup(delete_force
, self
.ldb
, userdn
)
1236 res
= self
._check
_account
(userdn
,
1241 lastLogonTimestamp
=('absent', None),
1243 dsdb
.UF_NORMAL_ACCOUNT |
1244 dsdb
.UF_ACCOUNTDISABLE |
1245 dsdb
.UF_PASSWD_NOTREQD
,
1246 msDSUserAccountControlComputed
=
1247 dsdb
.UF_PASSWORD_EXPIRED
)
1249 # SAMR doesn't have any impact if dsdb.UF_LOCKOUT isn't present.
1250 # It doesn't create "lockoutTime" = 0.
1251 self
._reset
_samr
(res
)
1253 res
= self
._check
_account
(userdn
,
1258 lastLogonTimestamp
=('absent', None),
1260 dsdb
.UF_NORMAL_ACCOUNT |
1261 dsdb
.UF_ACCOUNTDISABLE |
1262 dsdb
.UF_PASSWD_NOTREQD
,
1263 msDSUserAccountControlComputed
=
1264 dsdb
.UF_PASSWORD_EXPIRED
)
1266 # Tests a password change when we don't have any password yet with a
1267 # wrong old password
1269 self
.ldb
.modify_ldif("""
1270 dn: """ + userdn
+ """
1272 delete: userPassword
1273 userPassword: noPassword
1275 userPassword: thatsAcomplPASS2
1278 except LdbError
as e19
:
1279 (num
, msg
) = e19
.args
1280 self
.assertEquals(num
, ERR_CONSTRAINT_VIOLATION
)
1281 # Windows (2008 at least) seems to have some small bug here: it
1282 # returns "0000056A" on longer (always wrong) previous passwords.
1283 self
.assertTrue('00000056' in msg
, msg
)
1285 res
= self
._check
_account
(userdn
,
1287 badPasswordTime
=("greater", 0),
1290 lastLogonTimestamp
=('absent', None),
1292 dsdb
.UF_NORMAL_ACCOUNT |
1293 dsdb
.UF_ACCOUNTDISABLE |
1294 dsdb
.UF_PASSWD_NOTREQD
,
1295 msDSUserAccountControlComputed
=
1296 dsdb
.UF_PASSWORD_EXPIRED
)
1297 badPwdCount
= int(res
[0]["badPwdCount"][0])
1298 badPasswordTime
= int(res
[0]["badPasswordTime"][0])
1300 # Sets the initial user password with a "special" password change
1301 # I think that this internally is a password set operation and it can
1302 # only be performed by someone which has password set privileges on the
1303 # account (at least in s4 we do handle it like that).
1304 self
.ldb
.modify_ldif("""
1305 dn: """ + userdn
+ """
1307 delete: userPassword
1309 userPassword: """ + userpass
+ """
1312 res
= self
._check
_account
(userdn
,
1313 badPwdCount
=badPwdCount
,
1314 badPasswordTime
=badPasswordTime
,
1317 lastLogonTimestamp
=('absent', None),
1319 dsdb
.UF_NORMAL_ACCOUNT |
1320 dsdb
.UF_ACCOUNTDISABLE |
1321 dsdb
.UF_PASSWD_NOTREQD
,
1322 msDSUserAccountControlComputed
=0)
1324 # Enables the user account
1325 self
.ldb
.enable_account("(sAMAccountName=%s)" % username
)
1327 res
= self
._check
_account
(userdn
,
1328 badPwdCount
=badPwdCount
,
1329 badPasswordTime
=badPasswordTime
,
1332 lastLogonTimestamp
=('absent', None),
1334 dsdb
.UF_NORMAL_ACCOUNT
,
1335 msDSUserAccountControlComputed
=0)
1336 if lockOutObservationWindow
!= 0:
1337 time
.sleep(lockOutObservationWindow
+ 1)
1338 effective_bad_password_count
= 0
1340 effective_bad_password_count
= badPwdCount
1342 res
= self
._check
_account
(userdn
,
1343 badPwdCount
=badPwdCount
,
1344 effective_bad_password_count
=effective_bad_password_count
,
1345 badPasswordTime
=badPasswordTime
,
1348 lastLogonTimestamp
=('absent', None),
1350 dsdb
.UF_NORMAL_ACCOUNT
,
1351 msDSUserAccountControlComputed
=0)
1353 ldb
= SamDB(url
=self
.host_url
, credentials
=creds
, lp
=self
.lp
)
1355 if lockOutObservationWindow
== 0:
1357 effective_bad_password_count
= 0
1358 if use_kerberos
== MUST_USE_KERBEROS
:
1360 effective_bad_password_count
= 0
1362 res
= self
._check
_account
(userdn
,
1363 badPwdCount
=badPwdCount
,
1364 effective_bad_password_count
=effective_bad_password_count
,
1365 badPasswordTime
=badPasswordTime
,
1366 logonCount
=(logoncount_relation
, 0),
1367 lastLogon
=(lastlogon_relation
, 0),
1368 lastLogonTimestamp
=('greater', badPasswordTime
),
1370 dsdb
.UF_NORMAL_ACCOUNT
,
1371 msDSUserAccountControlComputed
=0)
1373 logonCount
= int(res
[0]["logonCount"][0])
1374 lastLogon
= int(res
[0]["lastLogon"][0])
1375 lastLogonTimestamp
= int(res
[0]["lastLogonTimestamp"][0])
1376 if lastlogon_relation
== 'greater':
1377 self
.assertGreater(lastLogon
, badPasswordTime
)
1378 self
.assertGreaterEqual(lastLogon
, lastLogonTimestamp
)
1380 res
= self
._check
_account
(userdn
,
1381 badPwdCount
=badPwdCount
,
1382 effective_bad_password_count
=effective_bad_password_count
,
1383 badPasswordTime
=badPasswordTime
,
1384 logonCount
=logonCount
,
1385 lastLogon
=lastLogon
,
1386 lastLogonTimestamp
=lastLogonTimestamp
,
1388 dsdb
.UF_NORMAL_ACCOUNT
,
1389 msDSUserAccountControlComputed
=0)
1392 def test_lockout_observation_window(self
):
1393 lockout3krb5_creds
= self
.insta_creds(self
.template_creds
,
1394 username
="lockout3krb5",
1395 userpass
="thatsAcomplPASS0",
1396 kerberos_state
=MUST_USE_KERBEROS
)
1397 self
._testing
_add
_user
(lockout3krb5_creds
)
1399 lockout4krb5_creds
= self
.insta_creds(self
.template_creds
,
1400 username
="lockout4krb5",
1401 userpass
="thatsAcomplPASS0",
1402 kerberos_state
=MUST_USE_KERBEROS
)
1403 self
._testing
_add
_user
(lockout4krb5_creds
,
1404 lockOutObservationWindow
=self
.lockout_observation_window
)
1406 lockout3ntlm_creds
= self
.insta_creds(self
.template_creds
,
1407 username
="lockout3ntlm",
1408 userpass
="thatsAcomplPASS0",
1409 kerberos_state
=DONT_USE_KERBEROS
)
1410 self
._testing
_add
_user
(lockout3ntlm_creds
)
1411 lockout4ntlm_creds
= self
.insta_creds(self
.template_creds
,
1412 username
="lockout4ntlm",
1413 userpass
="thatsAcomplPASS0",
1414 kerberos_state
=DONT_USE_KERBEROS
)
1415 self
._testing
_add
_user
(lockout4ntlm_creds
,
1416 lockOutObservationWindow
=self
.lockout_observation_window
)
1419 host_url
= "ldap://%s" % host
1421 TestProgram(module
=__name__
, opts
=subunitopts
)