eb34c7fe52cd34769c1da0409bcaf16dbd7b9640
[nivanova/samba-autobuild/.git] / source4 / dsdb / tests / python / password_lockout_base.py
1 from __future__ import print_function
2 import samba
3
4 from samba.auth import system_session
5 from samba.credentials import Credentials, DONT_USE_KERBEROS, MUST_USE_KERBEROS
6 from ldb import SCOPE_BASE, LdbError
7 from ldb import ERR_CONSTRAINT_VIOLATION
8 from ldb import ERR_INVALID_CREDENTIALS
9 from ldb import Message, MessageElement, Dn
10 from ldb import FLAG_MOD_REPLACE
11 from samba import gensec, dsdb
12 from samba.samdb import SamDB
13 import samba.tests
14 from samba.tests import delete_force
15 from samba.dcerpc import security, samr
16 from samba.ndr import ndr_unpack
17 from samba.tests.password_test import PasswordTestCase
18
19 import time
20
21 class BasePasswordTestCase(PasswordTestCase):
22     def _open_samr_user(self, res):
23         self.assertTrue("objectSid" in res[0])
24
25         (domain_sid, rid) = ndr_unpack(security.dom_sid, res[0]["objectSid"][0]).split()
26         self.assertEquals(self.domain_sid, domain_sid)
27
28         return self.samr.OpenUser(self.samr_domain, security.SEC_FLAG_MAXIMUM_ALLOWED, rid)
29
30     def _check_attribute(self, res, name, value):
31         if value is None:
32             self.assertTrue(name not in res[0],
33                             msg="attr[%s]=%r on dn[%s]" %
34                             (name, res[0], res[0].dn))
35             return
36
37         if isinstance(value, tuple):
38             (mode, value) = value
39         else:
40             mode = "equal"
41
42         if mode == "ignore":
43             return
44
45         if mode == "absent":
46             self.assertFalse(name in res[0],
47                              msg="attr[%s] not missing on dn[%s]" %
48                              (name, res[0].dn))
49             return
50
51         self.assertTrue(name in res[0],
52                         msg="attr[%s] missing on dn[%s]" %
53                         (name, res[0].dn))
54         self.assertTrue(len(res[0][name]) == 1,
55                         msg="attr[%s]=%r on dn[%s]" %
56                         (name, res[0][name], res[0].dn))
57
58
59         print("%s = '%s'" % (name, res[0][name][0]))
60
61         if mode == "present":
62             return
63
64         if mode == "equal":
65             v = int(res[0][name][0])
66             value = int(value)
67             msg = ("attr[%s]=[%s] != [%s] on dn[%s]\n"
68                    "(diff %d; actual value is %s than expected)"  %
69                    (name, v, value, res[0].dn, v - value,
70                     ('less' if v < value else 'greater')))
71
72             self.assertTrue(v == value, msg)
73             return
74
75         if mode == "greater":
76             v = int(res[0][name][0])
77             self.assertTrue(v > int(value),
78                             msg="attr[%s]=[%s] <= [%s] on dn[%s] (diff %d)" %
79                             (name, v, int(value), res[0].dn, v - int(value)))
80             return
81         if mode == "less":
82             v = int(res[0][name][0])
83             self.assertTrue(v < int(value),
84                             msg="attr[%s]=[%s] >= [%s] on dn[%s] (diff %d)" %
85                             (name, v, int(value), res[0].dn, v - int(value)))
86             return
87         self.assertEqual(mode, not mode, "Invalid Mode[%s]" % mode)
88
89     def _check_account_initial(self, userdn):
90         self._check_account(userdn,
91                             badPwdCount=0,
92                             badPasswordTime=0,
93                             logonCount=0,
94                             lastLogon=0,
95                             lastLogonTimestamp=("absent", None),
96                             userAccountControl=
97                             dsdb.UF_NORMAL_ACCOUNT,
98                             msDSUserAccountControlComputed=0)
99
100     def _check_account(self, dn,
101                        badPwdCount=None,
102                        badPasswordTime=None,
103                        logonCount=None,
104                        lastLogon=None,
105                        lastLogonTimestamp=None,
106                        lockoutTime=None,
107                        userAccountControl=None,
108                        msDSUserAccountControlComputed=None,
109                        effective_bad_password_count=None,
110                        msg=None,
111                        badPwdCountOnly=False):
112         print('-=' * 36)
113         if msg is not None:
114             print("\033[01;32m %s \033[00m\n" % msg)
115         attrs = [
116             "objectSid",
117            "badPwdCount",
118            "badPasswordTime",
119            "lastLogon",
120            "lastLogonTimestamp",
121            "logonCount",
122            "lockoutTime",
123            "userAccountControl",
124            "msDS-User-Account-Control-Computed"
125         ]
126
127         # in order to prevent some time resolution problems we sleep for
128         # 10 micro second
129         time.sleep(0.01)
130
131         res = self.ldb.search(dn, scope=SCOPE_BASE, attrs=attrs)
132         self.assertTrue(len(res) == 1)
133         self._check_attribute(res, "badPwdCount", badPwdCount)
134         self._check_attribute(res, "lockoutTime", lockoutTime)
135         self._check_attribute(res, "badPasswordTime", badPasswordTime)
136         if not badPwdCountOnly:
137             self._check_attribute(res, "logonCount", logonCount)
138             self._check_attribute(res, "lastLogon", lastLogon)
139             self._check_attribute(res, "lastLogonTimestamp", lastLogonTimestamp)
140             self._check_attribute(res, "userAccountControl", userAccountControl)
141             self._check_attribute(res, "msDS-User-Account-Control-Computed",
142                                   msDSUserAccountControlComputed)
143
144             lastLogon = int(res[0]["lastLogon"][0])
145             logonCount = int(res[0]["logonCount"][0])
146
147         samr_user = self._open_samr_user(res)
148         uinfo3 = self.samr.QueryUserInfo(samr_user, 3)
149         uinfo5 = self.samr.QueryUserInfo(samr_user, 5)
150         uinfo16 = self.samr.QueryUserInfo(samr_user, 16)
151         uinfo21 = self.samr.QueryUserInfo(samr_user, 21)
152         self.samr.Close(samr_user)
153
154         expected_acb_info = 0
155         if not badPwdCountOnly:
156             if userAccountControl & dsdb.UF_NORMAL_ACCOUNT:
157                 expected_acb_info |= samr.ACB_NORMAL
158             if userAccountControl & dsdb.UF_ACCOUNTDISABLE:
159                 expected_acb_info |= samr.ACB_DISABLED
160             if userAccountControl & dsdb.UF_PASSWD_NOTREQD:
161                 expected_acb_info |= samr.ACB_PWNOTREQ
162             if msDSUserAccountControlComputed & dsdb.UF_LOCKOUT:
163                 expected_acb_info |= samr.ACB_AUTOLOCK
164             if msDSUserAccountControlComputed & dsdb.UF_PASSWORD_EXPIRED:
165                 expected_acb_info |= samr.ACB_PW_EXPIRED
166
167             self.assertEquals(uinfo3.acct_flags, expected_acb_info)
168             self.assertEquals(uinfo3.last_logon, lastLogon)
169             self.assertEquals(uinfo3.logon_count, logonCount)
170
171         expected_bad_password_count = 0
172         if badPwdCount is not None:
173             expected_bad_password_count = badPwdCount
174         if effective_bad_password_count is None:
175             effective_bad_password_count = expected_bad_password_count
176
177         self.assertEquals(uinfo3.bad_password_count, expected_bad_password_count)
178
179         if not badPwdCountOnly:
180             self.assertEquals(uinfo5.acct_flags, expected_acb_info)
181             self.assertEquals(uinfo5.bad_password_count, effective_bad_password_count)
182             self.assertEquals(uinfo5.last_logon, lastLogon)
183             self.assertEquals(uinfo5.logon_count, logonCount)
184
185             self.assertEquals(uinfo16.acct_flags, expected_acb_info)
186
187             self.assertEquals(uinfo21.acct_flags, expected_acb_info)
188             self.assertEquals(uinfo21.bad_password_count, effective_bad_password_count)
189             self.assertEquals(uinfo21.last_logon, lastLogon)
190             self.assertEquals(uinfo21.logon_count, logonCount)
191
192
193         # check LDAP again and make sure the samr.QueryUserInfo
194         # doesn't have any impact.
195         res2 = self.ldb.search(dn, scope=SCOPE_BASE, attrs=attrs)
196         self.assertEquals(res[0], res2[0])
197
198         # in order to prevent some time resolution problems we sleep for
199         # 10 micro second
200         time.sleep(0.01)
201         return res
202
203     def update_lockout_settings(self, threshold, duration, observation_window):
204         """Updates the global user lockout settings"""
205         m = Message()
206         m.dn = Dn(self.ldb, self.base_dn)
207         account_lockout_duration_ticks = -int(duration * (1e7))
208         m["lockoutDuration"] = MessageElement(str(account_lockout_duration_ticks),
209                                               FLAG_MOD_REPLACE, "lockoutDuration")
210         m["lockoutThreshold"] = MessageElement(str(threshold),
211                                                FLAG_MOD_REPLACE, "lockoutThreshold")
212         lockout_observation_window_ticks = -int(observation_window * (1e7))
213         m["lockOutObservationWindow"] = MessageElement(str(lockout_observation_window_ticks),
214                                                        FLAG_MOD_REPLACE, "lockOutObservationWindow")
215         self.ldb.modify(m)
216
217     def _readd_user(self, creds, lockOutObservationWindow=0):
218         username = creds.get_username()
219         userpass = creds.get_password()
220         userdn = "cn=%s,cn=users,%s" % (username, self.base_dn)
221
222         delete_force(self.ldb, userdn)
223         self.ldb.add({
224              "dn": userdn,
225              "objectclass": "user",
226              "sAMAccountName": username})
227
228         self.addCleanup(delete_force, self.ldb, userdn)
229
230         # Sets the initial user password with a "special" password change
231         # I think that this internally is a password set operation and it can
232         # only be performed by someone which has password set privileges on the
233         # account (at least in s4 we do handle it like that).
234         self.ldb.modify_ldif("""
235 dn: """ + userdn + """
236 changetype: modify
237 delete: userPassword
238 add: userPassword
239 userPassword: """ + userpass + """
240 """)
241         # Enables the user account
242         self.ldb.enable_account("(sAMAccountName=%s)" % username)
243
244         use_kerberos = creds.get_kerberos_state()
245         fail_creds = self.insta_creds(self.template_creds,
246                                       username=username,
247                                       userpass=userpass + "X",
248                                       kerberos_state=use_kerberos)
249         self._check_account_initial(userdn)
250
251         # Fail once to get a badPasswordTime
252         try:
253             ldb = SamDB(url=self.host_url, credentials=fail_creds, lp=self.lp)
254             self.fail()
255         except LdbError as e:
256             (num, msg) = e.args
257             self.assertEquals(num, ERR_INVALID_CREDENTIALS)
258
259         # Succeed to reset everything to 0
260         ldb = SamDB(url=self.host_url, credentials=creds, lp=self.lp)
261
262         return ldb
263
264     def assertLoginFailure(self, url, creds, lp, errno=ERR_INVALID_CREDENTIALS):
265         try:
266             ldb = SamDB(url=url, credentials=creds, lp=lp)
267             self.fail("Login unexpectedly succeeded")
268         except LdbError as e1:
269             (num, msg) = e1.args
270             if errno is not None:
271                 self.assertEquals(num, errno, ("Login failed in the wrong way"
272                                                "(got err %d, expected %d)" %
273                                                (num, errno)))
274
275     def setUp(self):
276         super(BasePasswordTestCase, self).setUp()
277
278         self.global_creds.set_gensec_features(self.global_creds.get_gensec_features() |
279                                               gensec.FEATURE_SEAL)
280
281         self.template_creds = Credentials()
282         self.template_creds.set_username("testuser")
283         self.template_creds.set_password("thatsAcomplPASS1")
284         self.template_creds.set_domain(self.global_creds.get_domain())
285         self.template_creds.set_realm(self.global_creds.get_realm())
286         self.template_creds.set_workstation(self.global_creds.get_workstation())
287         self.template_creds.set_gensec_features(self.global_creds.get_gensec_features())
288         self.template_creds.set_kerberos_state(self.global_creds.get_kerberos_state())
289
290         # Gets back the basedn
291         base_dn = self.ldb.domain_dn()
292
293         # Gets back the configuration basedn
294         configuration_dn = self.ldb.get_config_basedn().get_linearized()
295
296         res = self.ldb.search(base_dn,
297                               scope=SCOPE_BASE, attrs=["lockoutDuration", "lockOutObservationWindow", "lockoutThreshold"])
298
299         if "lockoutDuration" in res[0]:
300             lockoutDuration = res[0]["lockoutDuration"][0]
301         else:
302             lockoutDuration = 0
303
304         if "lockoutObservationWindow" in res[0]:
305             lockoutObservationWindow = res[0]["lockoutObservationWindow"][0]
306         else:
307             lockoutObservationWindow = 0
308
309         if "lockoutThreshold" in res[0]:
310             lockoutThreshold = res[0]["lockoutThreshold"][0]
311         else:
312             lockoutTreshold = 0
313
314         self.addCleanup(self.ldb.modify_ldif, """
315 dn: """ + base_dn + """
316 changetype: modify
317 replace: lockoutDuration
318 lockoutDuration: """ + str(lockoutDuration) + """
319 replace: lockoutObservationWindow
320 lockoutObservationWindow: """ + str(lockoutObservationWindow) + """
321 replace: lockoutThreshold
322 lockoutThreshold: """ + str(lockoutThreshold) + """
323 """)
324
325         self.base_dn = self.ldb.domain_dn()
326         self.account_lockout_duration = 3
327         self.lockout_observation_window = 3
328         self.update_lockout_settings(threshold=3,
329                                      duration=self.account_lockout_duration,
330                                      observation_window=self.lockout_observation_window)
331
332         # update DC to allow password changes for the duration of this test
333         self.allow_password_changes()
334
335         self.domain_sid = security.dom_sid(self.ldb.get_domain_sid())
336         self.samr = samr.samr("ncacn_ip_tcp:%s[seal]" % self.host, self.lp, self.global_creds)
337         self.samr_handle = self.samr.Connect2(None, security.SEC_FLAG_MAXIMUM_ALLOWED)
338         self.samr_domain = self.samr.OpenDomain(self.samr_handle, security.SEC_FLAG_MAXIMUM_ALLOWED, self.domain_sid)
339
340         self.addCleanup(self.delete_ldb_connections)
341
342         # (Re)adds the test user accounts
343         self.lockout1krb5_creds = self.insta_creds(self.template_creds,
344                                                    username="lockout1krb5",
345                                                    userpass="thatsAcomplPASS0",
346                                                    kerberos_state=MUST_USE_KERBEROS)
347         self.lockout1krb5_ldb = self._readd_user(self.lockout1krb5_creds)
348         self.lockout1ntlm_creds = self.insta_creds(self.template_creds,
349                                                    username="lockout1ntlm",
350                                                    userpass="thatsAcomplPASS0",
351                                                    kerberos_state=DONT_USE_KERBEROS)
352         self.lockout1ntlm_ldb = self._readd_user(self.lockout1ntlm_creds)
353
354     def delete_ldb_connections(self):
355         del self.lockout1krb5_ldb
356         del self.lockout1ntlm_ldb
357         del self.ldb
358
359     def tearDown(self):
360         super(BasePasswordTestCase, self).tearDown()
361
362     def _test_login_lockout(self, creds):
363         username = creds.get_username()
364         userpass = creds.get_password()
365         userdn = "cn=%s,cn=users,%s" % (username, self.base_dn)
366
367         use_kerberos = creds.get_kerberos_state()
368         # This unlocks by waiting for account_lockout_duration
369         if use_kerberos == MUST_USE_KERBEROS:
370             logoncount_relation = 'greater'
371             lastlogon_relation = 'greater'
372             print("Performs a lockout attempt against LDAP using Kerberos")
373         else:
374             logoncount_relation = 'equal'
375             lastlogon_relation = 'equal'
376             print("Performs a lockout attempt against LDAP using NTLM")
377
378         # Change password on a connection as another user
379         res = self._check_account(userdn,
380                                   badPwdCount=0,
381                                   badPasswordTime=("greater", 0),
382                                   logonCount=(logoncount_relation, 0),
383                                   lastLogon=("greater", 0),
384                                   lastLogonTimestamp=("greater", 0),
385                                   userAccountControl=
386                                   dsdb.UF_NORMAL_ACCOUNT,
387                                   msDSUserAccountControlComputed=0)
388         badPasswordTime = int(res[0]["badPasswordTime"][0])
389         logonCount = int(res[0]["logonCount"][0])
390         lastLogon = int(res[0]["lastLogon"][0])
391         firstLogon = lastLogon
392         lastLogonTimestamp = int(res[0]["lastLogonTimestamp"][0])
393         print(firstLogon)
394         print(lastLogonTimestamp)
395
396
397         self.assertGreater(lastLogon, badPasswordTime)
398         self.assertGreaterEqual(lastLogon, lastLogonTimestamp)
399
400         # Open a second LDB connection with the user credentials. Use the
401         # command line credentials for informations like the domain, the realm
402         # and the workstation.
403         creds_lockout = self.insta_creds(creds)
404
405         # The wrong password
406         creds_lockout.set_password("thatsAcomplPASS1x")
407
408         self.assertLoginFailure(self.host_url, creds_lockout, self.lp)
409
410         res = self._check_account(userdn,
411                                   badPwdCount=1,
412                                   badPasswordTime=("greater", badPasswordTime),
413                                   logonCount=logonCount,
414                                   lastLogon=lastLogon,
415                                   lastLogonTimestamp=lastLogonTimestamp,
416                                   userAccountControl=
417                                   dsdb.UF_NORMAL_ACCOUNT,
418                                   msDSUserAccountControlComputed=0,
419                                   msg='lastlogontimestamp with wrong password')
420         badPasswordTime = int(res[0]["badPasswordTime"][0])
421
422         # Correct old password
423         creds_lockout.set_password(userpass)
424
425         ldb_lockout = SamDB(url=self.host_url, credentials=creds_lockout, lp=self.lp)
426
427         # lastLogonTimestamp should not change
428         # lastLogon increases if badPwdCount is non-zero (!)
429         res = self._check_account(userdn,
430                                   badPwdCount=0,
431                                   badPasswordTime=badPasswordTime,
432                                   logonCount=(logoncount_relation, logonCount),
433                                   lastLogon=('greater', lastLogon),
434                                   lastLogonTimestamp=lastLogonTimestamp,
435                                   userAccountControl=
436                                   dsdb.UF_NORMAL_ACCOUNT,
437                                   msDSUserAccountControlComputed=0,
438                                   msg='LLTimestamp is updated to lastlogon')
439
440         logonCount = int(res[0]["logonCount"][0])
441         lastLogon = int(res[0]["lastLogon"][0])
442         self.assertGreater(lastLogon, badPasswordTime)
443         self.assertGreaterEqual(lastLogon, lastLogonTimestamp)
444
445         # The wrong password
446         creds_lockout.set_password("thatsAcomplPASS1x")
447
448         self.assertLoginFailure(self.host_url, creds_lockout, self.lp)
449
450         res = self._check_account(userdn,
451                                   badPwdCount=1,
452                                   badPasswordTime=("greater", badPasswordTime),
453                                   logonCount=logonCount,
454                                   lastLogon=lastLogon,
455                                   lastLogonTimestamp=lastLogonTimestamp,
456                                   userAccountControl=
457                                   dsdb.UF_NORMAL_ACCOUNT,
458                                   msDSUserAccountControlComputed=0)
459         badPasswordTime = int(res[0]["badPasswordTime"][0])
460
461         # The wrong password
462         creds_lockout.set_password("thatsAcomplPASS1x")
463
464         try:
465             ldb_lockout = SamDB(url=self.host_url, credentials=creds_lockout, lp=self.lp)
466             self.fail()
467
468         except LdbError as e2:
469             (num, msg) = e2.args
470             self.assertEquals(num, ERR_INVALID_CREDENTIALS)
471
472         res = self._check_account(userdn,
473                                   badPwdCount=2,
474                                   badPasswordTime=("greater", badPasswordTime),
475                                   logonCount=logonCount,
476                                   lastLogon=lastLogon,
477                                   lastLogonTimestamp=lastLogonTimestamp,
478                                   userAccountControl=
479                                   dsdb.UF_NORMAL_ACCOUNT,
480                                   msDSUserAccountControlComputed=0)
481         badPasswordTime = int(res[0]["badPasswordTime"][0])
482
483         print("two failed password change")
484
485         # The wrong password
486         creds_lockout.set_password("thatsAcomplPASS1x")
487
488         try:
489             ldb_lockout = SamDB(url=self.host_url, credentials=creds_lockout, lp=self.lp)
490             self.fail()
491
492         except LdbError as e3:
493             (num, msg) = e3.args
494             self.assertEquals(num, ERR_INVALID_CREDENTIALS)
495
496         res = self._check_account(userdn,
497                                   badPwdCount=3,
498                                   badPasswordTime=("greater", badPasswordTime),
499                                   logonCount=logonCount,
500                                   lastLogon=lastLogon,
501                                   lastLogonTimestamp=lastLogonTimestamp,
502                                   lockoutTime=("greater", badPasswordTime),
503                                   userAccountControl=
504                                   dsdb.UF_NORMAL_ACCOUNT,
505                                   msDSUserAccountControlComputed=dsdb.UF_LOCKOUT)
506         badPasswordTime = int(res[0]["badPasswordTime"][0])
507         lockoutTime = int(res[0]["lockoutTime"][0])
508
509         # The wrong password
510         creds_lockout.set_password("thatsAcomplPASS1x")
511         try:
512             ldb_lockout = SamDB(url=self.host_url, credentials=creds_lockout, lp=self.lp)
513             self.fail()
514         except LdbError as e4:
515             (num, msg) = e4.args
516             self.assertEquals(num, ERR_INVALID_CREDENTIALS)
517
518         res = self._check_account(userdn,
519                                   badPwdCount=3,
520                                   badPasswordTime=badPasswordTime,
521                                   logonCount=logonCount,
522                                   lastLogon=lastLogon,
523                                   lastLogonTimestamp=lastLogonTimestamp,
524                                   lockoutTime=lockoutTime,
525                                   userAccountControl=
526                                   dsdb.UF_NORMAL_ACCOUNT,
527                                   msDSUserAccountControlComputed=dsdb.UF_LOCKOUT)
528
529         # The wrong password
530         creds_lockout.set_password("thatsAcomplPASS1x")
531         try:
532             ldb_lockout = SamDB(url=self.host_url, credentials=creds_lockout, lp=self.lp)
533             self.fail()
534         except LdbError as e5:
535             (num, msg) = e5.args
536             self.assertEquals(num, ERR_INVALID_CREDENTIALS)
537
538         res = self._check_account(userdn,
539                                   badPwdCount=3,
540                                   badPasswordTime=badPasswordTime,
541                                   logonCount=logonCount,
542                                   lastLogon=lastLogon,
543                                   lastLogonTimestamp=lastLogonTimestamp,
544                                   lockoutTime=lockoutTime,
545                                   userAccountControl=
546                                   dsdb.UF_NORMAL_ACCOUNT,
547                                   msDSUserAccountControlComputed=dsdb.UF_LOCKOUT)
548
549         # The correct password, but we are locked out
550         creds_lockout.set_password(userpass)
551         try:
552             ldb_lockout = SamDB(url=self.host_url, credentials=creds_lockout, lp=self.lp)
553             self.fail()
554         except LdbError as e6:
555             (num, msg) = e6.args
556             self.assertEquals(num, ERR_INVALID_CREDENTIALS)
557
558         res = self._check_account(userdn,
559                                   badPwdCount=3,
560                                   badPasswordTime=badPasswordTime,
561                                   logonCount=logonCount,
562                                   lastLogon=lastLogon,
563                                   lastLogonTimestamp=lastLogonTimestamp,
564                                   lockoutTime=lockoutTime,
565                                   userAccountControl=
566                                   dsdb.UF_NORMAL_ACCOUNT,
567                                   msDSUserAccountControlComputed=dsdb.UF_LOCKOUT)
568
569         # wait for the lockout to end
570         time.sleep(self.account_lockout_duration + 1)
571         print(self.account_lockout_duration + 1)
572
573         res = self._check_account(userdn,
574                                   badPwdCount=3, effective_bad_password_count=0,
575                                   badPasswordTime=badPasswordTime,
576                                   logonCount=logonCount,
577                                   lockoutTime=lockoutTime,
578                                   lastLogon=lastLogon,
579                                   lastLogonTimestamp=lastLogonTimestamp,
580                                   userAccountControl=
581                                   dsdb.UF_NORMAL_ACCOUNT,
582                                   msDSUserAccountControlComputed=0)
583
584         # The correct password after letting the timeout expire
585
586         creds_lockout.set_password(userpass)
587
588         creds_lockout2 = self.insta_creds(creds_lockout)
589
590         ldb_lockout = SamDB(url=self.host_url, credentials=creds_lockout2, lp=self.lp)
591         time.sleep(3)
592
593         res = self._check_account(userdn,
594                                   badPwdCount=0,
595                                   badPasswordTime=badPasswordTime,
596                                   logonCount=(logoncount_relation, logonCount),
597                                   lastLogon=(lastlogon_relation, lastLogon),
598                                   lastLogonTimestamp=lastLogonTimestamp,
599                                   lockoutTime=0,
600                                   userAccountControl=
601                                   dsdb.UF_NORMAL_ACCOUNT,
602                                   msDSUserAccountControlComputed=0,
603                                   msg="lastLogon is way off")
604
605         logonCount = int(res[0]["logonCount"][0])
606         lastLogon = int(res[0]["lastLogon"][0])
607
608         # The wrong password
609         creds_lockout.set_password("thatsAcomplPASS1x")
610         try:
611             ldb_lockout = SamDB(url=self.host_url, credentials=creds_lockout, lp=self.lp)
612             self.fail()
613         except LdbError as e7:
614             (num, msg) = e7.args
615             self.assertEquals(num, ERR_INVALID_CREDENTIALS)
616
617         res = self._check_account(userdn,
618                                   badPwdCount=1,
619                                   badPasswordTime=("greater", badPasswordTime),
620                                   logonCount=logonCount,
621                                   lockoutTime=0,
622                                   lastLogon=lastLogon,
623                                   lastLogonTimestamp=lastLogonTimestamp,
624                                   userAccountControl=
625                                   dsdb.UF_NORMAL_ACCOUNT,
626                                   msDSUserAccountControlComputed=0)
627         badPasswordTime = int(res[0]["badPasswordTime"][0])
628
629         # The wrong password
630         creds_lockout.set_password("thatsAcomplPASS1x")
631         try:
632             ldb_lockout = SamDB(url=self.host_url, credentials=creds_lockout, lp=self.lp)
633             self.fail()
634         except LdbError as e8:
635             (num, msg) = e8.args
636             self.assertEquals(num, ERR_INVALID_CREDENTIALS)
637
638         res = self._check_account(userdn,
639                                   badPwdCount=2,
640                                   badPasswordTime=("greater", badPasswordTime),
641                                   logonCount=logonCount,
642                                   lockoutTime=0,
643                                   lastLogon=lastLogon,
644                                   lastLogonTimestamp=lastLogonTimestamp,
645                                   userAccountControl=
646                                   dsdb.UF_NORMAL_ACCOUNT,
647                                   msDSUserAccountControlComputed=0)
648         badPasswordTime = int(res[0]["badPasswordTime"][0])
649
650         time.sleep(self.lockout_observation_window + 1)
651
652         res = self._check_account(userdn,
653                                   badPwdCount=2, effective_bad_password_count=0,
654                                   badPasswordTime=badPasswordTime,
655                                   logonCount=logonCount,
656                                   lockoutTime=0,
657                                   lastLogon=lastLogon,
658                                   lastLogonTimestamp=lastLogonTimestamp,
659                                   userAccountControl=
660                                   dsdb.UF_NORMAL_ACCOUNT,
661                                   msDSUserAccountControlComputed=0)
662
663         # The wrong password
664         creds_lockout.set_password("thatsAcomplPASS1x")
665         try:
666             ldb_lockout = SamDB(url=self.host_url, credentials=creds_lockout, lp=self.lp)
667             self.fail()
668         except LdbError as e9:
669             (num, msg) = e9.args
670             self.assertEquals(num, ERR_INVALID_CREDENTIALS)
671
672         res = self._check_account(userdn,
673                                   badPwdCount=1,
674                                   badPasswordTime=("greater", badPasswordTime),
675                                   logonCount=logonCount,
676                                   lockoutTime=0,
677                                   lastLogon=lastLogon,
678                                   lastLogonTimestamp=lastLogonTimestamp,
679                                   userAccountControl=
680                                   dsdb.UF_NORMAL_ACCOUNT,
681                                   msDSUserAccountControlComputed=0)
682         badPasswordTime = int(res[0]["badPasswordTime"][0])
683
684         # The correct password without letting the timeout expire
685         creds_lockout.set_password(userpass)
686         ldb_lockout = SamDB(url=self.host_url, credentials=creds_lockout, lp=self.lp)
687
688         res = self._check_account(userdn,
689                                   badPwdCount=0,
690                                   badPasswordTime=badPasswordTime,
691                                   logonCount=(logoncount_relation, logonCount),
692                                   lockoutTime=0,
693                                   lastLogon=("greater", lastLogon),
694                                   lastLogonTimestamp=lastLogonTimestamp,
695                                   userAccountControl=
696                                   dsdb.UF_NORMAL_ACCOUNT,
697                                   msDSUserAccountControlComputed=0)
698
699     def _test_multiple_logon(self, creds):
700         # Test the happy case in which a user logs on correctly, then
701         # logs on correctly again, so that the bad password and
702         # lockout times are both zero the second time. The lastlogon
703         # time should increase.
704
705         # Open a second LDB connection with the user credentials. Use the
706         # command line credentials for informations like the domain, the realm
707         # and the workstation.
708         username = creds.get_username()
709         userdn = "cn=%s,cn=users,%s" % (username, self.base_dn)
710
711         use_kerberos = creds.get_kerberos_state()
712         if use_kerberos == MUST_USE_KERBEROS:
713             print("Testing multiple logon with Kerberos")
714             logoncount_relation = 'greater'
715             lastlogon_relation = 'greater'
716         else:
717             print("Testing multiple logon with NTLM")
718             logoncount_relation = 'equal'
719             lastlogon_relation = 'equal'
720
721         SamDB(url=self.host_url, credentials=self.insta_creds(creds), lp=self.lp)
722
723         res = self._check_account(userdn,
724                                   badPwdCount=0,
725                                   badPasswordTime=("greater", 0),
726                                   logonCount=(logoncount_relation, 0),
727                                   lastLogon=("greater", 0),
728                                   lastLogonTimestamp=("greater", 0),
729                                   userAccountControl=
730                                   dsdb.UF_NORMAL_ACCOUNT,
731                                   msDSUserAccountControlComputed=0)
732         badPasswordTime = int(res[0]["badPasswordTime"][0])
733         logonCount = int(res[0]["logonCount"][0])
734         lastLogon = int(res[0]["lastLogon"][0])
735         lastLogonTimestamp = int(res[0]["lastLogonTimestamp"][0])
736         firstLogon = lastLogon
737         print("last logon is %d" % lastLogon)
738         self.assertGreater(lastLogon, badPasswordTime)
739         self.assertGreaterEqual(lastLogon, lastLogonTimestamp)
740
741         time.sleep(1)
742         SamDB(url=self.host_url, credentials=self.insta_creds(creds), lp=self.lp)
743
744         res = self._check_account(userdn,
745                                   badPwdCount=0,
746                                   badPasswordTime=badPasswordTime,
747                                   logonCount=(logoncount_relation, logonCount),
748                                   lastLogon=(lastlogon_relation, lastLogon),
749                                   lastLogonTimestamp=lastLogonTimestamp,
750                                   userAccountControl=
751                                   dsdb.UF_NORMAL_ACCOUNT,
752                                   msDSUserAccountControlComputed=0,
753                                   msg=("second logon, firstlogon was %s" %
754                                        firstLogon))
755
756
757         lastLogon = int(res[0]["lastLogon"][0])
758
759         time.sleep(1)
760
761         SamDB(url=self.host_url, credentials=self.insta_creds(creds), lp=self.lp)
762
763         res = self._check_account(userdn,
764                                   badPwdCount=0,
765                                   badPasswordTime=badPasswordTime,
766                                   logonCount=(logoncount_relation, logonCount),
767                                   lastLogon=(lastlogon_relation, lastLogon),
768                                   lastLogonTimestamp=lastLogonTimestamp,
769                                   userAccountControl=
770                                   dsdb.UF_NORMAL_ACCOUNT,
771                                   msDSUserAccountControlComputed=0)