password lockout: Use samba.tests.subunitrun.
[Samba.git] / source4 / dsdb / tests / python / password_lockout.py
blob133b40b9369c7595505136fe2542e1d56b3bb7a5
1 #!/usr/bin/env python
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 import optparse
11 import sys
12 import base64
13 import time
15 sys.path.insert(0, "bin/python")
16 import samba
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
31 import samba.tests
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("passwords.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()
47 if len(args) < 1:
48 parser.print_usage()
49 sys.exit(1)
51 host = args[0]
53 lp = sambaopts.get_loadparm()
54 creds = credopts.get_credentials(lp)
56 # Force an encrypted connection
57 creds.set_gensec_features(creds.get_gensec_features() | gensec.FEATURE_SEAL)
60 # Tests start here
63 class PasswordTests(samba.tests.TestCase):
65 def _open_samr_user(self, res):
66 self.assertTrue("objectSid" in res[0])
68 (domain_sid, rid) = ndr_unpack(security.dom_sid, res[0]["objectSid"][0]).split()
69 self.assertEquals(self.domain_sid, domain_sid)
71 return self.samr.OpenUser(self.samr_domain, security.SEC_FLAG_MAXIMUM_ALLOWED, rid)
73 def _reset_samr(self, res):
75 # Now reset the lockout, by removing ACB_AUTOLOCK (which removes the lock, despite being a generated attribute)
76 samr_user = self._open_samr_user(res)
77 acb_info = self.samr.QueryUserInfo(samr_user, 16)
78 acb_info.acct_flags &= ~samr.ACB_AUTOLOCK
79 self.samr.SetUserInfo(samr_user, 16, acb_info)
80 self.samr.Close(samr_user)
82 def _reset_ldap_lockoutTime(self, res):
83 self.ldb.modify_ldif("""
84 dn: """ + str(res[0].dn) + """
85 changetype: modify
86 replace: lockoutTime
87 lockoutTime: 0
88 """)
90 def _reset_ldap_userAccountControl(self, res):
91 self.assertTrue("userAccountControl" in res[0])
92 self.assertTrue("msDS-User-Account-Control-Computed" in res[0])
94 uac = int(res[0]["userAccountControl"][0])
95 uacc = int(res[0]["msDS-User-Account-Control-Computed"][0])
97 uac |= uacc
98 uac = uac & ~dsdb.UF_LOCKOUT
100 self.ldb.modify_ldif("""
101 dn: """ + str(res[0].dn) + """
102 changetype: modify
103 replace: userAccountControl
104 userAccountControl: %d
105 """ % uac)
107 def _reset_by_method(self, res, method):
108 if method is "ldap_userAccountControl":
109 self._reset_ldap_userAccountControl(res)
110 elif method is "ldap_lockoutTime":
111 self._reset_ldap_lockoutTime(res)
112 elif method is "samr":
113 self._reset_samr(res)
114 else:
115 self.assertTrue(False, msg="Invalid reset method[%s]" % method)
117 def _check_attribute(self, res, name, value):
118 if value is None:
119 self.assertTrue(name not in res[0],
120 msg="attr[%s]=%r on dn[%s]" %
121 (name, res[0], res[0].dn))
122 return
124 if isinstance(value, tuple):
125 (mode, value) = value
126 else:
127 mode = "equal"
129 if mode == "ignore":
130 return
132 self.assertTrue(name in res[0],
133 msg="attr[%s] missing on dn[%s]" %
134 (name, res[0].dn))
135 self.assertTrue(len(res[0][name]) == 1,
136 msg="attr[%s]=%r on dn[%s]" %
137 (name, res[0][name], res[0].dn))
139 if mode == "present":
140 return
141 if mode == "equal":
142 self.assertTrue(str(res[0][name][0]) == str(value),
143 msg="attr[%s]=[%s] != [%s] on dn[%s]" %
144 (name, str(res[0][name][0]), str(value), res[0].dn))
145 return
146 if mode == "greater":
147 v = int(res[0][name][0])
148 self.assertTrue(v > int(value),
149 msg="attr[%s]=[%s] <= [%s] on dn[%s]" %
150 (name, v, int(value), res[0].dn))
151 return
152 if mode == "less":
153 v = int(res[0][name][0])
154 self.assertTrue(v < int(value),
155 msg="attr[%s]=[%s] >= [%s] on dn[%s]" %
156 (name, v, int(value), res[0].dn))
157 return
158 self.assertEqual(mode, not mode, "Invalid Mode[%s]" % mode)
160 def _check_account(self, dn,
161 badPwdCount=None,
162 badPasswordTime=None,
163 lockoutTime=None,
164 userAccountControl=None,
165 msDSUserAccountControlComputed=None,
166 effective_bad_password_count=None):
168 attrs = [
169 "objectSid",
170 "badPwdCount",
171 "badPasswordTime",
172 "lockoutTime",
173 "userAccountControl",
174 "msDS-User-Account-Control-Computed"
177 # in order to prevent some time resolution problems we sleep for
178 # 10 micro second
179 time.sleep(0.01)
181 res = self.ldb.search(dn, scope=SCOPE_BASE, attrs=attrs)
182 self.assertTrue(len(res) == 1)
183 self._check_attribute(res, "badPwdCount", badPwdCount)
184 self._check_attribute(res, "badPasswordTime", badPasswordTime)
185 self._check_attribute(res, "lockoutTime", lockoutTime)
186 self._check_attribute(res, "userAccountControl", userAccountControl)
187 self._check_attribute(res, "msDS-User-Account-Control-Computed",
188 msDSUserAccountControlComputed)
190 samr_user = self._open_samr_user(res)
191 uinfo3 = self.samr.QueryUserInfo(samr_user, 3)
192 uinfo5 = self.samr.QueryUserInfo(samr_user, 5)
193 uinfo16 = self.samr.QueryUserInfo(samr_user, 16)
194 uinfo21 = self.samr.QueryUserInfo(samr_user, 21)
195 self.samr.Close(samr_user)
197 expected_acb_info = 0
198 if userAccountControl & dsdb.UF_NORMAL_ACCOUNT:
199 expected_acb_info |= samr.ACB_NORMAL
200 if userAccountControl & dsdb.UF_ACCOUNTDISABLE:
201 expected_acb_info |= samr.ACB_DISABLED
202 if userAccountControl & dsdb.UF_PASSWD_NOTREQD:
203 expected_acb_info |= samr.ACB_PWNOTREQ
204 if msDSUserAccountControlComputed & dsdb.UF_LOCKOUT:
205 expected_acb_info |= samr.ACB_AUTOLOCK
206 if msDSUserAccountControlComputed & dsdb.UF_PASSWORD_EXPIRED:
207 expected_acb_info |= samr.ACB_PW_EXPIRED
209 expected_bad_password_count = 0
210 if badPwdCount is not None:
211 expected_bad_password_count = badPwdCount
212 if effective_bad_password_count is None:
213 effective_bad_password_count = expected_bad_password_count
215 self.assertEquals(uinfo3.acct_flags, expected_acb_info)
216 self.assertEquals(uinfo3.bad_password_count, expected_bad_password_count)
218 self.assertEquals(uinfo5.acct_flags, expected_acb_info)
219 self.assertEquals(uinfo5.bad_password_count, effective_bad_password_count)
221 self.assertEquals(uinfo16.acct_flags, expected_acb_info)
223 self.assertEquals(uinfo21.acct_flags, expected_acb_info)
224 self.assertEquals(uinfo21.bad_password_count, effective_bad_password_count)
226 # check LDAP again and make sure the samr.QueryUserInfo
227 # doesn't have any impact.
228 res2 = self.ldb.search(dn, scope=SCOPE_BASE, attrs=attrs)
229 self.assertEquals(res[0], res2[0])
231 # in order to prevent some time resolution problems we sleep for
232 # 10 micro second
233 time.sleep(0.01)
234 return res
236 def setUp(self):
237 super(PasswordTests, self).setUp()
239 self.ldb = SamDB(url=host_url, session_info=system_session(lp), credentials=creds, lp=lp)
241 # Gets back the basedn
242 base_dn = self.ldb.domain_dn()
244 # Gets back the configuration basedn
245 configuration_dn = self.ldb.get_config_basedn().get_linearized()
247 # Get the old "dSHeuristics" if it was set
248 dsheuristics = self.ldb.get_dsheuristics()
250 # Reset the "dSHeuristics" as they were before
251 self.addCleanup(self.ldb.set_dsheuristics, dsheuristics)
253 res = self.ldb.search(base_dn,
254 scope=SCOPE_BASE, attrs=["lockoutDuration", "lockOutObservationWindow", "lockoutThreshold"])
256 if "lockoutDuration" in res[0]:
257 lockoutDuration = res[0]["lockoutDuration"][0]
258 else:
259 lockoutDuration = 0
261 if "lockoutObservationWindow" in res[0]:
262 lockoutObservationWindow = res[0]["lockoutObservationWindow"][0]
263 else:
264 lockoutObservationWindow = 0
266 if "lockoutThreshold" in res[0]:
267 lockoutThreshold = res[0]["lockoutThreshold"][0]
268 else:
269 lockoutTreshold = 0
271 self.addCleanup(self.ldb.modify_ldif, """
272 dn: """ + base_dn + """
273 changetype: modify
274 replace: lockoutDuration
275 lockoutDuration: """ + str(lockoutDuration) + """
276 replace: lockoutObservationWindow
277 lockoutObservationWindow: """ + str(lockoutObservationWindow) + """
278 replace: lockoutThreshold
279 lockoutThreshold: """ + str(lockoutThreshold) + """
280 """)
282 m = Message()
283 m.dn = Dn(self.ldb, base_dn)
285 self.account_lockout_duration = 10
286 account_lockout_duration_ticks = -int(self.account_lockout_duration * (1e7))
288 m["lockoutDuration"] = MessageElement(str(account_lockout_duration_ticks),
289 FLAG_MOD_REPLACE, "lockoutDuration")
291 account_lockout_threshold = 3
292 m["lockoutThreshold"] = MessageElement(str(account_lockout_threshold),
293 FLAG_MOD_REPLACE, "lockoutThreshold")
295 self.lockout_observation_window = 5
296 lockout_observation_window_ticks = -int(self.lockout_observation_window * (1e7))
298 m["lockOutObservationWindow"] = MessageElement(str(lockout_observation_window_ticks),
299 FLAG_MOD_REPLACE, "lockOutObservationWindow")
301 self.ldb.modify(m)
303 # Set the "dSHeuristics" to activate the correct "userPassword" behaviour
304 self.ldb.set_dsheuristics("000000001")
306 # Get the old "minPwdAge"
307 minPwdAge = self.ldb.get_minPwdAge()
309 # Reset the "minPwdAge" as it was before
310 self.addCleanup(self.ldb.set_minPwdAge, minPwdAge)
312 # Set it temporarely to "0"
313 self.ldb.set_minPwdAge("0")
315 self.base_dn = self.ldb.domain_dn()
317 self.domain_sid = security.dom_sid(self.ldb.get_domain_sid())
318 self.samr = samr.samr("ncacn_ip_tcp:%s[sign]" % host, lp, creds)
319 self.samr_handle = self.samr.Connect2(None, security.SEC_FLAG_MAXIMUM_ALLOWED)
320 self.samr_domain = self.samr.OpenDomain(self.samr_handle, security.SEC_FLAG_MAXIMUM_ALLOWED, self.domain_sid)
322 # (Re)adds the test user "testuser" with no password atm
323 delete_force(self.ldb, "cn=testuser,cn=users," + self.base_dn)
324 self.ldb.add({
325 "dn": "cn=testuser,cn=users," + self.base_dn,
326 "objectclass": "user",
327 "sAMAccountName": "testuser"})
329 res = self._check_account("cn=testuser,cn=users," + self.base_dn,
330 badPwdCount=0,
331 badPasswordTime=0,
332 userAccountControl=
333 dsdb.UF_NORMAL_ACCOUNT |
334 dsdb.UF_ACCOUNTDISABLE |
335 dsdb.UF_PASSWD_NOTREQD,
336 msDSUserAccountControlComputed=
337 dsdb.UF_PASSWORD_EXPIRED)
339 # SAMR doesn't have any impact if dsdb.UF_LOCKOUT isn't present.
340 # It doesn't create "lockoutTime" = 0.
341 self._reset_samr(res)
343 res = self._check_account("cn=testuser,cn=users," + self.base_dn,
344 badPwdCount=0,
345 badPasswordTime=0,
346 userAccountControl=
347 dsdb.UF_NORMAL_ACCOUNT |
348 dsdb.UF_ACCOUNTDISABLE |
349 dsdb.UF_PASSWD_NOTREQD,
350 msDSUserAccountControlComputed=
351 dsdb.UF_PASSWORD_EXPIRED)
353 # Tests a password change when we don't have any password yet with a
354 # wrong old password
355 try:
356 self.ldb.modify_ldif("""
357 dn: cn=testuser,cn=users,""" + self.base_dn + """
358 changetype: modify
359 delete: userPassword
360 userPassword: noPassword
361 add: userPassword
362 userPassword: thatsAcomplPASS2
363 """)
364 self.fail()
365 except LdbError, (num, msg):
366 self.assertEquals(num, ERR_CONSTRAINT_VIOLATION)
367 # Windows (2008 at least) seems to have some small bug here: it
368 # returns "0000056A" on longer (always wrong) previous passwords.
369 self.assertTrue('00000056' in msg)
371 res = self._check_account("cn=testuser,cn=users," + self.base_dn,
372 badPwdCount=1,
373 badPasswordTime=("greater", 0),
374 userAccountControl=
375 dsdb.UF_NORMAL_ACCOUNT |
376 dsdb.UF_ACCOUNTDISABLE |
377 dsdb.UF_PASSWD_NOTREQD,
378 msDSUserAccountControlComputed=
379 dsdb.UF_PASSWORD_EXPIRED)
380 badPasswordTime = int(res[0]["badPasswordTime"][0])
382 # Sets the initial user password with a "special" password change
383 # I think that this internally is a password set operation and it can
384 # only be performed by someone which has password set privileges on the
385 # account (at least in s4 we do handle it like that).
386 self.ldb.modify_ldif("""
387 dn: cn=testuser,cn=users,""" + self.base_dn + """
388 changetype: modify
389 delete: userPassword
390 add: userPassword
391 userPassword: thatsAcomplPASS1
392 """)
394 res = self._check_account("cn=testuser,cn=users," + self.base_dn,
395 badPwdCount=1,
396 badPasswordTime=badPasswordTime,
397 userAccountControl=
398 dsdb.UF_NORMAL_ACCOUNT |
399 dsdb.UF_ACCOUNTDISABLE |
400 dsdb.UF_PASSWD_NOTREQD,
401 msDSUserAccountControlComputed=0)
403 # Enables the user account
404 self.ldb.enable_account("(sAMAccountName=testuser)")
406 res = self._check_account("cn=testuser,cn=users," + self.base_dn,
407 badPwdCount=1,
408 badPasswordTime=badPasswordTime,
409 userAccountControl=
410 dsdb.UF_NORMAL_ACCOUNT,
411 msDSUserAccountControlComputed=0)
413 # Open a second LDB connection with the user credentials. Use the
414 # command line credentials for informations like the domain, the realm
415 # and the workstation.
416 creds2 = Credentials()
417 creds2.set_username("testuser")
418 creds2.set_password("thatsAcomplPASS1")
419 creds2.set_domain(creds.get_domain())
420 creds2.set_realm(creds.get_realm())
421 creds2.set_workstation(creds.get_workstation())
422 creds2.set_gensec_features(creds2.get_gensec_features()
423 | gensec.FEATURE_SEAL)
425 self.ldb2 = SamDB(url=host_url, credentials=creds2, lp=lp)
427 res = self._check_account("cn=testuser,cn=users," + self.base_dn,
428 badPwdCount=0,
429 badPasswordTime=badPasswordTime,
430 userAccountControl=
431 dsdb.UF_NORMAL_ACCOUNT,
432 msDSUserAccountControlComputed=0)
434 # (Re)adds the test user "testuser3" with no password atm
435 delete_force(self.ldb, "cn=testuser3,cn=users," + self.base_dn)
436 self.ldb.add({
437 "dn": "cn=testuser3,cn=users," + self.base_dn,
438 "objectclass": "user",
439 "sAMAccountName": "testuser3"})
441 res = self._check_account("cn=testuser3,cn=users," + self.base_dn,
442 badPwdCount=0,
443 badPasswordTime=0,
444 userAccountControl=
445 dsdb.UF_NORMAL_ACCOUNT |
446 dsdb.UF_ACCOUNTDISABLE |
447 dsdb.UF_PASSWD_NOTREQD,
448 msDSUserAccountControlComputed=
449 dsdb.UF_PASSWORD_EXPIRED)
451 # Tests a password change when we don't have any password yet with a
452 # wrong old password
453 try:
454 self.ldb.modify_ldif("""
455 dn: cn=testuser3,cn=users,""" + self.base_dn + """
456 changetype: modify
457 delete: userPassword
458 userPassword: noPassword
459 add: userPassword
460 userPassword: thatsAcomplPASS2
461 """)
462 self.fail()
463 except LdbError, (num, msg):
464 self.assertEquals(num, ERR_CONSTRAINT_VIOLATION)
465 # Windows (2008 at least) seems to have some small bug here: it
466 # returns "0000056A" on longer (always wrong) previous passwords.
467 self.assertTrue('00000056' in msg)
469 res = self._check_account("cn=testuser3,cn=users," + self.base_dn,
470 badPwdCount=1,
471 badPasswordTime=("greater", 0),
472 userAccountControl=
473 dsdb.UF_NORMAL_ACCOUNT |
474 dsdb.UF_ACCOUNTDISABLE |
475 dsdb.UF_PASSWD_NOTREQD,
476 msDSUserAccountControlComputed=
477 dsdb.UF_PASSWORD_EXPIRED)
478 badPasswordTime3 = int(res[0]["badPasswordTime"][0])
480 # Sets the initial user password with a "special" password change
481 # I think that this internally is a password set operation and it can
482 # only be performed by someone which has password set privileges on the
483 # account (at least in s4 we do handle it like that).
484 self.ldb.modify_ldif("""
485 dn: cn=testuser3,cn=users,""" + self.base_dn + """
486 changetype: modify
487 delete: userPassword
488 add: userPassword
489 userPassword: thatsAcomplPASS1
490 """)
492 res = self._check_account("cn=testuser3,cn=users," + self.base_dn,
493 badPwdCount=1,
494 badPasswordTime=badPasswordTime3,
495 userAccountControl=
496 dsdb.UF_NORMAL_ACCOUNT |
497 dsdb.UF_ACCOUNTDISABLE |
498 dsdb.UF_PASSWD_NOTREQD,
499 msDSUserAccountControlComputed=0)
501 # Enables the user account
502 self.ldb.enable_account("(sAMAccountName=testuser3)")
504 res = self._check_account("cn=testuser3,cn=users," + self.base_dn,
505 badPwdCount=1,
506 badPasswordTime=badPasswordTime3,
507 userAccountControl=
508 dsdb.UF_NORMAL_ACCOUNT,
509 msDSUserAccountControlComputed=0)
511 # Open a second LDB connection with the user credentials. Use the
512 # command line credentials for informations like the domain, the realm
513 # and the workstation.
514 creds3 = Credentials()
515 creds3.set_username("testuser3")
516 creds3.set_password("thatsAcomplPASS1")
517 creds3.set_domain(creds.get_domain())
518 creds3.set_realm(creds.get_realm())
519 creds3.set_workstation(creds.get_workstation())
520 creds3.set_gensec_features(creds3.get_gensec_features()
521 | gensec.FEATURE_SEAL)
522 self.ldb3 = SamDB(url=host_url, credentials=creds3, lp=lp)
524 res = self._check_account("cn=testuser3,cn=users," + self.base_dn,
525 badPwdCount=0,
526 badPasswordTime=badPasswordTime3,
527 userAccountControl=
528 dsdb.UF_NORMAL_ACCOUNT,
529 msDSUserAccountControlComputed=0)
531 def _test_userPassword_lockout_with_clear_change(self, method):
532 print "Performs a password cleartext change operation on 'userPassword'"
533 # Notice: This works only against Windows if "dSHeuristics" has been set
534 # properly
536 res = self._check_account("cn=testuser,cn=users," + self.base_dn,
537 badPwdCount=0,
538 badPasswordTime=("greater", 0),
539 userAccountControl=
540 dsdb.UF_NORMAL_ACCOUNT,
541 msDSUserAccountControlComputed=0)
542 badPasswordTime = int(res[0]["badPasswordTime"][0])
544 # Change password on a connection as another user
546 # Wrong old password
547 try:
548 self.ldb3.modify_ldif("""
549 dn: cn=testuser,cn=users,""" + self.base_dn + """
550 changetype: modify
551 delete: userPassword
552 userPassword: thatsAcomplPASS1x
553 add: userPassword
554 userPassword: thatsAcomplPASS2
555 """)
556 self.fail()
557 except LdbError, (num, msg):
558 self.assertTrue('00000056' in msg)
559 self.assertEquals(num, ERR_CONSTRAINT_VIOLATION)
561 res = self._check_account("cn=testuser,cn=users," + self.base_dn,
562 badPwdCount=1,
563 badPasswordTime=("greater", badPasswordTime),
564 userAccountControl=
565 dsdb.UF_NORMAL_ACCOUNT,
566 msDSUserAccountControlComputed=0)
567 badPasswordTime = int(res[0]["badPasswordTime"][0])
569 # Correct old password
570 self.ldb3.modify_ldif("""
571 dn: cn=testuser,cn=users,""" + self.base_dn + """
572 changetype: modify
573 delete: userPassword
574 userPassword: thatsAcomplPASS1
575 add: userPassword
576 userPassword: thatsAcomplPASS2
577 """)
579 res = self._check_account("cn=testuser,cn=users," + self.base_dn,
580 badPwdCount=1,
581 badPasswordTime=badPasswordTime,
582 userAccountControl=
583 dsdb.UF_NORMAL_ACCOUNT,
584 msDSUserAccountControlComputed=0)
586 # Wrong old password
587 try:
588 self.ldb3.modify_ldif("""
589 dn: cn=testuser,cn=users,""" + self.base_dn + """
590 changetype: modify
591 delete: userPassword
592 userPassword: thatsAcomplPASS1x
593 add: userPassword
594 userPassword: thatsAcomplPASS2
595 """)
596 self.fail()
597 except LdbError, (num, msg):
598 self.assertTrue('00000056' in msg)
599 self.assertEquals(num, ERR_CONSTRAINT_VIOLATION)
601 res = self._check_account("cn=testuser,cn=users," + self.base_dn,
602 badPwdCount=2,
603 badPasswordTime=("greater", badPasswordTime),
604 userAccountControl=
605 dsdb.UF_NORMAL_ACCOUNT,
606 msDSUserAccountControlComputed=0)
607 badPasswordTime = int(res[0]["badPasswordTime"][0])
609 print "two failed password change"
611 # Wrong old password
612 try:
613 self.ldb3.modify_ldif("""
614 dn: cn=testuser,cn=users,""" + self.base_dn + """
615 changetype: modify
616 delete: userPassword
617 userPassword: thatsAcomplPASS1x
618 add: userPassword
619 userPassword: thatsAcomplPASS2
620 """)
621 self.fail()
622 except LdbError, (num, msg):
623 self.assertTrue('00000056' in msg)
624 self.assertEquals(num, ERR_CONSTRAINT_VIOLATION)
626 res = self._check_account("cn=testuser,cn=users," + self.base_dn,
627 badPwdCount=3,
628 badPasswordTime=("greater", badPasswordTime),
629 lockoutTime=("greater", badPasswordTime),
630 userAccountControl=
631 dsdb.UF_NORMAL_ACCOUNT,
632 msDSUserAccountControlComputed=dsdb.UF_LOCKOUT)
633 badPasswordTime = int(res[0]["badPasswordTime"][0])
634 lockoutTime = int(res[0]["lockoutTime"][0])
636 # Wrong old password
637 try:
638 self.ldb3.modify_ldif("""
639 dn: cn=testuser,cn=users,""" + self.base_dn + """
640 changetype: modify
641 delete: userPassword
642 userPassword: thatsAcomplPASS1x
643 add: userPassword
644 userPassword: thatsAcomplPASS2
645 """)
646 self.fail()
647 except LdbError, (num, msg):
648 self.assertEquals(num, ERR_CONSTRAINT_VIOLATION)
649 self.assertTrue('00000775' in msg)
651 res = self._check_account("cn=testuser,cn=users," + self.base_dn,
652 badPwdCount=3,
653 badPasswordTime=badPasswordTime,
654 lockoutTime=lockoutTime,
655 userAccountControl=
656 dsdb.UF_NORMAL_ACCOUNT,
657 msDSUserAccountControlComputed=dsdb.UF_LOCKOUT)
659 # Wrong old password
660 try:
661 self.ldb3.modify_ldif("""
662 dn: cn=testuser,cn=users,""" + self.base_dn + """
663 changetype: modify
664 delete: userPassword
665 userPassword: thatsAcomplPASS1x
666 add: userPassword
667 userPassword: thatsAcomplPASS2
668 """)
669 self.fail()
670 except LdbError, (num, msg):
671 self.assertEquals(num, ERR_CONSTRAINT_VIOLATION)
672 self.assertTrue('00000775' in msg)
674 res = self._check_account("cn=testuser,cn=users," + self.base_dn,
675 badPwdCount=3,
676 badPasswordTime=badPasswordTime,
677 lockoutTime=lockoutTime,
678 userAccountControl=
679 dsdb.UF_NORMAL_ACCOUNT,
680 msDSUserAccountControlComputed=dsdb.UF_LOCKOUT)
682 try:
683 # Correct old password
684 self.ldb3.modify_ldif("""
685 dn: cn=testuser,cn=users,""" + self.base_dn + """
686 changetype: modify
687 delete: userPassword
688 userPassword: thatsAcomplPASS2
689 add: userPassword
690 userPassword: thatsAcomplPASS2x
691 """)
692 self.fail()
693 except LdbError, (num, msg):
694 self.assertEquals(num, ERR_CONSTRAINT_VIOLATION)
695 self.assertTrue('0000775' in msg)
697 res = self._check_account("cn=testuser,cn=users," + self.base_dn,
698 badPwdCount=3,
699 badPasswordTime=badPasswordTime,
700 lockoutTime=lockoutTime,
701 userAccountControl=
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: cn=testuser,cn=users,""" + self.base_dn + """
708 changetype: modify
709 replace: userPassword
710 userPassword: thatsAcomplPASS2
711 """)
713 res = self._check_account("cn=testuser,cn=users," + self.base_dn,
714 badPwdCount=3,
715 badPasswordTime=badPasswordTime,
716 lockoutTime=lockoutTime,
717 userAccountControl=
718 dsdb.UF_NORMAL_ACCOUNT,
719 msDSUserAccountControlComputed=dsdb.UF_LOCKOUT)
721 try:
722 # Correct old password
723 self.ldb3.modify_ldif("""
724 dn: cn=testuser,cn=users,""" + self.base_dn + """
725 changetype: modify
726 delete: userPassword
727 userPassword: thatsAcomplPASS2
728 add: userPassword
729 userPassword: thatsAcomplPASS2x
730 """)
731 self.fail()
732 except LdbError, (num, msg):
733 self.assertEquals(num, ERR_CONSTRAINT_VIOLATION)
734 self.assertTrue('0000775' in msg)
736 res = self._check_account("cn=testuser,cn=users," + self.base_dn,
737 badPwdCount=3,
738 badPasswordTime=badPasswordTime,
739 lockoutTime=lockoutTime,
740 userAccountControl=
741 dsdb.UF_NORMAL_ACCOUNT,
742 msDSUserAccountControlComputed=dsdb.UF_LOCKOUT)
744 m = Message()
745 m.dn = Dn(self.ldb, "cn=testuser,cn=users," + self.base_dn)
746 m["userAccountControl"] = MessageElement(
747 str(dsdb.UF_LOCKOUT),
748 FLAG_MOD_REPLACE, "userAccountControl")
750 self.ldb.modify(m)
752 # This shows that setting the UF_LOCKOUT flag alone makes no difference
753 res = self._check_account("cn=testuser,cn=users," + self.base_dn,
754 badPwdCount=3,
755 badPasswordTime=badPasswordTime,
756 lockoutTime=lockoutTime,
757 userAccountControl=
758 dsdb.UF_NORMAL_ACCOUNT,
759 msDSUserAccountControlComputed=dsdb.UF_LOCKOUT)
761 # This shows that setting the UF_LOCKOUT flag makes no difference
762 try:
763 # Correct old password
764 self.ldb3.modify_ldif("""
765 dn: cn=testuser,cn=users,""" + self.base_dn + """
766 changetype: modify
767 delete: unicodePwd
768 unicodePwd:: """ + base64.b64encode("\"thatsAcomplPASS2\"".encode('utf-16-le')) + """
769 add: unicodePwd
770 unicodePwd:: """ + base64.b64encode("\"thatsAcomplPASS2x\"".encode('utf-16-le')) + """
771 """)
772 self.fail()
773 except LdbError, (num, msg):
774 self.assertEquals(num, ERR_CONSTRAINT_VIOLATION)
775 self.assertTrue('0000775' in msg)
777 res = self._check_account("cn=testuser,cn=users," + self.base_dn,
778 badPwdCount=3,
779 badPasswordTime=badPasswordTime,
780 lockoutTime=lockoutTime,
781 userAccountControl=
782 dsdb.UF_NORMAL_ACCOUNT,
783 msDSUserAccountControlComputed=dsdb.UF_LOCKOUT)
785 self._reset_by_method(res, method)
787 res = self._check_account("cn=testuser,cn=users," + self.base_dn,
788 badPwdCount=0,
789 badPasswordTime=badPasswordTime,
790 lockoutTime=0,
791 userAccountControl=
792 dsdb.UF_NORMAL_ACCOUNT,
793 msDSUserAccountControlComputed=0)
795 # The correct password after doing the unlock
797 self.ldb3.modify_ldif("""
798 dn: cn=testuser,cn=users,""" + self.base_dn + """
799 changetype: modify
800 delete: unicodePwd
801 unicodePwd:: """ + base64.b64encode("\"thatsAcomplPASS2\"".encode('utf-16-le')) + """
802 add: unicodePwd
803 unicodePwd:: """ + base64.b64encode("\"thatsAcomplPASS2x\"".encode('utf-16-le')) + """
804 """)
806 res = self._check_account("cn=testuser,cn=users," + self.base_dn,
807 badPwdCount=0,
808 badPasswordTime=badPasswordTime,
809 lockoutTime=0,
810 userAccountControl=
811 dsdb.UF_NORMAL_ACCOUNT,
812 msDSUserAccountControlComputed=0)
814 # Wrong old password
815 try:
816 self.ldb3.modify_ldif("""
817 dn: cn=testuser,cn=users,""" + self.base_dn + """
818 changetype: modify
819 delete: userPassword
820 userPassword: thatsAcomplPASS1xyz
821 add: userPassword
822 userPassword: thatsAcomplPASS2XYZ
823 """)
824 self.fail()
825 except LdbError, (num, msg):
826 self.assertEquals(num, ERR_CONSTRAINT_VIOLATION)
827 self.assertTrue('00000056' in msg)
829 res = self._check_account("cn=testuser,cn=users," + self.base_dn,
830 badPwdCount=1,
831 badPasswordTime=("greater", badPasswordTime),
832 lockoutTime=0,
833 userAccountControl=
834 dsdb.UF_NORMAL_ACCOUNT,
835 msDSUserAccountControlComputed=0)
836 badPasswordTime = int(res[0]["badPasswordTime"][0])
838 # Wrong old password
839 try:
840 self.ldb3.modify_ldif("""
841 dn: cn=testuser,cn=users,""" + self.base_dn + """
842 changetype: modify
843 delete: userPassword
844 userPassword: thatsAcomplPASS1xyz
845 add: userPassword
846 userPassword: thatsAcomplPASS2XYZ
847 """)
848 self.fail()
849 except LdbError, (num, msg):
850 self.assertEquals(num, ERR_CONSTRAINT_VIOLATION)
851 self.assertTrue('00000056' in msg)
853 res = self._check_account("cn=testuser,cn=users," + self.base_dn,
854 badPwdCount=2,
855 badPasswordTime=("greater", badPasswordTime),
856 lockoutTime=0,
857 userAccountControl=
858 dsdb.UF_NORMAL_ACCOUNT,
859 msDSUserAccountControlComputed=0)
860 badPasswordTime = int(res[0]["badPasswordTime"][0])
862 self._reset_ldap_lockoutTime(res)
864 res = self._check_account("cn=testuser,cn=users," + self.base_dn,
865 badPwdCount=0,
866 badPasswordTime=badPasswordTime,
867 lockoutTime=0,
868 userAccountControl=
869 dsdb.UF_NORMAL_ACCOUNT,
870 msDSUserAccountControlComputed=0)
872 def test_userPassword_lockout_with_clear_change_ldap_userAccountControl(self):
873 self._test_userPassword_lockout_with_clear_change("ldap_userAccountControl")
875 def test_userPassword_lockout_with_clear_change_ldap_lockoutTime(self):
876 self._test_userPassword_lockout_with_clear_change("ldap_lockoutTime")
878 def test_userPassword_lockout_with_clear_change_samr(self):
879 self._test_userPassword_lockout_with_clear_change("samr")
882 def test_unicodePwd_lockout_with_clear_change(self):
883 print "Performs a password cleartext change operation on 'unicodePwd'"
885 res = self._check_account("cn=testuser,cn=users," + self.base_dn,
886 badPwdCount=0,
887 badPasswordTime=("greater", 0),
888 userAccountControl=
889 dsdb.UF_NORMAL_ACCOUNT,
890 msDSUserAccountControlComputed=0)
891 badPasswordTime = int(res[0]["badPasswordTime"][0])
893 # Change password on a connection as another user
895 # Wrong old password
896 try:
897 self.ldb3.modify_ldif("""
898 dn: cn=testuser,cn=users,""" + self.base_dn + """
899 changetype: modify
900 delete: unicodePwd
901 unicodePwd:: """ + base64.b64encode("\"thatsAcomplPASS1x\"".encode('utf-16-le')) + """
902 add: unicodePwd
903 unicodePwd:: """ + base64.b64encode("\"thatsAcomplPASS2\"".encode('utf-16-le')) + """
904 """)
905 self.fail()
906 except LdbError, (num, msg):
907 self.assertTrue('00000056' in msg)
908 self.assertEquals(num, ERR_CONSTRAINT_VIOLATION)
910 res = self._check_account("cn=testuser,cn=users," + self.base_dn,
911 badPwdCount=1,
912 badPasswordTime=("greater", badPasswordTime),
913 userAccountControl=
914 dsdb.UF_NORMAL_ACCOUNT,
915 msDSUserAccountControlComputed=0)
916 badPasswordTime = int(res[0]["badPasswordTime"][0])
918 # Correct old password
919 self.ldb3.modify_ldif("""
920 dn: cn=testuser,cn=users,""" + self.base_dn + """
921 changetype: modify
922 delete: unicodePwd
923 unicodePwd:: """ + base64.b64encode("\"thatsAcomplPASS1\"".encode('utf-16-le')) + """
924 add: unicodePwd
925 unicodePwd:: """ + base64.b64encode("\"thatsAcomplPASS2\"".encode('utf-16-le')) + """
926 """)
928 res = self._check_account("cn=testuser,cn=users," + self.base_dn,
929 badPwdCount=1,
930 badPasswordTime=badPasswordTime,
931 userAccountControl=
932 dsdb.UF_NORMAL_ACCOUNT,
933 msDSUserAccountControlComputed=0)
935 # Wrong old password
936 try:
937 self.ldb3.modify_ldif("""
938 dn: cn=testuser,cn=users,""" + self.base_dn + """
939 changetype: modify
940 delete: unicodePwd
941 unicodePwd:: """ + base64.b64encode("\"thatsAcomplPASS1\"".encode('utf-16-le')) + """
942 add: unicodePwd
943 unicodePwd:: """ + base64.b64encode("\"thatsAcomplPASS2\"".encode('utf-16-le')) + """
944 """)
945 self.fail()
946 except LdbError, (num, msg):
947 self.assertTrue('00000056' in msg)
948 self.assertEquals(num, ERR_CONSTRAINT_VIOLATION)
950 res = self._check_account("cn=testuser,cn=users," + self.base_dn,
951 badPwdCount=2,
952 badPasswordTime=("greater", badPasswordTime),
953 userAccountControl=
954 dsdb.UF_NORMAL_ACCOUNT,
955 msDSUserAccountControlComputed=0)
956 badPasswordTime = int(res[0]["badPasswordTime"][0])
958 # SAMR doesn't have any impact if dsdb.UF_LOCKOUT isn't present.
959 # It doesn't create "lockoutTime" = 0 and doesn't
960 # reset "badPwdCount" = 0.
961 self._reset_samr(res)
963 res = self._check_account("cn=testuser,cn=users," + self.base_dn,
964 badPwdCount=2,
965 badPasswordTime=badPasswordTime,
966 userAccountControl=
967 dsdb.UF_NORMAL_ACCOUNT,
968 msDSUserAccountControlComputed=0)
970 print "two failed password change"
972 # Wrong old password
973 try:
974 self.ldb3.modify_ldif("""
975 dn: cn=testuser,cn=users,""" + self.base_dn + """
976 changetype: modify
977 delete: unicodePwd
978 unicodePwd:: """ + base64.b64encode("\"thatsAcomplPASS1x\"".encode('utf-16-le')) + """
979 add: unicodePwd
980 unicodePwd:: """ + base64.b64encode("\"thatsAcomplPASS2\"".encode('utf-16-le')) + """
981 """)
982 self.fail()
983 except LdbError, (num, msg):
984 self.assertTrue('00000056' in msg)
985 self.assertEquals(num, ERR_CONSTRAINT_VIOLATION)
987 # this is strange, why do we have lockoutTime=badPasswordTime here?
988 res = self._check_account("cn=testuser,cn=users," + self.base_dn,
989 badPwdCount=3,
990 badPasswordTime=("greater", badPasswordTime),
991 lockoutTime=("greater", badPasswordTime),
992 userAccountControl=
993 dsdb.UF_NORMAL_ACCOUNT,
994 msDSUserAccountControlComputed=dsdb.UF_LOCKOUT)
995 badPasswordTime = int(res[0]["badPasswordTime"][0])
996 lockoutTime = int(res[0]["lockoutTime"][0])
998 # Wrong old password
999 try:
1000 self.ldb3.modify_ldif("""
1001 dn: cn=testuser,cn=users,""" + self.base_dn + """
1002 changetype: modify
1003 delete: unicodePwd
1004 unicodePwd:: """ + base64.b64encode("\"thatsAcomplPASS1x\"".encode('utf-16-le')) + """
1005 add: unicodePwd
1006 unicodePwd:: """ + base64.b64encode("\"thatsAcomplPASS2\"".encode('utf-16-le')) + """
1007 """)
1008 self.fail()
1009 except LdbError, (num, msg):
1010 self.assertEquals(num, ERR_CONSTRAINT_VIOLATION)
1011 self.assertTrue('00000775' in msg)
1013 res = self._check_account("cn=testuser,cn=users," + self.base_dn,
1014 badPwdCount=3,
1015 badPasswordTime=badPasswordTime,
1016 lockoutTime=lockoutTime,
1017 userAccountControl=
1018 dsdb.UF_NORMAL_ACCOUNT,
1019 msDSUserAccountControlComputed=dsdb.UF_LOCKOUT)
1021 # Wrong old password
1022 try:
1023 self.ldb3.modify_ldif("""
1024 dn: cn=testuser,cn=users,""" + self.base_dn + """
1025 changetype: modify
1026 delete: unicodePwd
1027 unicodePwd:: """ + base64.b64encode("\"thatsAcomplPASS1x\"".encode('utf-16-le')) + """
1028 add: unicodePwd
1029 unicodePwd:: """ + base64.b64encode("\"thatsAcomplPASS2\"".encode('utf-16-le')) + """
1030 """)
1031 self.fail()
1032 except LdbError, (num, msg):
1033 self.assertEquals(num, ERR_CONSTRAINT_VIOLATION)
1034 self.assertTrue('00000775' in msg)
1036 res = self._check_account("cn=testuser,cn=users," + self.base_dn,
1037 badPwdCount=3,
1038 badPasswordTime=badPasswordTime,
1039 lockoutTime=lockoutTime,
1040 userAccountControl=
1041 dsdb.UF_NORMAL_ACCOUNT,
1042 msDSUserAccountControlComputed=dsdb.UF_LOCKOUT)
1044 try:
1045 # Correct old password
1046 self.ldb3.modify_ldif("""
1047 dn: cn=testuser,cn=users,""" + self.base_dn + """
1048 changetype: modify
1049 delete: unicodePwd
1050 unicodePwd:: """ + base64.b64encode("\"thatsAcomplPASS2\"".encode('utf-16-le')) + """
1051 add: unicodePwd
1052 unicodePwd:: """ + base64.b64encode("\"thatsAcomplPASS2x\"".encode('utf-16-le')) + """
1053 """)
1054 self.fail()
1055 except LdbError, (num, msg):
1056 self.assertEquals(num, ERR_CONSTRAINT_VIOLATION)
1057 self.assertTrue('0000775' in msg)
1059 res = self._check_account("cn=testuser,cn=users," + self.base_dn,
1060 badPwdCount=3,
1061 badPasswordTime=badPasswordTime,
1062 lockoutTime=lockoutTime,
1063 userAccountControl=
1064 dsdb.UF_NORMAL_ACCOUNT,
1065 msDSUserAccountControlComputed=dsdb.UF_LOCKOUT)
1067 # Now reset the lockout, by removing ACB_AUTOLOCK (which removes the lock, despite being a generated attribute)
1068 self._reset_samr(res);
1070 res = self._check_account("cn=testuser,cn=users," + self.base_dn,
1071 badPwdCount=0,
1072 badPasswordTime=badPasswordTime,
1073 lockoutTime=0,
1074 userAccountControl=
1075 dsdb.UF_NORMAL_ACCOUNT,
1076 msDSUserAccountControlComputed=0)
1078 # Correct old password
1079 self.ldb3.modify_ldif("""
1080 dn: cn=testuser,cn=users,""" + self.base_dn + """
1081 changetype: modify
1082 delete: unicodePwd
1083 unicodePwd:: """ + base64.b64encode("\"thatsAcomplPASS2\"".encode('utf-16-le')) + """
1084 add: unicodePwd
1085 unicodePwd:: """ + base64.b64encode("\"thatsAcomplPASS2x\"".encode('utf-16-le')) + """
1086 """)
1088 res = self._check_account("cn=testuser,cn=users," + self.base_dn,
1089 badPwdCount=0,
1090 badPasswordTime=badPasswordTime,
1091 lockoutTime=0,
1092 userAccountControl=
1093 dsdb.UF_NORMAL_ACCOUNT,
1094 msDSUserAccountControlComputed=0)
1096 # Wrong old password
1097 try:
1098 self.ldb3.modify_ldif("""
1099 dn: cn=testuser,cn=users,""" + self.base_dn + """
1100 changetype: modify
1101 delete: unicodePwd
1102 unicodePwd:: """ + base64.b64encode("\"thatsAcomplPASS1x\"".encode('utf-16-le')) + """
1103 add: unicodePwd
1104 unicodePwd:: """ + base64.b64encode("\"thatsAcomplPASS2\"".encode('utf-16-le')) + """
1105 """)
1106 self.fail()
1107 except LdbError, (num, msg):
1108 self.assertTrue('00000056' in msg)
1109 self.assertEquals(num, ERR_CONSTRAINT_VIOLATION)
1111 res = self._check_account("cn=testuser,cn=users," + self.base_dn,
1112 badPwdCount=1,
1113 badPasswordTime=("greater", badPasswordTime),
1114 lockoutTime=0,
1115 userAccountControl=
1116 dsdb.UF_NORMAL_ACCOUNT,
1117 msDSUserAccountControlComputed=0)
1118 badPasswordTime = int(res[0]["badPasswordTime"][0])
1120 # Wrong old password
1121 try:
1122 self.ldb3.modify_ldif("""
1123 dn: cn=testuser,cn=users,""" + self.base_dn + """
1124 changetype: modify
1125 delete: unicodePwd
1126 unicodePwd:: """ + base64.b64encode("\"thatsAcomplPASS1x\"".encode('utf-16-le')) + """
1127 add: unicodePwd
1128 unicodePwd:: """ + base64.b64encode("\"thatsAcomplPASS2\"".encode('utf-16-le')) + """
1129 """)
1130 self.fail()
1131 except LdbError, (num, msg):
1132 self.assertTrue('00000056' in msg)
1133 self.assertEquals(num, ERR_CONSTRAINT_VIOLATION)
1135 res = self._check_account("cn=testuser,cn=users," + self.base_dn,
1136 badPwdCount=2,
1137 badPasswordTime=("greater", badPasswordTime),
1138 lockoutTime=0,
1139 userAccountControl=
1140 dsdb.UF_NORMAL_ACCOUNT,
1141 msDSUserAccountControlComputed=0)
1142 badPasswordTime = int(res[0]["badPasswordTime"][0])
1144 # SAMR doesn't have any impact if dsdb.UF_LOCKOUT isn't present.
1145 # It doesn't reset "badPwdCount" = 0.
1146 self._reset_samr(res)
1148 res = self._check_account("cn=testuser,cn=users," + self.base_dn,
1149 badPwdCount=2,
1150 badPasswordTime=badPasswordTime,
1151 lockoutTime=0,
1152 userAccountControl=
1153 dsdb.UF_NORMAL_ACCOUNT,
1154 msDSUserAccountControlComputed=0)
1156 # Wrong old password
1157 try:
1158 self.ldb3.modify_ldif("""
1159 dn: cn=testuser,cn=users,""" + self.base_dn + """
1160 changetype: modify
1161 delete: unicodePwd
1162 unicodePwd:: """ + base64.b64encode("\"thatsAcomplPASS1x\"".encode('utf-16-le')) + """
1163 add: unicodePwd
1164 unicodePwd:: """ + base64.b64encode("\"thatsAcomplPASS2\"".encode('utf-16-le')) + """
1165 """)
1166 self.fail()
1167 except LdbError, (num, msg):
1168 self.assertTrue('00000056' in msg)
1169 self.assertEquals(num, ERR_CONSTRAINT_VIOLATION)
1171 res = self._check_account("cn=testuser,cn=users," + self.base_dn,
1172 badPwdCount=3,
1173 badPasswordTime=("greater", badPasswordTime),
1174 lockoutTime=("greater", badPasswordTime),
1175 userAccountControl=
1176 dsdb.UF_NORMAL_ACCOUNT,
1177 msDSUserAccountControlComputed=dsdb.UF_LOCKOUT)
1178 badPasswordTime = int(res[0]["badPasswordTime"][0])
1179 lockoutTime = int(res[0]["lockoutTime"][0])
1181 time.sleep(self.account_lockout_duration + 1)
1183 res = self._check_account("cn=testuser,cn=users," + self.base_dn,
1184 badPwdCount=3, effective_bad_password_count=0,
1185 badPasswordTime=badPasswordTime,
1186 lockoutTime=lockoutTime,
1187 userAccountControl=
1188 dsdb.UF_NORMAL_ACCOUNT,
1189 msDSUserAccountControlComputed=0)
1191 # SAMR doesn't have any impact if dsdb.UF_LOCKOUT isn't present.
1192 # It doesn't reset "lockoutTime" = 0 and doesn't
1193 # reset "badPwdCount" = 0.
1194 self._reset_samr(res)
1196 res = self._check_account("cn=testuser,cn=users," + self.base_dn,
1197 badPwdCount=3, effective_bad_password_count=0,
1198 badPasswordTime=badPasswordTime,
1199 lockoutTime=lockoutTime,
1200 userAccountControl=
1201 dsdb.UF_NORMAL_ACCOUNT,
1202 msDSUserAccountControlComputed=0)
1204 def _test_login_lockout(self, use_kerberos):
1205 # This unlocks by waiting for account_lockout_duration
1206 print "Performs a lockout attempt against LDAP using NTLM or Kerberos"
1208 # Change password on a connection as another user
1210 res = self._check_account("cn=testuser,cn=users," + self.base_dn,
1211 badPwdCount=0,
1212 badPasswordTime=("greater", 0),
1213 userAccountControl=
1214 dsdb.UF_NORMAL_ACCOUNT,
1215 msDSUserAccountControlComputed=0)
1216 badPasswordTime = int(res[0]["badPasswordTime"][0])
1218 # Open a second LDB connection with the user credentials. Use the
1219 # command line credentials for informations like the domain, the realm
1220 # and the workstation.
1221 creds_lockout = Credentials()
1222 creds_lockout.set_username("testuser")
1223 creds_lockout.set_domain(creds.get_domain())
1224 creds_lockout.set_realm(creds.get_realm())
1225 creds_lockout.set_workstation(creds.get_workstation())
1226 creds_lockout.set_gensec_features(creds_lockout.get_gensec_features()
1227 | gensec.FEATURE_SEAL)
1228 creds_lockout.set_kerberos_state(use_kerberos)
1230 # The wrong password
1231 creds_lockout.set_password("thatsAcomplPASS1x")
1233 try:
1234 ldb_lockout = SamDB(url=host_url, credentials=creds_lockout, lp=lp)
1236 except LdbError, (num, msg):
1237 self.assertEquals(num, ERR_INVALID_CREDENTIALS)
1239 res = self._check_account("cn=testuser,cn=users," + self.base_dn,
1240 badPwdCount=1,
1241 badPasswordTime=("greater", badPasswordTime),
1242 userAccountControl=
1243 dsdb.UF_NORMAL_ACCOUNT,
1244 msDSUserAccountControlComputed=0)
1245 badPasswordTime = int(res[0]["badPasswordTime"][0])
1247 # Correct old password
1248 creds_lockout.set_password("thatsAcomplPASS1")
1250 ldb_lockout = SamDB(url=host_url, credentials=creds_lockout, lp=lp)
1252 res = self._check_account("cn=testuser,cn=users," + self.base_dn,
1253 badPwdCount=0,
1254 badPasswordTime=badPasswordTime,
1255 userAccountControl=
1256 dsdb.UF_NORMAL_ACCOUNT,
1257 msDSUserAccountControlComputed=0)
1259 # The wrong password
1260 creds_lockout.set_password("thatsAcomplPASS1x")
1262 try:
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,
1269 badPwdCount=1,
1270 badPasswordTime=("greater", badPasswordTime),
1271 userAccountControl=
1272 dsdb.UF_NORMAL_ACCOUNT,
1273 msDSUserAccountControlComputed=0)
1274 badPasswordTime = int(res[0]["badPasswordTime"][0])
1276 # The wrong password
1277 creds_lockout.set_password("thatsAcomplPASS1x")
1279 try:
1280 ldb_lockout = SamDB(url=host_url, credentials=creds_lockout, lp=lp)
1281 self.fail()
1283 except LdbError, (num, msg):
1284 self.assertEquals(num, ERR_INVALID_CREDENTIALS)
1286 res = self._check_account("cn=testuser,cn=users," + self.base_dn,
1287 badPwdCount=2,
1288 badPasswordTime=("greater", badPasswordTime),
1289 userAccountControl=
1290 dsdb.UF_NORMAL_ACCOUNT,
1291 msDSUserAccountControlComputed=0)
1292 badPasswordTime = int(res[0]["badPasswordTime"][0])
1294 print "two failed password change"
1296 # The wrong password
1297 creds_lockout.set_password("thatsAcomplPASS1x")
1299 try:
1300 ldb_lockout = SamDB(url=host_url, credentials=creds_lockout, lp=lp)
1301 self.fail()
1303 except LdbError, (num, msg):
1304 self.assertEquals(num, ERR_INVALID_CREDENTIALS)
1306 res = self._check_account("cn=testuser,cn=users," + self.base_dn,
1307 badPwdCount=3,
1308 badPasswordTime=("greater", badPasswordTime),
1309 lockoutTime=("greater", badPasswordTime),
1310 userAccountControl=
1311 dsdb.UF_NORMAL_ACCOUNT,
1312 msDSUserAccountControlComputed=dsdb.UF_LOCKOUT)
1313 badPasswordTime = int(res[0]["badPasswordTime"][0])
1314 lockoutTime = int(res[0]["lockoutTime"][0])
1316 # The wrong password
1317 creds_lockout.set_password("thatsAcomplPASS1x")
1318 try:
1319 ldb_lockout = SamDB(url=host_url, credentials=creds_lockout, lp=lp)
1320 self.fail()
1321 except LdbError, (num, msg):
1322 self.assertEquals(num, ERR_INVALID_CREDENTIALS)
1324 res = self._check_account("cn=testuser,cn=users," + self.base_dn,
1325 badPwdCount=3,
1326 badPasswordTime=badPasswordTime,
1327 lockoutTime=lockoutTime,
1328 userAccountControl=
1329 dsdb.UF_NORMAL_ACCOUNT,
1330 msDSUserAccountControlComputed=dsdb.UF_LOCKOUT)
1332 # The wrong password
1333 creds_lockout.set_password("thatsAcomplPASS1x")
1334 try:
1335 ldb_lockout = SamDB(url=host_url, credentials=creds_lockout, lp=lp)
1336 self.fail()
1337 except LdbError, (num, msg):
1338 self.assertEquals(num, ERR_INVALID_CREDENTIALS)
1340 res = self._check_account("cn=testuser,cn=users," + self.base_dn,
1341 badPwdCount=3,
1342 badPasswordTime=badPasswordTime,
1343 lockoutTime=lockoutTime,
1344 userAccountControl=
1345 dsdb.UF_NORMAL_ACCOUNT,
1346 msDSUserAccountControlComputed=dsdb.UF_LOCKOUT)
1348 # The correct password
1349 creds_lockout.set_password("thatsAcomplPASS1")
1350 try:
1351 ldb_lockout = SamDB(url=host_url, credentials=creds_lockout, lp=lp)
1352 self.fail()
1353 except LdbError, (num, msg):
1354 self.assertEquals(num, ERR_INVALID_CREDENTIALS)
1356 res = self._check_account("cn=testuser,cn=users," + self.base_dn,
1357 badPwdCount=3,
1358 badPasswordTime=badPasswordTime,
1359 lockoutTime=lockoutTime,
1360 userAccountControl=
1361 dsdb.UF_NORMAL_ACCOUNT,
1362 msDSUserAccountControlComputed=dsdb.UF_LOCKOUT)
1364 time.sleep(self.account_lockout_duration + 1)
1366 res = self._check_account("cn=testuser,cn=users," + self.base_dn,
1367 badPwdCount=3, effective_bad_password_count=0,
1368 badPasswordTime=badPasswordTime,
1369 lockoutTime=lockoutTime,
1370 userAccountControl=
1371 dsdb.UF_NORMAL_ACCOUNT,
1372 msDSUserAccountControlComputed=0)
1374 # The correct password after letting the timeout expire
1375 creds_lockout.set_password("thatsAcomplPASS1")
1376 ldb_lockout = SamDB(url=host_url, credentials=creds_lockout, lp=lp)
1378 res = self._check_account("cn=testuser,cn=users," + self.base_dn,
1379 badPwdCount=0,
1380 badPasswordTime=badPasswordTime,
1381 lockoutTime=0,
1382 userAccountControl=
1383 dsdb.UF_NORMAL_ACCOUNT,
1384 msDSUserAccountControlComputed=0)
1386 # The wrong password
1387 creds_lockout.set_password("thatsAcomplPASS1x")
1388 try:
1389 ldb_lockout = SamDB(url=host_url, credentials=creds_lockout, lp=lp)
1390 self.fail()
1391 except LdbError, (num, msg):
1392 self.assertEquals(num, ERR_INVALID_CREDENTIALS)
1394 res = self._check_account("cn=testuser,cn=users," + self.base_dn,
1395 badPwdCount=1,
1396 badPasswordTime=("greater", badPasswordTime),
1397 lockoutTime=0,
1398 userAccountControl=
1399 dsdb.UF_NORMAL_ACCOUNT,
1400 msDSUserAccountControlComputed=0)
1401 badPasswordTime = int(res[0]["badPasswordTime"][0])
1403 # The wrong password
1404 creds_lockout.set_password("thatsAcomplPASS1x")
1405 try:
1406 ldb_lockout = SamDB(url=host_url, credentials=creds_lockout, lp=lp)
1407 self.fail()
1408 except LdbError, (num, msg):
1409 self.assertEquals(num, ERR_INVALID_CREDENTIALS)
1411 res = self._check_account("cn=testuser,cn=users," + self.base_dn,
1412 badPwdCount=2,
1413 badPasswordTime=("greater", badPasswordTime),
1414 lockoutTime=0,
1415 userAccountControl=
1416 dsdb.UF_NORMAL_ACCOUNT,
1417 msDSUserAccountControlComputed=0)
1418 badPasswordTime = int(res[0]["badPasswordTime"][0])
1420 time.sleep(self.lockout_observation_window + 1)
1422 res = self._check_account("cn=testuser,cn=users," + self.base_dn,
1423 badPwdCount=2, effective_bad_password_count=0,
1424 badPasswordTime=badPasswordTime,
1425 lockoutTime=0,
1426 userAccountControl=
1427 dsdb.UF_NORMAL_ACCOUNT,
1428 msDSUserAccountControlComputed=0)
1430 # The wrong password
1431 creds_lockout.set_password("thatsAcomplPASS1x")
1432 try:
1433 ldb_lockout = SamDB(url=host_url, credentials=creds_lockout, lp=lp)
1434 self.fail()
1435 except LdbError, (num, msg):
1436 self.assertEquals(num, ERR_INVALID_CREDENTIALS)
1438 res = self._check_account("cn=testuser,cn=users," + self.base_dn,
1439 badPwdCount=1,
1440 badPasswordTime=("greater", badPasswordTime),
1441 lockoutTime=0,
1442 userAccountControl=
1443 dsdb.UF_NORMAL_ACCOUNT,
1444 msDSUserAccountControlComputed=0)
1445 badPasswordTime = int(res[0]["badPasswordTime"][0])
1447 # The correct password without letting the timeout expire
1448 creds_lockout.set_password("thatsAcomplPASS1")
1449 ldb_lockout = SamDB(url=host_url, credentials=creds_lockout, lp=lp)
1451 res = self._check_account("cn=testuser,cn=users," + self.base_dn,
1452 badPwdCount=0,
1453 badPasswordTime=badPasswordTime,
1454 lockoutTime=0,
1455 userAccountControl=
1456 dsdb.UF_NORMAL_ACCOUNT,
1457 msDSUserAccountControlComputed=0)
1459 def test_login_lockout_ntlm(self):
1460 self._test_login_lockout(DONT_USE_KERBEROS)
1462 def test_login_lockout_kerberos(self):
1463 self._test_login_lockout(MUST_USE_KERBEROS)
1465 def tearDown(self):
1466 super(PasswordTests, self).tearDown()
1467 delete_force(self.ldb, "cn=testuser,cn=users," + self.base_dn)
1468 delete_force(self.ldb, "cn=testuser2,cn=users," + self.base_dn)
1469 delete_force(self.ldb, "cn=testuser3,cn=users," + self.base_dn)
1470 # Close the second LDB connection (with the user credentials)
1471 self.ldb2 = None
1473 host_url = "ldap://%s" % host
1475 TestProgram(module=__name__, opts=subunitopts)