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