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
16 sys
.path
.insert(0, "bin/python")
18 samba
.ensure_external_module("testtools", "testtools")
19 samba
.ensure_external_module("subunit", "subunit/python")
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_ATTRIBUTE_OR_VALUE_EXISTS
27 from ldb
import ERR_UNWILLING_TO_PERFORM
, ERR_INSUFFICIENT_ACCESS_RIGHTS
28 from ldb
import ERR_NO_SUCH_ATTRIBUTE
29 from ldb
import ERR_CONSTRAINT_VIOLATION
30 from ldb
import ERR_INVALID_CREDENTIALS
31 from ldb
import Message
, MessageElement
, Dn
32 from ldb
import FLAG_MOD_ADD
, FLAG_MOD_REPLACE
, FLAG_MOD_DELETE
33 from samba
import gensec
, dsdb
34 from samba
.samdb
import SamDB
36 from samba
.tests
import delete_force
37 from subunit
.run
import SubunitTestRunner
39 from samba
.dcerpc
import security
, samr
40 from samba
.ndr
import ndr_unpack
42 parser
= optparse
.OptionParser("passwords.py [options] <host>")
43 sambaopts
= options
.SambaOptions(parser
)
44 parser
.add_option_group(sambaopts
)
45 parser
.add_option_group(options
.VersionOptions(parser
))
46 # use command line creds if available
47 credopts
= options
.CredentialsOptions(parser
)
48 parser
.add_option_group(credopts
)
49 opts
, args
= parser
.parse_args()
57 lp
= sambaopts
.get_loadparm()
58 creds
= credopts
.get_credentials(lp
)
60 # Force an encrypted connection
61 creds
.set_gensec_features(creds
.get_gensec_features() | gensec
.FEATURE_SEAL
)
67 class PasswordTests(samba
.tests
.TestCase
):
69 def _open_samr_user(self
, res
):
70 self
.assertTrue("objectSid" in res
[0])
72 (domain_sid
, rid
) = ndr_unpack(security
.dom_sid
, res
[0]["objectSid"][0]).split()
73 self
.assertEquals(self
.domain_sid
, domain_sid
)
75 return self
.samr
.OpenUser(self
.samr_domain
, security
.SEC_FLAG_MAXIMUM_ALLOWED
, rid
)
77 def _reset_samr(self
, res
):
79 # Now reset the lockout, by removing ACB_AUTOLOCK (which removes the lock, despite being a generated attribute)
80 samr_user
= self
._open
_samr
_user
(res
)
81 acb_info
= self
.samr
.QueryUserInfo(samr_user
, 16)
82 acb_info
.acct_flags
&= ~samr
.ACB_AUTOLOCK
83 self
.samr
.SetUserInfo(samr_user
, 16, acb_info
)
84 self
.samr
.Close(samr_user
)
86 def _reset_ldap_lockoutTime(self
, res
):
87 self
.ldb
.modify_ldif("""
88 dn: """ + str(res
[0].dn
) + """
94 def _reset_ldap_userAccountControl(self
, res
):
95 self
.assertTrue("userAccountControl" in res
[0])
96 self
.assertTrue("msDS-User-Account-Control-Computed" in res
[0])
98 uac
= int(res
[0]["userAccountControl"][0])
99 uacc
= int(res
[0]["msDS-User-Account-Control-Computed"][0])
102 uac
= uac
& ~dsdb
.UF_LOCKOUT
104 self
.ldb
.modify_ldif("""
105 dn: """ + str(res
[0].dn
) + """
107 replace: userAccountControl
108 userAccountControl: %d
111 def _reset_by_method(self
, res
, method
):
112 if method
is "ldap_userAccountControl":
113 self
._reset
_ldap
_userAccountControl
(res
)
114 elif method
is "ldap_lockoutTime":
115 self
._reset
_ldap
_lockoutTime
(res
)
116 elif method
is "samr":
117 self
._reset
_samr
(res
)
119 self
.assertTrue(False, msg
="Invalid reset method[%s]" % method
)
121 def _check_attribute(self
, res
, name
, value
):
123 self
.assertTrue(name
not in res
[0],
124 msg
="attr[%s]=%r on dn[%s]" %
125 (name
, res
[0], res
[0].dn
))
128 if isinstance(value
, tuple):
129 (mode
, value
) = value
136 self
.assertTrue(name
in res
[0],
137 msg
="attr[%s] missing on dn[%s]" %
139 self
.assertTrue(len(res
[0][name
]) == 1,
140 msg
="attr[%s]=%r on dn[%s]" %
141 (name
, res
[0][name
], res
[0].dn
))
143 if mode
== "present":
146 self
.assertTrue(str(res
[0][name
][0]) == str(value
),
147 msg
="attr[%s]=[%s] != [%s] on dn[%s]" %
148 (name
, str(res
[0][name
][0]), str(value
), res
[0].dn
))
150 if mode
== "greater":
151 v
= int(res
[0][name
][0])
152 self
.assertTrue(v
> int(value
),
153 msg
="attr[%s]=[%s] <= [%s] on dn[%s]" %
154 (name
, v
, int(value
), res
[0].dn
))
157 v
= int(res
[0][name
][0])
158 self
.assertTrue(v
< int(value
),
159 msg
="attr[%s]=[%s] >= [%s] on dn[%s]" %
160 (name
, v
, int(value
), res
[0].dn
))
162 self
.assertEqual(mode
, not mode
, "Invalid Mode[%s]" % mode
)
164 def _check_account(self
, dn
,
166 badPasswordTime
=None,
168 userAccountControl
=None,
169 msDSUserAccountControlComputed
=None,
170 effective_bad_password_count
=None):
177 "userAccountControl",
178 "msDS-User-Account-Control-Computed"
181 # in order to prevent some time resolution problems we sleep for
185 res
= ldb
.search(dn
, scope
=SCOPE_BASE
, attrs
=attrs
)
186 self
.assertTrue(len(res
) == 1)
187 self
._check
_attribute
(res
, "badPwdCount", badPwdCount
)
188 self
._check
_attribute
(res
, "badPasswordTime", badPasswordTime
)
189 self
._check
_attribute
(res
, "lockoutTime", lockoutTime
)
190 self
._check
_attribute
(res
, "userAccountControl", userAccountControl
)
191 self
._check
_attribute
(res
, "msDS-User-Account-Control-Computed",
192 msDSUserAccountControlComputed
)
194 samr_user
= self
._open
_samr
_user
(res
)
195 uinfo3
= self
.samr
.QueryUserInfo(samr_user
, 3)
196 uinfo5
= self
.samr
.QueryUserInfo(samr_user
, 5)
197 uinfo16
= self
.samr
.QueryUserInfo(samr_user
, 16)
198 uinfo21
= self
.samr
.QueryUserInfo(samr_user
, 21)
199 self
.samr
.Close(samr_user
)
201 expected_acb_info
= 0
202 if userAccountControl
& dsdb
.UF_NORMAL_ACCOUNT
:
203 expected_acb_info |
= samr
.ACB_NORMAL
204 if userAccountControl
& dsdb
.UF_ACCOUNTDISABLE
:
205 expected_acb_info |
= samr
.ACB_DISABLED
206 if userAccountControl
& dsdb
.UF_PASSWD_NOTREQD
:
207 expected_acb_info |
= samr
.ACB_PWNOTREQ
208 if msDSUserAccountControlComputed
& dsdb
.UF_LOCKOUT
:
209 expected_acb_info |
= samr
.ACB_AUTOLOCK
210 if msDSUserAccountControlComputed
& dsdb
.UF_PASSWORD_EXPIRED
:
211 expected_acb_info |
= samr
.ACB_PW_EXPIRED
213 expected_bad_password_count
= 0
214 if badPwdCount
is not None:
215 expected_bad_password_count
= badPwdCount
216 if effective_bad_password_count
is None:
217 effective_bad_password_count
= expected_bad_password_count
219 self
.assertEquals(uinfo3
.acct_flags
, expected_acb_info
)
220 self
.assertEquals(uinfo3
.bad_password_count
, expected_bad_password_count
)
222 self
.assertEquals(uinfo5
.acct_flags
, expected_acb_info
)
223 self
.assertEquals(uinfo5
.bad_password_count
, effective_bad_password_count
)
225 self
.assertEquals(uinfo16
.acct_flags
, expected_acb_info
)
227 self
.assertEquals(uinfo21
.acct_flags
, expected_acb_info
)
228 self
.assertEquals(uinfo21
.bad_password_count
, effective_bad_password_count
)
230 # check LDAP again and make sure the samr.QueryUserInfo
231 # doesn't have any impact.
232 res2
= ldb
.search(dn
, scope
=SCOPE_BASE
, attrs
=attrs
)
233 self
.assertEquals(res
[0], res2
[0])
235 # in order to prevent some time resolution problems we sleep for
241 super(PasswordTests
, self
).setUp()
243 self
.base_dn
= ldb
.domain_dn()
245 self
.domain_sid
= security
.dom_sid(self
.ldb
.get_domain_sid())
246 self
.samr
= samr
.samr("ncacn_ip_tcp:%s[sign]" % host
, lp
, creds
)
247 self
.samr_handle
= self
.samr
.Connect2(None, security
.SEC_FLAG_MAXIMUM_ALLOWED
)
248 self
.samr_domain
= self
.samr
.OpenDomain(self
.samr_handle
, security
.SEC_FLAG_MAXIMUM_ALLOWED
, self
.domain_sid
)
250 # (Re)adds the test user "testuser" with no password atm
251 delete_force(self
.ldb
, "cn=testuser,cn=users," + self
.base_dn
)
253 "dn": "cn=testuser,cn=users," + self
.base_dn
,
254 "objectclass": "user",
255 "sAMAccountName": "testuser"})
257 res
= self
._check
_account
("cn=testuser,cn=users," + self
.base_dn
,
261 dsdb
.UF_NORMAL_ACCOUNT |
262 dsdb
.UF_ACCOUNTDISABLE |
263 dsdb
.UF_PASSWD_NOTREQD
,
264 msDSUserAccountControlComputed
=
265 dsdb
.UF_PASSWORD_EXPIRED
)
267 # SAMR doesn't have any impact if dsdb.UF_LOCKOUT isn't present.
268 # It doesn't create "lockoutTime" = 0.
269 self
._reset
_samr
(res
)
271 res
= self
._check
_account
("cn=testuser,cn=users," + self
.base_dn
,
275 dsdb
.UF_NORMAL_ACCOUNT |
276 dsdb
.UF_ACCOUNTDISABLE |
277 dsdb
.UF_PASSWD_NOTREQD
,
278 msDSUserAccountControlComputed
=
279 dsdb
.UF_PASSWORD_EXPIRED
)
281 # Tests a password change when we don't have any password yet with a
284 self
.ldb
.modify_ldif("""
285 dn: cn=testuser,cn=users,""" + self
.base_dn
+ """
288 userPassword: noPassword
290 userPassword: thatsAcomplPASS2
293 except LdbError
, (num
, msg
):
294 self
.assertEquals(num
, ERR_CONSTRAINT_VIOLATION
)
295 # Windows (2008 at least) seems to have some small bug here: it
296 # returns "0000056A" on longer (always wrong) previous passwords.
297 self
.assertTrue('00000056' in msg
)
299 res
= self
._check
_account
("cn=testuser,cn=users," + self
.base_dn
,
301 badPasswordTime
=("greater", 0),
303 dsdb
.UF_NORMAL_ACCOUNT |
304 dsdb
.UF_ACCOUNTDISABLE |
305 dsdb
.UF_PASSWD_NOTREQD
,
306 msDSUserAccountControlComputed
=
307 dsdb
.UF_PASSWORD_EXPIRED
)
308 badPasswordTime
= int(res
[0]["badPasswordTime"][0])
310 # Sets the initial user password with a "special" password change
311 # I think that this internally is a password set operation and it can
312 # only be performed by someone which has password set privileges on the
313 # account (at least in s4 we do handle it like that).
314 self
.ldb
.modify_ldif("""
315 dn: cn=testuser,cn=users,""" + self
.base_dn
+ """
319 userPassword: thatsAcomplPASS1
322 res
= self
._check
_account
("cn=testuser,cn=users," + self
.base_dn
,
324 badPasswordTime
=badPasswordTime
,
326 dsdb
.UF_NORMAL_ACCOUNT |
327 dsdb
.UF_ACCOUNTDISABLE |
328 dsdb
.UF_PASSWD_NOTREQD
,
329 msDSUserAccountControlComputed
=0)
331 # Enables the user account
332 self
.ldb
.enable_account("(sAMAccountName=testuser)")
334 res
= self
._check
_account
("cn=testuser,cn=users," + self
.base_dn
,
336 badPasswordTime
=badPasswordTime
,
338 dsdb
.UF_NORMAL_ACCOUNT
,
339 msDSUserAccountControlComputed
=0)
341 # Open a second LDB connection with the user credentials. Use the
342 # command line credentials for informations like the domain, the realm
343 # and the workstation.
344 creds2
= Credentials()
345 creds2
.set_username("testuser")
346 creds2
.set_password("thatsAcomplPASS1")
347 creds2
.set_domain(creds
.get_domain())
348 creds2
.set_realm(creds
.get_realm())
349 creds2
.set_workstation(creds
.get_workstation())
350 creds2
.set_gensec_features(creds2
.get_gensec_features()
351 | gensec
.FEATURE_SEAL
)
353 self
.ldb2
= SamDB(url
=host_url
, credentials
=creds2
, lp
=lp
)
355 res
= self
._check
_account
("cn=testuser,cn=users," + self
.base_dn
,
357 badPasswordTime
=badPasswordTime
,
359 dsdb
.UF_NORMAL_ACCOUNT
,
360 msDSUserAccountControlComputed
=0)
362 # (Re)adds the test user "testuser3" with no password atm
363 delete_force(self
.ldb
, "cn=testuser3,cn=users," + self
.base_dn
)
365 "dn": "cn=testuser3,cn=users," + self
.base_dn
,
366 "objectclass": "user",
367 "sAMAccountName": "testuser3"})
369 res
= self
._check
_account
("cn=testuser3,cn=users," + self
.base_dn
,
373 dsdb
.UF_NORMAL_ACCOUNT |
374 dsdb
.UF_ACCOUNTDISABLE |
375 dsdb
.UF_PASSWD_NOTREQD
,
376 msDSUserAccountControlComputed
=
377 dsdb
.UF_PASSWORD_EXPIRED
)
379 # Tests a password change when we don't have any password yet with a
382 self
.ldb
.modify_ldif("""
383 dn: cn=testuser3,cn=users,""" + self
.base_dn
+ """
386 userPassword: noPassword
388 userPassword: thatsAcomplPASS2
391 except LdbError
, (num
, msg
):
392 self
.assertEquals(num
, ERR_CONSTRAINT_VIOLATION
)
393 # Windows (2008 at least) seems to have some small bug here: it
394 # returns "0000056A" on longer (always wrong) previous passwords.
395 self
.assertTrue('00000056' in msg
)
397 res
= self
._check
_account
("cn=testuser3,cn=users," + self
.base_dn
,
399 badPasswordTime
=("greater", 0),
401 dsdb
.UF_NORMAL_ACCOUNT |
402 dsdb
.UF_ACCOUNTDISABLE |
403 dsdb
.UF_PASSWD_NOTREQD
,
404 msDSUserAccountControlComputed
=
405 dsdb
.UF_PASSWORD_EXPIRED
)
406 badPasswordTime3
= int(res
[0]["badPasswordTime"][0])
408 # Sets the initial user password with a "special" password change
409 # I think that this internally is a password set operation and it can
410 # only be performed by someone which has password set privileges on the
411 # account (at least in s4 we do handle it like that).
412 self
.ldb
.modify_ldif("""
413 dn: cn=testuser3,cn=users,""" + self
.base_dn
+ """
417 userPassword: thatsAcomplPASS1
420 res
= self
._check
_account
("cn=testuser3,cn=users," + self
.base_dn
,
422 badPasswordTime
=badPasswordTime3
,
424 dsdb
.UF_NORMAL_ACCOUNT |
425 dsdb
.UF_ACCOUNTDISABLE |
426 dsdb
.UF_PASSWD_NOTREQD
,
427 msDSUserAccountControlComputed
=0)
429 # Enables the user account
430 self
.ldb
.enable_account("(sAMAccountName=testuser3)")
432 res
= self
._check
_account
("cn=testuser3,cn=users," + self
.base_dn
,
434 badPasswordTime
=badPasswordTime3
,
436 dsdb
.UF_NORMAL_ACCOUNT
,
437 msDSUserAccountControlComputed
=0)
439 # Open a second LDB connection with the user credentials. Use the
440 # command line credentials for informations like the domain, the realm
441 # and the workstation.
442 creds3
= Credentials()
443 creds3
.set_username("testuser3")
444 creds3
.set_password("thatsAcomplPASS1")
445 creds3
.set_domain(creds
.get_domain())
446 creds3
.set_realm(creds
.get_realm())
447 creds3
.set_workstation(creds
.get_workstation())
448 creds3
.set_gensec_features(creds3
.get_gensec_features()
449 | gensec
.FEATURE_SEAL
)
450 self
.ldb3
= SamDB(url
=host_url
, credentials
=creds3
, lp
=lp
)
452 res
= self
._check
_account
("cn=testuser3,cn=users," + self
.base_dn
,
454 badPasswordTime
=badPasswordTime3
,
456 dsdb
.UF_NORMAL_ACCOUNT
,
457 msDSUserAccountControlComputed
=0)
459 def _test_userPassword_lockout_with_clear_change(self
, method
):
460 print "Performs a password cleartext change operation on 'userPassword'"
461 # Notice: This works only against Windows if "dSHeuristics" has been set
464 res
= self
._check
_account
("cn=testuser,cn=users," + self
.base_dn
,
466 badPasswordTime
=("greater", 0),
468 dsdb
.UF_NORMAL_ACCOUNT
,
469 msDSUserAccountControlComputed
=0)
470 badPasswordTime
= int(res
[0]["badPasswordTime"][0])
472 # Change password on a connection as another user
476 self
.ldb3
.modify_ldif("""
477 dn: cn=testuser,cn=users,""" + self
.base_dn
+ """
480 userPassword: thatsAcomplPASS1x
482 userPassword: thatsAcomplPASS2
485 except LdbError
, (num
, msg
):
486 self
.assertTrue('00000056' in msg
)
487 self
.assertEquals(num
, ERR_CONSTRAINT_VIOLATION
)
489 res
= self
._check
_account
("cn=testuser,cn=users," + self
.base_dn
,
491 badPasswordTime
=("greater", badPasswordTime
),
493 dsdb
.UF_NORMAL_ACCOUNT
,
494 msDSUserAccountControlComputed
=0)
495 badPasswordTime
= int(res
[0]["badPasswordTime"][0])
497 # Correct old password
498 self
.ldb3
.modify_ldif("""
499 dn: cn=testuser,cn=users,""" + self
.base_dn
+ """
502 userPassword: thatsAcomplPASS1
504 userPassword: thatsAcomplPASS2
507 res
= self
._check
_account
("cn=testuser,cn=users," + self
.base_dn
,
509 badPasswordTime
=badPasswordTime
,
511 dsdb
.UF_NORMAL_ACCOUNT
,
512 msDSUserAccountControlComputed
=0)
516 self
.ldb3
.modify_ldif("""
517 dn: cn=testuser,cn=users,""" + self
.base_dn
+ """
520 userPassword: thatsAcomplPASS1x
522 userPassword: thatsAcomplPASS2
525 except LdbError
, (num
, msg
):
526 self
.assertTrue('00000056' in msg
)
527 self
.assertEquals(num
, ERR_CONSTRAINT_VIOLATION
)
529 res
= self
._check
_account
("cn=testuser,cn=users," + self
.base_dn
,
531 badPasswordTime
=("greater", badPasswordTime
),
533 dsdb
.UF_NORMAL_ACCOUNT
,
534 msDSUserAccountControlComputed
=0)
535 badPasswordTime
= int(res
[0]["badPasswordTime"][0])
537 print "two failed password change"
541 self
.ldb3
.modify_ldif("""
542 dn: cn=testuser,cn=users,""" + self
.base_dn
+ """
545 userPassword: thatsAcomplPASS1x
547 userPassword: thatsAcomplPASS2
550 except LdbError
, (num
, msg
):
551 self
.assertTrue('00000056' in msg
)
552 self
.assertEquals(num
, ERR_CONSTRAINT_VIOLATION
)
554 res
= self
._check
_account
("cn=testuser,cn=users," + self
.base_dn
,
556 badPasswordTime
=("greater", badPasswordTime
),
557 lockoutTime
=("greater", badPasswordTime
),
559 dsdb
.UF_NORMAL_ACCOUNT
,
560 msDSUserAccountControlComputed
=dsdb
.UF_LOCKOUT
)
561 badPasswordTime
= int(res
[0]["badPasswordTime"][0])
562 lockoutTime
= int(res
[0]["lockoutTime"][0])
566 self
.ldb3
.modify_ldif("""
567 dn: cn=testuser,cn=users,""" + self
.base_dn
+ """
570 userPassword: thatsAcomplPASS1x
572 userPassword: thatsAcomplPASS2
575 except LdbError
, (num
, msg
):
576 self
.assertEquals(num
, ERR_CONSTRAINT_VIOLATION
)
577 self
.assertTrue('00000775' in msg
)
579 res
= self
._check
_account
("cn=testuser,cn=users," + self
.base_dn
,
581 badPasswordTime
=badPasswordTime
,
582 lockoutTime
=lockoutTime
,
584 dsdb
.UF_NORMAL_ACCOUNT
,
585 msDSUserAccountControlComputed
=dsdb
.UF_LOCKOUT
)
589 self
.ldb3
.modify_ldif("""
590 dn: cn=testuser,cn=users,""" + self
.base_dn
+ """
593 userPassword: thatsAcomplPASS1x
595 userPassword: thatsAcomplPASS2
598 except LdbError
, (num
, msg
):
599 self
.assertEquals(num
, ERR_CONSTRAINT_VIOLATION
)
600 self
.assertTrue('00000775' in msg
)
602 res
= self
._check
_account
("cn=testuser,cn=users," + self
.base_dn
,
604 badPasswordTime
=badPasswordTime
,
605 lockoutTime
=lockoutTime
,
607 dsdb
.UF_NORMAL_ACCOUNT
,
608 msDSUserAccountControlComputed
=dsdb
.UF_LOCKOUT
)
611 # Correct old password
612 self
.ldb3
.modify_ldif("""
613 dn: cn=testuser,cn=users,""" + self
.base_dn
+ """
616 userPassword: thatsAcomplPASS2
618 userPassword: thatsAcomplPASS2x
621 except LdbError
, (num
, msg
):
622 self
.assertEquals(num
, ERR_CONSTRAINT_VIOLATION
)
623 self
.assertTrue('0000775' in msg
)
625 res
= self
._check
_account
("cn=testuser,cn=users," + self
.base_dn
,
627 badPasswordTime
=badPasswordTime
,
628 lockoutTime
=lockoutTime
,
630 dsdb
.UF_NORMAL_ACCOUNT
,
631 msDSUserAccountControlComputed
=dsdb
.UF_LOCKOUT
)
633 # Now reset the password, which does NOT change the lockout!
634 self
.ldb
.modify_ldif("""
635 dn: cn=testuser,cn=users,""" + self
.base_dn
+ """
637 replace: userPassword
638 userPassword: thatsAcomplPASS2
641 res
= self
._check
_account
("cn=testuser,cn=users," + self
.base_dn
,
643 badPasswordTime
=badPasswordTime
,
644 lockoutTime
=lockoutTime
,
646 dsdb
.UF_NORMAL_ACCOUNT
,
647 msDSUserAccountControlComputed
=dsdb
.UF_LOCKOUT
)
650 # Correct old password
651 self
.ldb3
.modify_ldif("""
652 dn: cn=testuser,cn=users,""" + self
.base_dn
+ """
655 userPassword: thatsAcomplPASS2
657 userPassword: thatsAcomplPASS2x
660 except LdbError
, (num
, msg
):
661 self
.assertEquals(num
, ERR_CONSTRAINT_VIOLATION
)
662 self
.assertTrue('0000775' in msg
)
664 res
= self
._check
_account
("cn=testuser,cn=users," + self
.base_dn
,
666 badPasswordTime
=badPasswordTime
,
667 lockoutTime
=lockoutTime
,
669 dsdb
.UF_NORMAL_ACCOUNT
,
670 msDSUserAccountControlComputed
=dsdb
.UF_LOCKOUT
)
673 m
.dn
= Dn(ldb
, "cn=testuser,cn=users," + self
.base_dn
)
674 m
["userAccountControl"] = MessageElement(
675 str(dsdb
.UF_LOCKOUT
),
676 FLAG_MOD_REPLACE
, "userAccountControl")
680 # This shows that setting the UF_LOCKOUT flag alone makes no difference
681 res
= self
._check
_account
("cn=testuser,cn=users," + self
.base_dn
,
683 badPasswordTime
=badPasswordTime
,
684 lockoutTime
=lockoutTime
,
686 dsdb
.UF_NORMAL_ACCOUNT
,
687 msDSUserAccountControlComputed
=dsdb
.UF_LOCKOUT
)
689 # This shows that setting the UF_LOCKOUT flag makes no difference
691 # Correct old password
692 self
.ldb3
.modify_ldif("""
693 dn: cn=testuser,cn=users,""" + self
.base_dn
+ """
696 unicodePwd:: """ + base64
.b64encode("\"thatsAcomplPASS2\"".encode('utf-16-le')) + """
698 unicodePwd:: """ + base64
.b64encode("\"thatsAcomplPASS2x\"".encode('utf-16-le')) + """
701 except LdbError
, (num
, msg
):
702 self
.assertEquals(num
, ERR_CONSTRAINT_VIOLATION
)
703 self
.assertTrue('0000775' in msg
)
705 res
= self
._check
_account
("cn=testuser,cn=users," + self
.base_dn
,
707 badPasswordTime
=badPasswordTime
,
708 lockoutTime
=lockoutTime
,
710 dsdb
.UF_NORMAL_ACCOUNT
,
711 msDSUserAccountControlComputed
=dsdb
.UF_LOCKOUT
)
713 self
._reset
_by
_method
(res
, method
)
715 res
= self
._check
_account
("cn=testuser,cn=users," + self
.base_dn
,
717 badPasswordTime
=badPasswordTime
,
720 dsdb
.UF_NORMAL_ACCOUNT
,
721 msDSUserAccountControlComputed
=0)
723 # The correct password after doing the unlock
725 self
.ldb3
.modify_ldif("""
726 dn: cn=testuser,cn=users,""" + self
.base_dn
+ """
729 unicodePwd:: """ + base64
.b64encode("\"thatsAcomplPASS2\"".encode('utf-16-le')) + """
731 unicodePwd:: """ + base64
.b64encode("\"thatsAcomplPASS2x\"".encode('utf-16-le')) + """
734 res
= self
._check
_account
("cn=testuser,cn=users," + self
.base_dn
,
736 badPasswordTime
=badPasswordTime
,
739 dsdb
.UF_NORMAL_ACCOUNT
,
740 msDSUserAccountControlComputed
=0)
744 self
.ldb3
.modify_ldif("""
745 dn: cn=testuser,cn=users,""" + self
.base_dn
+ """
748 userPassword: thatsAcomplPASS1xyz
750 userPassword: thatsAcomplPASS2XYZ
753 except LdbError
, (num
, msg
):
754 self
.assertEquals(num
, ERR_CONSTRAINT_VIOLATION
)
755 self
.assertTrue('00000056' in msg
)
757 res
= self
._check
_account
("cn=testuser,cn=users," + self
.base_dn
,
759 badPasswordTime
=("greater", badPasswordTime
),
762 dsdb
.UF_NORMAL_ACCOUNT
,
763 msDSUserAccountControlComputed
=0)
764 badPasswordTime
= int(res
[0]["badPasswordTime"][0])
768 self
.ldb3
.modify_ldif("""
769 dn: cn=testuser,cn=users,""" + self
.base_dn
+ """
772 userPassword: thatsAcomplPASS1xyz
774 userPassword: thatsAcomplPASS2XYZ
777 except LdbError
, (num
, msg
):
778 self
.assertEquals(num
, ERR_CONSTRAINT_VIOLATION
)
779 self
.assertTrue('00000056' in msg
)
781 res
= self
._check
_account
("cn=testuser,cn=users," + self
.base_dn
,
783 badPasswordTime
=("greater", badPasswordTime
),
786 dsdb
.UF_NORMAL_ACCOUNT
,
787 msDSUserAccountControlComputed
=0)
788 badPasswordTime
= int(res
[0]["badPasswordTime"][0])
790 self
._reset
_ldap
_lockoutTime
(res
)
792 res
= self
._check
_account
("cn=testuser,cn=users," + self
.base_dn
,
794 badPasswordTime
=badPasswordTime
,
797 dsdb
.UF_NORMAL_ACCOUNT
,
798 msDSUserAccountControlComputed
=0)
800 def test_userPassword_lockout_with_clear_change_ldap_userAccountControl(self
):
801 self
._test
_userPassword
_lockout
_with
_clear
_change
("ldap_userAccountControl")
803 def test_userPassword_lockout_with_clear_change_ldap_lockoutTime(self
):
804 self
._test
_userPassword
_lockout
_with
_clear
_change
("ldap_lockoutTime")
806 def test_userPassword_lockout_with_clear_change_samr(self
):
807 self
._test
_userPassword
_lockout
_with
_clear
_change
("samr")
810 def test_unicodePwd_lockout_with_clear_change(self
):
811 print "Performs a password cleartext change operation on 'unicodePwd'"
813 res
= self
._check
_account
("cn=testuser,cn=users," + self
.base_dn
,
815 badPasswordTime
=("greater", 0),
817 dsdb
.UF_NORMAL_ACCOUNT
,
818 msDSUserAccountControlComputed
=0)
819 badPasswordTime
= int(res
[0]["badPasswordTime"][0])
821 # Change password on a connection as another user
825 self
.ldb3
.modify_ldif("""
826 dn: cn=testuser,cn=users,""" + self
.base_dn
+ """
829 unicodePwd:: """ + base64
.b64encode("\"thatsAcomplPASS1x\"".encode('utf-16-le')) + """
831 unicodePwd:: """ + base64
.b64encode("\"thatsAcomplPASS2\"".encode('utf-16-le')) + """
834 except LdbError
, (num
, msg
):
835 self
.assertTrue('00000056' in msg
)
836 self
.assertEquals(num
, ERR_CONSTRAINT_VIOLATION
)
838 res
= self
._check
_account
("cn=testuser,cn=users," + self
.base_dn
,
840 badPasswordTime
=("greater", badPasswordTime
),
842 dsdb
.UF_NORMAL_ACCOUNT
,
843 msDSUserAccountControlComputed
=0)
844 badPasswordTime
= int(res
[0]["badPasswordTime"][0])
846 # Correct old password
847 self
.ldb3
.modify_ldif("""
848 dn: cn=testuser,cn=users,""" + self
.base_dn
+ """
851 unicodePwd:: """ + base64
.b64encode("\"thatsAcomplPASS1\"".encode('utf-16-le')) + """
853 unicodePwd:: """ + base64
.b64encode("\"thatsAcomplPASS2\"".encode('utf-16-le')) + """
856 res
= self
._check
_account
("cn=testuser,cn=users," + self
.base_dn
,
858 badPasswordTime
=badPasswordTime
,
860 dsdb
.UF_NORMAL_ACCOUNT
,
861 msDSUserAccountControlComputed
=0)
865 self
.ldb3
.modify_ldif("""
866 dn: cn=testuser,cn=users,""" + self
.base_dn
+ """
869 unicodePwd:: """ + base64
.b64encode("\"thatsAcomplPASS1\"".encode('utf-16-le')) + """
871 unicodePwd:: """ + base64
.b64encode("\"thatsAcomplPASS2\"".encode('utf-16-le')) + """
874 except LdbError
, (num
, msg
):
875 self
.assertTrue('00000056' in msg
)
876 self
.assertEquals(num
, ERR_CONSTRAINT_VIOLATION
)
878 res
= self
._check
_account
("cn=testuser,cn=users," + self
.base_dn
,
880 badPasswordTime
=("greater", badPasswordTime
),
882 dsdb
.UF_NORMAL_ACCOUNT
,
883 msDSUserAccountControlComputed
=0)
884 badPasswordTime
= int(res
[0]["badPasswordTime"][0])
886 # SAMR doesn't have any impact if dsdb.UF_LOCKOUT isn't present.
887 # It doesn't create "lockoutTime" = 0 and doesn't
888 # reset "badPwdCount" = 0.
889 self
._reset
_samr
(res
)
891 res
= self
._check
_account
("cn=testuser,cn=users," + self
.base_dn
,
893 badPasswordTime
=badPasswordTime
,
895 dsdb
.UF_NORMAL_ACCOUNT
,
896 msDSUserAccountControlComputed
=0)
898 print "two failed password change"
902 self
.ldb3
.modify_ldif("""
903 dn: cn=testuser,cn=users,""" + self
.base_dn
+ """
906 unicodePwd:: """ + base64
.b64encode("\"thatsAcomplPASS1x\"".encode('utf-16-le')) + """
908 unicodePwd:: """ + base64
.b64encode("\"thatsAcomplPASS2\"".encode('utf-16-le')) + """
911 except LdbError
, (num
, msg
):
912 self
.assertTrue('00000056' in msg
)
913 self
.assertEquals(num
, ERR_CONSTRAINT_VIOLATION
)
915 # this is strange, why do we have lockoutTime=badPasswordTime here?
916 res
= self
._check
_account
("cn=testuser,cn=users," + self
.base_dn
,
918 badPasswordTime
=("greater", badPasswordTime
),
919 lockoutTime
=("greater", badPasswordTime
),
921 dsdb
.UF_NORMAL_ACCOUNT
,
922 msDSUserAccountControlComputed
=dsdb
.UF_LOCKOUT
)
923 badPasswordTime
= int(res
[0]["badPasswordTime"][0])
924 lockoutTime
= int(res
[0]["lockoutTime"][0])
928 self
.ldb3
.modify_ldif("""
929 dn: cn=testuser,cn=users,""" + self
.base_dn
+ """
932 unicodePwd:: """ + base64
.b64encode("\"thatsAcomplPASS1x\"".encode('utf-16-le')) + """
934 unicodePwd:: """ + base64
.b64encode("\"thatsAcomplPASS2\"".encode('utf-16-le')) + """
937 except LdbError
, (num
, msg
):
938 self
.assertEquals(num
, ERR_CONSTRAINT_VIOLATION
)
939 self
.assertTrue('00000775' in msg
)
941 res
= self
._check
_account
("cn=testuser,cn=users," + self
.base_dn
,
943 badPasswordTime
=badPasswordTime
,
944 lockoutTime
=lockoutTime
,
946 dsdb
.UF_NORMAL_ACCOUNT
,
947 msDSUserAccountControlComputed
=dsdb
.UF_LOCKOUT
)
951 self
.ldb3
.modify_ldif("""
952 dn: cn=testuser,cn=users,""" + self
.base_dn
+ """
955 unicodePwd:: """ + base64
.b64encode("\"thatsAcomplPASS1x\"".encode('utf-16-le')) + """
957 unicodePwd:: """ + base64
.b64encode("\"thatsAcomplPASS2\"".encode('utf-16-le')) + """
960 except LdbError
, (num
, msg
):
961 self
.assertEquals(num
, ERR_CONSTRAINT_VIOLATION
)
962 self
.assertTrue('00000775' in msg
)
964 res
= self
._check
_account
("cn=testuser,cn=users," + self
.base_dn
,
966 badPasswordTime
=badPasswordTime
,
967 lockoutTime
=lockoutTime
,
969 dsdb
.UF_NORMAL_ACCOUNT
,
970 msDSUserAccountControlComputed
=dsdb
.UF_LOCKOUT
)
973 # Correct old password
974 self
.ldb3
.modify_ldif("""
975 dn: cn=testuser,cn=users,""" + self
.base_dn
+ """
978 unicodePwd:: """ + base64
.b64encode("\"thatsAcomplPASS2\"".encode('utf-16-le')) + """
980 unicodePwd:: """ + base64
.b64encode("\"thatsAcomplPASS2x\"".encode('utf-16-le')) + """
983 except LdbError
, (num
, msg
):
984 self
.assertEquals(num
, ERR_CONSTRAINT_VIOLATION
)
985 self
.assertTrue('0000775' in msg
)
987 res
= self
._check
_account
("cn=testuser,cn=users," + self
.base_dn
,
989 badPasswordTime
=badPasswordTime
,
990 lockoutTime
=lockoutTime
,
992 dsdb
.UF_NORMAL_ACCOUNT
,
993 msDSUserAccountControlComputed
=dsdb
.UF_LOCKOUT
)
995 # Now reset the lockout, by removing ACB_AUTOLOCK (which removes the lock, despite being a generated attribute)
996 self
._reset
_samr
(res
);
998 res
= self
._check
_account
("cn=testuser,cn=users," + self
.base_dn
,
1000 badPasswordTime
=badPasswordTime
,
1003 dsdb
.UF_NORMAL_ACCOUNT
,
1004 msDSUserAccountControlComputed
=0)
1006 # Correct old password
1007 self
.ldb3
.modify_ldif("""
1008 dn: cn=testuser,cn=users,""" + self
.base_dn
+ """
1011 unicodePwd:: """ + base64
.b64encode("\"thatsAcomplPASS2\"".encode('utf-16-le')) + """
1013 unicodePwd:: """ + base64
.b64encode("\"thatsAcomplPASS2x\"".encode('utf-16-le')) + """
1016 res
= self
._check
_account
("cn=testuser,cn=users," + self
.base_dn
,
1018 badPasswordTime
=badPasswordTime
,
1021 dsdb
.UF_NORMAL_ACCOUNT
,
1022 msDSUserAccountControlComputed
=0)
1024 # Wrong old password
1026 self
.ldb3
.modify_ldif("""
1027 dn: cn=testuser,cn=users,""" + self
.base_dn
+ """
1030 unicodePwd:: """ + base64
.b64encode("\"thatsAcomplPASS1x\"".encode('utf-16-le')) + """
1032 unicodePwd:: """ + base64
.b64encode("\"thatsAcomplPASS2\"".encode('utf-16-le')) + """
1035 except LdbError
, (num
, msg
):
1036 self
.assertTrue('00000056' in msg
)
1037 self
.assertEquals(num
, ERR_CONSTRAINT_VIOLATION
)
1039 res
= self
._check
_account
("cn=testuser,cn=users," + self
.base_dn
,
1041 badPasswordTime
=("greater", badPasswordTime
),
1044 dsdb
.UF_NORMAL_ACCOUNT
,
1045 msDSUserAccountControlComputed
=0)
1046 badPasswordTime
= int(res
[0]["badPasswordTime"][0])
1048 # Wrong old password
1050 self
.ldb3
.modify_ldif("""
1051 dn: cn=testuser,cn=users,""" + self
.base_dn
+ """
1054 unicodePwd:: """ + base64
.b64encode("\"thatsAcomplPASS1x\"".encode('utf-16-le')) + """
1056 unicodePwd:: """ + base64
.b64encode("\"thatsAcomplPASS2\"".encode('utf-16-le')) + """
1059 except LdbError
, (num
, msg
):
1060 self
.assertTrue('00000056' in msg
)
1061 self
.assertEquals(num
, ERR_CONSTRAINT_VIOLATION
)
1063 res
= self
._check
_account
("cn=testuser,cn=users," + self
.base_dn
,
1065 badPasswordTime
=("greater", badPasswordTime
),
1068 dsdb
.UF_NORMAL_ACCOUNT
,
1069 msDSUserAccountControlComputed
=0)
1070 badPasswordTime
= int(res
[0]["badPasswordTime"][0])
1072 # SAMR doesn't have any impact if dsdb.UF_LOCKOUT isn't present.
1073 # It doesn't reset "badPwdCount" = 0.
1074 self
._reset
_samr
(res
)
1076 res
= self
._check
_account
("cn=testuser,cn=users," + self
.base_dn
,
1078 badPasswordTime
=badPasswordTime
,
1081 dsdb
.UF_NORMAL_ACCOUNT
,
1082 msDSUserAccountControlComputed
=0)
1084 # Wrong old password
1086 self
.ldb3
.modify_ldif("""
1087 dn: cn=testuser,cn=users,""" + self
.base_dn
+ """
1090 unicodePwd:: """ + base64
.b64encode("\"thatsAcomplPASS1x\"".encode('utf-16-le')) + """
1092 unicodePwd:: """ + base64
.b64encode("\"thatsAcomplPASS2\"".encode('utf-16-le')) + """
1095 except LdbError
, (num
, msg
):
1096 self
.assertTrue('00000056' in msg
)
1097 self
.assertEquals(num
, ERR_CONSTRAINT_VIOLATION
)
1099 res
= self
._check
_account
("cn=testuser,cn=users," + self
.base_dn
,
1101 badPasswordTime
=("greater", badPasswordTime
),
1102 lockoutTime
=("greater", badPasswordTime
),
1104 dsdb
.UF_NORMAL_ACCOUNT
,
1105 msDSUserAccountControlComputed
=dsdb
.UF_LOCKOUT
)
1106 badPasswordTime
= int(res
[0]["badPasswordTime"][0])
1107 lockoutTime
= int(res
[0]["lockoutTime"][0])
1109 time
.sleep(account_lockout_duration
+ 1)
1111 res
= self
._check
_account
("cn=testuser,cn=users," + self
.base_dn
,
1112 badPwdCount
=3, effective_bad_password_count
=0,
1113 badPasswordTime
=badPasswordTime
,
1114 lockoutTime
=lockoutTime
,
1116 dsdb
.UF_NORMAL_ACCOUNT
,
1117 msDSUserAccountControlComputed
=0)
1119 # SAMR doesn't have any impact if dsdb.UF_LOCKOUT isn't present.
1120 # It doesn't reset "lockoutTime" = 0 and doesn't
1121 # reset "badPwdCount" = 0.
1122 self
._reset
_samr
(res
)
1124 res
= self
._check
_account
("cn=testuser,cn=users," + self
.base_dn
,
1125 badPwdCount
=3, effective_bad_password_count
=0,
1126 badPasswordTime
=badPasswordTime
,
1127 lockoutTime
=lockoutTime
,
1129 dsdb
.UF_NORMAL_ACCOUNT
,
1130 msDSUserAccountControlComputed
=0)
1132 def _test_login_lockout(self
, use_kerberos
):
1133 # This unlocks by waiting for account_lockout_duration
1134 print "Performs a lockout attempt against LDAP using NTLM or Kerberos"
1136 # Change password on a connection as another user
1138 res
= self
._check
_account
("cn=testuser,cn=users," + self
.base_dn
,
1140 badPasswordTime
=("greater", 0),
1142 dsdb
.UF_NORMAL_ACCOUNT
,
1143 msDSUserAccountControlComputed
=0)
1144 badPasswordTime
= int(res
[0]["badPasswordTime"][0])
1146 # Open a second LDB connection with the user credentials. Use the
1147 # command line credentials for informations like the domain, the realm
1148 # and the workstation.
1149 creds_lockout
= Credentials()
1150 creds_lockout
.set_username("testuser")
1151 creds_lockout
.set_domain(creds
.get_domain())
1152 creds_lockout
.set_realm(creds
.get_realm())
1153 creds_lockout
.set_workstation(creds
.get_workstation())
1154 creds_lockout
.set_gensec_features(creds_lockout
.get_gensec_features()
1155 | gensec
.FEATURE_SEAL
)
1156 creds_lockout
.set_kerberos_state(use_kerberos
)
1158 # The wrong password
1159 creds_lockout
.set_password("thatsAcomplPASS1x")
1162 ldb_lockout
= SamDB(url
=host_url
, credentials
=creds_lockout
, lp
=lp
)
1164 except LdbError
, (num
, msg
):
1165 self
.assertEquals(num
, ERR_INVALID_CREDENTIALS
)
1167 res
= self
._check
_account
("cn=testuser,cn=users," + self
.base_dn
,
1169 badPasswordTime
=("greater", badPasswordTime
),
1171 dsdb
.UF_NORMAL_ACCOUNT
,
1172 msDSUserAccountControlComputed
=0)
1173 badPasswordTime
= int(res
[0]["badPasswordTime"][0])
1175 # Correct old password
1176 creds_lockout
.set_password("thatsAcomplPASS1")
1178 ldb_lockout
= SamDB(url
=host_url
, credentials
=creds_lockout
, lp
=lp
)
1180 res
= self
._check
_account
("cn=testuser,cn=users," + self
.base_dn
,
1182 badPasswordTime
=badPasswordTime
,
1184 dsdb
.UF_NORMAL_ACCOUNT
,
1185 msDSUserAccountControlComputed
=0)
1187 # The wrong password
1188 creds_lockout
.set_password("thatsAcomplPASS1x")
1191 ldb_lockout
= SamDB(url
=host_url
, credentials
=creds_lockout
, lp
=lp
)
1193 except LdbError
, (num
, msg
):
1194 self
.assertEquals(num
, ERR_INVALID_CREDENTIALS
)
1196 res
= self
._check
_account
("cn=testuser,cn=users," + self
.base_dn
,
1198 badPasswordTime
=("greater", badPasswordTime
),
1200 dsdb
.UF_NORMAL_ACCOUNT
,
1201 msDSUserAccountControlComputed
=0)
1202 badPasswordTime
= int(res
[0]["badPasswordTime"][0])
1204 # The wrong password
1205 creds_lockout
.set_password("thatsAcomplPASS1x")
1208 ldb_lockout
= SamDB(url
=host_url
, credentials
=creds_lockout
, lp
=lp
)
1211 except LdbError
, (num
, msg
):
1212 self
.assertEquals(num
, ERR_INVALID_CREDENTIALS
)
1214 res
= self
._check
_account
("cn=testuser,cn=users," + self
.base_dn
,
1216 badPasswordTime
=("greater", badPasswordTime
),
1218 dsdb
.UF_NORMAL_ACCOUNT
,
1219 msDSUserAccountControlComputed
=0)
1220 badPasswordTime
= int(res
[0]["badPasswordTime"][0])
1222 print "two failed password change"
1224 # The wrong password
1225 creds_lockout
.set_password("thatsAcomplPASS1x")
1228 ldb_lockout
= SamDB(url
=host_url
, credentials
=creds_lockout
, lp
=lp
)
1231 except LdbError
, (num
, msg
):
1232 self
.assertEquals(num
, ERR_INVALID_CREDENTIALS
)
1234 res
= self
._check
_account
("cn=testuser,cn=users," + self
.base_dn
,
1236 badPasswordTime
=("greater", badPasswordTime
),
1237 lockoutTime
=("greater", badPasswordTime
),
1239 dsdb
.UF_NORMAL_ACCOUNT
,
1240 msDSUserAccountControlComputed
=dsdb
.UF_LOCKOUT
)
1241 badPasswordTime
= int(res
[0]["badPasswordTime"][0])
1242 lockoutTime
= int(res
[0]["lockoutTime"][0])
1244 # The wrong password
1245 creds_lockout
.set_password("thatsAcomplPASS1x")
1247 ldb_lockout
= SamDB(url
=host_url
, credentials
=creds_lockout
, lp
=lp
)
1249 except LdbError
, (num
, msg
):
1250 self
.assertEquals(num
, ERR_INVALID_CREDENTIALS
)
1252 res
= self
._check
_account
("cn=testuser,cn=users," + self
.base_dn
,
1254 badPasswordTime
=badPasswordTime
,
1255 lockoutTime
=lockoutTime
,
1257 dsdb
.UF_NORMAL_ACCOUNT
,
1258 msDSUserAccountControlComputed
=dsdb
.UF_LOCKOUT
)
1260 # The wrong password
1261 creds_lockout
.set_password("thatsAcomplPASS1x")
1263 ldb_lockout
= SamDB(url
=host_url
, credentials
=creds_lockout
, lp
=lp
)
1265 except LdbError
, (num
, msg
):
1266 self
.assertEquals(num
, ERR_INVALID_CREDENTIALS
)
1268 res
= self
._check
_account
("cn=testuser,cn=users," + self
.base_dn
,
1270 badPasswordTime
=badPasswordTime
,
1271 lockoutTime
=lockoutTime
,
1273 dsdb
.UF_NORMAL_ACCOUNT
,
1274 msDSUserAccountControlComputed
=dsdb
.UF_LOCKOUT
)
1276 # The correct password
1277 creds_lockout
.set_password("thatsAcomplPASS1")
1279 ldb_lockout
= SamDB(url
=host_url
, credentials
=creds_lockout
, lp
=lp
)
1281 except LdbError
, (num
, msg
):
1282 self
.assertEquals(num
, ERR_INVALID_CREDENTIALS
)
1284 res
= self
._check
_account
("cn=testuser,cn=users," + self
.base_dn
,
1286 badPasswordTime
=badPasswordTime
,
1287 lockoutTime
=lockoutTime
,
1289 dsdb
.UF_NORMAL_ACCOUNT
,
1290 msDSUserAccountControlComputed
=dsdb
.UF_LOCKOUT
)
1292 time
.sleep(account_lockout_duration
+ 1)
1294 res
= self
._check
_account
("cn=testuser,cn=users," + self
.base_dn
,
1295 badPwdCount
=3, effective_bad_password_count
=0,
1296 badPasswordTime
=badPasswordTime
,
1297 lockoutTime
=lockoutTime
,
1299 dsdb
.UF_NORMAL_ACCOUNT
,
1300 msDSUserAccountControlComputed
=0)
1302 # The correct password after letting the timeout expire
1303 creds_lockout
.set_password("thatsAcomplPASS1")
1304 ldb_lockout
= SamDB(url
=host_url
, credentials
=creds_lockout
, lp
=lp
)
1306 res
= self
._check
_account
("cn=testuser,cn=users," + self
.base_dn
,
1308 badPasswordTime
=badPasswordTime
,
1311 dsdb
.UF_NORMAL_ACCOUNT
,
1312 msDSUserAccountControlComputed
=0)
1314 # The wrong password
1315 creds_lockout
.set_password("thatsAcomplPASS1x")
1317 ldb_lockout
= SamDB(url
=host_url
, credentials
=creds_lockout
, lp
=lp
)
1319 except LdbError
, (num
, msg
):
1320 self
.assertEquals(num
, ERR_INVALID_CREDENTIALS
)
1322 res
= self
._check
_account
("cn=testuser,cn=users," + self
.base_dn
,
1324 badPasswordTime
=("greater", badPasswordTime
),
1327 dsdb
.UF_NORMAL_ACCOUNT
,
1328 msDSUserAccountControlComputed
=0)
1329 badPasswordTime
= int(res
[0]["badPasswordTime"][0])
1331 # The wrong password
1332 creds_lockout
.set_password("thatsAcomplPASS1x")
1334 ldb_lockout
= SamDB(url
=host_url
, credentials
=creds_lockout
, lp
=lp
)
1336 except LdbError
, (num
, msg
):
1337 self
.assertEquals(num
, ERR_INVALID_CREDENTIALS
)
1339 res
= self
._check
_account
("cn=testuser,cn=users," + self
.base_dn
,
1341 badPasswordTime
=("greater", badPasswordTime
),
1344 dsdb
.UF_NORMAL_ACCOUNT
,
1345 msDSUserAccountControlComputed
=0)
1346 badPasswordTime
= int(res
[0]["badPasswordTime"][0])
1348 time
.sleep(lockout_observation_window
+ 1)
1350 res
= self
._check
_account
("cn=testuser,cn=users," + self
.base_dn
,
1351 badPwdCount
=2, effective_bad_password_count
=0,
1352 badPasswordTime
=badPasswordTime
,
1355 dsdb
.UF_NORMAL_ACCOUNT
,
1356 msDSUserAccountControlComputed
=0)
1358 # The wrong password
1359 creds_lockout
.set_password("thatsAcomplPASS1x")
1361 ldb_lockout
= SamDB(url
=host_url
, credentials
=creds_lockout
, lp
=lp
)
1363 except LdbError
, (num
, msg
):
1364 self
.assertEquals(num
, ERR_INVALID_CREDENTIALS
)
1366 res
= self
._check
_account
("cn=testuser,cn=users," + self
.base_dn
,
1368 badPasswordTime
=("greater", badPasswordTime
),
1371 dsdb
.UF_NORMAL_ACCOUNT
,
1372 msDSUserAccountControlComputed
=0)
1373 badPasswordTime
= int(res
[0]["badPasswordTime"][0])
1375 # The correct password without letting the timeout expire
1376 creds_lockout
.set_password("thatsAcomplPASS1")
1377 ldb_lockout
= SamDB(url
=host_url
, credentials
=creds_lockout
, lp
=lp
)
1379 res
= self
._check
_account
("cn=testuser,cn=users," + self
.base_dn
,
1381 badPasswordTime
=badPasswordTime
,
1384 dsdb
.UF_NORMAL_ACCOUNT
,
1385 msDSUserAccountControlComputed
=0)
1387 def test_login_lockout_ntlm(self
):
1388 self
._test
_login
_lockout
(DONT_USE_KERBEROS
)
1390 def test_login_lockout_kerberos(self
):
1391 self
._test
_login
_lockout
(MUST_USE_KERBEROS
)
1394 super(PasswordTests
, self
).tearDown()
1395 delete_force(self
.ldb
, "cn=testuser,cn=users," + self
.base_dn
)
1396 delete_force(self
.ldb
, "cn=testuser2,cn=users," + self
.base_dn
)
1397 delete_force(self
.ldb
, "cn=testuser3,cn=users," + self
.base_dn
)
1398 # Close the second LDB connection (with the user credentials)
1401 host_url
= "ldap://%s" % host
1403 ldb
= SamDB(url
=host_url
, session_info
=system_session(lp
), credentials
=creds
, lp
=lp
)
1405 # Gets back the basedn
1406 base_dn
= ldb
.domain_dn()
1408 # Gets back the configuration basedn
1409 configuration_dn
= ldb
.get_config_basedn().get_linearized()
1411 # Get the old "dSHeuristics" if it was set
1412 dsheuristics
= ldb
.get_dsheuristics()
1414 res
= ldb
.search(base_dn
,
1415 scope
=SCOPE_BASE
, attrs
=["lockoutDuration", "lockOutObservationWindow", "lockoutThreshold"])
1417 if "lockoutDuration" in res
[0]:
1418 lockoutDuration
= res
[0]["lockoutDuration"][0]
1422 if "lockoutObservationWindow" in res
[0]:
1423 lockoutObservationWindow
= res
[0]["lockoutObservationWindow"][0]
1425 lockoutObservationWindow
= 0
1427 if "lockoutThreshold" in res
[0]:
1428 lockoutThreshold
= res
[0]["lockoutThreshold"][0]
1433 m
.dn
= Dn(ldb
, base_dn
)
1435 account_lockout_duration
= 10
1436 account_lockout_duration_ticks
= -int(account_lockout_duration
* (1e7
))
1438 m
["lockoutDuration"] = MessageElement(str(account_lockout_duration_ticks
),
1439 FLAG_MOD_REPLACE
, "lockoutDuration")
1441 account_lockout_threshold
= 3
1442 m
["lockoutThreshold"] = MessageElement(str(account_lockout_threshold
),
1443 FLAG_MOD_REPLACE
, "lockoutThreshold")
1445 lockout_observation_window
= 5
1446 lockout_observation_window_ticks
= -int(lockout_observation_window
* (1e7
))
1448 m
["lockOutObservationWindow"] = MessageElement(str(lockout_observation_window_ticks
),
1449 FLAG_MOD_REPLACE
, "lockOutObservationWindow")
1453 # Set the "dSHeuristics" to activate the correct "userPassword" behaviour
1454 ldb
.set_dsheuristics("000000001")
1456 # Get the old "minPwdAge"
1457 minPwdAge
= ldb
.get_minPwdAge()
1459 # Set it temporarely to "0"
1460 ldb
.set_minPwdAge("0")
1462 runner
= SubunitTestRunner()
1464 if not runner
.run(unittest
.makeSuite(PasswordTests
)).wasSuccessful():
1467 # Reset the "dSHeuristics" as they were before
1468 ldb
.set_dsheuristics(dsheuristics
)
1470 # Reset the "minPwdAge" as it was before
1471 ldb
.set_minPwdAge(minPwdAge
)
1474 dn: """ + base_dn
+ """
1476 replace: lockoutDuration
1477 lockoutDuration: """ + str(lockoutDuration
) + """
1478 replace: lockoutObservationWindow
1479 lockoutObservationWindow: """ + str(lockoutObservationWindow
) + """
1480 replace: lockoutThreshold
1481 lockoutThreshold: """ + str(lockoutThreshold
) + """