s4:password_hash LDB module - allow empty ("") passwords
[Samba/gebeck_regimport.git] / source4 / dsdb / tests / python / passwords.py
blobe43298e34d81813c1e82f3d9a31d83e040c71c00
1 #!/usr/bin/env python
2 # -*- coding: utf-8 -*-
3 # This tests the password changes over LDAP for AD implementations
5 # Copyright Matthias Dieter Wallnoefer 2010
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
11 import optparse
12 import sys
13 import base64
14 import time
15 import os
17 sys.path.append("bin/python")
18 import samba
19 samba.ensure_external_module("testtools", "testtools")
20 samba.ensure_external_module("subunit", "subunit/python")
22 import samba.getopt as options
24 from samba.auth import system_session
25 from samba.credentials import Credentials
26 from ldb import SCOPE_BASE, LdbError
27 from ldb import ERR_NO_SUCH_OBJECT, ERR_ATTRIBUTE_OR_VALUE_EXISTS
28 from ldb import ERR_UNWILLING_TO_PERFORM, ERR_INSUFFICIENT_ACCESS_RIGHTS
29 from ldb import ERR_NO_SUCH_ATTRIBUTE
30 from ldb import ERR_CONSTRAINT_VIOLATION
31 from ldb import Message, MessageElement, Dn
32 from ldb import FLAG_MOD_ADD, FLAG_MOD_REPLACE, FLAG_MOD_DELETE
33 from samba import gensec
34 from samba.samdb import SamDB
35 import samba.tests
36 from samba.tests import delete_force
37 from subunit.run import SubunitTestRunner
38 import unittest
40 parser = optparse.OptionParser("passwords.py [options] <host>")
41 sambaopts = options.SambaOptions(parser)
42 parser.add_option_group(sambaopts)
43 parser.add_option_group(options.VersionOptions(parser))
44 # use command line creds if available
45 credopts = options.CredentialsOptions(parser)
46 parser.add_option_group(credopts)
47 opts, args = parser.parse_args()
49 if len(args) < 1:
50 parser.print_usage()
51 sys.exit(1)
53 host = args[0]
55 lp = sambaopts.get_loadparm()
56 creds = credopts.get_credentials(lp)
58 # Force an encrypted connection
59 creds.set_gensec_features(creds.get_gensec_features() | gensec.FEATURE_SEAL)
62 # Tests start here
65 class PasswordTests(samba.tests.TestCase):
67 def setUp(self):
68 super(PasswordTests, self).setUp()
69 self.ldb = ldb
70 self.base_dn = ldb.domain_dn()
72 # (Re)adds the test user "testuser" with no password atm
73 delete_force(self.ldb, "cn=testuser,cn=users," + self.base_dn)
74 self.ldb.add({
75 "dn": "cn=testuser,cn=users," + self.base_dn,
76 "objectclass": "user",
77 "sAMAccountName": "testuser"})
79 # Tests a password change when we don't have any password yet with a
80 # wrong old password
81 try:
82 self.ldb.modify_ldif("""
83 dn: cn=testuser,cn=users,""" + self.base_dn + """
84 changetype: modify
85 delete: userPassword
86 userPassword: noPassword
87 add: userPassword
88 userPassword: thatsAcomplPASS2
89 """)
90 self.fail()
91 except LdbError, (num, msg):
92 self.assertEquals(num, ERR_CONSTRAINT_VIOLATION)
93 # Windows (2008 at least) seems to have some small bug here: it
94 # returns "0000056A" on longer (always wrong) previous passwords.
95 self.assertTrue('00000056' in msg)
97 # Sets the initial user password with a "special" password change
98 # I think that this internally is a password set operation and it can
99 # only be performed by someone which has password set privileges on the
100 # account (at least in s4 we do handle it like that).
101 self.ldb.modify_ldif("""
102 dn: cn=testuser,cn=users,""" + self.base_dn + """
103 changetype: modify
104 delete: userPassword
105 add: userPassword
106 userPassword: thatsAcomplPASS1
107 """)
109 # But in the other way around this special syntax doesn't work
110 try:
111 self.ldb.modify_ldif("""
112 dn: cn=testuser,cn=users,""" + self.base_dn + """
113 changetype: modify
114 delete: userPassword
115 userPassword: thatsAcomplPASS1
116 add: userPassword
117 """)
118 self.fail()
119 except LdbError, (num, _):
120 self.assertEquals(num, ERR_CONSTRAINT_VIOLATION)
122 # Enables the user account
123 self.ldb.enable_account("(sAMAccountName=testuser)")
125 # Open a second LDB connection with the user credentials. Use the
126 # command line credentials for informations like the domain, the realm
127 # and the workstation.
128 creds2 = Credentials()
129 creds2.set_username("testuser")
130 creds2.set_password("thatsAcomplPASS1")
131 creds2.set_domain(creds.get_domain())
132 creds2.set_realm(creds.get_realm())
133 creds2.set_workstation(creds.get_workstation())
134 creds2.set_gensec_features(creds2.get_gensec_features()
135 | gensec.FEATURE_SEAL)
136 self.ldb2 = SamDB(url=host, credentials=creds2, lp=lp)
138 def test_unicodePwd_hash_set(self):
139 print "Performs a password hash set operation on 'unicodePwd' which should be prevented"
140 # Notice: Direct hash password sets should never work
142 m = Message()
143 m.dn = Dn(ldb, "cn=testuser,cn=users," + self.base_dn)
144 m["unicodePwd"] = MessageElement("XXXXXXXXXXXXXXXX", FLAG_MOD_REPLACE,
145 "unicodePwd")
146 try:
147 ldb.modify(m)
148 self.fail()
149 except LdbError, (num, _):
150 self.assertEquals(num, ERR_UNWILLING_TO_PERFORM)
152 def test_unicodePwd_hash_change(self):
153 print "Performs a password hash change operation on 'unicodePwd' which should be prevented"
154 # Notice: Direct hash password changes should never work
156 # Hash password changes should never work
157 try:
158 self.ldb2.modify_ldif("""
159 dn: cn=testuser,cn=users,""" + self.base_dn + """
160 changetype: modify
161 delete: unicodePwd
162 unicodePwd: XXXXXXXXXXXXXXXX
163 add: unicodePwd
164 unicodePwd: YYYYYYYYYYYYYYYY
165 """)
166 self.fail()
167 except LdbError, (num, _):
168 self.assertEquals(num, ERR_CONSTRAINT_VIOLATION)
170 def test_unicodePwd_clear_set(self):
171 print "Performs a password cleartext set operation on 'unicodePwd'"
173 m = Message()
174 m.dn = Dn(ldb, "cn=testuser,cn=users," + self.base_dn)
175 m["unicodePwd"] = MessageElement("\"thatsAcomplPASS2\"".encode('utf-16-le'),
176 FLAG_MOD_REPLACE, "unicodePwd")
177 ldb.modify(m)
179 def test_unicodePwd_clear_change(self):
180 print "Performs a password cleartext change operation on 'unicodePwd'"
182 self.ldb2.modify_ldif("""
183 dn: cn=testuser,cn=users,""" + self.base_dn + """
184 changetype: modify
185 delete: unicodePwd
186 unicodePwd:: """ + base64.b64encode("\"thatsAcomplPASS1\"".encode('utf-16-le')) + """
187 add: unicodePwd
188 unicodePwd:: """ + base64.b64encode("\"thatsAcomplPASS2\"".encode('utf-16-le')) + """
189 """)
191 # Wrong old password
192 try:
193 self.ldb2.modify_ldif("""
194 dn: cn=testuser,cn=users,""" + self.base_dn + """
195 changetype: modify
196 delete: unicodePwd
197 unicodePwd:: """ + base64.b64encode("\"thatsAcomplPASS3\"".encode('utf-16-le')) + """
198 add: unicodePwd
199 unicodePwd:: """ + base64.b64encode("\"thatsAcomplPASS4\"".encode('utf-16-le')) + """
200 """)
201 self.fail()
202 except LdbError, (num, msg):
203 self.assertEquals(num, ERR_CONSTRAINT_VIOLATION)
204 self.assertTrue('00000056' in msg)
206 # A change to the same password again will not work (password history)
207 try:
208 self.ldb2.modify_ldif("""
209 dn: cn=testuser,cn=users,""" + self.base_dn + """
210 changetype: modify
211 delete: unicodePwd
212 unicodePwd:: """ + base64.b64encode("\"thatsAcomplPASS2\"".encode('utf-16-le')) + """
213 add: unicodePwd
214 unicodePwd:: """ + base64.b64encode("\"thatsAcomplPASS2\"".encode('utf-16-le')) + """
215 """)
216 self.fail()
217 except LdbError, (num, msg):
218 self.assertEquals(num, ERR_CONSTRAINT_VIOLATION)
219 self.assertTrue('0000052D' in msg)
221 def test_dBCSPwd_hash_set(self):
222 print "Performs a password hash set operation on 'dBCSPwd' which should be prevented"
223 # Notice: Direct hash password sets should never work
225 m = Message()
226 m.dn = Dn(ldb, "cn=testuser,cn=users," + self.base_dn)
227 m["dBCSPwd"] = MessageElement("XXXXXXXXXXXXXXXX", FLAG_MOD_REPLACE,
228 "dBCSPwd")
229 try:
230 ldb.modify(m)
231 self.fail()
232 except LdbError, (num, _):
233 self.assertEquals(num, ERR_UNWILLING_TO_PERFORM)
235 def test_dBCSPwd_hash_change(self):
236 print "Performs a password hash change operation on 'dBCSPwd' which should be prevented"
237 # Notice: Direct hash password changes should never work
239 try:
240 self.ldb2.modify_ldif("""
241 dn: cn=testuser,cn=users,""" + self.base_dn + """
242 changetype: modify
243 delete: dBCSPwd
244 dBCSPwd: XXXXXXXXXXXXXXXX
245 add: dBCSPwd
246 dBCSPwd: YYYYYYYYYYYYYYYY
247 """)
248 self.fail()
249 except LdbError, (num, _):
250 self.assertEquals(num, ERR_UNWILLING_TO_PERFORM)
252 def test_userPassword_clear_set(self):
253 print "Performs a password cleartext set operation on 'userPassword'"
254 # Notice: This works only against Windows if "dSHeuristics" has been set
255 # properly
257 m = Message()
258 m.dn = Dn(ldb, "cn=testuser,cn=users," + self.base_dn)
259 m["userPassword"] = MessageElement("thatsAcomplPASS2", FLAG_MOD_REPLACE,
260 "userPassword")
261 ldb.modify(m)
263 def test_userPassword_clear_change(self):
264 print "Performs a password cleartext change operation on 'userPassword'"
265 # Notice: This works only against Windows if "dSHeuristics" has been set
266 # properly
268 self.ldb2.modify_ldif("""
269 dn: cn=testuser,cn=users,""" + self.base_dn + """
270 changetype: modify
271 delete: userPassword
272 userPassword: thatsAcomplPASS1
273 add: userPassword
274 userPassword: thatsAcomplPASS2
275 """)
277 # Wrong old password
278 try:
279 self.ldb2.modify_ldif("""
280 dn: cn=testuser,cn=users,""" + self.base_dn + """
281 changetype: modify
282 delete: userPassword
283 userPassword: thatsAcomplPASS3
284 add: userPassword
285 userPassword: thatsAcomplPASS4
286 """)
287 self.fail()
288 except LdbError, (num, msg):
289 self.assertEquals(num, ERR_CONSTRAINT_VIOLATION)
290 self.assertTrue('00000056' in msg)
292 # A change to the same password again will not work (password history)
293 try:
294 self.ldb2.modify_ldif("""
295 dn: cn=testuser,cn=users,""" + self.base_dn + """
296 changetype: modify
297 delete: userPassword
298 userPassword: thatsAcomplPASS2
299 add: userPassword
300 userPassword: thatsAcomplPASS2
301 """)
302 self.fail()
303 except LdbError, (num, msg):
304 self.assertEquals(num, ERR_CONSTRAINT_VIOLATION)
305 self.assertTrue('0000052D' in msg)
307 def test_clearTextPassword_clear_set(self):
308 print "Performs a password cleartext set operation on 'clearTextPassword'"
309 # Notice: This never works against Windows - only supported by us
311 try:
312 m = Message()
313 m.dn = Dn(ldb, "cn=testuser,cn=users," + self.base_dn)
314 m["clearTextPassword"] = MessageElement("thatsAcomplPASS2".encode('utf-16-le'),
315 FLAG_MOD_REPLACE, "clearTextPassword")
316 ldb.modify(m)
317 # this passes against s4
318 except LdbError, (num, msg):
319 # "NO_SUCH_ATTRIBUTE" is returned by Windows -> ignore it
320 if num != ERR_NO_SUCH_ATTRIBUTE:
321 raise LdbError(num, msg)
323 def test_clearTextPassword_clear_change(self):
324 print "Performs a password cleartext change operation on 'clearTextPassword'"
325 # Notice: This never works against Windows - only supported by us
327 try:
328 self.ldb2.modify_ldif("""
329 dn: cn=testuser,cn=users,""" + self.base_dn + """
330 changetype: modify
331 delete: clearTextPassword
332 clearTextPassword:: """ + base64.b64encode("thatsAcomplPASS1".encode('utf-16-le')) + """
333 add: clearTextPassword
334 clearTextPassword:: """ + base64.b64encode("thatsAcomplPASS2".encode('utf-16-le')) + """
335 """)
336 # this passes against s4
337 except LdbError, (num, msg):
338 # "NO_SUCH_ATTRIBUTE" is returned by Windows -> ignore it
339 if num != ERR_NO_SUCH_ATTRIBUTE:
340 raise LdbError(num, msg)
342 # Wrong old password
343 try:
344 self.ldb2.modify_ldif("""
345 dn: cn=testuser,cn=users,""" + self.base_dn + """
346 changetype: modify
347 delete: clearTextPassword
348 clearTextPassword:: """ + base64.b64encode("thatsAcomplPASS3".encode('utf-16-le')) + """
349 add: clearTextPassword
350 clearTextPassword:: """ + base64.b64encode("thatsAcomplPASS4".encode('utf-16-le')) + """
351 """)
352 self.fail()
353 except LdbError, (num, msg):
354 # "NO_SUCH_ATTRIBUTE" is returned by Windows -> ignore it
355 if num != ERR_NO_SUCH_ATTRIBUTE:
356 self.assertEquals(num, ERR_CONSTRAINT_VIOLATION)
357 self.assertTrue('00000056' in msg)
359 # A change to the same password again will not work (password history)
360 try:
361 self.ldb2.modify_ldif("""
362 dn: cn=testuser,cn=users,""" + self.base_dn + """
363 changetype: modify
364 delete: clearTextPassword
365 clearTextPassword:: """ + base64.b64encode("thatsAcomplPASS2".encode('utf-16-le')) + """
366 add: clearTextPassword
367 clearTextPassword:: """ + base64.b64encode("thatsAcomplPASS2".encode('utf-16-le')) + """
368 """)
369 self.fail()
370 except LdbError, (num, msg):
371 # "NO_SUCH_ATTRIBUTE" is returned by Windows -> ignore it
372 if num != ERR_NO_SUCH_ATTRIBUTE:
373 self.assertEquals(num, ERR_CONSTRAINT_VIOLATION)
374 self.assertTrue('0000052D' in msg)
376 def test_failures(self):
377 print "Performs some failure testing"
379 try:
380 ldb.modify_ldif("""
381 dn: cn=testuser,cn=users,""" + self.base_dn + """
382 changetype: modify
383 delete: userPassword
384 userPassword: thatsAcomplPASS1
385 """)
386 self.fail()
387 except LdbError, (num, _):
388 self.assertEquals(num, ERR_CONSTRAINT_VIOLATION)
390 try:
391 self.ldb2.modify_ldif("""
392 dn: cn=testuser,cn=users,""" + self.base_dn + """
393 changetype: modify
394 delete: userPassword
395 userPassword: thatsAcomplPASS1
396 """)
397 self.fail()
398 except LdbError, (num, _):
399 self.assertEquals(num, ERR_CONSTRAINT_VIOLATION)
401 try:
402 ldb.modify_ldif("""
403 dn: cn=testuser,cn=users,""" + self.base_dn + """
404 changetype: modify
405 delete: userPassword
406 """)
407 self.fail()
408 except LdbError, (num, _):
409 self.assertEquals(num, ERR_CONSTRAINT_VIOLATION)
411 try:
412 self.ldb2.modify_ldif("""
413 dn: cn=testuser,cn=users,""" + self.base_dn + """
414 changetype: modify
415 delete: userPassword
416 """)
417 self.fail()
418 except LdbError, (num, _):
419 self.assertEquals(num, ERR_CONSTRAINT_VIOLATION)
421 try:
422 ldb.modify_ldif("""
423 dn: cn=testuser,cn=users,""" + self.base_dn + """
424 changetype: modify
425 add: userPassword
426 userPassword: thatsAcomplPASS1
427 """)
428 self.fail()
429 except LdbError, (num, _):
430 self.assertEquals(num, ERR_UNWILLING_TO_PERFORM)
432 try:
433 self.ldb2.modify_ldif("""
434 dn: cn=testuser,cn=users,""" + self.base_dn + """
435 changetype: modify
436 add: userPassword
437 userPassword: thatsAcomplPASS1
438 """)
439 self.fail()
440 except LdbError, (num, _):
441 self.assertEquals(num, ERR_INSUFFICIENT_ACCESS_RIGHTS)
443 try:
444 ldb.modify_ldif("""
445 dn: cn=testuser,cn=users,""" + self.base_dn + """
446 changetype: modify
447 delete: userPassword
448 userPassword: thatsAcomplPASS1
449 add: userPassword
450 userPassword: thatsAcomplPASS2
451 userPassword: thatsAcomplPASS2
452 """)
453 self.fail()
454 except LdbError, (num, _):
455 self.assertEquals(num, ERR_CONSTRAINT_VIOLATION)
457 try:
458 self.ldb2.modify_ldif("""
459 dn: cn=testuser,cn=users,""" + self.base_dn + """
460 changetype: modify
461 delete: userPassword
462 userPassword: thatsAcomplPASS1
463 add: userPassword
464 userPassword: thatsAcomplPASS2
465 userPassword: thatsAcomplPASS2
466 """)
467 self.fail()
468 except LdbError, (num, _):
469 self.assertEquals(num, ERR_CONSTRAINT_VIOLATION)
471 try:
472 ldb.modify_ldif("""
473 dn: cn=testuser,cn=users,""" + self.base_dn + """
474 changetype: modify
475 delete: userPassword
476 userPassword: thatsAcomplPASS1
477 userPassword: thatsAcomplPASS1
478 add: userPassword
479 userPassword: thatsAcomplPASS2
480 """)
481 self.fail()
482 except LdbError, (num, _):
483 self.assertEquals(num, ERR_CONSTRAINT_VIOLATION)
485 try:
486 self.ldb2.modify_ldif("""
487 dn: cn=testuser,cn=users,""" + self.base_dn + """
488 changetype: modify
489 delete: userPassword
490 userPassword: thatsAcomplPASS1
491 userPassword: thatsAcomplPASS1
492 add: userPassword
493 userPassword: thatsAcomplPASS2
494 """)
495 self.fail()
496 except LdbError, (num, _):
497 self.assertEquals(num, ERR_CONSTRAINT_VIOLATION)
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 add: userPassword
508 userPassword: thatsAcomplPASS2
509 """)
510 self.fail()
511 except LdbError, (num, _):
512 self.assertEquals(num, ERR_UNWILLING_TO_PERFORM)
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 add: userPassword
523 userPassword: thatsAcomplPASS2
524 """)
525 self.fail()
526 except LdbError, (num, _):
527 self.assertEquals(num, ERR_INSUFFICIENT_ACCESS_RIGHTS)
529 try:
530 ldb.modify_ldif("""
531 dn: cn=testuser,cn=users,""" + self.base_dn + """
532 changetype: modify
533 delete: userPassword
534 userPassword: thatsAcomplPASS1
535 delete: userPassword
536 userPassword: thatsAcomplPASS1
537 add: userPassword
538 userPassword: thatsAcomplPASS2
539 """)
540 self.fail()
541 except LdbError, (num, _):
542 self.assertEquals(num, ERR_UNWILLING_TO_PERFORM)
544 try:
545 self.ldb2.modify_ldif("""
546 dn: cn=testuser,cn=users,""" + self.base_dn + """
547 changetype: modify
548 delete: userPassword
549 userPassword: thatsAcomplPASS1
550 delete: userPassword
551 userPassword: thatsAcomplPASS1
552 add: userPassword
553 userPassword: thatsAcomplPASS2
554 """)
555 self.fail()
556 except LdbError, (num, _):
557 self.assertEquals(num, ERR_INSUFFICIENT_ACCESS_RIGHTS)
559 try:
560 ldb.modify_ldif("""
561 dn: cn=testuser,cn=users,""" + self.base_dn + """
562 changetype: modify
563 delete: userPassword
564 userPassword: thatsAcomplPASS1
565 add: userPassword
566 userPassword: thatsAcomplPASS2
567 replace: userPassword
568 userPassword: thatsAcomplPASS3
569 """)
570 self.fail()
571 except LdbError, (num, _):
572 self.assertEquals(num, ERR_UNWILLING_TO_PERFORM)
574 try:
575 self.ldb2.modify_ldif("""
576 dn: cn=testuser,cn=users,""" + self.base_dn + """
577 changetype: modify
578 delete: userPassword
579 userPassword: thatsAcomplPASS1
580 add: userPassword
581 userPassword: thatsAcomplPASS2
582 replace: userPassword
583 userPassword: thatsAcomplPASS3
584 """)
585 self.fail()
586 except LdbError, (num, _):
587 self.assertEquals(num, ERR_INSUFFICIENT_ACCESS_RIGHTS)
589 # Reverse order does work
590 self.ldb2.modify_ldif("""
591 dn: cn=testuser,cn=users,""" + self.base_dn + """
592 changetype: modify
593 add: userPassword
594 userPassword: thatsAcomplPASS2
595 delete: userPassword
596 userPassword: thatsAcomplPASS1
597 """)
599 try:
600 self.ldb2.modify_ldif("""
601 dn: cn=testuser,cn=users,""" + self.base_dn + """
602 changetype: modify
603 delete: userPassword
604 userPassword: thatsAcomplPASS2
605 add: unicodePwd
606 unicodePwd:: """ + base64.b64encode("\"thatsAcomplPASS3\"".encode('utf-16-le')) + """
607 """)
608 # this passes against s4
609 except LdbError, (num, _):
610 self.assertEquals(num, ERR_ATTRIBUTE_OR_VALUE_EXISTS)
612 try:
613 self.ldb2.modify_ldif("""
614 dn: cn=testuser,cn=users,""" + self.base_dn + """
615 changetype: modify
616 delete: unicodePwd
617 unicodePwd:: """ + base64.b64encode("\"thatsAcomplPASS3\"".encode('utf-16-le')) + """
618 add: userPassword
619 userPassword: thatsAcomplPASS4
620 """)
621 # this passes against s4
622 except LdbError, (num, _):
623 self.assertEquals(num, ERR_NO_SUCH_ATTRIBUTE)
625 # Several password changes at once are allowed
626 ldb.modify_ldif("""
627 dn: cn=testuser,cn=users,""" + self.base_dn + """
628 changetype: modify
629 replace: userPassword
630 userPassword: thatsAcomplPASS1
631 userPassword: thatsAcomplPASS2
632 """)
634 # Several password changes at once are allowed
635 ldb.modify_ldif("""
636 dn: cn=testuser,cn=users,""" + self.base_dn + """
637 changetype: modify
638 replace: userPassword
639 userPassword: thatsAcomplPASS1
640 userPassword: thatsAcomplPASS2
641 replace: userPassword
642 userPassword: thatsAcomplPASS3
643 replace: userPassword
644 userPassword: thatsAcomplPASS4
645 """)
647 # This surprisingly should work
648 delete_force(self.ldb, "cn=testuser2,cn=users," + self.base_dn)
649 self.ldb.add({
650 "dn": "cn=testuser2,cn=users," + self.base_dn,
651 "objectclass": "user",
652 "userPassword": ["thatsAcomplPASS1", "thatsAcomplPASS2"] })
654 # This surprisingly should work
655 delete_force(self.ldb, "cn=testuser2,cn=users," + self.base_dn)
656 self.ldb.add({
657 "dn": "cn=testuser2,cn=users," + self.base_dn,
658 "objectclass": "user",
659 "userPassword": ["thatsAcomplPASS1", "thatsAcomplPASS1"] })
661 def test_empty_passwords(self):
662 print "Performs some empty passwords testing"
664 try:
665 self.ldb.add({
666 "dn": "cn=testuser2,cn=users," + self.base_dn,
667 "objectclass": "user",
668 "unicodePwd": [] })
669 self.fail()
670 except LdbError, (num, _):
671 self.assertEquals(num, ERR_CONSTRAINT_VIOLATION)
673 try:
674 self.ldb.add({
675 "dn": "cn=testuser2,cn=users," + self.base_dn,
676 "objectclass": "user",
677 "dBCSPwd": [] })
678 self.fail()
679 except LdbError, (num, _):
680 self.assertEquals(num, ERR_CONSTRAINT_VIOLATION)
682 try:
683 self.ldb.add({
684 "dn": "cn=testuser2,cn=users," + self.base_dn,
685 "objectclass": "user",
686 "userPassword": [] })
687 self.fail()
688 except LdbError, (num, _):
689 self.assertEquals(num, ERR_CONSTRAINT_VIOLATION)
691 try:
692 self.ldb.add({
693 "dn": "cn=testuser2,cn=users," + self.base_dn,
694 "objectclass": "user",
695 "clearTextPassword": [] })
696 self.fail()
697 except LdbError, (num, _):
698 self.assertTrue(num == ERR_CONSTRAINT_VIOLATION or
699 num == ERR_NO_SUCH_ATTRIBUTE) # for Windows
701 delete_force(self.ldb, "cn=testuser2,cn=users," + self.base_dn)
703 m = Message()
704 m.dn = Dn(ldb, "cn=testuser,cn=users," + self.base_dn)
705 m["unicodePwd"] = MessageElement([], FLAG_MOD_ADD, "unicodePwd")
706 try:
707 ldb.modify(m)
708 self.fail()
709 except LdbError, (num, _):
710 self.assertEquals(num, ERR_CONSTRAINT_VIOLATION)
712 m = Message()
713 m.dn = Dn(ldb, "cn=testuser,cn=users," + self.base_dn)
714 m["dBCSPwd"] = MessageElement([], FLAG_MOD_ADD, "dBCSPwd")
715 try:
716 ldb.modify(m)
717 self.fail()
718 except LdbError, (num, _):
719 self.assertEquals(num, ERR_CONSTRAINT_VIOLATION)
721 m = Message()
722 m.dn = Dn(ldb, "cn=testuser,cn=users," + self.base_dn)
723 m["userPassword"] = MessageElement([], FLAG_MOD_ADD, "userPassword")
724 try:
725 ldb.modify(m)
726 self.fail()
727 except LdbError, (num, _):
728 self.assertEquals(num, ERR_CONSTRAINT_VIOLATION)
730 m = Message()
731 m.dn = Dn(ldb, "cn=testuser,cn=users," + self.base_dn)
732 m["clearTextPassword"] = MessageElement([], FLAG_MOD_ADD, "clearTextPassword")
733 try:
734 ldb.modify(m)
735 self.fail()
736 except LdbError, (num, _):
737 self.assertTrue(num == ERR_CONSTRAINT_VIOLATION or
738 num == ERR_NO_SUCH_ATTRIBUTE) # for Windows
740 m = Message()
741 m.dn = Dn(ldb, "cn=testuser,cn=users," + self.base_dn)
742 m["unicodePwd"] = MessageElement([], FLAG_MOD_REPLACE, "unicodePwd")
743 try:
744 ldb.modify(m)
745 self.fail()
746 except LdbError, (num, _):
747 self.assertEquals(num, ERR_UNWILLING_TO_PERFORM)
749 m = Message()
750 m.dn = Dn(ldb, "cn=testuser,cn=users," + self.base_dn)
751 m["dBCSPwd"] = MessageElement([], FLAG_MOD_REPLACE, "dBCSPwd")
752 try:
753 ldb.modify(m)
754 self.fail()
755 except LdbError, (num, _):
756 self.assertEquals(num, ERR_UNWILLING_TO_PERFORM)
758 m = Message()
759 m.dn = Dn(ldb, "cn=testuser,cn=users," + self.base_dn)
760 m["userPassword"] = MessageElement([], FLAG_MOD_REPLACE, "userPassword")
761 try:
762 ldb.modify(m)
763 self.fail()
764 except LdbError, (num, _):
765 self.assertEquals(num, ERR_UNWILLING_TO_PERFORM)
767 m = Message()
768 m.dn = Dn(ldb, "cn=testuser,cn=users," + self.base_dn)
769 m["clearTextPassword"] = MessageElement([], FLAG_MOD_REPLACE, "clearTextPassword")
770 try:
771 ldb.modify(m)
772 self.fail()
773 except LdbError, (num, _):
774 self.assertTrue(num == ERR_UNWILLING_TO_PERFORM or
775 num == ERR_NO_SUCH_ATTRIBUTE) # for Windows
777 m = Message()
778 m.dn = Dn(ldb, "cn=testuser,cn=users," + self.base_dn)
779 m["unicodePwd"] = MessageElement([], FLAG_MOD_DELETE, "unicodePwd")
780 try:
781 ldb.modify(m)
782 self.fail()
783 except LdbError, (num, _):
784 self.assertEquals(num, ERR_UNWILLING_TO_PERFORM)
786 m = Message()
787 m.dn = Dn(ldb, "cn=testuser,cn=users," + self.base_dn)
788 m["dBCSPwd"] = MessageElement([], FLAG_MOD_DELETE, "dBCSPwd")
789 try:
790 ldb.modify(m)
791 self.fail()
792 except LdbError, (num, _):
793 self.assertEquals(num, ERR_UNWILLING_TO_PERFORM)
795 m = Message()
796 m.dn = Dn(ldb, "cn=testuser,cn=users," + self.base_dn)
797 m["userPassword"] = MessageElement([], FLAG_MOD_DELETE, "userPassword")
798 try:
799 ldb.modify(m)
800 self.fail()
801 except LdbError, (num, _):
802 self.assertEquals(num, ERR_CONSTRAINT_VIOLATION)
804 m = Message()
805 m.dn = Dn(ldb, "cn=testuser,cn=users," + self.base_dn)
806 m["clearTextPassword"] = MessageElement([], FLAG_MOD_DELETE, "clearTextPassword")
807 try:
808 ldb.modify(m)
809 self.fail()
810 except LdbError, (num, _):
811 self.assertTrue(num == ERR_CONSTRAINT_VIOLATION or
812 num == ERR_NO_SUCH_ATTRIBUTE) # for Windows
814 def test_plain_userPassword(self):
815 print "Performs testing about the standard 'userPassword' behaviour"
817 # Delete the "dSHeuristics"
818 ldb.set_dsheuristics(None)
820 time.sleep(1) # This switching time is strictly needed!
822 m = Message()
823 m.dn = Dn(ldb, "cn=testuser,cn=users," + self.base_dn)
824 m["userPassword"] = MessageElement("myPassword", FLAG_MOD_ADD,
825 "userPassword")
826 ldb.modify(m)
828 res = ldb.search("cn=testuser,cn=users," + self.base_dn,
829 scope=SCOPE_BASE, attrs=["userPassword"])
830 self.assertTrue(len(res) == 1)
831 self.assertTrue("userPassword" in res[0])
832 self.assertEquals(res[0]["userPassword"][0], "myPassword")
834 m = Message()
835 m.dn = Dn(ldb, "cn=testuser,cn=users," + self.base_dn)
836 m["userPassword"] = MessageElement("myPassword2", FLAG_MOD_REPLACE,
837 "userPassword")
838 ldb.modify(m)
840 res = ldb.search("cn=testuser,cn=users," + self.base_dn,
841 scope=SCOPE_BASE, attrs=["userPassword"])
842 self.assertTrue(len(res) == 1)
843 self.assertTrue("userPassword" in res[0])
844 self.assertEquals(res[0]["userPassword"][0], "myPassword2")
846 m = Message()
847 m.dn = Dn(ldb, "cn=testuser,cn=users," + self.base_dn)
848 m["userPassword"] = MessageElement([], FLAG_MOD_DELETE,
849 "userPassword")
850 ldb.modify(m)
852 res = ldb.search("cn=testuser,cn=users," + self.base_dn,
853 scope=SCOPE_BASE, attrs=["userPassword"])
854 self.assertTrue(len(res) == 1)
855 self.assertFalse("userPassword" in res[0])
857 # Set the test "dSHeuristics" to deactivate "userPassword" pwd changes
858 ldb.set_dsheuristics("000000000")
860 m = Message()
861 m.dn = Dn(ldb, "cn=testuser,cn=users," + self.base_dn)
862 m["userPassword"] = MessageElement("myPassword3", FLAG_MOD_REPLACE,
863 "userPassword")
864 ldb.modify(m)
866 res = ldb.search("cn=testuser,cn=users," + self.base_dn,
867 scope=SCOPE_BASE, attrs=["userPassword"])
868 self.assertTrue(len(res) == 1)
869 self.assertTrue("userPassword" in res[0])
870 self.assertEquals(res[0]["userPassword"][0], "myPassword3")
872 # Set the test "dSHeuristics" to deactivate "userPassword" pwd changes
873 ldb.set_dsheuristics("000000002")
875 m = Message()
876 m.dn = Dn(ldb, "cn=testuser,cn=users," + self.base_dn)
877 m["userPassword"] = MessageElement("myPassword4", FLAG_MOD_REPLACE,
878 "userPassword")
879 ldb.modify(m)
881 res = ldb.search("cn=testuser,cn=users," + self.base_dn,
882 scope=SCOPE_BASE, attrs=["userPassword"])
883 self.assertTrue(len(res) == 1)
884 self.assertTrue("userPassword" in res[0])
885 self.assertEquals(res[0]["userPassword"][0], "myPassword4")
887 # Reset the test "dSHeuristics" (reactivate "userPassword" pwd changes)
888 ldb.set_dsheuristics("000000001")
890 def test_zero_length(self):
891 # Get the old "minPwdLength"
892 minPwdLength = ldb.get_minPwdLength()
893 # Set it temporarely to "0"
894 ldb.set_minPwdLength("0")
896 # Get the old "pwdProperties"
897 pwdProperties = ldb.get_pwdProperties()
898 # Set them temporarely to "0" (to deactivate eventually the complexity)
899 ldb.set_pwdProperties("0")
901 ldb.setpassword("(sAMAccountName=testuser)", "")
903 # Reset the "pwdProperties" as they were before
904 ldb.set_pwdProperties(pwdProperties)
906 # Reset the "minPwdLength" as it was before
907 ldb.set_minPwdLength(minPwdLength)
909 def tearDown(self):
910 super(PasswordTests, self).tearDown()
911 delete_force(self.ldb, "cn=testuser,cn=users," + self.base_dn)
912 delete_force(self.ldb, "cn=testuser2,cn=users," + self.base_dn)
913 # Close the second LDB connection (with the user credentials)
914 self.ldb2 = None
916 if not "://" in host:
917 if os.path.isfile(host):
918 host = "tdb://%s" % host
919 else:
920 host = "ldap://%s" % host
922 ldb = SamDB(url=host, session_info=system_session(), credentials=creds, lp=lp)
924 # Gets back the basedn
925 base_dn = ldb.domain_dn()
927 # Gets back the configuration basedn
928 configuration_dn = ldb.get_config_basedn().get_linearized()
930 # Get the old "dSHeuristics" if it was set
931 dsheuristics = ldb.get_dsheuristics()
933 # Set the "dSHeuristics" to activate the correct "userPassword" behaviour
934 ldb.set_dsheuristics("000000001")
936 # Get the old "minPwdAge"
937 minPwdAge = ldb.get_minPwdAge()
939 # Set it temporarely to "0"
940 ldb.set_minPwdAge("0")
942 runner = SubunitTestRunner()
943 rc = 0
944 if not runner.run(unittest.makeSuite(PasswordTests)).wasSuccessful():
945 rc = 1
947 # Reset the "dSHeuristics" as they were before
948 ldb.set_dsheuristics(dsheuristics)
950 # Reset the "minPwdAge" as it was before
951 ldb.set_minPwdAge(minPwdAge)
953 sys.exit(rc)