~ubuntu-branches/ubuntu/vivid/samba/vivid

« back to all changes in this revision

Viewing changes to source4/dsdb/tests/python/passwords.py

  • Committer: Package Import Robot
  • Author(s): Chuck Short
  • Date: 2011-12-21 13:18:04 UTC
  • mfrom: (0.39.21 sid)
  • Revision ID: package-import@ubuntu.com-20111221131804-xtlr39wx6njehxxr
Tags: 2:3.6.1-3ubuntu1
* Merge from Debian testing.  Remaining changes:
  + debian/patches/VERSION.patch:
    - set SAMBA_VERSION_SUFFIX to Ubuntu.
  + debian/patches/error-trans.fix-276472:
    - Add the translation of Unix Error code -ENOTSUP to NT Error Code
    - NT_STATUS_NOT_SUPPORTED to prevent the Permission denied error.
  + debian/smb.conf:
    - add "(Samba, Ubuntu)" to server string.
    - comment out the default [homes] share, and add a comment about
      "valid users = %S" to show users how to restrict access to
      \\server\username to only username.
    - Set 'usershare allow guests', so that usershare admins are 
      allowed to create public shares in addition to authenticated
      ones.
    - add map to guest = Bad user, maps bad username to guest access.
  + debian/samba-common.config:
    - Do not change priority to high if dhclient3 is installed.
    - Use priority medium instead of high for the workgroup question.
  + debian/control:
    - Don't build against or suggest ctdb.
    - Add dependency on samba-common-bin to samba.
  + Add ufw integration:
    - Created debian/samba.ufw.profile
    - debian/rules, debian/samba.dirs, debian/samba.files: install
      profile
    - debian/control: have samba suggest ufw
  + Add apport hook:
    - Created debian/source_samba.py.
    - debian/rules, debian/samba.dirs, debian/samba-common-bin.files: install
  + Switch to upstart:
    - Add debian/samba.{nmbd,smbd}.upstart.
  + debian/samba.logrotate, debian/samba-common.dhcp, debian/samba.if-up:
    - Make them upstart compatible
  + debian/samba.postinst: 
    - Avoid scary pdbedit warnings on first import.
  + debian/samba-common.postinst: Add more informative error message for
    the case where smb.conf was manually deleted
  + debian/patches/fix-debuglevel-name-conflict.patch: don't use 'debug_level'
    as a global variable name in an NSS module 
  + Dropped:
    - debian/patches/error-trans.fix-276472
    - debian/patches/fix-debuglevel-name-conflict.patch

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
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.insert(0, "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_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)