s4:dsdb/tests: let password_lockout.py test with all combinations of krb5, ntlmssp...
[nivanova/samba-autobuild/.git] / source4 / dsdb / tests / python / password_lockout.py
1 #!/usr/bin/env python
2 # -*- coding: utf-8 -*-
3 # This tests the password lockout behavior for AD implementations
4 #
5 # Copyright Matthias Dieter Wallnoefer 2010
6 # Copyright Andrew Bartlett 2013
7 # Copyright Stefan Metzmacher 2014
8 #
9
10 import optparse
11 import sys
12 import base64
13 import time
14
15 sys.path.insert(0, "bin/python")
16 import samba
17
18 from samba.tests.subunitrun import TestProgram, SubunitOptions
19
20 import samba.getopt as options
21
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
35
36 parser = optparse.OptionParser("password_lockout.py [options] <host>")
37 sambaopts = options.SambaOptions(parser)
38 parser.add_option_group(sambaopts)
39 parser.add_option_group(options.VersionOptions(parser))
40 # use command line creds if available
41 credopts = options.CredentialsOptions(parser)
42 parser.add_option_group(credopts)
43 subunitopts = SubunitOptions(parser)
44 parser.add_option_group(subunitopts)
45 opts, args = parser.parse_args()
46
47 if len(args) < 1:
48     parser.print_usage()
49     sys.exit(1)
50
51 host = args[0]
52
53 lp = sambaopts.get_loadparm()
54 global_creds = credopts.get_credentials(lp)
55
56 # Force an encrypted connection
57 global_creds.set_gensec_features(global_creds.get_gensec_features() |
58                                  gensec.FEATURE_SEAL)
59
60 template_creds = Credentials()
61 template_creds.set_username("testuser")
62 template_creds.set_password("thatsAcomplPASS1")
63 template_creds.set_domain(global_creds.get_domain())
64 template_creds.set_realm(global_creds.get_realm())
65 template_creds.set_workstation(global_creds.get_workstation())
66 template_creds.set_gensec_features(global_creds.get_gensec_features())
67 template_creds.set_kerberos_state(global_creds.get_kerberos_state())
68
69 def insta_creds(template=template_creds, username=None, userpass=None, kerberos_state=None):
70     if username is not None:
71         assert userpass is not None
72
73     if username is None:
74         assert userpass is None
75
76         username = template.get_username()
77         userpass = template.get_password()
78
79     if kerberos_state is None:
80         kerberos_state = template.get_kerberos_state()
81
82     # get a copy of the global creds or a the passed in creds
83     c = Credentials()
84     c.set_username(username)
85     c.set_password(userpass)
86     c.set_domain(template.get_domain())
87     c.set_realm(template.get_realm())
88     c.set_workstation(template.get_workstation())
89     c.set_gensec_features(c.get_gensec_features()
90                           | gensec.FEATURE_SEAL)
91     c.set_kerberos_state(kerberos_state)
92     return c
93
94 #
95 # Tests start here
96 #
97
98 class PasswordTests(samba.tests.TestCase):
99
100     def _open_samr_user(self, res):
101         self.assertTrue("objectSid" in res[0])
102
103         (domain_sid, rid) = ndr_unpack(security.dom_sid, res[0]["objectSid"][0]).split()
104         self.assertEquals(self.domain_sid, domain_sid)
105
106         return self.samr.OpenUser(self.samr_domain, security.SEC_FLAG_MAXIMUM_ALLOWED, rid)
107
108     def _reset_samr(self, res):
109
110         # Now reset the lockout, by removing ACB_AUTOLOCK (which removes the lock, despite being a generated attribute)
111         samr_user = self._open_samr_user(res)
112         acb_info = self.samr.QueryUserInfo(samr_user, 16)
113         acb_info.acct_flags &= ~samr.ACB_AUTOLOCK
114         self.samr.SetUserInfo(samr_user, 16, acb_info)
115         self.samr.Close(samr_user)
116
117     def _reset_ldap_lockoutTime(self, res):
118         self.ldb.modify_ldif("""
119 dn: """ + str(res[0].dn) + """
120 changetype: modify
121 replace: lockoutTime
122 lockoutTime: 0
123 """)
124
125     def _reset_ldap_userAccountControl(self, res):
126         self.assertTrue("userAccountControl" in res[0])
127         self.assertTrue("msDS-User-Account-Control-Computed" in res[0])
128
129         uac = int(res[0]["userAccountControl"][0])
130         uacc = int(res[0]["msDS-User-Account-Control-Computed"][0])
131
132         uac |= uacc
133         uac = uac & ~dsdb.UF_LOCKOUT
134
135         self.ldb.modify_ldif("""
136 dn: """ + str(res[0].dn) + """
137 changetype: modify
138 replace: userAccountControl
139 userAccountControl: %d
140 """ % uac)
141
142     def _reset_by_method(self, res, method):
143         if method is "ldap_userAccountControl":
144             self._reset_ldap_userAccountControl(res)
145         elif method is "ldap_lockoutTime":
146             self._reset_ldap_lockoutTime(res)
147         elif method is "samr":
148             self._reset_samr(res)
149         else:
150             self.assertTrue(False, msg="Invalid reset method[%s]" % method)
151
152     def _check_attribute(self, res, name, value):
153         if value is None:
154             self.assertTrue(name not in res[0],
155                             msg="attr[%s]=%r on dn[%s]" %
156                             (name, res[0], res[0].dn))
157             return
158
159         if isinstance(value, tuple):
160             (mode, value) = value
161         else:
162             mode = "equal"
163
164         if mode == "ignore":
165             return
166
167         if mode == "absent":
168             self.assertFalse(name in res[0],
169                             msg="attr[%s] not missing on dn[%s]" %
170                             (name, res[0].dn))
171             return
172
173         self.assertTrue(name in res[0],
174                         msg="attr[%s] missing on dn[%s]" %
175                         (name, res[0].dn))
176         self.assertTrue(len(res[0][name]) == 1,
177                         msg="attr[%s]=%r on dn[%s]" %
178                         (name, res[0][name], res[0].dn))
179
180
181         print  "%s = '%s'" % (name, res[0][name][0])
182
183         if mode == "present":
184             return
185
186         if mode == "equal":
187             v = int(res[0][name][0])
188             value = int(value)
189             msg = ("attr[%s]=[%s] != [%s] on dn[%s]\n"
190                    "(diff %d; actual value is %s than expected)"  %
191                    (name, v, value, res[0].dn, v - value,
192                     ('less' if v < value else 'greater')))
193
194             self.assertTrue(v == value, msg)
195             return
196
197         if mode == "greater":
198             v = int(res[0][name][0])
199             self.assertTrue(v > int(value),
200                             msg="attr[%s]=[%s] <= [%s] on dn[%s] (diff %d)" %
201                             (name, v, int(value), res[0].dn, v - int(value)))
202             return
203         if mode == "less":
204             v = int(res[0][name][0])
205             self.assertTrue(v < int(value),
206                             msg="attr[%s]=[%s] >= [%s] on dn[%s] (diff %d)" %
207                             (name, v, int(value), res[0].dn, v - int(value)))
208             return
209         self.assertEqual(mode, not mode, "Invalid Mode[%s]" % mode)
210
211     def _check_account(self, dn,
212                        badPwdCount=None,
213                        badPasswordTime=None,
214                        lastLogon=None,
215                        lastLogonTimestamp=None,
216                        lockoutTime=None,
217                        userAccountControl=None,
218                        msDSUserAccountControlComputed=None,
219                        effective_bad_password_count=None,
220                        msg=None):
221         print '-=' * 36
222         if msg is not None:
223             print  "\033[01;32m %s \033[00m\n" % msg
224         attrs = [
225            "objectSid",
226            "badPwdCount",
227            "badPasswordTime",
228            "lastLogon",
229            "lastLogonTimestamp",
230            "lockoutTime",
231            "userAccountControl",
232            "msDS-User-Account-Control-Computed"
233         ]
234
235         # in order to prevent some time resolution problems we sleep for
236         # 10 micro second
237         time.sleep(0.01)
238
239         res = self.ldb.search(dn, scope=SCOPE_BASE, attrs=attrs)
240         self.assertTrue(len(res) == 1)
241         self._check_attribute(res, "badPwdCount", badPwdCount)
242         self._check_attribute(res, "badPasswordTime", badPasswordTime)
243         self._check_attribute(res, "lastLogon", lastLogon)
244         self._check_attribute(res, "lastLogonTimestamp", lastLogonTimestamp)
245         self._check_attribute(res, "lockoutTime", lockoutTime)
246         self._check_attribute(res, "userAccountControl", userAccountControl)
247         self._check_attribute(res, "msDS-User-Account-Control-Computed",
248                               msDSUserAccountControlComputed)
249
250         lastLogon = int(res[0]["lastLogon"][0])
251
252         samr_user = self._open_samr_user(res)
253         uinfo3 = self.samr.QueryUserInfo(samr_user, 3)
254         uinfo5 = self.samr.QueryUserInfo(samr_user, 5)
255         uinfo16 = self.samr.QueryUserInfo(samr_user, 16)
256         uinfo21 = self.samr.QueryUserInfo(samr_user, 21)
257         self.samr.Close(samr_user)
258
259         expected_acb_info = 0
260         if userAccountControl & dsdb.UF_NORMAL_ACCOUNT:
261             expected_acb_info |= samr.ACB_NORMAL
262         if userAccountControl & dsdb.UF_ACCOUNTDISABLE:
263             expected_acb_info |= samr.ACB_DISABLED
264         if userAccountControl & dsdb.UF_PASSWD_NOTREQD:
265             expected_acb_info |= samr.ACB_PWNOTREQ
266         if msDSUserAccountControlComputed & dsdb.UF_LOCKOUT:
267             expected_acb_info |= samr.ACB_AUTOLOCK
268         if msDSUserAccountControlComputed & dsdb.UF_PASSWORD_EXPIRED:
269             expected_acb_info |= samr.ACB_PW_EXPIRED
270
271         expected_bad_password_count = 0
272         if badPwdCount is not None:
273             expected_bad_password_count = badPwdCount
274         if effective_bad_password_count is None:
275             effective_bad_password_count = expected_bad_password_count
276
277         self.assertEquals(uinfo3.acct_flags, expected_acb_info)
278         self.assertEquals(uinfo3.bad_password_count, expected_bad_password_count)
279         self.assertEquals(uinfo3.last_logon, lastLogon)
280
281         self.assertEquals(uinfo5.acct_flags, expected_acb_info)
282         self.assertEquals(uinfo5.bad_password_count, effective_bad_password_count)
283         self.assertEquals(uinfo5.last_logon, lastLogon)
284
285         self.assertEquals(uinfo16.acct_flags, expected_acb_info)
286
287         self.assertEquals(uinfo21.acct_flags, expected_acb_info)
288         self.assertEquals(uinfo21.bad_password_count, effective_bad_password_count)
289         self.assertEquals(uinfo21.last_logon, lastLogon)
290
291         # check LDAP again and make sure the samr.QueryUserInfo
292         # doesn't have any impact.
293         res2 = self.ldb.search(dn, scope=SCOPE_BASE, attrs=attrs)
294         self.assertEquals(res[0], res2[0])
295
296         # in order to prevent some time resolution problems we sleep for
297         # 10 micro second
298         time.sleep(0.01)
299         return res
300
301     def _readd_user(self, creds, lockOutObservationWindow=0):
302         username = creds.get_username()
303         userpass = creds.get_password()
304         userdn = "cn=%s,cn=users,%s" % (username, self.base_dn)
305
306         use_kerberos = creds.get_kerberos_state()
307         if use_kerberos == MUST_USE_KERBEROS:
308             lastlogon_relation = 'greater'
309         else:
310             if lockOutObservationWindow == 0:
311                 lastlogon_relation = 'greater'
312             else:
313                 lastlogon_relation = 'equal'
314
315         delete_force(self.ldb, userdn)
316         self.ldb.add({
317              "dn": userdn,
318              "objectclass": "user",
319              "sAMAccountName": username})
320
321         self.addCleanup(delete_force, self.ldb, userdn)
322
323         res = self._check_account(userdn,
324                                   badPwdCount=0,
325                                   badPasswordTime=0,
326                                   lastLogon=0,
327                                   lastLogonTimestamp=('absent', None),
328                                   userAccountControl=
329                                     dsdb.UF_NORMAL_ACCOUNT |
330                                     dsdb.UF_ACCOUNTDISABLE |
331                                     dsdb.UF_PASSWD_NOTREQD,
332                                   msDSUserAccountControlComputed=
333                                     dsdb.UF_PASSWORD_EXPIRED)
334
335         # SAMR doesn't have any impact if dsdb.UF_LOCKOUT isn't present.
336         # It doesn't create "lockoutTime" = 0.
337         self._reset_samr(res)
338
339         res = self._check_account(userdn,
340                                   badPwdCount=0,
341                                   badPasswordTime=0,
342                                   lastLogon=0,
343                                   lastLogonTimestamp=('absent', None),
344                                   userAccountControl=
345                                     dsdb.UF_NORMAL_ACCOUNT |
346                                     dsdb.UF_ACCOUNTDISABLE |
347                                     dsdb.UF_PASSWD_NOTREQD,
348                                   msDSUserAccountControlComputed=
349                                     dsdb.UF_PASSWORD_EXPIRED)
350
351         # Tests a password change when we don't have any password yet with a
352         # wrong old password
353         try:
354             self.ldb.modify_ldif("""
355 dn: """ + userdn + """
356 changetype: modify
357 delete: userPassword
358 userPassword: noPassword
359 add: userPassword
360 userPassword: thatsAcomplPASS2
361 """)
362             self.fail()
363         except LdbError, (num, msg):
364             self.assertEquals(num, ERR_CONSTRAINT_VIOLATION)
365             # Windows (2008 at least) seems to have some small bug here: it
366             # returns "0000056A" on longer (always wrong) previous passwords.
367             self.assertTrue('00000056' in msg, msg)
368
369         res = self._check_account(userdn,
370                                   badPwdCount=1,
371                                   badPasswordTime=("greater", 0),
372                                   lastLogon=0,
373                                   lastLogonTimestamp=('absent', None),
374                                   userAccountControl=
375                                     dsdb.UF_NORMAL_ACCOUNT |
376                                     dsdb.UF_ACCOUNTDISABLE |
377                                     dsdb.UF_PASSWD_NOTREQD,
378                                   msDSUserAccountControlComputed=
379                                     dsdb.UF_PASSWORD_EXPIRED)
380         badPwdCount = int(res[0]["badPwdCount"][0])
381         badPasswordTime = int(res[0]["badPasswordTime"][0])
382
383         # Sets the initial user password with a "special" password change
384         # I think that this internally is a password set operation and it can
385         # only be performed by someone which has password set privileges on the
386         # account (at least in s4 we do handle it like that).
387         self.ldb.modify_ldif("""
388 dn: """ + userdn + """
389 changetype: modify
390 delete: userPassword
391 add: userPassword
392 userPassword: """ + userpass + """
393 """)
394
395         res = self._check_account(userdn,
396                                   badPwdCount=badPwdCount,
397                                   badPasswordTime=badPasswordTime,
398                                   lastLogon=0,
399                                   lastLogonTimestamp=('absent', None),
400                                   userAccountControl=
401                                     dsdb.UF_NORMAL_ACCOUNT |
402                                     dsdb.UF_ACCOUNTDISABLE |
403                                     dsdb.UF_PASSWD_NOTREQD,
404                                   msDSUserAccountControlComputed=0)
405
406         # Enables the user account
407         self.ldb.enable_account("(sAMAccountName=%s)" % username)
408
409         res = self._check_account(userdn,
410                                   badPwdCount=badPwdCount,
411                                   badPasswordTime=badPasswordTime,
412                                   lastLogon=0,
413                                   lastLogonTimestamp=('absent', None),
414                                   userAccountControl=
415                                     dsdb.UF_NORMAL_ACCOUNT,
416                                   msDSUserAccountControlComputed=0)
417         if lockOutObservationWindow != 0:
418             time.sleep(lockOutObservationWindow + 1)
419             effective_bad_password_count = 0
420         else:
421             effective_bad_password_count = badPwdCount
422
423         res = self._check_account(userdn,
424                                   badPwdCount=badPwdCount,
425                                   effective_bad_password_count=effective_bad_password_count,
426                                   badPasswordTime=badPasswordTime,
427                                   lastLogon=0,
428                                   lastLogonTimestamp=('absent', None),
429                                   userAccountControl=
430                                     dsdb.UF_NORMAL_ACCOUNT,
431                                   msDSUserAccountControlComputed=0)
432
433         ldb = SamDB(url=host_url, credentials=creds, lp=lp)
434
435         if lockOutObservationWindow == 0:
436             badPwdCount = 0
437             effective_bad_password_count = 0
438         if use_kerberos == MUST_USE_KERBEROS:
439             badPwdCount = 0
440             effective_bad_password_count = 0
441
442         res = self._check_account(userdn,
443                                   badPwdCount=badPwdCount,
444                                   effective_bad_password_count=effective_bad_password_count,
445                                   badPasswordTime=badPasswordTime,
446                                   lastLogon=(lastlogon_relation, 0),
447                                   lastLogonTimestamp=('greater', badPasswordTime),
448                                   userAccountControl=
449                                     dsdb.UF_NORMAL_ACCOUNT,
450                                   msDSUserAccountControlComputed=0)
451
452         lastLogon = int(res[0]["lastLogon"][0])
453         lastLogonTimestamp = int(res[0]["lastLogonTimestamp"][0])
454         if lastlogon_relation == 'greater':
455             self.assertGreater(lastLogon, badPasswordTime)
456             self.assertGreaterEqual(lastLogon, lastLogonTimestamp)
457
458         res = self._check_account(userdn,
459                                   badPwdCount=badPwdCount,
460                                   effective_bad_password_count=effective_bad_password_count,
461                                   badPasswordTime=badPasswordTime,
462                                   lastLogon=lastLogon,
463                                   lastLogonTimestamp=lastLogonTimestamp,
464                                   userAccountControl=
465                                     dsdb.UF_NORMAL_ACCOUNT,
466                                   msDSUserAccountControlComputed=0)
467         return ldb
468
469     def assertLoginFailure(self, url, creds, lp, errno=ERR_INVALID_CREDENTIALS):
470         try:
471             ldb = SamDB(url=url, credentials=creds, lp=lp)
472             self.fail("Login unexpectedly succeeded")
473         except LdbError, (num, msg):
474             if errno is not None:
475                 self.assertEquals(num, errno, ("Login failed in the wrong way"
476                                                "(got err %d, expected %d)" %
477                                                (num, errno)))
478
479     def setUp(self):
480         super(PasswordTests, self).setUp()
481
482         self.ldb = SamDB(url=host_url, session_info=system_session(lp),
483                          credentials=global_creds, lp=lp)
484
485         # Gets back the basedn
486         base_dn = self.ldb.domain_dn()
487
488         # Gets back the configuration basedn
489         configuration_dn = self.ldb.get_config_basedn().get_linearized()
490
491         # Get the old "dSHeuristics" if it was set
492         dsheuristics = self.ldb.get_dsheuristics()
493
494         # Reset the "dSHeuristics" as they were before
495         self.addCleanup(self.ldb.set_dsheuristics, dsheuristics)
496
497         res = self.ldb.search(base_dn,
498                          scope=SCOPE_BASE, attrs=["lockoutDuration", "lockOutObservationWindow", "lockoutThreshold"])
499
500         if "lockoutDuration" in res[0]:
501             lockoutDuration = res[0]["lockoutDuration"][0]
502         else:
503             lockoutDuration = 0
504
505         if "lockoutObservationWindow" in res[0]:
506             lockoutObservationWindow = res[0]["lockoutObservationWindow"][0]
507         else:
508             lockoutObservationWindow = 0
509
510         if "lockoutThreshold" in res[0]:
511             lockoutThreshold = res[0]["lockoutThreshold"][0]
512         else:
513             lockoutTreshold = 0
514
515         self.addCleanup(self.ldb.modify_ldif, """
516 dn: """ + base_dn + """
517 changetype: modify
518 replace: lockoutDuration
519 lockoutDuration: """ + str(lockoutDuration) + """
520 replace: lockoutObservationWindow
521 lockoutObservationWindow: """ + str(lockoutObservationWindow) + """
522 replace: lockoutThreshold
523 lockoutThreshold: """ + str(lockoutThreshold) + """
524 """)
525
526         m = Message()
527         m.dn = Dn(self.ldb, base_dn)
528
529         self.account_lockout_duration = 2
530         account_lockout_duration_ticks = -int(self.account_lockout_duration * (1e7))
531
532         m["lockoutDuration"] = MessageElement(str(account_lockout_duration_ticks),
533                                               FLAG_MOD_REPLACE, "lockoutDuration")
534
535         account_lockout_threshold = 3
536         m["lockoutThreshold"] = MessageElement(str(account_lockout_threshold),
537                                                FLAG_MOD_REPLACE, "lockoutThreshold")
538
539         self.lockout_observation_window = 2
540         lockout_observation_window_ticks = -int(self.lockout_observation_window * (1e7))
541
542         m["lockOutObservationWindow"] = MessageElement(str(lockout_observation_window_ticks),
543                                                        FLAG_MOD_REPLACE, "lockOutObservationWindow")
544
545         self.ldb.modify(m)
546
547         # Set the "dSHeuristics" to activate the correct "userPassword" behaviour
548         self.ldb.set_dsheuristics("000000001")
549
550         # Get the old "minPwdAge"
551         minPwdAge = self.ldb.get_minPwdAge()
552
553         # Reset the "minPwdAge" as it was before
554         self.addCleanup(self.ldb.set_minPwdAge, minPwdAge)
555
556         # Set it temporarely to "0"
557         self.ldb.set_minPwdAge("0")
558
559         self.base_dn = self.ldb.domain_dn()
560
561         self.domain_sid = security.dom_sid(self.ldb.get_domain_sid())
562         self.samr = samr.samr("ncacn_ip_tcp:%s[seal]" % host, lp, global_creds)
563         self.samr_handle = self.samr.Connect2(None, security.SEC_FLAG_MAXIMUM_ALLOWED)
564         self.samr_domain = self.samr.OpenDomain(self.samr_handle, security.SEC_FLAG_MAXIMUM_ALLOWED, self.domain_sid)
565
566         # (Re)adds the test user accounts
567         self.lockout1krb5_creds = insta_creds(username="lockout1krb5",
568                                               userpass="thatsAcomplPASS0",
569                                               kerberos_state=MUST_USE_KERBEROS)
570         self.lockout1krb5_ldb = self._readd_user(self.lockout1krb5_creds)
571         self.lockout2krb5_creds = insta_creds(username="lockout2krb5",
572                                               userpass="thatsAcomplPASS0",
573                                               kerberos_state=MUST_USE_KERBEROS)
574         self.lockout2krb5_ldb = self._readd_user(self.lockout2krb5_creds,
575                                         lockOutObservationWindow=self.lockout_observation_window)
576         self.lockout1ntlm_creds = insta_creds(username="lockout1ntlm",
577                                               userpass="thatsAcomplPASS0",
578                                               kerberos_state=DONT_USE_KERBEROS)
579         self.lockout1ntlm_ldb = self._readd_user(self.lockout1ntlm_creds)
580         self.lockout2ntlm_creds = insta_creds(username="lockout2ntlm",
581                                               userpass="thatsAcomplPASS0",
582                                               kerberos_state=DONT_USE_KERBEROS)
583         self.lockout2ntlm_ldb = self._readd_user(self.lockout2ntlm_creds,
584                                         lockOutObservationWindow=self.lockout_observation_window)
585
586     def _test_userPassword_lockout_with_clear_change(self, creds, other_ldb, method):
587         print "Performs a password cleartext change operation on 'userPassword'"
588         # Notice: This works only against Windows if "dSHeuristics" has been set
589         # properly
590         username = creds.get_username()
591         userpass = creds.get_password()
592         userdn = "cn=%s,cn=users,%s" % (username, self.base_dn)
593
594         res = self._check_account(userdn,
595                                   badPwdCount=0,
596                                   badPasswordTime=("greater", 0),
597                                   lastLogon=('greater', 0),
598                                   lastLogonTimestamp=('greater', 0),
599                                   userAccountControl=
600                                     dsdb.UF_NORMAL_ACCOUNT,
601                                   msDSUserAccountControlComputed=0)
602         badPasswordTime = int(res[0]["badPasswordTime"][0])
603         lastLogon = int(res[0]["lastLogon"][0])
604         lastLogonTimestamp = int(res[0]["lastLogonTimestamp"][0])
605
606         # Change password on a connection as another user
607
608         # Wrong old password
609         try:
610             other_ldb.modify_ldif("""
611 dn: """ + userdn + """
612 changetype: modify
613 delete: userPassword
614 userPassword: thatsAcomplPASS1x
615 add: userPassword
616 userPassword: thatsAcomplPASS2
617 """)
618             self.fail()
619         except LdbError, (num, msg):
620             self.assertEquals(num, ERR_CONSTRAINT_VIOLATION)
621             self.assertTrue('00000056' in msg, msg)
622
623         res = self._check_account(userdn,
624                                   badPwdCount=1,
625                                   badPasswordTime=("greater", badPasswordTime),
626                                   lastLogon=lastLogon,
627                                   lastLogonTimestamp=lastLogonTimestamp,
628                                   userAccountControl=
629                                     dsdb.UF_NORMAL_ACCOUNT,
630                                   msDSUserAccountControlComputed=0)
631         badPasswordTime = int(res[0]["badPasswordTime"][0])
632
633         # Correct old password
634         other_ldb.modify_ldif("""
635 dn: """ + userdn + """
636 changetype: modify
637 delete: userPassword
638 userPassword: """ + userpass + """
639 add: userPassword
640 userPassword: thatsAcomplPASS2
641 """)
642
643         res = self._check_account(userdn,
644                                   badPwdCount=1,
645                                   badPasswordTime=badPasswordTime,
646                                   lastLogon=lastLogon,
647                                   lastLogonTimestamp=lastLogonTimestamp,
648                                   userAccountControl=
649                                     dsdb.UF_NORMAL_ACCOUNT,
650                                   msDSUserAccountControlComputed=0)
651
652         # Wrong old password
653         try:
654             other_ldb.modify_ldif("""
655 dn: """ + userdn + """
656 changetype: modify
657 delete: userPassword
658 userPassword: thatsAcomplPASS1x
659 add: userPassword
660 userPassword: thatsAcomplPASS2
661 """)
662             self.fail()
663         except LdbError, (num, msg):
664             self.assertEquals(num, ERR_CONSTRAINT_VIOLATION)
665             self.assertTrue('00000056' in msg, msg)
666
667         res = self._check_account(userdn,
668                                   badPwdCount=2,
669                                   badPasswordTime=("greater", badPasswordTime),
670                                   lastLogon=lastLogon,
671                                   lastLogonTimestamp=lastLogonTimestamp,
672                                   userAccountControl=
673                                     dsdb.UF_NORMAL_ACCOUNT,
674                                   msDSUserAccountControlComputed=0)
675         badPasswordTime = int(res[0]["badPasswordTime"][0])
676
677         print "two failed password change"
678
679         # Wrong old password
680         try:
681             other_ldb.modify_ldif("""
682 dn: """ + userdn + """
683 changetype: modify
684 delete: userPassword
685 userPassword: thatsAcomplPASS1x
686 add: userPassword
687 userPassword: thatsAcomplPASS2
688 """)
689             self.fail()
690         except LdbError, (num, msg):
691             self.assertEquals(num, ERR_CONSTRAINT_VIOLATION)
692             self.assertTrue('00000056' in msg, msg)
693
694         res = self._check_account(userdn,
695                                   badPwdCount=3,
696                                   badPasswordTime=("greater", badPasswordTime),
697                                   lastLogon=lastLogon,
698                                   lastLogonTimestamp=lastLogonTimestamp,
699                                   lockoutTime=("greater", badPasswordTime),
700                                   userAccountControl=
701                                     dsdb.UF_NORMAL_ACCOUNT,
702                                   msDSUserAccountControlComputed=dsdb.UF_LOCKOUT)
703         badPasswordTime = int(res[0]["badPasswordTime"][0])
704         lockoutTime = int(res[0]["lockoutTime"][0])
705
706         # Wrong old password
707         try:
708             other_ldb.modify_ldif("""
709 dn: """ + userdn + """
710 changetype: modify
711 delete: userPassword
712 userPassword: thatsAcomplPASS1x
713 add: userPassword
714 userPassword: thatsAcomplPASS2
715 """)
716             self.fail()
717         except LdbError, (num, msg):
718             self.assertEquals(num, ERR_CONSTRAINT_VIOLATION)
719             self.assertTrue('00000775' in msg, msg)
720
721         res = self._check_account(userdn,
722                                   badPwdCount=3,
723                                   badPasswordTime=badPasswordTime,
724                                   lastLogon=lastLogon,
725                                   lastLogonTimestamp=lastLogonTimestamp,
726                                   lockoutTime=lockoutTime,
727                                   userAccountControl=
728                                     dsdb.UF_NORMAL_ACCOUNT,
729                                   msDSUserAccountControlComputed=dsdb.UF_LOCKOUT)
730
731         # Wrong old password
732         try:
733             other_ldb.modify_ldif("""
734 dn: """ + userdn + """
735 changetype: modify
736 delete: userPassword
737 userPassword: thatsAcomplPASS1x
738 add: userPassword
739 userPassword: thatsAcomplPASS2
740 """)
741             self.fail()
742         except LdbError, (num, msg):
743             self.assertEquals(num, ERR_CONSTRAINT_VIOLATION)
744             self.assertTrue('00000775' in msg, msg)
745
746         res = self._check_account(userdn,
747                                   badPwdCount=3,
748                                   badPasswordTime=badPasswordTime,
749                                   lockoutTime=lockoutTime,
750                                   lastLogon=lastLogon,
751                                   lastLogonTimestamp=lastLogonTimestamp,
752                                   userAccountControl=
753                                     dsdb.UF_NORMAL_ACCOUNT,
754                                   msDSUserAccountControlComputed=dsdb.UF_LOCKOUT)
755
756         try:
757             # Correct old password
758             other_ldb.modify_ldif("""
759 dn: """ + userdn + """
760 changetype: modify
761 delete: userPassword
762 userPassword: thatsAcomplPASS2
763 add: userPassword
764 userPassword: thatsAcomplPASS2x
765 """)
766             self.fail()
767         except LdbError, (num, msg):
768             self.assertEquals(num, ERR_CONSTRAINT_VIOLATION)
769             self.assertTrue('00000775' in msg, msg)
770
771         res = self._check_account(userdn,
772                                   badPwdCount=3,
773                                   badPasswordTime=badPasswordTime,
774                                   lastLogon=lastLogon,
775                                   lastLogonTimestamp=lastLogonTimestamp,
776                                   lockoutTime=lockoutTime,
777                                   userAccountControl=
778                                     dsdb.UF_NORMAL_ACCOUNT,
779                                   msDSUserAccountControlComputed=dsdb.UF_LOCKOUT)
780
781         # Now reset the password, which does NOT change the lockout!
782         self.ldb.modify_ldif("""
783 dn: """ + userdn + """
784 changetype: modify
785 replace: userPassword
786 userPassword: thatsAcomplPASS2
787 """)
788
789         res = self._check_account(userdn,
790                                   badPwdCount=3,
791                                   badPasswordTime=badPasswordTime,
792                                   lastLogon=lastLogon,
793                                   lastLogonTimestamp=lastLogonTimestamp,
794                                   lockoutTime=lockoutTime,
795                                   userAccountControl=
796                                     dsdb.UF_NORMAL_ACCOUNT,
797                                   msDSUserAccountControlComputed=dsdb.UF_LOCKOUT)
798
799         try:
800             # Correct old password
801             other_ldb.modify_ldif("""
802 dn: """ + userdn + """
803 changetype: modify
804 delete: userPassword
805 userPassword: thatsAcomplPASS2
806 add: userPassword
807 userPassword: thatsAcomplPASS2x
808 """)
809             self.fail()
810         except LdbError, (num, msg):
811             self.assertEquals(num, ERR_CONSTRAINT_VIOLATION)
812             self.assertTrue('00000775' in msg, msg)
813
814         res = self._check_account(userdn,
815                                   badPwdCount=3,
816                                   badPasswordTime=badPasswordTime,
817                                   lastLogon=lastLogon,
818                                   lastLogonTimestamp=lastLogonTimestamp,
819                                   lockoutTime=lockoutTime,
820                                   userAccountControl=
821                                     dsdb.UF_NORMAL_ACCOUNT,
822                                   msDSUserAccountControlComputed=dsdb.UF_LOCKOUT)
823
824         m = Message()
825         m.dn = Dn(self.ldb, userdn)
826         m["userAccountControl"] = MessageElement(
827           str(dsdb.UF_LOCKOUT),
828           FLAG_MOD_REPLACE, "userAccountControl")
829
830         self.ldb.modify(m)
831
832         # This shows that setting the UF_LOCKOUT flag alone makes no difference
833         res = self._check_account(userdn,
834                                   badPwdCount=3,
835                                   badPasswordTime=badPasswordTime,
836                                   lastLogon=lastLogon,
837                                   lastLogonTimestamp=lastLogonTimestamp,
838                                   lockoutTime=lockoutTime,
839                                   userAccountControl=
840                                     dsdb.UF_NORMAL_ACCOUNT,
841                                   msDSUserAccountControlComputed=dsdb.UF_LOCKOUT)
842
843         # This shows that setting the UF_LOCKOUT flag makes no difference
844         try:
845             # Correct old password
846             other_ldb.modify_ldif("""
847 dn: """ + userdn + """
848 changetype: modify
849 delete: unicodePwd
850 unicodePwd:: """ + base64.b64encode("\"thatsAcomplPASS2\"".encode('utf-16-le')) + """
851 add: unicodePwd
852 unicodePwd:: """ + base64.b64encode("\"thatsAcomplPASS2x\"".encode('utf-16-le')) + """
853 """)
854             self.fail()
855         except LdbError, (num, msg):
856             self.assertEquals(num, ERR_CONSTRAINT_VIOLATION)
857             self.assertTrue('00000775' in msg, msg)
858
859         res = self._check_account(userdn,
860                                   badPwdCount=3,
861                                   badPasswordTime=badPasswordTime,
862                                   lockoutTime=lockoutTime,
863                                   lastLogon=lastLogon,
864                                   lastLogonTimestamp=lastLogonTimestamp,
865                                   userAccountControl=
866                                     dsdb.UF_NORMAL_ACCOUNT,
867                                   msDSUserAccountControlComputed=dsdb.UF_LOCKOUT)
868
869         self._reset_by_method(res, method)
870
871         # Here bad password counts are reset without logon success.
872         res = self._check_account(userdn,
873                                   badPwdCount=0,
874                                   badPasswordTime=badPasswordTime,
875                                   lockoutTime=0,
876                                   lastLogon=lastLogon,
877                                   lastLogonTimestamp=lastLogonTimestamp,
878                                   userAccountControl=
879                                     dsdb.UF_NORMAL_ACCOUNT,
880                                   msDSUserAccountControlComputed=0)
881
882         # The correct password after doing the unlock
883
884         other_ldb.modify_ldif("""
885 dn: """ + userdn + """
886 changetype: modify
887 delete: unicodePwd
888 unicodePwd:: """ + base64.b64encode("\"thatsAcomplPASS2\"".encode('utf-16-le')) + """
889 add: unicodePwd
890 unicodePwd:: """ + base64.b64encode("\"thatsAcomplPASS2x\"".encode('utf-16-le')) + """
891 """)
892         userpass = "thatsAcomplPASS2x"
893         creds.set_password(userpass)
894
895         res = self._check_account(userdn,
896                                   badPwdCount=0,
897                                   badPasswordTime=badPasswordTime,
898                                   lockoutTime=0,
899                                   lastLogon=lastLogon,
900                                   lastLogonTimestamp=lastLogonTimestamp,
901                                   userAccountControl=
902                                     dsdb.UF_NORMAL_ACCOUNT,
903                                   msDSUserAccountControlComputed=0)
904
905         # Wrong old password
906         try:
907             other_ldb.modify_ldif("""
908 dn: """ + userdn + """
909 changetype: modify
910 delete: userPassword
911 userPassword: thatsAcomplPASS1xyz
912 add: userPassword
913 userPassword: thatsAcomplPASS2XYZ
914 """)
915             self.fail()
916         except LdbError, (num, msg):
917             self.assertEquals(num, ERR_CONSTRAINT_VIOLATION)
918             self.assertTrue('00000056' in msg, msg)
919
920         res = self._check_account(userdn,
921                                   badPwdCount=1,
922                                   badPasswordTime=("greater", badPasswordTime),
923                                   lockoutTime=0,
924                                   lastLogon=lastLogon,
925                                   lastLogonTimestamp=lastLogonTimestamp,
926                                   userAccountControl=
927                                     dsdb.UF_NORMAL_ACCOUNT,
928                                   msDSUserAccountControlComputed=0)
929         badPasswordTime = int(res[0]["badPasswordTime"][0])
930
931         # Wrong old password
932         try:
933             other_ldb.modify_ldif("""
934 dn: """ + userdn + """
935 changetype: modify
936 delete: userPassword
937 userPassword: thatsAcomplPASS1xyz
938 add: userPassword
939 userPassword: thatsAcomplPASS2XYZ
940 """)
941             self.fail()
942         except LdbError, (num, msg):
943             self.assertEquals(num, ERR_CONSTRAINT_VIOLATION)
944             self.assertTrue('00000056' in msg, msg)
945
946         res = self._check_account(userdn,
947                                   badPwdCount=2,
948                                   badPasswordTime=("greater", badPasswordTime),
949                                   lockoutTime=0,
950                                   lastLogon=lastLogon,
951                                   lastLogonTimestamp=lastLogonTimestamp,
952                                   userAccountControl=
953                                     dsdb.UF_NORMAL_ACCOUNT,
954                                   msDSUserAccountControlComputed=0)
955         badPasswordTime = int(res[0]["badPasswordTime"][0])
956
957         self._reset_ldap_lockoutTime(res)
958
959         res = self._check_account(userdn,
960                                   badPwdCount=0,
961                                   badPasswordTime=badPasswordTime,
962                                   lastLogon=lastLogon,
963                                   lastLogonTimestamp=lastLogonTimestamp,
964                                   lockoutTime=0,
965                                   userAccountControl=
966                                     dsdb.UF_NORMAL_ACCOUNT,
967                                   msDSUserAccountControlComputed=0)
968
969     def test_userPassword_lockout_with_clear_change_krb5_ldap_userAccountControl(self):
970         self._test_userPassword_lockout_with_clear_change(self.lockout1krb5_creds,
971                                                           self.lockout2krb5_ldb,
972                                                           "ldap_userAccountControl")
973
974     def test_userPassword_lockout_with_clear_change_krb5_ldap_lockoutTime(self):
975         self._test_userPassword_lockout_with_clear_change(self.lockout1krb5_creds,
976                                                           self.lockout2krb5_ldb,
977                                                           "ldap_lockoutTime")
978
979     def test_userPassword_lockout_with_clear_change_krb5_samr(self):
980         self._test_userPassword_lockout_with_clear_change(self.lockout1krb5_creds,
981                                                           self.lockout2krb5_ldb,
982                                                           "samr")
983
984     def test_userPassword_lockout_with_clear_change_ntlm_ldap_userAccountControl(self):
985         self._test_userPassword_lockout_with_clear_change(self.lockout1ntlm_creds,
986                                                           self.lockout2ntlm_ldb,
987                                                           "ldap_userAccountControl")
988
989     def test_userPassword_lockout_with_clear_change_ntlm_ldap_lockoutTime(self):
990         self._test_userPassword_lockout_with_clear_change(self.lockout1ntlm_creds,
991                                                           self.lockout2ntlm_ldb,
992                                                           "ldap_lockoutTime")
993
994     def test_userPassword_lockout_with_clear_change_ntlm_samr(self):
995         self._test_userPassword_lockout_with_clear_change(self.lockout1ntlm_creds,
996                                                           self.lockout2ntlm_ldb,
997                                                           "samr")
998
999     def _test_unicodePwd_lockout_with_clear_change(self, creds, other_ldb):
1000         print "Performs a password cleartext change operation on 'unicodePwd'"
1001         username = creds.get_username()
1002         userpass = creds.get_password()
1003         userdn = "cn=%s,cn=users,%s" % (username, self.base_dn)
1004
1005         res = self._check_account(userdn,
1006                                   badPwdCount=0,
1007                                   badPasswordTime=("greater", 0),
1008                                   lastLogon=("greater", 0),
1009                                   lastLogonTimestamp=("greater", 0),
1010                                   userAccountControl=
1011                                     dsdb.UF_NORMAL_ACCOUNT,
1012                                   msDSUserAccountControlComputed=0)
1013         badPasswordTime = int(res[0]["badPasswordTime"][0])
1014         lastLogon = int(res[0]["lastLogon"][0])
1015
1016         # Change password on a connection as another user
1017
1018         # Wrong old password
1019         try:
1020             other_ldb.modify_ldif("""
1021 dn: """ + userdn + """
1022 changetype: modify
1023 delete: unicodePwd
1024 unicodePwd:: """ + base64.b64encode("\"thatsAcomplPASS1x\"".encode('utf-16-le')) + """
1025 add: unicodePwd
1026 unicodePwd:: """ + base64.b64encode("\"thatsAcomplPASS2\"".encode('utf-16-le')) + """
1027 """)
1028             self.fail()
1029         except LdbError, (num, msg):
1030             self.assertEquals(num, ERR_CONSTRAINT_VIOLATION)
1031             self.assertTrue('00000056' in msg, msg)
1032
1033         res = self._check_account(userdn,
1034                                   badPwdCount=1,
1035                                   badPasswordTime=("greater", badPasswordTime),
1036                                   lastLogon=lastLogon,
1037                                   lastLogonTimestamp=lastLogon,
1038                                   userAccountControl=
1039                                     dsdb.UF_NORMAL_ACCOUNT,
1040                                   msDSUserAccountControlComputed=0)
1041         badPasswordTime = int(res[0]["badPasswordTime"][0])
1042
1043         # Correct old password
1044         old_utf16 = ("\"%s\"" % userpass).encode('utf-16-le')
1045         invalid_utf16 = "\"thatsAcomplPASSX\"".encode('utf-16-le')
1046         userpass = "thatsAcomplPASS2"
1047         creds.set_password(userpass)
1048         new_utf16 = ("\"%s\"" % userpass).encode('utf-16-le')
1049
1050         other_ldb.modify_ldif("""
1051 dn: """ + userdn + """
1052 changetype: modify
1053 delete: unicodePwd
1054 unicodePwd:: """ + base64.b64encode(old_utf16) + """
1055 add: unicodePwd
1056 unicodePwd:: """ + base64.b64encode(new_utf16) + """
1057 """)
1058
1059         res = self._check_account(userdn,
1060                                   badPwdCount=1,
1061                                   badPasswordTime=badPasswordTime,
1062                                   lastLogon=lastLogon,
1063                                   lastLogonTimestamp=lastLogon,
1064                                   userAccountControl=
1065                                     dsdb.UF_NORMAL_ACCOUNT,
1066                                   msDSUserAccountControlComputed=0)
1067
1068         # Wrong old password
1069         try:
1070             other_ldb.modify_ldif("""
1071 dn: """ + userdn + """
1072 changetype: modify
1073 delete: unicodePwd
1074 unicodePwd:: """ + base64.b64encode(old_utf16) + """
1075 add: unicodePwd
1076 unicodePwd:: """ + base64.b64encode(new_utf16) + """
1077 """)
1078             self.fail()
1079         except LdbError, (num, msg):
1080             self.assertEquals(num, ERR_CONSTRAINT_VIOLATION)
1081             self.assertTrue('00000056' in msg, msg)
1082
1083         res = self._check_account(userdn,
1084                                   badPwdCount=2,
1085                                   badPasswordTime=("greater", badPasswordTime),
1086                                   lastLogon=lastLogon,
1087                                   lastLogonTimestamp=lastLogon,
1088                                   userAccountControl=
1089                                     dsdb.UF_NORMAL_ACCOUNT,
1090                                   msDSUserAccountControlComputed=0)
1091         badPasswordTime = int(res[0]["badPasswordTime"][0])
1092
1093         # SAMR doesn't have any impact if dsdb.UF_LOCKOUT isn't present.
1094         # It doesn't create "lockoutTime" = 0 and doesn't
1095         # reset "badPwdCount" = 0.
1096         self._reset_samr(res)
1097
1098         res = self._check_account(userdn,
1099                                   badPwdCount=2,
1100                                   badPasswordTime=badPasswordTime,
1101                                   lastLogon=lastLogon,
1102                                   lastLogonTimestamp=lastLogon,
1103                                   userAccountControl=
1104                                     dsdb.UF_NORMAL_ACCOUNT,
1105                                   msDSUserAccountControlComputed=0)
1106
1107         print "two failed password change"
1108
1109         # Wrong old password
1110         try:
1111             other_ldb.modify_ldif("""
1112 dn: """ + userdn + """
1113 changetype: modify
1114 delete: unicodePwd
1115 unicodePwd:: """ + base64.b64encode(invalid_utf16) + """
1116 add: unicodePwd
1117 unicodePwd:: """ + base64.b64encode(new_utf16) + """
1118 """)
1119             self.fail()
1120         except LdbError, (num, msg):
1121             self.assertEquals(num, ERR_CONSTRAINT_VIOLATION)
1122             self.assertTrue('00000056' in msg, msg)
1123
1124         # this is strange, why do we have lockoutTime=badPasswordTime here?
1125         res = self._check_account(userdn,
1126                                   badPwdCount=3,
1127                                   badPasswordTime=("greater", badPasswordTime),
1128                                   lastLogon=lastLogon,
1129                                   lastLogonTimestamp=lastLogon,
1130                                   lockoutTime=("greater", badPasswordTime),
1131                                   userAccountControl=
1132                                     dsdb.UF_NORMAL_ACCOUNT,
1133                                   msDSUserAccountControlComputed=dsdb.UF_LOCKOUT)
1134         badPasswordTime = int(res[0]["badPasswordTime"][0])
1135         lockoutTime = int(res[0]["lockoutTime"][0])
1136
1137         # Wrong old password
1138         try:
1139             other_ldb.modify_ldif("""
1140 dn: """ + userdn + """
1141 changetype: modify
1142 delete: unicodePwd
1143 unicodePwd:: """ + base64.b64encode(invalid_utf16) + """
1144 add: unicodePwd
1145 unicodePwd:: """ + base64.b64encode(new_utf16) + """
1146 """)
1147             self.fail()
1148         except LdbError, (num, msg):
1149             self.assertEquals(num, ERR_CONSTRAINT_VIOLATION)
1150             self.assertTrue('00000775' in msg, msg)
1151
1152         res = self._check_account(userdn,
1153                                   badPwdCount=3,
1154                                   badPasswordTime=badPasswordTime,
1155                                   lastLogon=lastLogon,
1156                                   lastLogonTimestamp=lastLogon,
1157                                   lockoutTime=lockoutTime,
1158                                   userAccountControl=
1159                                     dsdb.UF_NORMAL_ACCOUNT,
1160                                   msDSUserAccountControlComputed=dsdb.UF_LOCKOUT)
1161
1162         # Wrong old password
1163         try:
1164             other_ldb.modify_ldif("""
1165 dn: """ + userdn + """
1166 changetype: modify
1167 delete: unicodePwd
1168 unicodePwd:: """ + base64.b64encode(invalid_utf16) + """
1169 add: unicodePwd
1170 unicodePwd:: """ + base64.b64encode(new_utf16) + """
1171 """)
1172             self.fail()
1173         except LdbError, (num, msg):
1174             self.assertEquals(num, ERR_CONSTRAINT_VIOLATION)
1175             self.assertTrue('00000775' in msg, msg)
1176
1177         res = self._check_account(userdn,
1178                                   badPwdCount=3,
1179                                   badPasswordTime=badPasswordTime,
1180                                   lastLogon=lastLogon,
1181                                   lastLogonTimestamp=lastLogon,
1182                                   lockoutTime=lockoutTime,
1183                                   userAccountControl=
1184                                     dsdb.UF_NORMAL_ACCOUNT,
1185                                   msDSUserAccountControlComputed=dsdb.UF_LOCKOUT)
1186
1187         try:
1188             # Correct old password
1189             other_ldb.modify_ldif("""
1190 dn: """ + userdn + """
1191 changetype: modify
1192 delete: unicodePwd
1193 unicodePwd:: """ + base64.b64encode(new_utf16) + """
1194 add: unicodePwd
1195 unicodePwd:: """ + base64.b64encode(invalid_utf16) + """
1196 """)
1197             self.fail()
1198         except LdbError, (num, msg):
1199             self.assertEquals(num, ERR_CONSTRAINT_VIOLATION)
1200             self.assertTrue('00000775' in msg, msg)
1201
1202         res = self._check_account(userdn,
1203                                   badPwdCount=3,
1204                                   badPasswordTime=badPasswordTime,
1205                                   lastLogon=lastLogon,
1206                                   lastLogonTimestamp=lastLogon,
1207                                   lockoutTime=lockoutTime,
1208                                   userAccountControl=
1209                                     dsdb.UF_NORMAL_ACCOUNT,
1210                                   msDSUserAccountControlComputed=dsdb.UF_LOCKOUT)
1211
1212         # Now reset the lockout, by removing ACB_AUTOLOCK (which removes the lock, despite being a generated attribute)
1213         self._reset_samr(res);
1214
1215         res = self._check_account(userdn,
1216                                   badPwdCount=0,
1217                                   badPasswordTime=badPasswordTime,
1218                                   lastLogon=lastLogon,
1219                                   lastLogonTimestamp=lastLogon,
1220                                   lockoutTime=0,
1221                                   userAccountControl=
1222                                     dsdb.UF_NORMAL_ACCOUNT,
1223                                   msDSUserAccountControlComputed=0)
1224
1225         # Correct old password
1226         old_utf16 = ("\"%s\"" % userpass).encode('utf-16-le')
1227         invalid_utf16 = "\"thatsAcomplPASSiX\"".encode('utf-16-le')
1228         userpass = "thatsAcomplPASS2x"
1229         creds.set_password(userpass)
1230         new_utf16 = ("\"%s\"" % userpass).encode('utf-16-le')
1231
1232         other_ldb.modify_ldif("""
1233 dn: """ + userdn + """
1234 changetype: modify
1235 delete: unicodePwd
1236 unicodePwd:: """ + base64.b64encode(old_utf16) + """
1237 add: unicodePwd
1238 unicodePwd:: """ + base64.b64encode(new_utf16) + """
1239 """)
1240
1241         res = self._check_account(userdn,
1242                                   badPwdCount=0,
1243                                   badPasswordTime=badPasswordTime,
1244                                   lastLogon=lastLogon,
1245                                   lastLogonTimestamp=lastLogon,
1246                                   lockoutTime=0,
1247                                   userAccountControl=
1248                                     dsdb.UF_NORMAL_ACCOUNT,
1249                                   msDSUserAccountControlComputed=0)
1250
1251         # Wrong old password
1252         try:
1253             other_ldb.modify_ldif("""
1254 dn: """ + userdn + """
1255 changetype: modify
1256 delete: unicodePwd
1257 unicodePwd:: """ + base64.b64encode(invalid_utf16) + """
1258 add: unicodePwd
1259 unicodePwd:: """ + base64.b64encode(new_utf16) + """
1260 """)
1261             self.fail()
1262         except LdbError, (num, msg):
1263             self.assertEquals(num, ERR_CONSTRAINT_VIOLATION)
1264             self.assertTrue('00000056' in msg, msg)
1265
1266         res = self._check_account(userdn,
1267                                   badPwdCount=1,
1268                                   badPasswordTime=("greater", badPasswordTime),
1269                                   lastLogon=lastLogon,
1270                                   lastLogonTimestamp=lastLogon,
1271                                   lockoutTime=0,
1272                                   userAccountControl=
1273                                     dsdb.UF_NORMAL_ACCOUNT,
1274                                   msDSUserAccountControlComputed=0)
1275         badPasswordTime = int(res[0]["badPasswordTime"][0])
1276
1277         # Wrong old password
1278         try:
1279             other_ldb.modify_ldif("""
1280 dn: """ + userdn + """
1281 changetype: modify
1282 delete: unicodePwd
1283 unicodePwd:: """ + base64.b64encode(invalid_utf16) + """
1284 add: unicodePwd
1285 unicodePwd:: """ + base64.b64encode(new_utf16) + """
1286 """)
1287             self.fail()
1288         except LdbError, (num, msg):
1289             self.assertEquals(num, ERR_CONSTRAINT_VIOLATION)
1290             self.assertTrue('00000056' in msg, msg)
1291
1292         res = self._check_account(userdn,
1293                                   badPwdCount=2,
1294                                   badPasswordTime=("greater", badPasswordTime),
1295                                   lastLogon=lastLogon,
1296                                   lastLogonTimestamp=lastLogon,
1297                                   lockoutTime=0,
1298                                   userAccountControl=
1299                                     dsdb.UF_NORMAL_ACCOUNT,
1300                                   msDSUserAccountControlComputed=0)
1301         badPasswordTime = int(res[0]["badPasswordTime"][0])
1302
1303         # SAMR doesn't have any impact if dsdb.UF_LOCKOUT isn't present.
1304         # It doesn't reset "badPwdCount" = 0.
1305         self._reset_samr(res)
1306
1307         res = self._check_account(userdn,
1308                                   badPwdCount=2,
1309                                   badPasswordTime=badPasswordTime,
1310                                   lastLogon=lastLogon,
1311                                   lastLogonTimestamp=lastLogon,
1312                                   lockoutTime=0,
1313                                   userAccountControl=
1314                                     dsdb.UF_NORMAL_ACCOUNT,
1315                                   msDSUserAccountControlComputed=0)
1316
1317         # Wrong old password
1318         try:
1319             other_ldb.modify_ldif("""
1320 dn: """ + userdn + """
1321 changetype: modify
1322 delete: unicodePwd
1323 unicodePwd:: """ + base64.b64encode(invalid_utf16) + """
1324 add: unicodePwd
1325 unicodePwd:: """ + base64.b64encode(new_utf16) + """
1326 """)
1327             self.fail()
1328         except LdbError, (num, msg):
1329             self.assertEquals(num, ERR_CONSTRAINT_VIOLATION)
1330             self.assertTrue('00000056' in msg, msg)
1331
1332         res = self._check_account(userdn,
1333                                   badPwdCount=3,
1334                                   badPasswordTime=("greater", badPasswordTime),
1335                                   lastLogon=lastLogon,
1336                                   lastLogonTimestamp=lastLogon,
1337                                   lockoutTime=("greater", badPasswordTime),
1338                                   userAccountControl=
1339                                     dsdb.UF_NORMAL_ACCOUNT,
1340                                   msDSUserAccountControlComputed=dsdb.UF_LOCKOUT)
1341         badPasswordTime = int(res[0]["badPasswordTime"][0])
1342         lockoutTime = int(res[0]["lockoutTime"][0])
1343
1344         time.sleep(self.account_lockout_duration + 1)
1345
1346         res = self._check_account(userdn,
1347                                   badPwdCount=3, effective_bad_password_count=0,
1348                                   badPasswordTime=badPasswordTime,
1349                                   lastLogon=lastLogon,
1350                                   lastLogonTimestamp=lastLogon,
1351                                   lockoutTime=lockoutTime,
1352                                   userAccountControl=
1353                                     dsdb.UF_NORMAL_ACCOUNT,
1354                                   msDSUserAccountControlComputed=0)
1355
1356         # SAMR doesn't have any impact if dsdb.UF_LOCKOUT isn't present.
1357         # It doesn't reset "lockoutTime" = 0 and doesn't
1358         # reset "badPwdCount" = 0.
1359         self._reset_samr(res)
1360
1361         res = self._check_account(userdn,
1362                                   badPwdCount=3, effective_bad_password_count=0,
1363                                   badPasswordTime=badPasswordTime,
1364                                   lockoutTime=lockoutTime,
1365                                   lastLogon=lastLogon,
1366                                   lastLogonTimestamp=lastLogon,
1367                                   userAccountControl=
1368                                     dsdb.UF_NORMAL_ACCOUNT,
1369                                   msDSUserAccountControlComputed=0)
1370
1371     def test_unicodePwd_lockout_with_clear_change_krb5(self):
1372         self._test_unicodePwd_lockout_with_clear_change(self.lockout1krb5_creds,
1373                                                         self.lockout2krb5_ldb)
1374
1375     def test_unicodePwd_lockout_with_clear_change_ntlm(self):
1376         self._test_unicodePwd_lockout_with_clear_change(self.lockout1ntlm_creds,
1377                                                         self.lockout2ntlm_ldb)
1378
1379     def _test_login_lockout(self, creds):
1380         username = creds.get_username()
1381         userpass = creds.get_password()
1382         userdn = "cn=%s,cn=users,%s" % (username, self.base_dn)
1383
1384         use_kerberos = creds.get_kerberos_state()
1385         # This unlocks by waiting for account_lockout_duration
1386         if use_kerberos == MUST_USE_KERBEROS:
1387             lastlogon_relation = 'greater'
1388             print "Performs a lockout attempt against LDAP using Kerberos"
1389         else:
1390             lastlogon_relation = 'equal'
1391             print "Performs a lockout attempt against LDAP using NTLM"
1392
1393         # Change password on a connection as another user
1394         res = self._check_account(userdn,
1395                                   badPwdCount=0,
1396                                   badPasswordTime=("greater", 0),
1397                                   lastLogon=("greater", 0),
1398                                   lastLogonTimestamp=("greater", 0),
1399                                   userAccountControl=
1400                                     dsdb.UF_NORMAL_ACCOUNT,
1401                                   msDSUserAccountControlComputed=0)
1402         badPasswordTime = int(res[0]["badPasswordTime"][0])
1403         lastLogon = int(res[0]["lastLogon"][0])
1404         firstLogon = lastLogon
1405         lastLogonTimestamp = int(res[0]["lastLogonTimestamp"][0])
1406         print firstLogon
1407         print lastLogonTimestamp
1408
1409
1410         self.assertGreater(lastLogon, badPasswordTime)
1411
1412         # Open a second LDB connection with the user credentials. Use the
1413         # command line credentials for informations like the domain, the realm
1414         # and the workstation.
1415         creds_lockout = insta_creds(creds)
1416
1417         # The wrong password
1418         creds_lockout.set_password("thatsAcomplPASS1x")
1419
1420         self.assertLoginFailure(host_url, creds_lockout, lp)
1421
1422         res = self._check_account(userdn,
1423                                   badPwdCount=1,
1424                                   badPasswordTime=("greater", badPasswordTime),
1425                                   lastLogon=lastLogon,
1426                                   lastLogonTimestamp=lastLogonTimestamp,
1427                                   userAccountControl=
1428                                     dsdb.UF_NORMAL_ACCOUNT,
1429                                   msDSUserAccountControlComputed=0,
1430                                   msg='lastlogontimestamp with wrong password')
1431         badPasswordTime = int(res[0]["badPasswordTime"][0])
1432
1433         # Correct old password
1434         creds_lockout.set_password(userpass)
1435
1436         ldb_lockout = SamDB(url=host_url, credentials=creds_lockout, lp=lp)
1437
1438         # lastLogonTimestamp should not change
1439         # lastLogon increases if badPwdCount is non-zero (!)
1440         res = self._check_account(userdn,
1441                                   badPwdCount=0,
1442                                   badPasswordTime=badPasswordTime,
1443                                   lastLogon=('greater', lastLogon),
1444                                   lastLogonTimestamp=lastLogonTimestamp,
1445                                   userAccountControl=
1446                                     dsdb.UF_NORMAL_ACCOUNT,
1447                                   msDSUserAccountControlComputed=0,
1448                                   msg='LLTimestamp is updated to lastlogon')
1449
1450         lastLogon = int(res[0]["lastLogon"][0])
1451         self.assertGreater(lastLogon, badPasswordTime)
1452
1453         # The wrong password
1454         creds_lockout.set_password("thatsAcomplPASS1x")
1455
1456         self.assertLoginFailure(host_url, creds_lockout, lp)
1457
1458         res = self._check_account(userdn,
1459                                   badPwdCount=1,
1460                                   badPasswordTime=("greater", badPasswordTime),
1461                                   lastLogon=lastLogon,
1462                                   lastLogonTimestamp=lastLogonTimestamp,
1463                                   userAccountControl=
1464                                     dsdb.UF_NORMAL_ACCOUNT,
1465                                   msDSUserAccountControlComputed=0)
1466         badPasswordTime = int(res[0]["badPasswordTime"][0])
1467
1468         # The wrong password
1469         creds_lockout.set_password("thatsAcomplPASS1x")
1470
1471         try:
1472             ldb_lockout = SamDB(url=host_url, credentials=creds_lockout, lp=lp)
1473             self.fail()
1474
1475         except LdbError, (num, msg):
1476             self.assertEquals(num, ERR_INVALID_CREDENTIALS)
1477
1478         res = self._check_account(userdn,
1479                                   badPwdCount=2,
1480                                   badPasswordTime=("greater", badPasswordTime),
1481                                   lastLogon=lastLogon,
1482                                   lastLogonTimestamp=lastLogonTimestamp,
1483                                   userAccountControl=
1484                                     dsdb.UF_NORMAL_ACCOUNT,
1485                                   msDSUserAccountControlComputed=0)
1486         badPasswordTime = int(res[0]["badPasswordTime"][0])
1487
1488         print "two failed password change"
1489
1490         # The wrong password
1491         creds_lockout.set_password("thatsAcomplPASS1x")
1492
1493         try:
1494             ldb_lockout = SamDB(url=host_url, credentials=creds_lockout, lp=lp)
1495             self.fail()
1496
1497         except LdbError, (num, msg):
1498             self.assertEquals(num, ERR_INVALID_CREDENTIALS)
1499
1500         res = self._check_account(userdn,
1501                                   badPwdCount=3,
1502                                   badPasswordTime=("greater", badPasswordTime),
1503                                   lastLogon=lastLogon,
1504                                   lastLogonTimestamp=lastLogonTimestamp,
1505                                   lockoutTime=("greater", badPasswordTime),
1506                                   userAccountControl=
1507                                     dsdb.UF_NORMAL_ACCOUNT,
1508                                   msDSUserAccountControlComputed=dsdb.UF_LOCKOUT)
1509         badPasswordTime = int(res[0]["badPasswordTime"][0])
1510         lockoutTime = int(res[0]["lockoutTime"][0])
1511
1512         # The wrong password
1513         creds_lockout.set_password("thatsAcomplPASS1x")
1514         try:
1515             ldb_lockout = SamDB(url=host_url, credentials=creds_lockout, lp=lp)
1516             self.fail()
1517         except LdbError, (num, msg):
1518             self.assertEquals(num, ERR_INVALID_CREDENTIALS)
1519
1520         res = self._check_account(userdn,
1521                                   badPwdCount=3,
1522                                   badPasswordTime=badPasswordTime,
1523                                   lastLogon=lastLogon,
1524                                   lastLogonTimestamp=lastLogonTimestamp,
1525                                   lockoutTime=lockoutTime,
1526                                   userAccountControl=
1527                                     dsdb.UF_NORMAL_ACCOUNT,
1528                                   msDSUserAccountControlComputed=dsdb.UF_LOCKOUT)
1529
1530         # The wrong password
1531         creds_lockout.set_password("thatsAcomplPASS1x")
1532         try:
1533             ldb_lockout = SamDB(url=host_url, credentials=creds_lockout, lp=lp)
1534             self.fail()
1535         except LdbError, (num, msg):
1536             self.assertEquals(num, ERR_INVALID_CREDENTIALS)
1537
1538         res = self._check_account(userdn,
1539                                   badPwdCount=3,
1540                                   badPasswordTime=badPasswordTime,
1541                                   lastLogon=lastLogon,
1542                                   lastLogonTimestamp=lastLogonTimestamp,
1543                                   lockoutTime=lockoutTime,
1544                                   userAccountControl=
1545                                     dsdb.UF_NORMAL_ACCOUNT,
1546                                   msDSUserAccountControlComputed=dsdb.UF_LOCKOUT)
1547
1548         # The correct password, but we are locked out
1549         creds_lockout.set_password(userpass)
1550         try:
1551             ldb_lockout = SamDB(url=host_url, credentials=creds_lockout, lp=lp)
1552             self.fail()
1553         except LdbError, (num, msg):
1554             self.assertEquals(num, ERR_INVALID_CREDENTIALS)
1555
1556         res = self._check_account(userdn,
1557                                   badPwdCount=3,
1558                                   badPasswordTime=badPasswordTime,
1559                                   lastLogon=lastLogon,
1560                                   lastLogonTimestamp=lastLogonTimestamp,
1561                                   lockoutTime=lockoutTime,
1562                                   userAccountControl=
1563                                     dsdb.UF_NORMAL_ACCOUNT,
1564                                   msDSUserAccountControlComputed=dsdb.UF_LOCKOUT)
1565
1566         # wait for the lockout to end
1567         time.sleep(self.account_lockout_duration + 1)
1568         print self.account_lockout_duration + 1
1569
1570         res = self._check_account(userdn,
1571                                   badPwdCount=3, effective_bad_password_count=0,
1572                                   badPasswordTime=badPasswordTime,
1573                                   lockoutTime=lockoutTime,
1574                                   lastLogon=lastLogon,
1575                                   lastLogonTimestamp=lastLogonTimestamp,
1576                                   userAccountControl=
1577                                     dsdb.UF_NORMAL_ACCOUNT,
1578                                   msDSUserAccountControlComputed=0)
1579
1580         lastLogon = int(res[0]["lastLogon"][0])
1581
1582         # The correct password after letting the timeout expire
1583
1584         creds_lockout.set_password(userpass)
1585
1586         creds_lockout2 = insta_creds(creds_lockout)
1587
1588         ldb_lockout = SamDB(url=host_url, credentials=creds_lockout2, lp=lp)
1589         time.sleep(3)
1590
1591         res = self._check_account(userdn,
1592                                   badPwdCount=0,
1593                                   badPasswordTime=badPasswordTime,
1594                                   lastLogon=(lastlogon_relation, lastLogon),
1595                                   lastLogonTimestamp=lastLogonTimestamp,
1596                                   lockoutTime=0,
1597                                   userAccountControl=
1598                                     dsdb.UF_NORMAL_ACCOUNT,
1599                                   msDSUserAccountControlComputed=0,
1600                                   msg="lastLogon is way off")
1601
1602         lastLogon = int(res[0]["lastLogon"][0])
1603
1604         # The wrong password
1605         creds_lockout.set_password("thatsAcomplPASS1x")
1606         try:
1607             ldb_lockout = SamDB(url=host_url, credentials=creds_lockout, lp=lp)
1608             self.fail()
1609         except LdbError, (num, msg):
1610             self.assertEquals(num, ERR_INVALID_CREDENTIALS)
1611
1612         res = self._check_account(userdn,
1613                                   badPwdCount=1,
1614                                   badPasswordTime=("greater", badPasswordTime),
1615                                   lockoutTime=0,
1616                                   lastLogon=lastLogon,
1617                                   lastLogonTimestamp=lastLogonTimestamp,
1618                                   userAccountControl=
1619                                     dsdb.UF_NORMAL_ACCOUNT,
1620                                   msDSUserAccountControlComputed=0)
1621         badPasswordTime = int(res[0]["badPasswordTime"][0])
1622
1623         # The wrong password
1624         creds_lockout.set_password("thatsAcomplPASS1x")
1625         try:
1626             ldb_lockout = SamDB(url=host_url, credentials=creds_lockout, lp=lp)
1627             self.fail()
1628         except LdbError, (num, msg):
1629             self.assertEquals(num, ERR_INVALID_CREDENTIALS)
1630
1631         res = self._check_account(userdn,
1632                                   badPwdCount=2,
1633                                   badPasswordTime=("greater", badPasswordTime),
1634                                   lockoutTime=0,
1635                                   lastLogon=lastLogon,
1636                                   lastLogonTimestamp=lastLogonTimestamp,
1637                                   userAccountControl=
1638                                     dsdb.UF_NORMAL_ACCOUNT,
1639                                   msDSUserAccountControlComputed=0)
1640         badPasswordTime = int(res[0]["badPasswordTime"][0])
1641
1642         time.sleep(self.lockout_observation_window + 1)
1643
1644         res = self._check_account(userdn,
1645                                   badPwdCount=2, effective_bad_password_count=0,
1646                                   badPasswordTime=badPasswordTime,
1647                                   lockoutTime=0,
1648                                   lastLogon=lastLogon,
1649                                   lastLogonTimestamp=lastLogonTimestamp,
1650                                   userAccountControl=
1651                                     dsdb.UF_NORMAL_ACCOUNT,
1652                                   msDSUserAccountControlComputed=0)
1653
1654         # The wrong password
1655         creds_lockout.set_password("thatsAcomplPASS1x")
1656         try:
1657             ldb_lockout = SamDB(url=host_url, credentials=creds_lockout, lp=lp)
1658             self.fail()
1659         except LdbError, (num, msg):
1660             self.assertEquals(num, ERR_INVALID_CREDENTIALS)
1661
1662         res = self._check_account(userdn,
1663                                   badPwdCount=1,
1664                                   badPasswordTime=("greater", badPasswordTime),
1665                                   lockoutTime=0,
1666                                   lastLogon=lastLogon,
1667                                   lastLogonTimestamp=lastLogonTimestamp,
1668                                   userAccountControl=
1669                                     dsdb.UF_NORMAL_ACCOUNT,
1670                                   msDSUserAccountControlComputed=0)
1671         badPasswordTime = int(res[0]["badPasswordTime"][0])
1672
1673         # The correct password without letting the timeout expire
1674         creds_lockout.set_password(userpass)
1675         ldb_lockout = SamDB(url=host_url, credentials=creds_lockout, lp=lp)
1676
1677         res = self._check_account(userdn,
1678                                   badPwdCount=0,
1679                                   badPasswordTime=badPasswordTime,
1680                                   lockoutTime=0,
1681                                   lastLogon=("greater", lastLogon),
1682                                   lastLogonTimestamp=lastLogonTimestamp,
1683                                   userAccountControl=
1684                                     dsdb.UF_NORMAL_ACCOUNT,
1685                                   msDSUserAccountControlComputed=0)
1686
1687
1688     def test_login_lockout_krb5(self):
1689         self._test_login_lockout(self.lockout1krb5_creds)
1690
1691     def test_login_lockout_ntlm(self):
1692         self._test_login_lockout(self.lockout1ntlm_creds)
1693
1694     def _test_multiple_logon(self, creds):
1695         # Test the happy case in which a user logs on correctly, then
1696         # logs on correctly again, so that the bad password and
1697         # lockout times are both zero the second time. The lastlogon
1698         # time should increase.
1699
1700         # Open a second LDB connection with the user credentials. Use the
1701         # command line credentials for informations like the domain, the realm
1702         # and the workstation.
1703         username = creds.get_username()
1704         userdn = "cn=%s,cn=users,%s" % (username, self.base_dn)
1705
1706         use_kerberos = creds.get_kerberos_state()
1707         if use_kerberos == MUST_USE_KERBEROS:
1708             print "Testing multiple logon with Kerberos"
1709             lastlogon_relation = 'greater'
1710         else:
1711             print "Testing multiple logon with NTLM"
1712             lastlogon_relation = 'equal'
1713
1714         SamDB(url=host_url, credentials=insta_creds(creds), lp=lp)
1715
1716         res = self._check_account(userdn,
1717                                   badPwdCount=0,
1718                                   badPasswordTime=("greater", 0),
1719                                   lastLogon=("greater", 0),
1720                                   lastLogonTimestamp=("greater", 0),
1721                                   userAccountControl=
1722                                     dsdb.UF_NORMAL_ACCOUNT,
1723                                   msDSUserAccountControlComputed=0)
1724         badPasswordTime = int(res[0]["badPasswordTime"][0])
1725         lastLogon = int(res[0]["lastLogon"][0])
1726         lastLogonTimestamp = int(res[0]["lastLogonTimestamp"][0])
1727         firstLogon = lastLogon
1728         print "last logon is %d" % lastLogon
1729         self.assertGreater(lastLogon, badPasswordTime)
1730
1731         time.sleep(1)
1732         SamDB(url=host_url, credentials=insta_creds(creds), lp=lp)
1733
1734         res = self._check_account(userdn,
1735                                   badPwdCount=0,
1736                                   badPasswordTime=badPasswordTime,
1737                                   lastLogon=(lastlogon_relation, lastLogon),
1738                                   lastLogonTimestamp=lastLogonTimestamp,
1739                                   userAccountControl=
1740                                   dsdb.UF_NORMAL_ACCOUNT,
1741                                   msDSUserAccountControlComputed=0,
1742                                   msg=("second logon, firstlogon was %s" %
1743                                        firstLogon))
1744
1745
1746         lastLogon = int(res[0]["lastLogon"][0])
1747
1748         time.sleep(1)
1749
1750         SamDB(url=host_url, credentials=insta_creds(creds), lp=lp)
1751
1752         res = self._check_account(userdn,
1753                                   badPwdCount=0,
1754                                   badPasswordTime=badPasswordTime,
1755                                   lastLogon=(lastlogon_relation, lastLogon),
1756                                   lastLogonTimestamp=lastLogonTimestamp,
1757                                   userAccountControl=
1758                                     dsdb.UF_NORMAL_ACCOUNT,
1759                                   msDSUserAccountControlComputed=0)
1760
1761     def test_multiple_logon_krb5(self):
1762         self._test_multiple_logon(self.lockout1krb5_creds)
1763
1764     def test_multiple_logon_ntlm(self):
1765         self._test_multiple_logon(self.lockout1ntlm_creds)
1766
1767
1768     def tearDown(self):
1769         super(PasswordTests, self).tearDown()
1770
1771 host_url = "ldap://%s" % host
1772
1773 TestProgram(module=__name__, opts=subunitopts)