247ed1681bae9ba98ea6827eb8399afbe6701406
[ira/wip.git] / source4 / dsdb / tests / python / passwords.py
1 #!/usr/bin/env python
2 # -*- coding: utf-8 -*-
3 # This tests the password changes over LDAP for AD implementations
4 #
5 # Copyright Matthias Dieter Wallnoefer 2010
6 #
7 # Notice: This tests will also work against Windows Server if the connection is
8 # secured enough (SASL with a minimum of 128 Bit encryption) - consider
9 # MS-ADTS 3.1.1.3.1.5
10
11 import optparse
12 import sys
13 import base64
14 import os
15
16 sys.path.append("bin/python")
17 import samba
18 samba.ensure_external_module("subunit", "subunit/python")
19 samba.ensure_external_module("testtools", "testtools")
20
21 import samba.getopt as options
22
23 from samba.auth import system_session
24 from samba.credentials import Credentials
25 from ldb import SCOPE_BASE, LdbError
26 from ldb import ERR_NO_SUCH_OBJECT, ERR_ATTRIBUTE_OR_VALUE_EXISTS
27 from ldb import ERR_UNWILLING_TO_PERFORM, ERR_INSUFFICIENT_ACCESS_RIGHTS
28 from ldb import ERR_NO_SUCH_ATTRIBUTE
29 from ldb import ERR_CONSTRAINT_VIOLATION
30 from ldb import Message, MessageElement, Dn
31 from ldb import FLAG_MOD_REPLACE, FLAG_MOD_DELETE
32 from samba import gensec
33 from samba.samdb import SamDB
34 import samba.tests
35 from subunit.run import SubunitTestRunner
36 import unittest
37
38 parser = optparse.OptionParser("passwords [options] <host>")
39 sambaopts = options.SambaOptions(parser)
40 parser.add_option_group(sambaopts)
41 parser.add_option_group(options.VersionOptions(parser))
42 # use command line creds if available
43 credopts = options.CredentialsOptions(parser)
44 parser.add_option_group(credopts)
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 creds = credopts.get_credentials(lp)
55
56 # Force an encrypted connection
57 creds.set_gensec_features(creds.get_gensec_features() | gensec.FEATURE_SEAL)
58
59 #
60 # Tests start here
61 #
62
63 class PasswordTests(samba.tests.TestCase):
64
65     def delete_force(self, ldb, dn):
66         try:
67             ldb.delete(dn)
68         except LdbError, (num, _):
69             self.assertEquals(num, ERR_NO_SUCH_OBJECT)
70
71     def find_basedn(self, ldb):
72         res = ldb.search(base="", expression="", scope=SCOPE_BASE,
73                          attrs=["defaultNamingContext"])
74         self.assertEquals(len(res), 1)
75         return res[0]["defaultNamingContext"][0]
76
77     def setUp(self):
78         super(PasswordTests, self).setUp()
79         self.ldb = ldb
80         self.base_dn = self.find_basedn(ldb)
81
82         # (Re)adds the test user "testuser" with the inital password
83         # "thatsAcomplPASS1"
84         self.delete_force(self.ldb, "cn=testuser,cn=users," + self.base_dn)
85         self.ldb.add({
86              "dn": "cn=testuser,cn=users," + self.base_dn,
87              "objectclass": ["user", "person"],
88              "sAMAccountName": "testuser",
89              "userPassword": "thatsAcomplPASS1" })
90         self.ldb.enable_account("(sAMAccountName=testuser)")
91
92         # Open a second LDB connection with the user credentials. Use the
93         # command line credentials for informations like the domain, the realm
94         # and the workstation.
95         creds2 = Credentials()
96         creds2.set_username("testuser")
97         creds2.set_password("thatsAcomplPASS1")
98         creds2.set_domain(creds.get_domain())
99         creds2.set_realm(creds.get_realm())
100         creds2.set_workstation(creds.get_workstation())
101         creds2.set_gensec_features(creds2.get_gensec_features()
102                                                           | gensec.FEATURE_SEAL)
103         self.ldb2 = SamDB(url=host, credentials=creds2, lp=lp)
104
105     def test_unicodePwd_hash_set(self):
106         print "Performs a password hash set operation on 'unicodePwd' which should be prevented"
107         # Notice: Direct hash password sets should never work
108
109         m = Message()
110         m.dn = Dn(ldb, "cn=testuser,cn=users," + self.base_dn)
111         m["unicodePwd"] = MessageElement("XXXXXXXXXXXXXXXX", FLAG_MOD_REPLACE,
112           "unicodePwd")
113         try:
114             ldb.modify(m)
115             self.fail()
116         except LdbError, (num, _):
117             self.assertEquals(num, ERR_UNWILLING_TO_PERFORM)
118
119     def test_unicodePwd_hash_change(self):
120         print "Performs a password hash change operation on 'unicodePwd' which should be prevented"
121         # Notice: Direct hash password changes should never work
122
123         # Hash password changes should never work
124         try:
125             self.ldb2.modify_ldif("""
126 dn: cn=testuser,cn=users,""" + self.base_dn + """
127 changetype: modify
128 delete: unicodePwd
129 unicodePwd: XXXXXXXXXXXXXXXX
130 add: unicodePwd
131 unicodePwd: YYYYYYYYYYYYYYYY
132 """)
133             self.fail()
134         except LdbError, (num, _):
135             self.assertEquals(num, ERR_CONSTRAINT_VIOLATION)
136
137     def test_unicodePwd_clear_set(self):
138         print "Performs a password cleartext set operation on 'unicodePwd'"
139
140         m = Message()
141         m.dn = Dn(ldb, "cn=testuser,cn=users," + self.base_dn)
142         m["unicodePwd"] = MessageElement("\"thatsAcomplPASS2\"".encode('utf-16-le'),
143           FLAG_MOD_REPLACE, "unicodePwd")
144         ldb.modify(m)
145
146     def test_unicodePwd_clear_change(self):
147         print "Performs a password cleartext change operation on 'unicodePwd'"
148
149         self.ldb2.modify_ldif("""
150 dn: cn=testuser,cn=users,""" + self.base_dn + """
151 changetype: modify
152 delete: unicodePwd
153 unicodePwd:: """ + base64.b64encode("\"thatsAcomplPASS1\"".encode('utf-16-le')) + """
154 add: unicodePwd
155 unicodePwd:: """ + base64.b64encode("\"thatsAcomplPASS2\"".encode('utf-16-le')) + """
156 """)
157
158         # Wrong old password
159         try:
160             self.ldb2.modify_ldif("""
161 dn: cn=testuser,cn=users,""" + self.base_dn + """
162 changetype: modify
163 delete: unicodePwd
164 unicodePwd:: """ + base64.b64encode("\"thatsAcomplPASS3\"".encode('utf-16-le')) + """
165 add: unicodePwd
166 unicodePwd:: """ + base64.b64encode("\"thatsAcomplPASS4\"".encode('utf-16-le')) + """
167 """)
168             self.fail()
169         except LdbError, (num, _):
170             self.assertEquals(num, ERR_CONSTRAINT_VIOLATION)
171
172         # A change to the same password again will not work (password history)
173         try:
174             self.ldb2.modify_ldif("""
175 dn: cn=testuser,cn=users,""" + self.base_dn + """
176 changetype: modify
177 delete: unicodePwd
178 unicodePwd:: """ + base64.b64encode("\"thatsAcomplPASS2\"".encode('utf-16-le')) + """
179 add: unicodePwd
180 unicodePwd:: """ + base64.b64encode("\"thatsAcomplPASS2\"".encode('utf-16-le')) + """
181 """)
182             self.fail()
183         except LdbError, (num, _):
184             self.assertEquals(num, ERR_CONSTRAINT_VIOLATION)
185
186     def test_dBCSPwd_hash_set(self):
187         print "Performs a password hash set operation on 'dBCSPwd' which should be prevented"
188         # Notice: Direct hash password sets should never work
189
190         m = Message()
191         m.dn = Dn(ldb, "cn=testuser,cn=users," + self.base_dn)
192         m["dBCSPwd"] = MessageElement("XXXXXXXXXXXXXXXX", FLAG_MOD_REPLACE,
193           "dBCSPwd")
194         try:
195             ldb.modify(m)
196             self.fail()
197         except LdbError, (num, _):
198             self.assertEquals(num, ERR_UNWILLING_TO_PERFORM)
199
200     def test_dBCSPwd_hash_change(self):
201         print "Performs a password hash change operation on 'dBCSPwd' which should be prevented"
202         # Notice: Direct hash password changes should never work
203
204         try:
205             self.ldb2.modify_ldif("""
206 dn: cn=testuser,cn=users,""" + self.base_dn + """
207 changetype: modify
208 delete: dBCSPwd
209 dBCSPwd: XXXXXXXXXXXXXXXX
210 add: dBCSPwd
211 dBCSPwd: YYYYYYYYYYYYYYYY
212 """)
213             self.fail()
214         except LdbError, (num, _):
215             self.assertEquals(num, ERR_UNWILLING_TO_PERFORM)
216
217     def test_userPassword_clear_set(self):
218         print "Performs a password cleartext set operation on 'userPassword'"
219         # Notice: This works only against Windows if "dSHeuristics" has been set
220         # properly
221
222         m = Message()
223         m.dn = Dn(ldb, "cn=testuser,cn=users," + self.base_dn)
224         m["userPassword"] = MessageElement("thatsAcomplPASS2", FLAG_MOD_REPLACE,
225           "userPassword")
226         ldb.modify(m)
227
228     def test_userPassword_clear_change(self):
229         print "Performs a password cleartext change operation on 'userPassword'"
230         # Notice: This works only against Windows if "dSHeuristics" has been set
231         # properly
232
233         self.ldb2.modify_ldif("""
234 dn: cn=testuser,cn=users,""" + self.base_dn + """
235 changetype: modify
236 delete: userPassword
237 userPassword: thatsAcomplPASS1
238 add: userPassword
239 userPassword: thatsAcomplPASS2
240 """)
241
242         # Wrong old password
243         try:
244             self.ldb2.modify_ldif("""
245 dn: cn=testuser,cn=users,""" + self.base_dn + """
246 changetype: modify
247 delete: userPassword
248 userPassword: thatsAcomplPASS3
249 add: userPassword
250 userPassword: thatsAcomplPASS4
251 """)
252             self.fail()
253         except LdbError, (num, _):
254             self.assertEquals(num, ERR_CONSTRAINT_VIOLATION)
255
256         # A change to the same password again will not work (password history)
257         try:
258             self.ldb2.modify_ldif("""
259 dn: cn=testuser,cn=users,""" + self.base_dn + """
260 changetype: modify
261 delete: userPassword
262 userPassword: thatsAcomplPASS2
263 add: userPassword
264 userPassword: thatsAcomplPASS2
265 """)
266             self.fail()
267         except LdbError, (num, _):
268             self.assertEquals(num, ERR_CONSTRAINT_VIOLATION)
269
270     def test_clearTextPassword_clear_set(self):
271         print "Performs a password cleartext set operation on 'clearTextPassword'"
272         # Notice: This never works against Windows - only supported by us
273
274         try:
275             m = Message()
276             m.dn = Dn(ldb, "cn=testuser,cn=users," + self.base_dn)
277             m["clearTextPassword"] = MessageElement("thatsAcomplPASS2".encode('utf-16-le'),
278               FLAG_MOD_REPLACE, "clearTextPassword")
279             ldb.modify(m)
280             # this passes against s4
281         except LdbError, (num, msg):
282             # "NO_SUCH_ATTRIBUTE" is returned by Windows -> ignore it
283             if num != ERR_NO_SUCH_ATTRIBUTE:
284                 raise LdbError(num, msg)
285
286     def test_clearTextPassword_clear_change(self):
287         print "Performs a password cleartext change operation on 'clearTextPassword'"
288         # Notice: This never works against Windows - only supported by us
289
290         try:
291             self.ldb2.modify_ldif("""
292 dn: cn=testuser,cn=users,""" + self.base_dn + """
293 changetype: modify
294 delete: clearTextPassword
295 clearTextPassword:: """ + base64.b64encode("thatsAcomplPASS1".encode('utf-16-le')) + """
296 add: clearTextPassword
297 clearTextPassword:: """ + base64.b64encode("thatsAcomplPASS2".encode('utf-16-le')) + """
298 """)
299             # this passes against s4
300         except LdbError, (num, msg):
301             # "NO_SUCH_ATTRIBUTE" is returned by Windows -> ignore it
302             if num != ERR_NO_SUCH_ATTRIBUTE:
303                 raise LdbError(num, msg)
304
305         # Wrong old password
306         try:
307             self.ldb2.modify_ldif("""
308 dn: cn=testuser,cn=users,""" + self.base_dn + """
309 changetype: modify
310 delete: clearTextPassword
311 clearTextPassword:: """ + base64.b64encode("thatsAcomplPASS3".encode('utf-16-le')) + """
312 add: clearTextPassword
313 clearTextPassword:: """ + base64.b64encode("thatsAcomplPASS4".encode('utf-16-le')) + """
314 """)
315             self.fail()
316         except LdbError, (num, _):
317             # "NO_SUCH_ATTRIBUTE" is returned by Windows -> ignore it
318             if num != ERR_NO_SUCH_ATTRIBUTE:
319                 self.assertEquals(num, ERR_CONSTRAINT_VIOLATION)
320
321         # A change to the same password again will not work (password history)
322         try:
323             self.ldb2.modify_ldif("""
324 dn: cn=testuser,cn=users,""" + self.base_dn + """
325 changetype: modify
326 delete: clearTextPassword
327 clearTextPassword:: """ + base64.b64encode("thatsAcomplPASS2".encode('utf-16-le')) + """
328 add: clearTextPassword
329 clearTextPassword:: """ + base64.b64encode("thatsAcomplPASS2".encode('utf-16-le')) + """
330 """)
331             self.fail()
332         except LdbError, (num, _):
333             # "NO_SUCH_ATTRIBUTE" is returned by Windows -> ignore it
334             if num != ERR_NO_SUCH_ATTRIBUTE:
335                 self.assertEquals(num, ERR_CONSTRAINT_VIOLATION)
336
337     def test_failures(self):
338         print "Performs some failure testing"
339
340         try:
341             ldb.modify_ldif("""
342 dn: cn=testuser,cn=users,""" + self.base_dn + """
343 changetype: modify
344 delete: userPassword
345 userPassword: thatsAcomplPASS1
346 """)
347             self.fail()
348         except LdbError, (num, _):
349             self.assertEquals(num, ERR_CONSTRAINT_VIOLATION)
350
351         try:
352             self.ldb2.modify_ldif("""
353 dn: cn=testuser,cn=users,""" + self.base_dn + """
354 changetype: modify
355 delete: userPassword
356 """)
357             self.fail()
358         except LdbError, (num, _):
359             self.assertEquals(num, ERR_CONSTRAINT_VIOLATION)
360
361         try:
362             ldb.modify_ldif("""
363 dn: cn=testuser,cn=users,""" + self.base_dn + """
364 changetype: modify
365 add: userPassword
366 userPassword: thatsAcomplPASS1
367 """)
368             self.fail()
369         except LdbError, (num, _):
370             self.assertEquals(num, ERR_UNWILLING_TO_PERFORM)
371
372         try:
373             self.ldb2.modify_ldif("""
374 dn: cn=testuser,cn=users,""" + self.base_dn + """
375 changetype: modify
376 add: userPassword
377 userPassword: thatsAcomplPASS1
378 """)
379             self.fail()
380         except LdbError, (num, _):
381             self.assertEquals(num, ERR_INSUFFICIENT_ACCESS_RIGHTS)
382
383         try:
384             ldb.modify_ldif("""
385 dn: cn=testuser,cn=users,""" + self.base_dn + """
386 changetype: modify
387 delete: userPassword
388 userPassword: thatsAcomplPASS1
389 add: userPassword
390 userPassword: thatsAcomplPASS2
391 userPassword: thatsAcomplPASS2
392 """)
393             self.fail()
394         except LdbError, (num, _):
395             self.assertEquals(num, ERR_CONSTRAINT_VIOLATION)
396
397         try:
398             self.ldb2.modify_ldif("""
399 dn: cn=testuser,cn=users,""" + self.base_dn + """
400 changetype: modify
401 delete: userPassword
402 userPassword: thatsAcomplPASS1
403 add: userPassword
404 userPassword: thatsAcomplPASS2
405 userPassword: thatsAcomplPASS2
406 """)
407             self.fail()
408         except LdbError, (num, _):
409             self.assertEquals(num, ERR_CONSTRAINT_VIOLATION)
410
411         try:
412             ldb.modify_ldif("""
413 dn: cn=testuser,cn=users,""" + self.base_dn + """
414 changetype: modify
415 delete: userPassword
416 userPassword: thatsAcomplPASS1
417 userPassword: thatsAcomplPASS1
418 add: userPassword
419 userPassword: thatsAcomplPASS2
420 """)
421             self.fail()
422         except LdbError, (num, _):
423             self.assertEquals(num, ERR_CONSTRAINT_VIOLATION)
424
425         try:
426             self.ldb2.modify_ldif("""
427 dn: cn=testuser,cn=users,""" + self.base_dn + """
428 changetype: modify
429 delete: userPassword
430 userPassword: thatsAcomplPASS1
431 userPassword: thatsAcomplPASS1
432 add: userPassword
433 userPassword: thatsAcomplPASS2
434 """)
435             self.fail()
436         except LdbError, (num, _):
437             self.assertEquals(num, ERR_CONSTRAINT_VIOLATION)
438
439         try:
440             ldb.modify_ldif("""
441 dn: cn=testuser,cn=users,""" + self.base_dn + """
442 changetype: modify
443 delete: userPassword
444 userPassword: thatsAcomplPASS1
445 add: userPassword
446 userPassword: thatsAcomplPASS2
447 add: userPassword
448 userPassword: thatsAcomplPASS2
449 """)
450             self.fail()
451         except LdbError, (num, _):
452             self.assertEquals(num, ERR_UNWILLING_TO_PERFORM)
453
454         try:
455             self.ldb2.modify_ldif("""
456 dn: cn=testuser,cn=users,""" + self.base_dn + """
457 changetype: modify
458 delete: userPassword
459 userPassword: thatsAcomplPASS1
460 add: userPassword
461 userPassword: thatsAcomplPASS2
462 add: userPassword
463 userPassword: thatsAcomplPASS2
464 """)
465             self.fail()
466         except LdbError, (num, _):
467             self.assertEquals(num, ERR_INSUFFICIENT_ACCESS_RIGHTS)
468
469         try:
470             ldb.modify_ldif("""
471 dn: cn=testuser,cn=users,""" + self.base_dn + """
472 changetype: modify
473 delete: userPassword
474 userPassword: thatsAcomplPASS1
475 delete: userPassword
476 userPassword: thatsAcomplPASS1
477 add: userPassword
478 userPassword: thatsAcomplPASS2
479 """)
480             self.fail()
481         except LdbError, (num, _):
482             self.assertEquals(num, ERR_UNWILLING_TO_PERFORM)
483
484         try:
485             self.ldb2.modify_ldif("""
486 dn: cn=testuser,cn=users,""" + self.base_dn + """
487 changetype: modify
488 delete: userPassword
489 userPassword: thatsAcomplPASS1
490 delete: userPassword
491 userPassword: thatsAcomplPASS1
492 add: userPassword
493 userPassword: thatsAcomplPASS2
494 """)
495             self.fail()
496         except LdbError, (num, _):
497             self.assertEquals(num, ERR_INSUFFICIENT_ACCESS_RIGHTS)
498
499         try:
500             ldb.modify_ldif("""
501 dn: cn=testuser,cn=users,""" + self.base_dn + """
502 changetype: modify
503 delete: userPassword
504 userPassword: thatsAcomplPASS1
505 add: userPassword
506 userPassword: thatsAcomplPASS2
507 replace: userPassword
508 userPassword: thatsAcomplPASS3
509 """)
510             self.fail()
511         except LdbError, (num, _):
512             self.assertEquals(num, ERR_UNWILLING_TO_PERFORM)
513
514         try:
515             self.ldb2.modify_ldif("""
516 dn: cn=testuser,cn=users,""" + self.base_dn + """
517 changetype: modify
518 delete: userPassword
519 userPassword: thatsAcomplPASS1
520 add: userPassword
521 userPassword: thatsAcomplPASS2
522 replace: userPassword
523 userPassword: thatsAcomplPASS3
524 """)
525             self.fail()
526         except LdbError, (num, _):
527             self.assertEquals(num, ERR_INSUFFICIENT_ACCESS_RIGHTS)
528
529         # Reverse order does work
530         self.ldb2.modify_ldif("""
531 dn: cn=testuser,cn=users,""" + self.base_dn + """
532 changetype: modify
533 add: userPassword
534 userPassword: thatsAcomplPASS2
535 delete: userPassword
536 userPassword: thatsAcomplPASS1
537 """)
538
539         try:
540             self.ldb2.modify_ldif("""
541 dn: cn=testuser,cn=users,""" + self.base_dn + """
542 changetype: modify
543 delete: userPassword
544 userPassword: thatsAcomplPASS2
545 add: unicodePwd
546 unicodePwd:: """ + base64.b64encode("\"thatsAcomplPASS3\"".encode('utf-16-le')) + """
547 """)
548              # this passes against s4
549         except LdbError, (num, _):
550             self.assertEquals(num, ERR_ATTRIBUTE_OR_VALUE_EXISTS)
551
552         try:
553             self.ldb2.modify_ldif("""
554 dn: cn=testuser,cn=users,""" + self.base_dn + """
555 changetype: modify
556 delete: unicodePwd
557 unicodePwd:: """ + base64.b64encode("\"thatsAcomplPASS3\"".encode('utf-16-le')) + """
558 add: userPassword
559 userPassword: thatsAcomplPASS4
560 """)
561              # this passes against s4
562         except LdbError, (num, _):
563             self.assertEquals(num, ERR_NO_SUCH_ATTRIBUTE)
564
565         # Several password changes at once are allowed
566         ldb.modify_ldif("""
567 dn: cn=testuser,cn=users,""" + self.base_dn + """
568 changetype: modify
569 replace: userPassword
570 userPassword: thatsAcomplPASS1
571 userPassword: thatsAcomplPASS2
572 """)
573
574         # Several password changes at once are allowed
575         ldb.modify_ldif("""
576 dn: cn=testuser,cn=users,""" + self.base_dn + """
577 changetype: modify
578 replace: userPassword
579 userPassword: thatsAcomplPASS1
580 userPassword: thatsAcomplPASS2
581 replace: userPassword
582 userPassword: thatsAcomplPASS3
583 replace: userPassword
584 userPassword: thatsAcomplPASS4
585 """)
586
587         # This surprisingly should work
588         self.delete_force(self.ldb, "cn=testuser2,cn=users," + self.base_dn)
589         self.ldb.add({
590              "dn": "cn=testuser2,cn=users," + self.base_dn,
591              "objectclass": ["user", "person"],
592              "userPassword": ["thatsAcomplPASS1", "thatsAcomplPASS2"] })
593
594         # This surprisingly should work
595         self.delete_force(self.ldb, "cn=testuser2,cn=users," + self.base_dn)
596         self.ldb.add({
597              "dn": "cn=testuser2,cn=users," + self.base_dn,
598              "objectclass": ["user", "person"],
599              "userPassword": ["thatsAcomplPASS1", "thatsAcomplPASS1"] })
600
601     def tearDown(self):
602         super(PasswordTests, self).tearDown()
603         self.delete_force(self.ldb, "cn=testuser,cn=users," + self.base_dn)
604         self.delete_force(self.ldb, "cn=testuser2,cn=users," + self.base_dn)
605         # Close the second LDB connection (with the user credentials)
606         self.ldb2 = None
607
608 if not "://" in host:
609     if os.path.isfile(host):
610         host = "tdb://%s" % host
611     else:
612         host = "ldap://%s" % host
613
614 ldb = SamDB(url=host, session_info=system_session(), credentials=creds, lp=lp)
615
616 # Gets back the configuration basedn
617 res = ldb.search(base="", expression="", scope=SCOPE_BASE,
618                  attrs=["configurationNamingContext"])
619 configuration_dn = res[0]["configurationNamingContext"][0]
620
621 # Gets back the basedn
622 res = ldb.search(base="", expression="", scope=SCOPE_BASE,
623                  attrs=["defaultNamingContext"])
624 base_dn = res[0]["defaultNamingContext"][0]
625
626 # Get the old "dSHeuristics" if it was set
627 res = ldb.search("CN=Directory Service, CN=Windows NT, CN=Services, "
628                  + configuration_dn, scope=SCOPE_BASE, attrs=["dSHeuristics"])
629 if "dSHeuristics" in res[0]:
630   dsheuristics = res[0]["dSHeuristics"][0]
631 else:
632   dsheuristics = None
633
634 # Set the "dSHeuristics" to have the tests run against Windows Server
635 m = Message()
636 m.dn = Dn(ldb, "CN=Directory Service, CN=Windows NT, CN=Services, "
637   + configuration_dn)
638 m["dSHeuristics"] = MessageElement("000000001", FLAG_MOD_REPLACE,
639   "dSHeuristics")
640 ldb.modify(m)
641
642 # Get the old "minPwdAge"
643 res = ldb.search(base_dn, scope=SCOPE_BASE, attrs=["minPwdAge"])
644 minPwdAge = res[0]["minPwdAge"][0]
645
646 # Set it temporarely to "0"
647 m = Message()
648 m.dn = Dn(ldb, base_dn)
649 m["minPwdAge"] = MessageElement("0", FLAG_MOD_REPLACE, "minPwdAge")
650 ldb.modify(m)
651
652 runner = SubunitTestRunner()
653 rc = 0
654 if not runner.run(unittest.makeSuite(PasswordTests)).wasSuccessful():
655     rc = 1
656
657 # Reset the "dSHeuristics" as they were before
658 m = Message()
659 m.dn = Dn(ldb, "CN=Directory Service, CN=Windows NT, CN=Services, "
660   + configuration_dn)
661 if dsheuristics is not None:
662     m["dSHeuristics"] = MessageElement(dsheuristics, FLAG_MOD_REPLACE,
663       "dSHeuristics")
664 else:
665     m["dSHeuristics"] = MessageElement([], FLAG_MOD_DELETE, "dsHeuristics")
666 ldb.modify(m)
667
668 # Reset the "minPwdAge" as it was before
669 m = Message()
670 m.dn = Dn(ldb, base_dn)
671 m["minPwdAge"] = MessageElement(minPwdAge, FLAG_MOD_REPLACE, "minPwdAge")
672 ldb.modify(m)
673
674 sys.exit(rc)