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