s4-dsdb/tests/python: Explicitly pass comamnd line LoadParm() instance to system_sess...
[kai/samba.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 time
15 import os
16
17 sys.path.append("bin/python")
18 import samba
19 samba.ensure_external_module("testtools", "testtools")
20 samba.ensure_external_module("subunit", "subunit/python")
21
22 import samba.getopt as options
23
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
39
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()
48
49 if len(args) < 1:
50     parser.print_usage()
51     sys.exit(1)
52
53 host = args[0]
54
55 lp = sambaopts.get_loadparm()
56 creds = credopts.get_credentials(lp)
57
58 # Force an encrypted connection
59 creds.set_gensec_features(creds.get_gensec_features() | gensec.FEATURE_SEAL)
60
61 #
62 # Tests start here
63 #
64
65 class PasswordTests(samba.tests.TestCase):
66
67     def setUp(self):
68         super(PasswordTests, self).setUp()
69         self.ldb = ldb
70         self.base_dn = ldb.domain_dn()
71
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"})
78
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)
96
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 """)
108
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)
121
122         # Enables the user account
123         self.ldb.enable_account("(sAMAccountName=testuser)")
124
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)
137
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
141
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)
151
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
155
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)
169
170     def test_unicodePwd_clear_set(self):
171         print "Performs a password cleartext set operation on 'unicodePwd'"
172
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)
178
179     def test_unicodePwd_clear_change(self):
180         print "Performs a password cleartext change operation on 'unicodePwd'"
181
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 """)
190
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)
205
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)
220
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
224
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)
234
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
238
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)
251
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
256
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)
262
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
267
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 """)
276
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)
291
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)
306
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
310
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)
322
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
326
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)
341
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)
358
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)
375
376     def test_failures(self):
377         print "Performs some failure testing"
378
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)
389
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)
400
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)
410
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)
420
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)
431
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)
442
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)
456
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)
470
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)
484
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)
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 add: userPassword
508 userPassword: thatsAcomplPASS2
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 add: userPassword
523 userPassword: thatsAcomplPASS2
524 """)
525             self.fail()
526         except LdbError, (num, _):
527             self.assertEquals(num, ERR_INSUFFICIENT_ACCESS_RIGHTS)
528
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)
543
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)
558
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)
573
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)
588
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 """)
598
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)
611
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)
624
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 """)
633
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 """)
646
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"] })
653
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"] })
660
661     def test_empty_passwords(self):
662         print "Performs some empty passwords testing"
663
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)
672
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)
681
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)
690
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
700
701         delete_force(self.ldb, "cn=testuser2,cn=users," + self.base_dn)
702
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)
711
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)
720
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)
729
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
739
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)
748
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)
757
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)
766
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
776
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)
785
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)
794
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)
803
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
813
814     def test_plain_userPassword(self):
815         print "Performs testing about the standard 'userPassword' behaviour"
816
817         # Delete the "dSHeuristics"
818         ldb.set_dsheuristics(None)
819
820         time.sleep(1) # This switching time is strictly needed!
821
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)
827
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")
833
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)
839
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")
845
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)
851
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])
856
857         # Set the test "dSHeuristics" to deactivate "userPassword" pwd changes
858         ldb.set_dsheuristics("000000000")
859
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)
865
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")
871
872         # Set the test "dSHeuristics" to deactivate "userPassword" pwd changes
873         ldb.set_dsheuristics("000000002")
874
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)
880
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")
886
887         # Reset the test "dSHeuristics" (reactivate "userPassword" pwd changes)
888         ldb.set_dsheuristics("000000001")
889
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")
895
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")
900
901         ldb.setpassword("(sAMAccountName=testuser)", "")
902
903         # Reset the "pwdProperties" as they were before
904         ldb.set_pwdProperties(pwdProperties)
905
906         # Reset the "minPwdLength" as it was before
907         ldb.set_minPwdLength(minPwdLength)
908
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
915
916 if not "://" in host:
917     if os.path.isfile(host):
918         host = "tdb://%s" % host
919     else:
920         host = "ldap://%s" % host
921
922 ldb = SamDB(url=host, session_info=system_session(lp), credentials=creds, lp=lp)
923
924 # Gets back the basedn
925 base_dn = ldb.domain_dn()
926
927 # Gets back the configuration basedn
928 configuration_dn = ldb.get_config_basedn().get_linearized()
929
930 # Get the old "dSHeuristics" if it was set
931 dsheuristics = ldb.get_dsheuristics()
932
933 # Set the "dSHeuristics" to activate the correct "userPassword" behaviour
934 ldb.set_dsheuristics("000000001")
935
936 # Get the old "minPwdAge"
937 minPwdAge = ldb.get_minPwdAge()
938
939 # Set it temporarely to "0"
940 ldb.set_minPwdAge("0")
941
942 runner = SubunitTestRunner()
943 rc = 0
944 if not runner.run(unittest.makeSuite(PasswordTests)).wasSuccessful():
945     rc = 1
946
947 # Reset the "dSHeuristics" as they were before
948 ldb.set_dsheuristics(dsheuristics)
949
950 # Reset the "minPwdAge" as it was before
951 ldb.set_minPwdAge(minPwdAge)
952
953 sys.exit(rc)