PEP8: fix E202: whitespace before ')'
[samba.git] / source4 / dsdb / tests / python / sam.py
1 #!/usr/bin/env python
2 # -*- coding: utf-8 -*-
3 # This is a port of the original in testprogs/ejs/ldap.js
4
5 from __future__ import print_function
6 import optparse
7 import sys
8 import os
9 import time
10
11 sys.path.insert(0, "bin/python")
12 import samba
13 from samba.tests.subunitrun import SubunitOptions, TestProgram
14
15 import samba.getopt as options
16
17 from samba.credentials import Credentials, DONT_USE_KERBEROS
18 from samba.auth import system_session
19 from ldb import SCOPE_BASE, LdbError
20 from ldb import ERR_NO_SUCH_OBJECT, ERR_ATTRIBUTE_OR_VALUE_EXISTS
21 from ldb import ERR_ENTRY_ALREADY_EXISTS, ERR_UNWILLING_TO_PERFORM
22 from ldb import ERR_OTHER, ERR_NO_SUCH_ATTRIBUTE
23 from ldb import ERR_OBJECT_CLASS_VIOLATION
24 from ldb import ERR_CONSTRAINT_VIOLATION
25 from ldb import ERR_UNDEFINED_ATTRIBUTE_TYPE
26 from ldb import ERR_INSUFFICIENT_ACCESS_RIGHTS
27 from ldb import ERR_INVALID_CREDENTIALS
28 from ldb import ERR_STRONG_AUTH_REQUIRED
29 from ldb import Message, MessageElement, Dn
30 from ldb import FLAG_MOD_ADD, FLAG_MOD_REPLACE, FLAG_MOD_DELETE
31 from samba.samdb import SamDB
32 from samba.dsdb import (UF_NORMAL_ACCOUNT, UF_ACCOUNTDISABLE,
33                         UF_WORKSTATION_TRUST_ACCOUNT, UF_SERVER_TRUST_ACCOUNT,
34                         UF_PARTIAL_SECRETS_ACCOUNT, UF_TEMP_DUPLICATE_ACCOUNT,
35                         UF_INTERDOMAIN_TRUST_ACCOUNT, UF_SMARTCARD_REQUIRED,
36                         UF_PASSWD_NOTREQD, UF_LOCKOUT, UF_PASSWORD_EXPIRED, ATYPE_NORMAL_ACCOUNT,
37                         GTYPE_SECURITY_BUILTIN_LOCAL_GROUP, GTYPE_SECURITY_DOMAIN_LOCAL_GROUP,
38                         GTYPE_SECURITY_GLOBAL_GROUP, GTYPE_SECURITY_UNIVERSAL_GROUP,
39                         GTYPE_DISTRIBUTION_DOMAIN_LOCAL_GROUP, GTYPE_DISTRIBUTION_GLOBAL_GROUP,
40                         GTYPE_DISTRIBUTION_UNIVERSAL_GROUP,
41                         ATYPE_SECURITY_GLOBAL_GROUP, ATYPE_SECURITY_UNIVERSAL_GROUP,
42                         ATYPE_SECURITY_LOCAL_GROUP, ATYPE_DISTRIBUTION_GLOBAL_GROUP,
43                         ATYPE_DISTRIBUTION_UNIVERSAL_GROUP, ATYPE_DISTRIBUTION_LOCAL_GROUP,
44                         ATYPE_WORKSTATION_TRUST)
45 from samba.dcerpc.security import (DOMAIN_RID_USERS, DOMAIN_RID_ADMINS,
46                                    DOMAIN_RID_DOMAIN_MEMBERS, DOMAIN_RID_DCS, DOMAIN_RID_READONLY_DCS)
47
48 from samba.ndr import ndr_unpack
49 from samba.dcerpc import drsblobs
50 from samba.dcerpc import drsuapi
51 from samba.dcerpc import security
52 from samba.tests import delete_force
53 from samba import gensec
54 from samba import werror
55
56 parser = optparse.OptionParser("sam.py [options] <host>")
57 sambaopts = options.SambaOptions(parser)
58 parser.add_option_group(sambaopts)
59 parser.add_option_group(options.VersionOptions(parser))
60 # use command line creds if available
61 credopts = options.CredentialsOptions(parser)
62 parser.add_option_group(credopts)
63 subunitopts = SubunitOptions(parser)
64 parser.add_option_group(subunitopts)
65 opts, args = parser.parse_args()
66
67 if len(args) < 1:
68     parser.print_usage()
69     sys.exit(1)
70
71 host = args[0]
72
73 lp = sambaopts.get_loadparm()
74 creds = credopts.get_credentials(lp)
75 creds.set_gensec_features(creds.get_gensec_features() | gensec.FEATURE_SEAL)
76
77 class SamTests(samba.tests.TestCase):
78
79     def setUp(self):
80         super(SamTests, self).setUp()
81         self.ldb = ldb
82         self.base_dn = ldb.domain_dn()
83
84         print("baseDN: %s\n" % self.base_dn)
85
86         delete_force(self.ldb, "cn=ldaptestuser,cn=users," + self.base_dn)
87         delete_force(self.ldb, "cn=ldaptestuser2,cn=users," + self.base_dn)
88         delete_force(self.ldb, "cn=ldaptest\,specialuser,cn=users," + self.base_dn)
89         delete_force(self.ldb, "cn=ldaptestcomputer,cn=computers," + self.base_dn)
90         delete_force(self.ldb, "cn=ldaptestgroup,cn=users," + self.base_dn)
91         delete_force(self.ldb, "cn=ldaptestgroup2,cn=users," + self.base_dn)
92
93     def test_users_groups(self):
94         """This tests the SAM users and groups behaviour"""
95         print("Testing users and groups behaviour\n")
96
97         ldb.add({
98             "dn": "cn=ldaptestgroup,cn=users," + self.base_dn,
99             "objectclass": "group"})
100
101         ldb.add({
102             "dn": "cn=ldaptestgroup2,cn=users," + self.base_dn,
103             "objectclass": "group"})
104
105         res1 = ldb.search("cn=ldaptestgroup,cn=users," + self.base_dn,
106                           scope=SCOPE_BASE, attrs=["objectSID"])
107         self.assertTrue(len(res1) == 1)
108         group_rid_1 = security.dom_sid(ldb.schema_format_value("objectSID",
109                                                                res1[0]["objectSID"][0])).split()[1]
110
111         res1 = ldb.search("cn=ldaptestgroup2,cn=users," + self.base_dn,
112                           scope=SCOPE_BASE, attrs=["objectSID"])
113         self.assertTrue(len(res1) == 1)
114         group_rid_2 = security.dom_sid(ldb.schema_format_value("objectSID",
115                                                                res1[0]["objectSID"][0])).split()[1]
116
117         # Try to create a user with an invalid account name
118         try:
119             ldb.add({
120                 "dn": "cn=ldaptestuser,cn=users," + self.base_dn,
121                 "objectclass": "user",
122                 "sAMAccountName": "administrator"})
123             self.fail()
124         except LdbError as e9:
125             (num, _) = e9.args
126             self.assertEquals(num, ERR_ENTRY_ALREADY_EXISTS)
127         delete_force(self.ldb, "cn=ldaptestuser,cn=users," + self.base_dn)
128
129         # Try to create a user with an invalid account name
130         try:
131             ldb.add({
132                 "dn": "cn=ldaptestuser,cn=users," + self.base_dn,
133                 "objectclass": "user",
134                 "sAMAccountName": []})
135             self.fail()
136         except LdbError as e10:
137             (num, _) = e10.args
138             self.assertEquals(num, ERR_CONSTRAINT_VIOLATION)
139         delete_force(self.ldb, "cn=ldaptestuser,cn=users," + self.base_dn)
140
141         # Try to create a user with an invalid primary group
142         try:
143             ldb.add({
144                 "dn": "cn=ldaptestuser,cn=users," + self.base_dn,
145                 "objectclass": "user",
146                 "primaryGroupID": "0"})
147             self.fail()
148         except LdbError as e11:
149             (num, _) = e11.args
150             self.assertEquals(num, ERR_UNWILLING_TO_PERFORM)
151         delete_force(self.ldb, "cn=ldaptestuser,cn=users," + self.base_dn)
152
153         # Try to Create a user with a valid primary group
154         try:
155             ldb.add({
156                 "dn": "cn=ldaptestuser,cn=users," + self.base_dn,
157                 "objectclass": "user",
158                 "primaryGroupID": str(group_rid_1)})
159             self.fail()
160         except LdbError as e12:
161             (num, _) = e12.args
162             self.assertEquals(num, ERR_UNWILLING_TO_PERFORM)
163         delete_force(self.ldb, "cn=ldaptestuser,cn=users," + self.base_dn)
164
165         # Test to see how we should behave when the user account doesn't
166         # exist
167         m = Message()
168         m.dn = Dn(ldb, "cn=ldaptestuser,cn=users," + self.base_dn)
169         m["primaryGroupID"] = MessageElement("0", FLAG_MOD_REPLACE,
170                                              "primaryGroupID")
171         try:
172             ldb.modify(m)
173             self.fail()
174         except LdbError as e13:
175             (num, _) = e13.args
176             self.assertEquals(num, ERR_NO_SUCH_OBJECT)
177
178         # Test to see how we should behave when the account isn't a user
179         m = Message()
180         m.dn = Dn(ldb, "cn=ldaptestgroup,cn=users," + self.base_dn)
181         m["primaryGroupID"] = MessageElement("0", FLAG_MOD_REPLACE,
182                                              "primaryGroupID")
183         try:
184             ldb.modify(m)
185             self.fail()
186         except LdbError as e14:
187             (num, _) = e14.args
188             self.assertEquals(num, ERR_OBJECT_CLASS_VIOLATION)
189
190         # Test default primary groups on add operations
191
192         ldb.add({
193             "dn": "cn=ldaptestuser,cn=users," + self.base_dn,
194             "objectclass": "user"})
195
196         res1 = ldb.search("cn=ldaptestuser,cn=users," + self.base_dn,
197                           scope=SCOPE_BASE, attrs=["primaryGroupID"])
198         self.assertTrue(len(res1) == 1)
199         self.assertEquals(res1[0]["primaryGroupID"][0], str(DOMAIN_RID_USERS))
200
201         delete_force(self.ldb, "cn=ldaptestuser,cn=users," + self.base_dn)
202
203         ldb.add({
204             "dn": "cn=ldaptestuser,cn=users," + self.base_dn,
205             "objectclass": "user",
206             "userAccountControl": str(UF_NORMAL_ACCOUNT | UF_PASSWD_NOTREQD)})
207
208         res1 = ldb.search("cn=ldaptestuser,cn=users," + self.base_dn,
209                           scope=SCOPE_BASE, attrs=["primaryGroupID"])
210         self.assertTrue(len(res1) == 1)
211         self.assertEquals(res1[0]["primaryGroupID"][0], str(DOMAIN_RID_USERS))
212
213         delete_force(self.ldb, "cn=ldaptestuser,cn=users," + self.base_dn)
214
215         # unfortunately the INTERDOMAIN_TRUST_ACCOUNT case cannot be tested
216         # since such accounts aren't directly creatable (ACCESS_DENIED)
217
218         ldb.add({
219             "dn": "cn=ldaptestuser,cn=users," + self.base_dn,
220             "objectclass": "computer",
221             "userAccountControl": str(UF_WORKSTATION_TRUST_ACCOUNT | UF_PASSWD_NOTREQD)})
222
223         res1 = ldb.search("cn=ldaptestuser,cn=users," + self.base_dn,
224                           scope=SCOPE_BASE, attrs=["primaryGroupID"])
225         self.assertTrue(len(res1) == 1)
226         self.assertEquals(res1[0]["primaryGroupID"][0], str(DOMAIN_RID_DOMAIN_MEMBERS))
227
228         delete_force(self.ldb, "cn=ldaptestuser,cn=users," + self.base_dn)
229
230         ldb.add({
231             "dn": "cn=ldaptestuser,cn=users," + self.base_dn,
232             "objectclass": "computer",
233             "userAccountControl": str(UF_SERVER_TRUST_ACCOUNT | UF_PASSWD_NOTREQD)})
234
235         res1 = ldb.search("cn=ldaptestuser,cn=users," + self.base_dn,
236                           scope=SCOPE_BASE, attrs=["primaryGroupID"])
237         self.assertTrue(len(res1) == 1)
238         self.assertEquals(res1[0]["primaryGroupID"][0], str(DOMAIN_RID_DCS))
239
240         delete_force(self.ldb, "cn=ldaptestuser,cn=users," + self.base_dn)
241
242         # Read-only DC accounts are only creatable by
243         # UF_WORKSTATION_TRUST_ACCOUNT and work only on DCs >= 2008 (therefore
244         # we have a fallback in the assertion)
245         ldb.add({
246             "dn": "cn=ldaptestuser,cn=users," + self.base_dn,
247             "objectclass": "computer",
248             "userAccountControl": str(UF_PARTIAL_SECRETS_ACCOUNT | UF_WORKSTATION_TRUST_ACCOUNT | UF_PASSWD_NOTREQD)})
249
250         res1 = ldb.search("cn=ldaptestuser,cn=users," + self.base_dn,
251                           scope=SCOPE_BASE, attrs=["primaryGroupID"])
252         self.assertTrue(len(res1) == 1)
253         self.assertTrue(res1[0]["primaryGroupID"][0] == str(DOMAIN_RID_READONLY_DCS) or
254                         res1[0]["primaryGroupID"][0] == str(DOMAIN_RID_DOMAIN_MEMBERS))
255
256         delete_force(self.ldb, "cn=ldaptestuser,cn=users," + self.base_dn)
257
258         # Test default primary groups on modify operations
259
260         ldb.add({
261             "dn": "cn=ldaptestuser,cn=users," + self.base_dn,
262             "objectclass": "user"})
263
264         m = Message()
265         m.dn = Dn(ldb, "cn=ldaptestuser,cn=users," + self.base_dn)
266         m["userAccountControl"] = MessageElement(str(UF_NORMAL_ACCOUNT | UF_PASSWD_NOTREQD), FLAG_MOD_REPLACE,
267                                                  "userAccountControl")
268         ldb.modify(m)
269
270         res1 = ldb.search("cn=ldaptestuser,cn=users," + self.base_dn,
271                           scope=SCOPE_BASE, attrs=["primaryGroupID"])
272         self.assertTrue(len(res1) == 1)
273         self.assertEquals(res1[0]["primaryGroupID"][0], str(DOMAIN_RID_USERS))
274
275         # unfortunately the INTERDOMAIN_TRUST_ACCOUNT case cannot be tested
276         # since such accounts aren't directly creatable (ACCESS_DENIED)
277
278         delete_force(self.ldb, "cn=ldaptestuser,cn=users," + self.base_dn)
279
280         ldb.add({
281             "dn": "cn=ldaptestuser,cn=users," + self.base_dn,
282             "objectclass": "computer"})
283
284         res1 = ldb.search("cn=ldaptestuser,cn=users," + self.base_dn,
285                           scope=SCOPE_BASE, attrs=["primaryGroupID"])
286         self.assertTrue(len(res1) == 1)
287         self.assertEquals(res1[0]["primaryGroupID"][0], str(DOMAIN_RID_USERS))
288
289         m = Message()
290         m.dn = Dn(ldb, "cn=ldaptestuser,cn=users," + self.base_dn)
291         m["userAccountControl"] = MessageElement(str(UF_WORKSTATION_TRUST_ACCOUNT | UF_PASSWD_NOTREQD), FLAG_MOD_REPLACE,
292                                                  "userAccountControl")
293         ldb.modify(m)
294
295         res1 = ldb.search("cn=ldaptestuser,cn=users," + self.base_dn,
296                           scope=SCOPE_BASE, attrs=["primaryGroupID"])
297         self.assertTrue(len(res1) == 1)
298         self.assertEquals(res1[0]["primaryGroupID"][0], str(DOMAIN_RID_DOMAIN_MEMBERS))
299
300         m = Message()
301         m.dn = Dn(ldb, "cn=ldaptestuser,cn=users," + self.base_dn)
302         m["userAccountControl"] = MessageElement(str(UF_SERVER_TRUST_ACCOUNT | UF_PASSWD_NOTREQD), FLAG_MOD_REPLACE,
303                                                  "userAccountControl")
304         ldb.modify(m)
305
306         res1 = ldb.search("cn=ldaptestuser,cn=users," + self.base_dn,
307                           scope=SCOPE_BASE, attrs=["primaryGroupID"])
308         self.assertTrue(len(res1) == 1)
309         self.assertEquals(res1[0]["primaryGroupID"][0], str(DOMAIN_RID_DCS))
310
311         # Read-only DC accounts are only creatable by
312         # UF_WORKSTATION_TRUST_ACCOUNT and work only on DCs >= 2008 (therefore
313         # we have a fallback in the assertion)
314         m = Message()
315         m.dn = Dn(ldb, "cn=ldaptestuser,cn=users," + self.base_dn)
316         m["userAccountControl"] = MessageElement(str(UF_PARTIAL_SECRETS_ACCOUNT | UF_WORKSTATION_TRUST_ACCOUNT | UF_PASSWD_NOTREQD), FLAG_MOD_REPLACE,
317                                                  "userAccountControl")
318         ldb.modify(m)
319
320         res1 = ldb.search("cn=ldaptestuser,cn=users," + self.base_dn,
321                           scope=SCOPE_BASE, attrs=["primaryGroupID"])
322         self.assertTrue(len(res1) == 1)
323         self.assertTrue(res1[0]["primaryGroupID"][0] == str(DOMAIN_RID_READONLY_DCS) or
324                         res1[0]["primaryGroupID"][0] == str(DOMAIN_RID_DOMAIN_MEMBERS))
325
326         delete_force(self.ldb, "cn=ldaptestuser,cn=users," + self.base_dn)
327
328         # Recreate account for further tests
329
330         ldb.add({
331             "dn": "cn=ldaptestuser,cn=users," + self.base_dn,
332             "objectclass": "user"})
333
334         # Try to set an invalid account name
335         m = Message()
336         m.dn = Dn(ldb, "cn=ldaptestuser,cn=users," + self.base_dn)
337         m["sAMAccountName"] = MessageElement("administrator", FLAG_MOD_REPLACE,
338                                              "sAMAccountName")
339         try:
340             ldb.modify(m)
341             self.fail()
342         except LdbError as e15:
343             (num, _) = e15.args
344             self.assertEquals(num, ERR_ENTRY_ALREADY_EXISTS)
345
346         # But to reset the actual "sAMAccountName" should still be possible
347         res1 = ldb.search("cn=ldaptestuser,cn=users," + self.base_dn,
348                           scope=SCOPE_BASE, attrs=["sAMAccountName"])
349         self.assertTrue(len(res1) == 1)
350         m = Message()
351         m.dn = Dn(ldb, "cn=ldaptestuser,cn=users," + self.base_dn)
352         m["sAMAccountName"] = MessageElement(res1[0]["sAMAccountName"][0], FLAG_MOD_REPLACE,
353                                              "sAMAccountName")
354         ldb.modify(m)
355
356         # And another (free) name should be possible as well
357         m = Message()
358         m.dn = Dn(ldb, "cn=ldaptestuser,cn=users," + self.base_dn)
359         m["sAMAccountName"] = MessageElement("xxx_ldaptestuser_xxx", FLAG_MOD_REPLACE,
360                                              "sAMAccountName")
361         ldb.modify(m)
362
363         # We should be able to reset our actual primary group
364         m = Message()
365         m.dn = Dn(ldb, "cn=ldaptestuser,cn=users," + self.base_dn)
366         m["primaryGroupID"] = MessageElement(str(DOMAIN_RID_USERS), FLAG_MOD_REPLACE,
367                                              "primaryGroupID")
368         ldb.modify(m)
369
370         # Try to add invalid primary group
371         m = Message()
372         m.dn = Dn(ldb, "cn=ldaptestuser,cn=users," + self.base_dn)
373         m["primaryGroupID"] = MessageElement("0", FLAG_MOD_REPLACE,
374                                              "primaryGroupID")
375         try:
376             ldb.modify(m)
377             self.fail()
378         except LdbError as e16:
379             (num, _) = e16.args
380             self.assertEquals(num, ERR_UNWILLING_TO_PERFORM)
381
382         # Try to make group 1 primary - should be denied since it is not yet
383         # secondary
384         m = Message()
385         m.dn = Dn(ldb, "cn=ldaptestuser,cn=users," + self.base_dn)
386         m["primaryGroupID"] = MessageElement(str(group_rid_1),
387                                              FLAG_MOD_REPLACE, "primaryGroupID")
388         try:
389             ldb.modify(m)
390             self.fail()
391         except LdbError as e17:
392             (num, _) = e17.args
393             self.assertEquals(num, ERR_UNWILLING_TO_PERFORM)
394
395         # Make group 1 secondary
396         m = Message()
397         m.dn = Dn(ldb, "cn=ldaptestgroup,cn=users," + self.base_dn)
398         m["member"] = MessageElement("cn=ldaptestuser,cn=users," + self.base_dn,
399                                      FLAG_MOD_REPLACE, "member")
400         ldb.modify(m)
401
402         # Make group 1 primary
403         m = Message()
404         m.dn = Dn(ldb, "cn=ldaptestuser,cn=users," + self.base_dn)
405         m["primaryGroupID"] = MessageElement(str(group_rid_1),
406                                              FLAG_MOD_REPLACE, "primaryGroupID")
407         ldb.modify(m)
408
409         # Try to delete group 1 - should be denied
410         try:
411             ldb.delete("cn=ldaptestgroup,cn=users," + self.base_dn)
412             self.fail()
413         except LdbError as e18:
414             (num, _) = e18.args
415             self.assertEquals(num, ERR_ENTRY_ALREADY_EXISTS)
416
417         # Try to add group 1 also as secondary - should be denied
418         m = Message()
419         m.dn = Dn(ldb, "cn=ldaptestgroup,cn=users," + self.base_dn)
420         m["member"] = MessageElement("cn=ldaptestuser,cn=users," + self.base_dn,
421                                      FLAG_MOD_ADD, "member")
422         try:
423             ldb.modify(m)
424             self.fail()
425         except LdbError as e19:
426             (num, _) = e19.args
427             self.assertEquals(num, ERR_ENTRY_ALREADY_EXISTS)
428
429         # Try to add invalid member to group 1 - should be denied
430         m = Message()
431         m.dn = Dn(ldb, "cn=ldaptestgroup,cn=users," + self.base_dn)
432         m["member"] = MessageElement(
433             "cn=ldaptestuser3,cn=users," + self.base_dn,
434             FLAG_MOD_ADD, "member")
435         try:
436             ldb.modify(m)
437             self.fail()
438         except LdbError as e20:
439             (num, _) = e20.args
440             self.assertEquals(num, ERR_NO_SUCH_OBJECT)
441
442         # Make group 2 secondary
443         m = Message()
444         m.dn = Dn(ldb, "cn=ldaptestgroup2,cn=users," + self.base_dn)
445         m["member"] = MessageElement("cn=ldaptestuser,cn=users," + self.base_dn,
446                                      FLAG_MOD_ADD, "member")
447         ldb.modify(m)
448
449         # Swap the groups
450         m = Message()
451         m.dn = Dn(ldb, "cn=ldaptestuser,cn=users," + self.base_dn)
452         m["primaryGroupID"] = MessageElement(str(group_rid_2),
453                                              FLAG_MOD_REPLACE, "primaryGroupID")
454         ldb.modify(m)
455
456         # Swap the groups (does not really make sense but does the same)
457         m = Message()
458         m.dn = Dn(ldb, "cn=ldaptestuser,cn=users," + self.base_dn)
459         m["primaryGroupID"] = MessageElement(str(group_rid_1),
460                                              FLAG_MOD_REPLACE, "primaryGroupID")
461         m["primaryGroupID"] = MessageElement(str(group_rid_2),
462                                              FLAG_MOD_REPLACE, "primaryGroupID")
463         ldb.modify(m)
464
465         # Old primary group should contain a "member" attribute for the user,
466         # the new shouldn't contain anymore one
467         res1 = ldb.search("cn=ldaptestgroup, cn=users," + self.base_dn,
468                           scope=SCOPE_BASE, attrs=["member"])
469         self.assertTrue(len(res1) == 1)
470         self.assertTrue(len(res1[0]["member"]) == 1)
471         self.assertEquals(res1[0]["member"][0].lower(),
472                           ("cn=ldaptestuser,cn=users," + self.base_dn).lower())
473
474         res1 = ldb.search("cn=ldaptestgroup2, cn=users," + self.base_dn,
475                           scope=SCOPE_BASE, attrs=["member"])
476         self.assertTrue(len(res1) == 1)
477         self.assertFalse("member" in res1[0])
478
479         # Primary group member
480         m = Message()
481         m.dn = Dn(ldb, "cn=ldaptestgroup2,cn=users," + self.base_dn)
482         m["member"] = MessageElement("cn=ldaptestuser,cn=users," + self.base_dn,
483                                      FLAG_MOD_DELETE, "member")
484         try:
485             ldb.modify(m)
486             self.fail()
487         except LdbError as e21:
488             (num, _) = e21.args
489             self.assertEquals(num, ERR_UNWILLING_TO_PERFORM)
490
491         # Delete invalid group member
492         m = Message()
493         m.dn = Dn(ldb, "cn=ldaptestgroup2,cn=users," + self.base_dn)
494         m["member"] = MessageElement("cn=ldaptestuser1,cn=users," + self.base_dn,
495                                      FLAG_MOD_DELETE, "member")
496         try:
497             ldb.modify(m)
498             self.fail()
499         except LdbError as e22:
500             (num, _) = e22.args
501             self.assertEquals(num, ERR_UNWILLING_TO_PERFORM)
502
503         # Also this should be denied
504         try:
505             ldb.add({
506                 "dn": "cn=ldaptestuser2,cn=users," + self.base_dn,
507                 "objectclass": "user",
508                 "primaryGroupID": "0"})
509             self.fail()
510         except LdbError as e23:
511             (num, _) = e23.args
512             self.assertEquals(num, ERR_UNWILLING_TO_PERFORM)
513
514         # Recreate user accounts
515
516         delete_force(self.ldb, "cn=ldaptestuser,cn=users," + self.base_dn)
517
518         ldb.add({
519             "dn": "cn=ldaptestuser,cn=users," + self.base_dn,
520             "objectclass": "user"})
521
522         ldb.add({
523             "dn": "cn=ldaptestuser2,cn=users," + self.base_dn,
524             "objectclass": "user"})
525
526         m = Message()
527         m.dn = Dn(ldb, "cn=ldaptestgroup2,cn=users," + self.base_dn)
528         m["member"] = MessageElement("cn=ldaptestuser,cn=users," + self.base_dn,
529                                      FLAG_MOD_ADD, "member")
530         ldb.modify(m)
531
532         # Already added
533         m = Message()
534         m.dn = Dn(ldb, "cn=ldaptestgroup2,cn=users," + self.base_dn)
535         m["member"] = MessageElement("cn=ldaptestuser,cn=users," + self.base_dn,
536                                      FLAG_MOD_ADD, "member")
537         try:
538             ldb.modify(m)
539             self.fail()
540         except LdbError as e24:
541             (num, _) = e24.args
542             self.assertEquals(num, ERR_ENTRY_ALREADY_EXISTS)
543
544         # Already added, but as <SID=...>
545         res1 = ldb.search("cn=ldaptestuser,cn=users," + self.base_dn,
546                           scope=SCOPE_BASE, attrs=["objectSid"])
547         self.assertTrue(len(res1) == 1)
548         sid_bin = res1[0]["objectSid"][0]
549         sid_str = ("<SID=" + ldb.schema_format_value("objectSid", sid_bin) + ">").upper()
550
551         m = Message()
552         m.dn = Dn(ldb, "cn=ldaptestgroup2,cn=users," + self.base_dn)
553         m["member"] = MessageElement(sid_str, FLAG_MOD_ADD, "member")
554         try:
555             ldb.modify(m)
556             self.fail()
557         except LdbError as e25:
558             (num, _) = e25.args
559             self.assertEquals(num, ERR_ENTRY_ALREADY_EXISTS)
560
561         # Invalid member
562         m = Message()
563         m.dn = Dn(ldb, "cn=ldaptestgroup2,cn=users," + self.base_dn)
564         m["member"] = MessageElement("cn=ldaptestuser1,cn=users," + self.base_dn,
565                                      FLAG_MOD_REPLACE, "member")
566         try:
567             ldb.modify(m)
568             self.fail()
569         except LdbError as e26:
570             (num, _) = e26.args
571             self.assertEquals(num, ERR_NO_SUCH_OBJECT)
572
573         # Invalid member
574         m = Message()
575         m.dn = Dn(ldb, "cn=ldaptestgroup2,cn=users," + self.base_dn)
576         m["member"] = MessageElement(["cn=ldaptestuser,cn=users," + self.base_dn,
577                                       "cn=ldaptestuser1,cn=users," + self.base_dn],
578                                      FLAG_MOD_REPLACE, "member")
579         try:
580             ldb.modify(m)
581             self.fail()
582         except LdbError as e27:
583             (num, _) = e27.args
584             self.assertEquals(num, ERR_NO_SUCH_OBJECT)
585
586         # Invalid member
587         m = Message()
588         m.dn = Dn(ldb, "cn=ldaptestgroup2,cn=users," + self.base_dn)
589         m["member"] = MessageElement("cn=ldaptestuser,cn=users," + self.base_dn,
590                                      FLAG_MOD_REPLACE, "member")
591         m["member"] = MessageElement("cn=ldaptestuser1,cn=users," + self.base_dn,
592                                      FLAG_MOD_ADD, "member")
593         try:
594             ldb.modify(m)
595             self.fail()
596         except LdbError as e28:
597             (num, _) = e28.args
598             self.assertEquals(num, ERR_NO_SUCH_OBJECT)
599
600         m = Message()
601         m.dn = Dn(ldb, "cn=ldaptestgroup2,cn=users," + self.base_dn)
602         m["member"] = MessageElement(["cn=ldaptestuser,cn=users," + self.base_dn,
603                                       "cn=ldaptestuser2,cn=users," + self.base_dn],
604                                      FLAG_MOD_REPLACE, "member")
605         ldb.modify(m)
606
607         delete_force(self.ldb, "cn=ldaptestuser,cn=users," + self.base_dn)
608         delete_force(self.ldb, "cn=ldaptestuser2,cn=users," + self.base_dn)
609         delete_force(self.ldb, "cn=ldaptestgroup,cn=users," + self.base_dn)
610         delete_force(self.ldb, "cn=ldaptestgroup2,cn=users," + self.base_dn)
611
612         # Make also a small test for accounts with special DNs ("," in this case)
613         ldb.add({
614             "dn": "cn=ldaptest\,specialuser,cn=users," + self.base_dn,
615             "objectclass": "user"})
616         delete_force(self.ldb, "cn=ldaptest\,specialuser,cn=users," + self.base_dn)
617
618     def test_sam_attributes(self):
619         """Test the behaviour of special attributes of SAM objects"""
620         print("Testing the behaviour of special attributes of SAM objects\n")
621
622         ldb.add({
623             "dn": "cn=ldaptestuser,cn=users," + self.base_dn,
624             "objectclass": "user"})
625         ldb.add({
626             "dn": "cn=ldaptestgroup,cn=users," + self.base_dn,
627             "objectclass": "group"})
628
629         m = Message()
630         m.dn = Dn(ldb, "cn=ldaptestgroup,cn=users," + self.base_dn)
631         m["groupType"] = MessageElement(str(GTYPE_SECURITY_GLOBAL_GROUP), FLAG_MOD_ADD,
632                                         "groupType")
633         try:
634             ldb.modify(m)
635             self.fail()
636         except LdbError as e29:
637             (num, _) = e29.args
638             self.assertEquals(num, ERR_ATTRIBUTE_OR_VALUE_EXISTS)
639
640         # Delete protection tests
641
642         for attr in ["nTSecurityDescriptor", "objectSid", "sAMAccountType",
643                      "sAMAccountName", "groupType"]:
644
645             m = Message()
646             m.dn = Dn(ldb, "cn=ldaptestgroup,cn=users," + self.base_dn)
647             m[attr] = MessageElement([], FLAG_MOD_REPLACE, attr)
648             try:
649                 ldb.modify(m)
650                 self.fail()
651             except LdbError as e:
652                 (num, _) = e.args
653                 self.assertEquals(num, ERR_UNWILLING_TO_PERFORM)
654
655             m = Message()
656             m.dn = Dn(ldb, "cn=ldaptestgroup,cn=users," + self.base_dn)
657             m[attr] = MessageElement([], FLAG_MOD_DELETE, attr)
658             try:
659                 ldb.modify(m)
660                 self.fail()
661             except LdbError as e1:
662                 (num, _) = e1.args
663                 self.assertEquals(num, ERR_UNWILLING_TO_PERFORM)
664
665         m = Message()
666         m.dn = Dn(ldb, "cn=ldaptestuser,cn=users," + self.base_dn)
667         m["primaryGroupID"] = MessageElement("513", FLAG_MOD_ADD,
668                                              "primaryGroupID")
669         try:
670             ldb.modify(m)
671             self.fail()
672         except LdbError as e30:
673             (num, _) = e30.args
674             self.assertEquals(num, ERR_ATTRIBUTE_OR_VALUE_EXISTS)
675
676         m = Message()
677         m.dn = Dn(ldb, "cn=ldaptestuser,cn=users," + self.base_dn)
678         m["userAccountControl"] = MessageElement(str(UF_NORMAL_ACCOUNT | UF_PASSWD_NOTREQD), FLAG_MOD_ADD,
679                                                  "userAccountControl")
680         try:
681             ldb.modify(m)
682             self.fail()
683         except LdbError as e31:
684             (num, _) = e31.args
685             self.assertEquals(num, ERR_ATTRIBUTE_OR_VALUE_EXISTS)
686
687         m = Message()
688         m.dn = Dn(ldb, "cn=ldaptestuser,cn=users," + self.base_dn)
689         m["objectSid"] = MessageElement("xxxxxxxxxxxxxxxx", FLAG_MOD_ADD,
690                                         "objectSid")
691         try:
692             ldb.modify(m)
693             self.fail()
694         except LdbError as e32:
695             (num, _) = e32.args
696             self.assertEquals(num, ERR_UNWILLING_TO_PERFORM)
697
698         m = Message()
699         m.dn = Dn(ldb, "cn=ldaptestuser,cn=users," + self.base_dn)
700         m["sAMAccountType"] = MessageElement("0", FLAG_MOD_ADD,
701                                              "sAMAccountType")
702         try:
703             ldb.modify(m)
704             self.fail()
705         except LdbError as e33:
706             (num, _) = e33.args
707             self.assertEquals(num, ERR_UNWILLING_TO_PERFORM)
708
709         m = Message()
710         m.dn = Dn(ldb, "cn=ldaptestuser,cn=users," + self.base_dn)
711         m["sAMAccountName"] = MessageElement("test", FLAG_MOD_ADD,
712                                              "sAMAccountName")
713         try:
714             ldb.modify(m)
715             self.fail()
716         except LdbError as e34:
717             (num, _) = e34.args
718             self.assertEquals(num, ERR_ATTRIBUTE_OR_VALUE_EXISTS)
719
720         # Delete protection tests
721
722         for attr in ["nTSecurityDescriptor", "objectSid", "sAMAccountType",
723                      "sAMAccountName", "primaryGroupID", "userAccountControl",
724                      "accountExpires", "badPasswordTime", "badPwdCount",
725                      "codePage", "countryCode", "lastLogoff", "lastLogon",
726                      "logonCount", "pwdLastSet"]:
727
728             m = Message()
729             m.dn = Dn(ldb, "cn=ldaptestuser,cn=users," + self.base_dn)
730             m[attr] = MessageElement([], FLAG_MOD_REPLACE, attr)
731             try:
732                 ldb.modify(m)
733                 self.fail()
734             except LdbError as e2:
735                 (num, _) = e2.args
736                 self.assertEquals(num, ERR_UNWILLING_TO_PERFORM)
737
738             m = Message()
739             m.dn = Dn(ldb, "cn=ldaptestuser,cn=users," + self.base_dn)
740             m[attr] = MessageElement([], FLAG_MOD_DELETE, attr)
741             try:
742                 ldb.modify(m)
743                 self.fail()
744             except LdbError as e3:
745                 (num, _) = e3.args
746                 self.assertEquals(num, ERR_UNWILLING_TO_PERFORM)
747
748         delete_force(self.ldb, "cn=ldaptestuser,cn=users," + self.base_dn)
749         delete_force(self.ldb, "cn=ldaptestgroup,cn=users," + self.base_dn)
750
751     def test_primary_group_token_constructed(self):
752         """Test the primary group token behaviour (hidden-generated-readonly attribute on groups) and some other constructed attributes"""
753         print("Testing primary group token behaviour and other constructed attributes\n")
754
755         try:
756             ldb.add({
757                 "dn": "cn=ldaptestgroup,cn=users," + self.base_dn,
758                 "objectclass": "group",
759                 "primaryGroupToken": "100"})
760             self.fail()
761         except LdbError as e35:
762             (num, _) = e35.args
763             self.assertEquals(num, ERR_UNDEFINED_ATTRIBUTE_TYPE)
764         delete_force(self.ldb, "cn=ldaptestgroup,cn=users," + self.base_dn)
765
766         ldb.add({
767             "dn": "cn=ldaptestuser,cn=users," + self.base_dn,
768             "objectclass": "user"})
769
770         ldb.add({
771             "dn": "cn=ldaptestgroup,cn=users," + self.base_dn,
772             "objectclass": "group"})
773
774         # Testing for one invalid, and one valid operational attribute, but also the things they are built from
775         res1 = ldb.search(self.base_dn,
776                           scope=SCOPE_BASE, attrs=["primaryGroupToken", "canonicalName", "objectClass", "objectSid"])
777         self.assertTrue(len(res1) == 1)
778         self.assertFalse("primaryGroupToken" in res1[0])
779         self.assertTrue("canonicalName" in res1[0])
780         self.assertTrue("objectClass" in res1[0])
781         self.assertTrue("objectSid" in res1[0])
782
783         res1 = ldb.search(self.base_dn,
784                           scope=SCOPE_BASE, attrs=["primaryGroupToken", "canonicalName"])
785         self.assertTrue(len(res1) == 1)
786         self.assertFalse("primaryGroupToken" in res1[0])
787         self.assertFalse("objectSid" in res1[0])
788         self.assertFalse("objectClass" in res1[0])
789         self.assertTrue("canonicalName" in res1[0])
790
791         res1 = ldb.search("cn=users," + self.base_dn,
792                           scope=SCOPE_BASE, attrs=["primaryGroupToken"])
793         self.assertTrue(len(res1) == 1)
794         self.assertFalse("primaryGroupToken" in res1[0])
795
796         res1 = ldb.search("cn=ldaptestuser, cn=users," + self.base_dn,
797                           scope=SCOPE_BASE, attrs=["primaryGroupToken"])
798         self.assertTrue(len(res1) == 1)
799         self.assertFalse("primaryGroupToken" in res1[0])
800
801         res1 = ldb.search("cn=ldaptestgroup,cn=users," + self.base_dn,
802                           scope=SCOPE_BASE)
803         self.assertTrue(len(res1) == 1)
804         self.assertFalse("primaryGroupToken" in res1[0])
805
806         res1 = ldb.search("cn=ldaptestgroup,cn=users," + self.base_dn,
807                           scope=SCOPE_BASE, attrs=["primaryGroupToken", "objectSID"])
808         self.assertTrue(len(res1) == 1)
809         primary_group_token = int(res1[0]["primaryGroupToken"][0])
810
811         rid = security.dom_sid(ldb.schema_format_value("objectSID", res1[0]["objectSID"][0])).split()[1]
812         self.assertEquals(primary_group_token, rid)
813
814         m = Message()
815         m.dn = Dn(ldb, "cn=ldaptestgroup,cn=users," + self.base_dn)
816         m["primaryGroupToken"] = "100"
817         try:
818             ldb.modify(m)
819             self.fail()
820         except LdbError as e36:
821             (num, _) = e36.args
822             self.assertEquals(num, ERR_CONSTRAINT_VIOLATION)
823
824         delete_force(self.ldb, "cn=ldaptestuser,cn=users," + self.base_dn)
825         delete_force(self.ldb, "cn=ldaptestgroup,cn=users," + self.base_dn)
826
827     def test_tokenGroups(self):
828         """Test the tokenGroups behaviour (hidden-generated-readonly attribute on SAM objects)"""
829         print("Testing tokenGroups behaviour\n")
830
831         # The domain object shouldn't contain any "tokenGroups" entry
832         res = ldb.search(self.base_dn, scope=SCOPE_BASE, attrs=["tokenGroups"])
833         self.assertTrue(len(res) == 1)
834         self.assertFalse("tokenGroups" in res[0])
835
836         # The domain administrator should contain "tokenGroups" entries
837         # (the exact number depends on the domain/forest function level and the
838         # DC software versions)
839         res = ldb.search("cn=Administrator,cn=Users," + self.base_dn,
840                          scope=SCOPE_BASE, attrs=["tokenGroups"])
841         self.assertTrue(len(res) == 1)
842         self.assertTrue("tokenGroups" in res[0])
843
844         ldb.add({
845             "dn": "cn=ldaptestuser,cn=users," + self.base_dn,
846             "objectclass": "user"})
847
848         # This testuser should contain at least two "tokenGroups" entries
849         # (exactly two on an unmodified "Domain Users" and "Users" group)
850         res = ldb.search("cn=ldaptestuser,cn=users," + self.base_dn,
851                          scope=SCOPE_BASE, attrs=["tokenGroups"])
852         self.assertTrue(len(res) == 1)
853         self.assertTrue(len(res[0]["tokenGroups"]) >= 2)
854
855         # one entry which we need to find should point to domains "Domain Users"
856         # group and another entry should point to the builtin "Users"group
857         domain_users_group_found = False
858         users_group_found = False
859         for sid in res[0]["tokenGroups"]:
860             rid = security.dom_sid(ldb.schema_format_value("objectSID", sid)).split()[1]
861             if rid == 513:
862                 domain_users_group_found = True
863             if rid == 545:
864                 users_group_found = True
865
866         self.assertTrue(domain_users_group_found)
867         self.assertTrue(users_group_found)
868
869         delete_force(self.ldb, "cn=ldaptestuser,cn=users," + self.base_dn)
870
871     def test_groupType(self):
872         """Test the groupType behaviour"""
873         print("Testing groupType behaviour\n")
874
875         # You can never create or change to a
876         # "GTYPE_SECURITY_BUILTIN_LOCAL_GROUP"
877
878         # Add operation
879
880         # Invalid attribute
881         try:
882             ldb.add({
883                 "dn": "cn=ldaptestgroup,cn=users," + self.base_dn,
884                 "objectclass": "group",
885                 "groupType": "0"})
886             self.fail()
887         except LdbError as e37:
888             (num, _) = e37.args
889             self.assertEquals(num, ERR_UNWILLING_TO_PERFORM)
890         delete_force(self.ldb, "cn=ldaptestgroup,cn=users," + self.base_dn)
891
892         try:
893             ldb.add({
894                 "dn": "cn=ldaptestgroup,cn=users," + self.base_dn,
895                 "objectclass": "group",
896                 "groupType": str(GTYPE_SECURITY_BUILTIN_LOCAL_GROUP)})
897             self.fail()
898         except LdbError as e38:
899             (num, _) = e38.args
900             self.assertEquals(num, ERR_UNWILLING_TO_PERFORM)
901         delete_force(self.ldb, "cn=ldaptestgroup,cn=users," + self.base_dn)
902
903         ldb.add({
904             "dn": "cn=ldaptestgroup,cn=users," + self.base_dn,
905             "objectclass": "group",
906             "groupType": str(GTYPE_SECURITY_GLOBAL_GROUP)})
907
908         res1 = ldb.search("cn=ldaptestgroup,cn=users," + self.base_dn,
909                           scope=SCOPE_BASE, attrs=["sAMAccountType"])
910         self.assertTrue(len(res1) == 1)
911         self.assertEquals(int(res1[0]["sAMAccountType"][0]),
912                           ATYPE_SECURITY_GLOBAL_GROUP)
913         delete_force(self.ldb, "cn=ldaptestgroup,cn=users," + self.base_dn)
914
915         ldb.add({
916             "dn": "cn=ldaptestgroup,cn=users," + self.base_dn,
917             "objectclass": "group",
918             "groupType": str(GTYPE_SECURITY_UNIVERSAL_GROUP)})
919
920         res1 = ldb.search("cn=ldaptestgroup,cn=users," + self.base_dn,
921                           scope=SCOPE_BASE, attrs=["sAMAccountType"])
922         self.assertTrue(len(res1) == 1)
923         self.assertEquals(int(res1[0]["sAMAccountType"][0]),
924                           ATYPE_SECURITY_UNIVERSAL_GROUP)
925         delete_force(self.ldb, "cn=ldaptestgroup,cn=users," + self.base_dn)
926
927         ldb.add({
928             "dn": "cn=ldaptestgroup,cn=users," + self.base_dn,
929             "objectclass": "group",
930             "groupType": str(GTYPE_SECURITY_DOMAIN_LOCAL_GROUP)})
931
932         res1 = ldb.search("cn=ldaptestgroup,cn=users," + self.base_dn,
933                           scope=SCOPE_BASE, attrs=["sAMAccountType"])
934         self.assertTrue(len(res1) == 1)
935         self.assertEquals(int(res1[0]["sAMAccountType"][0]),
936                           ATYPE_SECURITY_LOCAL_GROUP)
937         delete_force(self.ldb, "cn=ldaptestgroup,cn=users," + self.base_dn)
938
939         ldb.add({
940             "dn": "cn=ldaptestgroup,cn=users," + self.base_dn,
941             "objectclass": "group",
942             "groupType": str(GTYPE_DISTRIBUTION_GLOBAL_GROUP)})
943
944         res1 = ldb.search("cn=ldaptestgroup,cn=users," + self.base_dn,
945                           scope=SCOPE_BASE, attrs=["sAMAccountType"])
946         self.assertTrue(len(res1) == 1)
947         self.assertEquals(int(res1[0]["sAMAccountType"][0]),
948                           ATYPE_DISTRIBUTION_GLOBAL_GROUP)
949         delete_force(self.ldb, "cn=ldaptestgroup,cn=users," + self.base_dn)
950
951         ldb.add({
952             "dn": "cn=ldaptestgroup,cn=users," + self.base_dn,
953             "objectclass": "group",
954             "groupType": str(GTYPE_DISTRIBUTION_UNIVERSAL_GROUP)})
955
956         res1 = ldb.search("cn=ldaptestgroup,cn=users," + self.base_dn,
957                           scope=SCOPE_BASE, attrs=["sAMAccountType"])
958         self.assertTrue(len(res1) == 1)
959         self.assertEquals(int(res1[0]["sAMAccountType"][0]),
960                           ATYPE_DISTRIBUTION_UNIVERSAL_GROUP)
961         delete_force(self.ldb, "cn=ldaptestgroup,cn=users," + self.base_dn)
962
963         ldb.add({
964             "dn": "cn=ldaptestgroup,cn=users," + self.base_dn,
965             "objectclass": "group",
966             "groupType": str(GTYPE_DISTRIBUTION_DOMAIN_LOCAL_GROUP)})
967
968         res1 = ldb.search("cn=ldaptestgroup,cn=users," + self.base_dn,
969                           scope=SCOPE_BASE, attrs=["sAMAccountType"])
970         self.assertTrue(len(res1) == 1)
971         self.assertEquals(int(res1[0]["sAMAccountType"][0]),
972                           ATYPE_DISTRIBUTION_LOCAL_GROUP)
973         delete_force(self.ldb, "cn=ldaptestgroup,cn=users," + self.base_dn)
974
975         # Modify operation
976
977         ldb.add({
978             "dn": "cn=ldaptestgroup,cn=users," + self.base_dn,
979             "objectclass": "group"})
980
981         # We can change in this direction: global <-> universal <-> local
982         # On each step also the group type itself (security/distribution) is
983         # variable.
984
985         # After creation we should have a "security global group"
986         res1 = ldb.search("cn=ldaptestgroup,cn=users," + self.base_dn,
987                           scope=SCOPE_BASE, attrs=["sAMAccountType"])
988         self.assertTrue(len(res1) == 1)
989         self.assertEquals(int(res1[0]["sAMAccountType"][0]),
990                           ATYPE_SECURITY_GLOBAL_GROUP)
991
992         # Invalid attribute
993         try:
994             m = Message()
995             m.dn = Dn(ldb, "cn=ldaptestgroup,cn=users," + self.base_dn)
996             m["groupType"] = MessageElement("0",
997                                             FLAG_MOD_REPLACE, "groupType")
998             ldb.modify(m)
999             self.fail()
1000         except LdbError as e39:
1001             (num, _) = e39.args
1002             self.assertEquals(num, ERR_UNWILLING_TO_PERFORM)
1003
1004         # Security groups
1005
1006         # Default is "global group"
1007
1008         m = Message()
1009         m.dn = Dn(ldb, "cn=ldaptestgroup,cn=users," + self.base_dn)
1010         m["groupType"] = MessageElement(
1011             str(GTYPE_SECURITY_GLOBAL_GROUP),
1012             FLAG_MOD_REPLACE, "groupType")
1013         ldb.modify(m)
1014
1015         res1 = ldb.search("cn=ldaptestgroup,cn=users," + self.base_dn,
1016                           scope=SCOPE_BASE, attrs=["sAMAccountType"])
1017         self.assertTrue(len(res1) == 1)
1018         self.assertEquals(int(res1[0]["sAMAccountType"][0]),
1019                           ATYPE_SECURITY_GLOBAL_GROUP)
1020
1021         # Change to "local" (shouldn't work)
1022
1023         try:
1024             m = Message()
1025             m.dn = Dn(ldb, "cn=ldaptestgroup,cn=users," + self.base_dn)
1026             m["groupType"] = MessageElement(
1027                 str(GTYPE_SECURITY_DOMAIN_LOCAL_GROUP),
1028                 FLAG_MOD_REPLACE, "groupType")
1029             ldb.modify(m)
1030             self.fail()
1031         except LdbError as e40:
1032             (num, _) = e40.args
1033             self.assertEquals(num, ERR_UNWILLING_TO_PERFORM)
1034
1035         # Change to "universal"
1036
1037         m = Message()
1038         m.dn = Dn(ldb, "cn=ldaptestgroup,cn=users," + self.base_dn)
1039         m["groupType"] = MessageElement(
1040             str(GTYPE_SECURITY_UNIVERSAL_GROUP),
1041             FLAG_MOD_REPLACE, "groupType")
1042         ldb.modify(m)
1043
1044         res1 = ldb.search("cn=ldaptestgroup,cn=users," + self.base_dn,
1045                           scope=SCOPE_BASE, attrs=["sAMAccountType"])
1046         self.assertTrue(len(res1) == 1)
1047         self.assertEquals(int(res1[0]["sAMAccountType"][0]),
1048                           ATYPE_SECURITY_UNIVERSAL_GROUP)
1049
1050         # Change back to "global"
1051
1052         m = Message()
1053         m.dn = Dn(ldb, "cn=ldaptestgroup,cn=users," + self.base_dn)
1054         m["groupType"] = MessageElement(
1055             str(GTYPE_SECURITY_GLOBAL_GROUP),
1056             FLAG_MOD_REPLACE, "groupType")
1057         ldb.modify(m)
1058
1059         res1 = ldb.search("cn=ldaptestgroup,cn=users," + self.base_dn,
1060                           scope=SCOPE_BASE, attrs=["sAMAccountType"])
1061         self.assertTrue(len(res1) == 1)
1062         self.assertEquals(int(res1[0]["sAMAccountType"][0]),
1063                           ATYPE_SECURITY_GLOBAL_GROUP)
1064
1065         # Change back to "universal"
1066
1067         m = Message()
1068         m.dn = Dn(ldb, "cn=ldaptestgroup,cn=users," + self.base_dn)
1069         m["groupType"] = MessageElement(
1070             str(GTYPE_SECURITY_UNIVERSAL_GROUP),
1071             FLAG_MOD_REPLACE, "groupType")
1072         ldb.modify(m)
1073
1074         res1 = ldb.search("cn=ldaptestgroup,cn=users," + self.base_dn,
1075                           scope=SCOPE_BASE, attrs=["sAMAccountType"])
1076         self.assertTrue(len(res1) == 1)
1077         self.assertEquals(int(res1[0]["sAMAccountType"][0]),
1078                           ATYPE_SECURITY_UNIVERSAL_GROUP)
1079
1080         # Change to "local"
1081
1082         m = Message()
1083         m.dn = Dn(ldb, "cn=ldaptestgroup,cn=users," + self.base_dn)
1084         m["groupType"] = MessageElement(
1085             str(GTYPE_SECURITY_DOMAIN_LOCAL_GROUP),
1086             FLAG_MOD_REPLACE, "groupType")
1087         ldb.modify(m)
1088
1089         res1 = ldb.search("cn=ldaptestgroup,cn=users," + self.base_dn,
1090                           scope=SCOPE_BASE, attrs=["sAMAccountType"])
1091         self.assertTrue(len(res1) == 1)
1092         self.assertEquals(int(res1[0]["sAMAccountType"][0]),
1093                           ATYPE_SECURITY_LOCAL_GROUP)
1094
1095         # Change to "global" (shouldn't work)
1096
1097         try:
1098             m = Message()
1099             m.dn = Dn(ldb, "cn=ldaptestgroup,cn=users," + self.base_dn)
1100             m["groupType"] = MessageElement(
1101                 str(GTYPE_SECURITY_GLOBAL_GROUP),
1102                 FLAG_MOD_REPLACE, "groupType")
1103             ldb.modify(m)
1104             self.fail()
1105         except LdbError as e41:
1106             (num, _) = e41.args
1107             self.assertEquals(num, ERR_UNWILLING_TO_PERFORM)
1108
1109         # Change to "builtin local" (shouldn't work)
1110
1111         try:
1112             m = Message()
1113             m.dn = Dn(ldb, "cn=ldaptestgroup,cn=users," + self.base_dn)
1114             m["groupType"] = MessageElement(
1115                 str(GTYPE_SECURITY_BUILTIN_LOCAL_GROUP),
1116                 FLAG_MOD_REPLACE, "groupType")
1117             ldb.modify(m)
1118             self.fail()
1119         except LdbError as e42:
1120             (num, _) = e42.args
1121             self.assertEquals(num, ERR_UNWILLING_TO_PERFORM)
1122
1123         m = Message()
1124         m.dn = Dn(ldb, "cn=ldaptestgroup,cn=users," + self.base_dn)
1125
1126         # Change back to "universal"
1127
1128         m = Message()
1129         m.dn = Dn(ldb, "cn=ldaptestgroup,cn=users," + self.base_dn)
1130         m["groupType"] = MessageElement(
1131             str(GTYPE_SECURITY_UNIVERSAL_GROUP),
1132             FLAG_MOD_REPLACE, "groupType")
1133         ldb.modify(m)
1134
1135         res1 = ldb.search("cn=ldaptestgroup,cn=users," + self.base_dn,
1136                           scope=SCOPE_BASE, attrs=["sAMAccountType"])
1137         self.assertTrue(len(res1) == 1)
1138         self.assertEquals(int(res1[0]["sAMAccountType"][0]),
1139                           ATYPE_SECURITY_UNIVERSAL_GROUP)
1140
1141         # Change to "builtin local" (shouldn't work)
1142
1143         try:
1144             m = Message()
1145             m.dn = Dn(ldb, "cn=ldaptestgroup,cn=users," + self.base_dn)
1146             m["groupType"] = MessageElement(
1147                 str(GTYPE_SECURITY_BUILTIN_LOCAL_GROUP),
1148                 FLAG_MOD_REPLACE, "groupType")
1149             ldb.modify(m)
1150             self.fail()
1151         except LdbError as e43:
1152             (num, _) = e43.args
1153             self.assertEquals(num, ERR_UNWILLING_TO_PERFORM)
1154
1155         # Change back to "global"
1156
1157         m = Message()
1158         m.dn = Dn(ldb, "cn=ldaptestgroup,cn=users," + self.base_dn)
1159         m["groupType"] = MessageElement(
1160             str(GTYPE_SECURITY_GLOBAL_GROUP),
1161             FLAG_MOD_REPLACE, "groupType")
1162         ldb.modify(m)
1163
1164         res1 = ldb.search("cn=ldaptestgroup,cn=users," + self.base_dn,
1165                           scope=SCOPE_BASE, attrs=["sAMAccountType"])
1166         self.assertTrue(len(res1) == 1)
1167         self.assertEquals(int(res1[0]["sAMAccountType"][0]),
1168                           ATYPE_SECURITY_GLOBAL_GROUP)
1169
1170         # Change to "builtin local" (shouldn't work)
1171
1172         try:
1173             m = Message()
1174             m.dn = Dn(ldb, "cn=ldaptestgroup,cn=users," + self.base_dn)
1175             m["groupType"] = MessageElement(
1176                 str(GTYPE_SECURITY_BUILTIN_LOCAL_GROUP),
1177                 FLAG_MOD_REPLACE, "groupType")
1178             ldb.modify(m)
1179             self.fail()
1180         except LdbError as e44:
1181             (num, _) = e44.args
1182             self.assertEquals(num, ERR_UNWILLING_TO_PERFORM)
1183
1184         # Distribution groups
1185
1186         # Default is "global group"
1187
1188         m = Message()
1189         m.dn = Dn(ldb, "cn=ldaptestgroup,cn=users," + self.base_dn)
1190         m["groupType"] = MessageElement(
1191             str(GTYPE_DISTRIBUTION_GLOBAL_GROUP),
1192             FLAG_MOD_REPLACE, "groupType")
1193         ldb.modify(m)
1194
1195         res1 = ldb.search("cn=ldaptestgroup,cn=users," + self.base_dn,
1196                           scope=SCOPE_BASE, attrs=["sAMAccountType"])
1197         self.assertTrue(len(res1) == 1)
1198         self.assertEquals(int(res1[0]["sAMAccountType"][0]),
1199                           ATYPE_DISTRIBUTION_GLOBAL_GROUP)
1200
1201         # Change to local (shouldn't work)
1202
1203         try:
1204             m = Message()
1205             m.dn = Dn(ldb, "cn=ldaptestgroup,cn=users," + self.base_dn)
1206             m["groupType"] = MessageElement(
1207                 str(GTYPE_DISTRIBUTION_DOMAIN_LOCAL_GROUP),
1208                 FLAG_MOD_REPLACE, "groupType")
1209             ldb.modify(m)
1210             self.fail()
1211         except LdbError as e45:
1212             (num, _) = e45.args
1213             self.assertEquals(num, ERR_UNWILLING_TO_PERFORM)
1214
1215         # Change to "universal"
1216
1217         m = Message()
1218         m.dn = Dn(ldb, "cn=ldaptestgroup,cn=users," + self.base_dn)
1219         m["groupType"] = MessageElement(
1220             str(GTYPE_DISTRIBUTION_UNIVERSAL_GROUP),
1221             FLAG_MOD_REPLACE, "groupType")
1222         ldb.modify(m)
1223
1224         res1 = ldb.search("cn=ldaptestgroup,cn=users," + self.base_dn,
1225                           scope=SCOPE_BASE, attrs=["sAMAccountType"])
1226         self.assertTrue(len(res1) == 1)
1227         self.assertEquals(int(res1[0]["sAMAccountType"][0]),
1228                           ATYPE_DISTRIBUTION_UNIVERSAL_GROUP)
1229
1230         # Change back to "global"
1231
1232         m = Message()
1233         m.dn = Dn(ldb, "cn=ldaptestgroup,cn=users," + self.base_dn)
1234         m["groupType"] = MessageElement(
1235             str(GTYPE_DISTRIBUTION_GLOBAL_GROUP),
1236             FLAG_MOD_REPLACE, "groupType")
1237         ldb.modify(m)
1238
1239         res1 = ldb.search("cn=ldaptestgroup,cn=users," + self.base_dn,
1240                           scope=SCOPE_BASE, attrs=["sAMAccountType"])
1241         self.assertTrue(len(res1) == 1)
1242         self.assertEquals(int(res1[0]["sAMAccountType"][0]),
1243                           ATYPE_DISTRIBUTION_GLOBAL_GROUP)
1244
1245         # Change back to "universal"
1246
1247         m = Message()
1248         m.dn = Dn(ldb, "cn=ldaptestgroup,cn=users," + self.base_dn)
1249         m["groupType"] = MessageElement(
1250             str(GTYPE_DISTRIBUTION_UNIVERSAL_GROUP),
1251             FLAG_MOD_REPLACE, "groupType")
1252         ldb.modify(m)
1253
1254         res1 = ldb.search("cn=ldaptestgroup,cn=users," + self.base_dn,
1255                           scope=SCOPE_BASE, attrs=["sAMAccountType"])
1256         self.assertTrue(len(res1) == 1)
1257         self.assertEquals(int(res1[0]["sAMAccountType"][0]),
1258                           ATYPE_DISTRIBUTION_UNIVERSAL_GROUP)
1259
1260         # Change to "local"
1261
1262         m = Message()
1263         m.dn = Dn(ldb, "cn=ldaptestgroup,cn=users," + self.base_dn)
1264         m["groupType"] = MessageElement(
1265             str(GTYPE_DISTRIBUTION_DOMAIN_LOCAL_GROUP),
1266             FLAG_MOD_REPLACE, "groupType")
1267         ldb.modify(m)
1268
1269         res1 = ldb.search("cn=ldaptestgroup,cn=users," + self.base_dn,
1270                           scope=SCOPE_BASE, attrs=["sAMAccountType"])
1271         self.assertTrue(len(res1) == 1)
1272         self.assertEquals(int(res1[0]["sAMAccountType"][0]),
1273                           ATYPE_DISTRIBUTION_LOCAL_GROUP)
1274
1275         # Change to "global" (shouldn't work)
1276
1277         try:
1278             m = Message()
1279             m.dn = Dn(ldb, "cn=ldaptestgroup,cn=users," + self.base_dn)
1280             m["groupType"] = MessageElement(
1281                 str(GTYPE_DISTRIBUTION_GLOBAL_GROUP),
1282                 FLAG_MOD_REPLACE, "groupType")
1283             ldb.modify(m)
1284             self.fail()
1285         except LdbError as e46:
1286             (num, _) = e46.args
1287             self.assertEquals(num, ERR_UNWILLING_TO_PERFORM)
1288
1289         # Change back to "universal"
1290
1291         # Try to add invalid member to group 1 - should be denied
1292         m = Message()
1293         m.dn = Dn(ldb, "cn=ldaptestgroup,cn=users," + self.base_dn)
1294         m["member"] = MessageElement(
1295             "cn=ldaptestuser3,cn=users," + self.base_dn,
1296             FLAG_MOD_ADD, "member")
1297         try:
1298             ldb.modify(m)
1299             self.fail()
1300         except LdbError as e47:
1301             (num, _) = e47.args
1302             self.assertEquals(num, ERR_NO_SUCH_OBJECT)
1303
1304         # Make group 2 secondary
1305         m = Message()
1306         m.dn = Dn(ldb, "cn=ldaptestgroup,cn=users," + self.base_dn)
1307         m["groupType"] = MessageElement(
1308             str(GTYPE_DISTRIBUTION_UNIVERSAL_GROUP),
1309             FLAG_MOD_REPLACE, "groupType")
1310         ldb.modify(m)
1311
1312         res1 = ldb.search("cn=ldaptestgroup,cn=users," + self.base_dn,
1313                           scope=SCOPE_BASE, attrs=["sAMAccountType"])
1314         self.assertTrue(len(res1) == 1)
1315         self.assertEquals(int(res1[0]["sAMAccountType"][0]),
1316                           ATYPE_DISTRIBUTION_UNIVERSAL_GROUP)
1317
1318         # Change back to "global"
1319
1320         m = Message()
1321         m.dn = Dn(ldb, "cn=ldaptestgroup,cn=users," + self.base_dn)
1322         m["groupType"] = MessageElement(
1323             str(GTYPE_DISTRIBUTION_GLOBAL_GROUP),
1324             FLAG_MOD_REPLACE, "groupType")
1325         ldb.modify(m)
1326
1327         res1 = ldb.search("cn=ldaptestgroup,cn=users," + self.base_dn,
1328                           scope=SCOPE_BASE, attrs=["sAMAccountType"])
1329         self.assertTrue(len(res1) == 1)
1330         self.assertEquals(int(res1[0]["sAMAccountType"][0]),
1331                           ATYPE_DISTRIBUTION_GLOBAL_GROUP)
1332
1333         # Both group types: this performs only random checks - all possibilities
1334         # would require too much code.
1335
1336         # Default is "global group"
1337
1338         m = Message()
1339         m.dn = Dn(ldb, "cn=ldaptestgroup,cn=users," + self.base_dn)
1340         m["groupType"] = MessageElement(
1341             str(GTYPE_SECURITY_GLOBAL_GROUP),
1342             FLAG_MOD_REPLACE, "groupType")
1343         ldb.modify(m)
1344
1345         res1 = ldb.search("cn=ldaptestgroup,cn=users," + self.base_dn,
1346                           scope=SCOPE_BASE, attrs=["sAMAccountType"])
1347         self.assertTrue(len(res1) == 1)
1348         self.assertEquals(int(res1[0]["sAMAccountType"][0]),
1349                           ATYPE_SECURITY_GLOBAL_GROUP)
1350
1351         # Change to "local" (shouldn't work)
1352
1353         try:
1354             m = Message()
1355             m.dn = Dn(ldb, "cn=ldaptestgroup,cn=users," + self.base_dn)
1356             m["groupType"] = MessageElement(
1357                 str(GTYPE_DISTRIBUTION_DOMAIN_LOCAL_GROUP),
1358                 FLAG_MOD_REPLACE, "groupType")
1359             ldb.modify(m)
1360             self.fail()
1361         except LdbError as e48:
1362             (num, _) = e48.args
1363             self.assertEquals(num, ERR_UNWILLING_TO_PERFORM)
1364
1365         # Change to "universal"
1366
1367         m = Message()
1368         m.dn = Dn(ldb, "cn=ldaptestgroup,cn=users," + self.base_dn)
1369         m["groupType"] = MessageElement(
1370             str(GTYPE_DISTRIBUTION_UNIVERSAL_GROUP),
1371             FLAG_MOD_REPLACE, "groupType")
1372         ldb.modify(m)
1373
1374         res1 = ldb.search("cn=ldaptestgroup,cn=users," + self.base_dn,
1375                           scope=SCOPE_BASE, attrs=["sAMAccountType"])
1376         self.assertTrue(len(res1) == 1)
1377         self.assertEquals(int(res1[0]["sAMAccountType"][0]),
1378                           ATYPE_DISTRIBUTION_UNIVERSAL_GROUP)
1379
1380         # Change back to "global"
1381
1382         m = Message()
1383         m.dn = Dn(ldb, "cn=ldaptestgroup,cn=users," + self.base_dn)
1384         m["groupType"] = MessageElement(
1385             str(GTYPE_SECURITY_GLOBAL_GROUP),
1386             FLAG_MOD_REPLACE, "groupType")
1387         ldb.modify(m)
1388
1389         res1 = ldb.search("cn=ldaptestgroup,cn=users," + self.base_dn,
1390                           scope=SCOPE_BASE, attrs=["sAMAccountType"])
1391         self.assertTrue(len(res1) == 1)
1392         self.assertEquals(int(res1[0]["sAMAccountType"][0]),
1393                           ATYPE_SECURITY_GLOBAL_GROUP)
1394
1395         # Change back to "universal"
1396
1397         m = Message()
1398         m.dn = Dn(ldb, "cn=ldaptestgroup,cn=users," + self.base_dn)
1399         m["groupType"] = MessageElement(
1400             str(GTYPE_SECURITY_UNIVERSAL_GROUP),
1401             FLAG_MOD_REPLACE, "groupType")
1402         ldb.modify(m)
1403
1404         res1 = ldb.search("cn=ldaptestgroup,cn=users," + self.base_dn,
1405                           scope=SCOPE_BASE, attrs=["sAMAccountType"])
1406         self.assertTrue(len(res1) == 1)
1407         self.assertEquals(int(res1[0]["sAMAccountType"][0]),
1408                           ATYPE_SECURITY_UNIVERSAL_GROUP)
1409
1410         # Change to "local"
1411
1412         m = Message()
1413         m.dn = Dn(ldb, "cn=ldaptestgroup,cn=users," + self.base_dn)
1414         m["groupType"] = MessageElement(
1415             str(GTYPE_DISTRIBUTION_DOMAIN_LOCAL_GROUP),
1416             FLAG_MOD_REPLACE, "groupType")
1417         ldb.modify(m)
1418
1419         res1 = ldb.search("cn=ldaptestgroup,cn=users," + self.base_dn,
1420                           scope=SCOPE_BASE, attrs=["sAMAccountType"])
1421         self.assertTrue(len(res1) == 1)
1422         self.assertEquals(int(res1[0]["sAMAccountType"][0]),
1423                           ATYPE_DISTRIBUTION_LOCAL_GROUP)
1424
1425         # Change to "global" (shouldn't work)
1426
1427         try:
1428             m = Message()
1429             m.dn = Dn(ldb, "cn=ldaptestgroup,cn=users," + self.base_dn)
1430             m["groupType"] = MessageElement(
1431                 str(GTYPE_DISTRIBUTION_GLOBAL_GROUP),
1432                 FLAG_MOD_REPLACE, "groupType")
1433             ldb.modify(m)
1434             self.fail()
1435         except LdbError as e49:
1436             (num, _) = e49.args
1437             self.assertEquals(num, ERR_UNWILLING_TO_PERFORM)
1438
1439         # Change back to "universal"
1440
1441         m = Message()
1442         m.dn = Dn(ldb, "cn=ldaptestgroup,cn=users," + self.base_dn)
1443         m["groupType"] = MessageElement(
1444             str(GTYPE_SECURITY_UNIVERSAL_GROUP),
1445             FLAG_MOD_REPLACE, "groupType")
1446         ldb.modify(m)
1447
1448         res1 = ldb.search("cn=ldaptestgroup,cn=users," + self.base_dn,
1449                           scope=SCOPE_BASE, attrs=["sAMAccountType"])
1450         self.assertTrue(len(res1) == 1)
1451         self.assertEquals(int(res1[0]["sAMAccountType"][0]),
1452                           ATYPE_SECURITY_UNIVERSAL_GROUP)
1453
1454         # Change back to "global"
1455
1456         m = Message()
1457         m.dn = Dn(ldb, "cn=ldaptestgroup,cn=users," + self.base_dn)
1458         m["groupType"] = MessageElement(
1459             str(GTYPE_SECURITY_GLOBAL_GROUP),
1460             FLAG_MOD_REPLACE, "groupType")
1461         ldb.modify(m)
1462
1463         res1 = ldb.search("cn=ldaptestgroup,cn=users," + self.base_dn,
1464                           scope=SCOPE_BASE, attrs=["sAMAccountType"])
1465         self.assertTrue(len(res1) == 1)
1466         self.assertEquals(int(res1[0]["sAMAccountType"][0]),
1467                           ATYPE_SECURITY_GLOBAL_GROUP)
1468
1469         delete_force(self.ldb, "cn=ldaptestgroup,cn=users," + self.base_dn)
1470
1471     def test_pwdLastSet(self):
1472         """Test the pwdLastSet behaviour"""
1473         print("Testing pwdLastSet behaviour\n")
1474
1475         ldb.add({
1476             "dn": "cn=ldaptestuser,cn=users," + self.base_dn,
1477             "objectclass": "user",
1478             "pwdLastSet": "0"})
1479
1480         res1 = ldb.search("cn=ldaptestuser,cn=users," + self.base_dn,
1481                           scope=SCOPE_BASE,
1482                           attrs=["sAMAccountType", "userAccountControl", "pwdLastSet"])
1483         self.assertTrue(len(res1) == 1)
1484         self.assertEqual(int(res1[0]["sAMAccountType"][0]),
1485                          ATYPE_NORMAL_ACCOUNT)
1486         self.assertEqual(int(res1[0]["userAccountControl"][0]),
1487                          UF_NORMAL_ACCOUNT | UF_ACCOUNTDISABLE | UF_PASSWD_NOTREQD)
1488         self.assertEqual(int(res1[0]["pwdLastSet"][0]), 0)
1489         delete_force(self.ldb, "cn=ldaptestuser,cn=users," + self.base_dn)
1490
1491         ldb.add({
1492             "dn": "cn=ldaptestuser,cn=users," + self.base_dn,
1493             "objectclass": "user",
1494             "pwdLastSet": "-1"})
1495
1496         res1 = ldb.search("cn=ldaptestuser,cn=users," + self.base_dn,
1497                           scope=SCOPE_BASE,
1498                           attrs=["sAMAccountType", "userAccountControl", "pwdLastSet"])
1499         self.assertTrue(len(res1) == 1)
1500         self.assertEqual(int(res1[0]["sAMAccountType"][0]),
1501                          ATYPE_NORMAL_ACCOUNT)
1502         self.assertEqual(int(res1[0]["userAccountControl"][0]),
1503                          UF_NORMAL_ACCOUNT | UF_ACCOUNTDISABLE | UF_PASSWD_NOTREQD)
1504         self.assertNotEqual(int(res1[0]["pwdLastSet"][0]), 0)
1505         lastset = int(res1[0]["pwdLastSet"][0])
1506         delete_force(self.ldb, "cn=ldaptestuser,cn=users," + self.base_dn)
1507
1508         try:
1509             ldb.add({
1510                 "dn": "cn=ldaptestuser,cn=users," + self.base_dn,
1511                 "objectclass": "user",
1512                 "pwdLastSet": str(1)})
1513             self.fail()
1514         except LdbError as e50:
1515             (num, msg) = e50.args
1516             self.assertEquals(num, ERR_OTHER)
1517             self.assertTrue('00000057' in msg)
1518
1519         try:
1520             ldb.add({
1521                 "dn": "cn=ldaptestuser,cn=users," + self.base_dn,
1522                 "objectclass": "user",
1523                 "pwdLastSet": str(lastset)})
1524             self.fail()
1525         except LdbError as e51:
1526             (num, msg) = e51.args
1527             self.assertEquals(num, ERR_OTHER)
1528             self.assertTrue('00000057' in msg)
1529
1530         ldb.add({
1531             "dn": "cn=ldaptestuser,cn=users," + self.base_dn,
1532             "objectclass": "user"})
1533
1534         res1 = ldb.search("cn=ldaptestuser,cn=users," + self.base_dn,
1535                           scope=SCOPE_BASE,
1536                           attrs=["sAMAccountType", "userAccountControl", "pwdLastSet"])
1537         self.assertTrue(len(res1) == 1)
1538         self.assertEqual(int(res1[0]["sAMAccountType"][0]),
1539                          ATYPE_NORMAL_ACCOUNT)
1540         self.assertEqual(int(res1[0]["userAccountControl"][0]),
1541                          UF_NORMAL_ACCOUNT | UF_ACCOUNTDISABLE | UF_PASSWD_NOTREQD)
1542         self.assertEqual(int(res1[0]["pwdLastSet"][0]), 0)
1543
1544         m = Message()
1545         m.dn = Dn(ldb, "cn=ldaptestuser,cn=users," + self.base_dn)
1546         m["pls1"] = MessageElement(str(0),
1547                                    FLAG_MOD_REPLACE,
1548                                    "pwdLastSet")
1549         ldb.modify(m)
1550
1551         m = Message()
1552         m.dn = Dn(ldb, "cn=ldaptestuser,cn=users," + self.base_dn)
1553         m["pls1"] = MessageElement(str(0),
1554                                    FLAG_MOD_DELETE,
1555                                    "pwdLastSet")
1556         m["pls2"] = MessageElement(str(0),
1557                                    FLAG_MOD_ADD,
1558                                    "pwdLastSet")
1559         ldb.modify(m)
1560
1561         m = Message()
1562         m.dn = Dn(ldb, "cn=ldaptestuser,cn=users," + self.base_dn)
1563         m["pls1"] = MessageElement(str(-1),
1564                                    FLAG_MOD_REPLACE,
1565                                    "pwdLastSet")
1566         ldb.modify(m)
1567         res1 = ldb.search("cn=ldaptestuser,cn=users," + self.base_dn,
1568                           scope=SCOPE_BASE,
1569                           attrs=["sAMAccountType", "userAccountControl", "pwdLastSet"])
1570         self.assertTrue(len(res1) == 1)
1571         self.assertEqual(int(res1[0]["sAMAccountType"][0]),
1572                          ATYPE_NORMAL_ACCOUNT)
1573         self.assertEqual(int(res1[0]["userAccountControl"][0]),
1574                          UF_NORMAL_ACCOUNT | UF_ACCOUNTDISABLE | UF_PASSWD_NOTREQD)
1575         self.assertGreater(int(res1[0]["pwdLastSet"][0]), lastset)
1576         lastset = int(res1[0]["pwdLastSet"][0])
1577
1578         try:
1579             m = Message()
1580             m.dn = Dn(ldb, "cn=ldaptestuser,cn=users," + self.base_dn)
1581             m["pls1"] = MessageElement(str(0),
1582                                        FLAG_MOD_DELETE,
1583                                        "pwdLastSet")
1584             m["pls2"] = MessageElement(str(0),
1585                                        FLAG_MOD_ADD,
1586                                        "pwdLastSet")
1587             ldb.modify(m)
1588             self.fail()
1589         except LdbError as e52:
1590             (num, msg) = e52.args
1591             self.assertEquals(num, ERR_NO_SUCH_ATTRIBUTE)
1592             self.assertTrue('00002085' in msg)
1593
1594         try:
1595             m = Message()
1596             m.dn = Dn(ldb, "cn=ldaptestuser,cn=users," + self.base_dn)
1597             m["pls1"] = MessageElement(str(-1),
1598                                        FLAG_MOD_DELETE,
1599                                        "pwdLastSet")
1600             m["pls2"] = MessageElement(str(0),
1601                                        FLAG_MOD_ADD,
1602                                        "pwdLastSet")
1603             ldb.modify(m)
1604             self.fail()
1605         except LdbError as e53:
1606             (num, msg) = e53.args
1607             self.assertEquals(num, ERR_NO_SUCH_ATTRIBUTE)
1608             self.assertTrue('00002085' in msg)
1609
1610         m = Message()
1611         m.dn = Dn(ldb, "cn=ldaptestuser,cn=users," + self.base_dn)
1612         m["pls1"] = MessageElement(str(lastset),
1613                                    FLAG_MOD_DELETE,
1614                                    "pwdLastSet")
1615         m["pls2"] = MessageElement(str(-1),
1616                                    FLAG_MOD_ADD,
1617                                    "pwdLastSet")
1618         time.sleep(0.2)
1619         ldb.modify(m)
1620         res1 = ldb.search("cn=ldaptestuser,cn=users," + self.base_dn,
1621                           scope=SCOPE_BASE,
1622                           attrs=["sAMAccountType", "userAccountControl", "pwdLastSet"])
1623         self.assertTrue(len(res1) == 1)
1624         self.assertEqual(int(res1[0]["sAMAccountType"][0]),
1625                          ATYPE_NORMAL_ACCOUNT)
1626         self.assertEqual(int(res1[0]["userAccountControl"][0]),
1627                          UF_NORMAL_ACCOUNT | UF_ACCOUNTDISABLE | UF_PASSWD_NOTREQD)
1628         self.assertEqual(int(res1[0]["pwdLastSet"][0]), lastset)
1629
1630         try:
1631             m = Message()
1632             m.dn = Dn(ldb, "cn=ldaptestuser,cn=users," + self.base_dn)
1633             m["pls1"] = MessageElement(str(lastset),
1634                                        FLAG_MOD_DELETE,
1635                                        "pwdLastSet")
1636             m["pls2"] = MessageElement(str(lastset),
1637                                        FLAG_MOD_ADD,
1638                                        "pwdLastSet")
1639             ldb.modify(m)
1640             self.fail()
1641         except LdbError as e54:
1642             (num, msg) = e54.args
1643             self.assertEquals(num, ERR_OTHER)
1644             self.assertTrue('00000057' in msg)
1645
1646         m = Message()
1647         m.dn = Dn(ldb, "cn=ldaptestuser,cn=users," + self.base_dn)
1648         m["pls1"] = MessageElement(str(lastset),
1649                                    FLAG_MOD_DELETE,
1650                                    "pwdLastSet")
1651         m["pls2"] = MessageElement(str(0),
1652                                    FLAG_MOD_ADD,
1653                                    "pwdLastSet")
1654         ldb.modify(m)
1655         res1 = ldb.search("cn=ldaptestuser,cn=users," + self.base_dn,
1656                           scope=SCOPE_BASE,
1657                           attrs=["sAMAccountType", "userAccountControl", "pwdLastSet"])
1658         self.assertTrue(len(res1) == 1)
1659         self.assertEqual(int(res1[0]["sAMAccountType"][0]),
1660                          ATYPE_NORMAL_ACCOUNT)
1661         self.assertEqual(int(res1[0]["userAccountControl"][0]),
1662                          UF_NORMAL_ACCOUNT | UF_ACCOUNTDISABLE | UF_PASSWD_NOTREQD)
1663         uac = int(res1[0]["userAccountControl"][0])
1664         self.assertEqual(int(res1[0]["pwdLastSet"][0]), 0)
1665
1666         m = Message()
1667         m.dn = Dn(ldb, "cn=ldaptestuser,cn=users," + self.base_dn)
1668         m["uac1"] = MessageElement(str(uac|UF_PASSWORD_EXPIRED),
1669                                    FLAG_MOD_REPLACE,
1670                                    "userAccountControl")
1671         ldb.modify(m)
1672         res1 = ldb.search("cn=ldaptestuser,cn=users," + self.base_dn,
1673                           scope=SCOPE_BASE,
1674                           attrs=["sAMAccountType", "userAccountControl", "pwdLastSet"])
1675         self.assertTrue(len(res1) == 1)
1676         self.assertEqual(int(res1[0]["sAMAccountType"][0]),
1677                          ATYPE_NORMAL_ACCOUNT)
1678         self.assertEqual(int(res1[0]["userAccountControl"][0]),
1679                          UF_NORMAL_ACCOUNT | UF_ACCOUNTDISABLE | UF_PASSWD_NOTREQD)
1680         self.assertEqual(int(res1[0]["pwdLastSet"][0]), 0)
1681
1682         delete_force(self.ldb, "cn=ldaptestuser,cn=users," + self.base_dn)
1683
1684     def test_ldap_bind_must_change_pwd(self):
1685         """Test the error messages for failing LDAP binds"""
1686         print("Test the error messages for failing LDAP binds\n")
1687
1688         delete_force(self.ldb, "cn=ldaptestuser,cn=users," + self.base_dn)
1689
1690         def format_error_msg(hresult_v, dsid_v, werror_v):
1691             #
1692             # There are 4 lower case hex digits following 'v' at the end,
1693             # but different Windows Versions return different values:
1694             #
1695             # Windows 2008R2 uses 'v1db1'
1696             # Windows 2012R2 uses 'v2580'
1697             #
1698             return "%08X: LdapErr: DSID-%08X, comment: AcceptSecurityContext error, data %x, v" % (
1699                     hresult_v, dsid_v, werror_v)
1700
1701         HRES_SEC_E_LOGON_DENIED = 0x8009030C
1702         HRES_SEC_E_INVALID_TOKEN = 0x80090308
1703
1704         sasl_bind_dsid = 0x0C0904DC
1705         simple_bind_dsid = 0x0C0903A9
1706
1707         error_msg_sasl_wrong_pw = format_error_msg(
1708                                 HRES_SEC_E_LOGON_DENIED,
1709                                 sasl_bind_dsid,
1710                                 werror.WERR_LOGON_FAILURE)
1711         error_msg_sasl_must_change = format_error_msg(
1712                                 HRES_SEC_E_LOGON_DENIED,
1713                                 sasl_bind_dsid,
1714                                 werror.WERR_PASSWORD_MUST_CHANGE)
1715         error_msg_simple_wrong_pw = format_error_msg(
1716                                 HRES_SEC_E_INVALID_TOKEN,
1717                                 simple_bind_dsid,
1718                                 werror.WERR_LOGON_FAILURE)
1719         error_msg_simple_must_change = format_error_msg(
1720                                 HRES_SEC_E_INVALID_TOKEN,
1721                                 simple_bind_dsid,
1722                                 werror.WERR_PASSWORD_MUST_CHANGE)
1723
1724         username = "ldaptestuser"
1725         password = "thatsAcomplPASS2"
1726         utf16pw = unicode('"' + password.encode('utf-8') + '"', 'utf-8').encode('utf-16-le')
1727
1728         ldb.add({
1729             "dn": "cn=ldaptestuser,cn=users," + self.base_dn,
1730             "objectclass": "user",
1731             "sAMAccountName": username,
1732             "userAccountControl": str(UF_NORMAL_ACCOUNT),
1733             "unicodePwd": utf16pw,
1734         })
1735
1736         res1 = ldb.search("cn=ldaptestuser,cn=users," + self.base_dn,
1737                           scope=SCOPE_BASE,
1738                           attrs=["sAMAccountName", "sAMAccountType", "userAccountControl", "pwdLastSet"])
1739         self.assertTrue(len(res1) == 1)
1740         self.assertEqual(res1[0]["sAMAccountName"][0], username)
1741         self.assertEqual(int(res1[0]["sAMAccountType"][0]), ATYPE_NORMAL_ACCOUNT)
1742         self.assertEqual(int(res1[0]["userAccountControl"][0]), UF_NORMAL_ACCOUNT)
1743         self.assertNotEqual(int(res1[0]["pwdLastSet"][0]), 0)
1744
1745         # Open a second LDB connection with the user credentials. Use the
1746         # command line credentials for informations like the domain, the realm
1747         # and the workstation.
1748         sasl_creds = Credentials()
1749         sasl_creds.set_username(username)
1750         sasl_creds.set_password(password)
1751         sasl_creds.set_domain(creds.get_domain())
1752         sasl_creds.set_workstation(creds.get_workstation())
1753         sasl_creds.set_gensec_features(creds.get_gensec_features() | gensec.FEATURE_SEAL)
1754         sasl_creds.set_kerberos_state(DONT_USE_KERBEROS)
1755
1756         sasl_wrong_creds = Credentials()
1757         sasl_wrong_creds.set_username(username)
1758         sasl_wrong_creds.set_password("wrong")
1759         sasl_wrong_creds.set_domain(creds.get_domain())
1760         sasl_wrong_creds.set_workstation(creds.get_workstation())
1761         sasl_wrong_creds.set_gensec_features(creds.get_gensec_features() | gensec.FEATURE_SEAL)
1762         sasl_wrong_creds.set_kerberos_state(DONT_USE_KERBEROS)
1763
1764         simple_creds = Credentials()
1765         simple_creds.set_bind_dn("cn=ldaptestuser,cn=users," + self.base_dn)
1766         simple_creds.set_username(username)
1767         simple_creds.set_password(password)
1768         simple_creds.set_domain(creds.get_domain())
1769         simple_creds.set_workstation(creds.get_workstation())
1770         simple_creds.set_gensec_features(creds.get_gensec_features() | gensec.FEATURE_SEAL)
1771         simple_creds.set_kerberos_state(DONT_USE_KERBEROS)
1772
1773         simple_wrong_creds = Credentials()
1774         simple_wrong_creds.set_bind_dn("cn=ldaptestuser,cn=users," + self.base_dn)
1775         simple_wrong_creds.set_username(username)
1776         simple_wrong_creds.set_password("wrong")
1777         simple_wrong_creds.set_domain(creds.get_domain())
1778         simple_wrong_creds.set_workstation(creds.get_workstation())
1779         simple_wrong_creds.set_gensec_features(creds.get_gensec_features() | gensec.FEATURE_SEAL)
1780         simple_wrong_creds.set_kerberos_state(DONT_USE_KERBEROS)
1781
1782         sasl_ldb = SamDB(url=host, credentials=sasl_creds, lp=lp)
1783         self.assertIsNotNone(sasl_ldb)
1784         sasl_ldb = None
1785
1786         requires_strong_auth = False
1787         try:
1788             simple_ldb = SamDB(url=host, credentials=simple_creds, lp=lp)
1789             self.assertIsNotNone(simple_ldb)
1790             simple_ldb = None
1791         except LdbError as e55:
1792             (num, msg) = e55.args
1793             if num != ERR_STRONG_AUTH_REQUIRED:
1794                 raise
1795             requires_strong_auth = True
1796
1797         def assertLDAPErrorMsg(msg, expected_msg):
1798             self.assertTrue(expected_msg in msg,
1799                             "msg[%s] does not contain expected[%s]" % (
1800                                 msg, expected_msg))
1801
1802         try:
1803             ldb_fail = SamDB(url=host, credentials=sasl_wrong_creds, lp=lp)
1804             self.fail()
1805         except LdbError as e56:
1806             (num, msg) = e56.args
1807             self.assertEquals(num, ERR_INVALID_CREDENTIALS)
1808             self.assertTrue(error_msg_sasl_wrong_pw in msg)
1809
1810         if not requires_strong_auth:
1811             try:
1812                 ldb_fail = SamDB(url=host, credentials=simple_wrong_creds, lp=lp)
1813                 self.fail()
1814             except LdbError as e4:
1815                 (num, msg) = e4.args
1816                 self.assertEquals(num, ERR_INVALID_CREDENTIALS)
1817                 assertLDAPErrorMsg(msg, error_msg_simple_wrong_pw)
1818
1819         m = Message()
1820         m.dn = Dn(ldb, "cn=ldaptestuser,cn=users," + self.base_dn)
1821         m["pls1"] = MessageElement(str(0),
1822                                    FLAG_MOD_REPLACE,
1823                                    "pwdLastSet")
1824         ldb.modify(m)
1825
1826         res1 = ldb.search("cn=ldaptestuser,cn=users," + self.base_dn,
1827                           scope=SCOPE_BASE, attrs=["pwdLastSet"])
1828         self.assertEqual(int(res1[0]["pwdLastSet"][0]), 0)
1829
1830         try:
1831             ldb_fail = SamDB(url=host, credentials=sasl_wrong_creds, lp=lp)
1832             self.fail()
1833         except LdbError as e57:
1834             (num, msg) = e57.args
1835             self.assertEquals(num, ERR_INVALID_CREDENTIALS)
1836             assertLDAPErrorMsg(msg, error_msg_sasl_wrong_pw)
1837
1838         try:
1839             ldb_fail = SamDB(url=host, credentials=sasl_creds, lp=lp)
1840             self.fail()
1841         except LdbError as e58:
1842             (num, msg) = e58.args
1843             self.assertEquals(num, ERR_INVALID_CREDENTIALS)
1844             assertLDAPErrorMsg(msg, error_msg_sasl_must_change)
1845
1846         if not requires_strong_auth:
1847             try:
1848                 ldb_fail = SamDB(url=host, credentials=simple_wrong_creds, lp=lp)
1849                 self.fail()
1850             except LdbError as e5:
1851                 (num, msg) = e5.args
1852                 self.assertEquals(num, ERR_INVALID_CREDENTIALS)
1853                 assertLDAPErrorMsg(msg, error_msg_simple_wrong_pw)
1854
1855             try:
1856                 ldb_fail = SamDB(url=host, credentials=simple_creds, lp=lp)
1857                 self.fail()
1858             except LdbError as e6:
1859                 (num, msg) = e6.args
1860                 self.assertEquals(num, ERR_INVALID_CREDENTIALS)
1861                 assertLDAPErrorMsg(msg, error_msg_simple_must_change)
1862
1863         delete_force(self.ldb, "cn=ldaptestuser,cn=users," + self.base_dn)
1864
1865     def test_userAccountControl(self):
1866         """Test the userAccountControl behaviour"""
1867         print("Testing userAccountControl behaviour\n")
1868
1869         # With a user object
1870
1871         # Add operation
1872
1873         # As user you can only set a normal account.
1874         # The UF_PASSWD_NOTREQD flag is needed since we haven't requested a
1875         # password yet.
1876         # With SYSTEM rights you can set a interdomain trust account.
1877
1878         ldb.add({
1879             "dn": "cn=ldaptestuser,cn=users," + self.base_dn,
1880             "objectclass": "user",
1881             "userAccountControl": "0"})
1882
1883         res1 = ldb.search("cn=ldaptestuser,cn=users," + self.base_dn,
1884                           scope=SCOPE_BASE,
1885                           attrs=["sAMAccountType", "userAccountControl"])
1886         self.assertTrue(len(res1) == 1)
1887         self.assertEquals(int(res1[0]["sAMAccountType"][0]),
1888                           ATYPE_NORMAL_ACCOUNT)
1889         self.assertTrue(int(res1[0]["userAccountControl"][0]) & UF_ACCOUNTDISABLE == 0)
1890         self.assertTrue(int(res1[0]["userAccountControl"][0]) & UF_PASSWD_NOTREQD == 0)
1891         delete_force(self.ldb, "cn=ldaptestuser,cn=users," + self.base_dn)
1892
1893         ldb.add({
1894             "dn": "cn=ldaptestuser,cn=users," + self.base_dn,
1895             "objectclass": "user",
1896             "userAccountControl": str(UF_NORMAL_ACCOUNT)})
1897         delete_force(self.ldb, "cn=ldaptestuser,cn=users," + self.base_dn)
1898
1899         ldb.add({
1900             "dn": "cn=ldaptestuser,cn=users," + self.base_dn,
1901             "objectclass": "user",
1902             "userAccountControl": str(UF_NORMAL_ACCOUNT | UF_PASSWD_NOTREQD)})
1903
1904         res1 = ldb.search("cn=ldaptestuser,cn=users," + self.base_dn,
1905                           scope=SCOPE_BASE,
1906                           attrs=["sAMAccountType", "userAccountControl"])
1907         self.assertTrue(len(res1) == 1)
1908         self.assertEquals(int(res1[0]["sAMAccountType"][0]),
1909                           ATYPE_NORMAL_ACCOUNT)
1910         self.assertTrue(int(res1[0]["userAccountControl"][0]) & UF_ACCOUNTDISABLE == 0)
1911         delete_force(self.ldb, "cn=ldaptestuser,cn=users," + self.base_dn)
1912
1913         ldb.add({
1914             "dn": "cn=ldaptestuser,cn=users," + self.base_dn,
1915             "objectclass": "user",
1916             "userAccountControl": str(UF_NORMAL_ACCOUNT | UF_PASSWD_NOTREQD | UF_LOCKOUT | UF_PASSWORD_EXPIRED)})
1917
1918         res1 = ldb.search("cn=ldaptestuser,cn=users," + self.base_dn,
1919                           scope=SCOPE_BASE,
1920                           attrs=["sAMAccountType", "userAccountControl", "lockoutTime", "pwdLastSet"])
1921         self.assertTrue(len(res1) == 1)
1922         self.assertEquals(int(res1[0]["sAMAccountType"][0]),
1923                           ATYPE_NORMAL_ACCOUNT)
1924         self.assertTrue(int(res1[0]["userAccountControl"][0]) & (UF_LOCKOUT | UF_PASSWORD_EXPIRED) == 0)
1925         self.assertFalse("lockoutTime" in res1[0])
1926         self.assertTrue(int(res1[0]["pwdLastSet"][0]) == 0)
1927         delete_force(self.ldb, "cn=ldaptestuser,cn=users," + self.base_dn)
1928
1929         try:
1930             ldb.add({
1931                 "dn": "cn=ldaptestuser,cn=users," + self.base_dn,
1932                 "objectclass": "user",
1933                 "userAccountControl": str(UF_TEMP_DUPLICATE_ACCOUNT)})
1934             self.fail()
1935         except LdbError as e59:
1936             (num, _) = e59.args
1937             self.assertEquals(num, ERR_OTHER)
1938         delete_force(self.ldb, "cn=ldaptestuser,cn=users," + self.base_dn)
1939
1940         try:
1941             ldb.add({
1942                 "dn": "cn=ldaptestuser,cn=users," + self.base_dn,
1943                 "objectclass": "user",
1944                 "userAccountControl": str(UF_SERVER_TRUST_ACCOUNT)})
1945             self.fail()
1946         except LdbError as e60:
1947             (num, _) = e60.args
1948             self.assertEquals(num, ERR_OBJECT_CLASS_VIOLATION)
1949         delete_force(self.ldb, "cn=ldaptestuser,cn=users," + self.base_dn)
1950
1951         try:
1952             ldb.add({
1953                 "dn": "cn=ldaptestuser,cn=users," + self.base_dn,
1954                 "objectclass": "user",
1955                 "userAccountControl": str(UF_WORKSTATION_TRUST_ACCOUNT)})
1956         except LdbError as e61:
1957             (num, _) = e61.args
1958             self.assertEquals(num, ERR_OBJECT_CLASS_VIOLATION)
1959         delete_force(self.ldb, "cn=ldaptestuser,cn=users," + self.base_dn)
1960
1961         try:
1962             ldb.add({
1963                 "dn": "cn=ldaptestuser,cn=users," + self.base_dn,
1964                 "objectclass": "user",
1965                 "userAccountControl": str(UF_WORKSTATION_TRUST_ACCOUNT | UF_PARTIAL_SECRETS_ACCOUNT)})
1966         except LdbError as e62:
1967             (num, _) = e62.args
1968             self.assertEquals(num, ERR_OBJECT_CLASS_VIOLATION)
1969         delete_force(self.ldb, "cn=ldaptestuser,cn=users," + self.base_dn)
1970
1971         try:
1972             ldb.add({
1973                 "dn": "cn=ldaptestuser,cn=users," + self.base_dn,
1974                 "objectclass": "user",
1975                 "userAccountControl": str(UF_INTERDOMAIN_TRUST_ACCOUNT)})
1976             self.fail()
1977         except LdbError as e63:
1978             (num, _) = e63.args
1979             self.assertEquals(num, ERR_INSUFFICIENT_ACCESS_RIGHTS)
1980         delete_force(self.ldb, "cn=ldaptestuser,cn=users," + self.base_dn)
1981
1982         # Modify operation
1983
1984         ldb.add({
1985             "dn": "cn=ldaptestuser,cn=users," + self.base_dn,
1986             "objectclass": "user"})
1987
1988         # After creation we should have a normal account
1989         res1 = ldb.search("cn=ldaptestuser,cn=users," + self.base_dn,
1990                           scope=SCOPE_BASE,
1991                           attrs=["sAMAccountType", "userAccountControl"])
1992         self.assertTrue(len(res1) == 1)
1993         self.assertEquals(int(res1[0]["sAMAccountType"][0]),
1994                           ATYPE_NORMAL_ACCOUNT)
1995         self.assertTrue(int(res1[0]["userAccountControl"][0]) & UF_ACCOUNTDISABLE != 0)
1996
1997         # As user you can only switch from a normal account to a workstation
1998         # trust account and back.
1999         # The UF_PASSWD_NOTREQD flag is needed since we haven't requested a
2000         # password yet.
2001         # With SYSTEM rights you can switch to a interdomain trust account.
2002
2003         # Invalid attribute
2004         try:
2005             m = Message()
2006             m.dn = Dn(ldb, "cn=ldaptestuser,cn=users," + self.base_dn)
2007             m["userAccountControl"] = MessageElement("0",
2008                                                      FLAG_MOD_REPLACE, "userAccountControl")
2009             ldb.modify(m)
2010         except LdbError as e64:
2011             (num, _) = e64.args
2012             self.assertEquals(num, ERR_UNWILLING_TO_PERFORM)
2013
2014         try:
2015             m = Message()
2016             m.dn = Dn(ldb, "cn=ldaptestuser,cn=users," + self.base_dn)
2017             m["userAccountControl"] = MessageElement(
2018                 str(UF_NORMAL_ACCOUNT),
2019                 FLAG_MOD_REPLACE, "userAccountControl")
2020             ldb.modify(m)
2021         except LdbError as e65:
2022             (num, _) = e65.args
2023             self.assertEquals(num, ERR_UNWILLING_TO_PERFORM)
2024
2025         m = Message()
2026         m.dn = Dn(ldb, "cn=ldaptestuser,cn=users," + self.base_dn)
2027         m["userAccountControl"] = MessageElement(
2028             str(UF_NORMAL_ACCOUNT | UF_PASSWD_NOTREQD),
2029             FLAG_MOD_REPLACE, "userAccountControl")
2030         ldb.modify(m)
2031
2032         res1 = ldb.search("cn=ldaptestuser,cn=users," + self.base_dn,
2033                           scope=SCOPE_BASE,
2034                           attrs=["sAMAccountType", "userAccountControl"])
2035         self.assertTrue(len(res1) == 1)
2036         self.assertEquals(int(res1[0]["sAMAccountType"][0]),
2037                           ATYPE_NORMAL_ACCOUNT)
2038         self.assertTrue(int(res1[0]["userAccountControl"][0]) & UF_ACCOUNTDISABLE == 0)
2039
2040         m = Message()
2041         m.dn = Dn(ldb, "cn=ldaptestuser,cn=users," + self.base_dn)
2042         m["userAccountControl"] = MessageElement(
2043             str(UF_ACCOUNTDISABLE),
2044             FLAG_MOD_REPLACE, "userAccountControl")
2045         ldb.modify(m)
2046
2047         res1 = ldb.search("cn=ldaptestuser,cn=users," + self.base_dn,
2048                           scope=SCOPE_BASE,
2049                           attrs=["sAMAccountType", "userAccountControl"])
2050         self.assertTrue(len(res1) == 1)
2051         self.assertEquals(int(res1[0]["sAMAccountType"][0]),
2052                           ATYPE_NORMAL_ACCOUNT)
2053         self.assertTrue(int(res1[0]["userAccountControl"][0]) & UF_NORMAL_ACCOUNT != 0)
2054         self.assertTrue(int(res1[0]["userAccountControl"][0]) & UF_ACCOUNTDISABLE != 0)
2055
2056         m = Message()
2057         m.dn = Dn(ldb, "cn=ldaptestuser,cn=users," + self.base_dn)
2058         m["lockoutTime"] = MessageElement(str(samba.unix2nttime(0)), FLAG_MOD_REPLACE, "lockoutTime")
2059         m["pwdLastSet"] = MessageElement(str(samba.unix2nttime(0)), FLAG_MOD_REPLACE, "pwdLastSet")
2060         ldb.modify(m)
2061
2062         m = Message()
2063         m.dn = Dn(ldb, "cn=ldaptestuser,cn=users," + self.base_dn)
2064         m["userAccountControl"] = MessageElement(
2065             str(UF_LOCKOUT | UF_PASSWORD_EXPIRED),
2066             FLAG_MOD_REPLACE, "userAccountControl")
2067         ldb.modify(m)
2068
2069         res1 = ldb.search("cn=ldaptestuser,cn=users," + self.base_dn,
2070                           scope=SCOPE_BASE,
2071                           attrs=["sAMAccountType", "userAccountControl", "lockoutTime", "pwdLastSet"])
2072         self.assertTrue(len(res1) == 1)
2073         self.assertEquals(int(res1[0]["sAMAccountType"][0]),
2074                           ATYPE_NORMAL_ACCOUNT)
2075         self.assertTrue(int(res1[0]["userAccountControl"][0]) & UF_NORMAL_ACCOUNT != 0)
2076         self.assertTrue(int(res1[0]["userAccountControl"][0]) & (UF_LOCKOUT | UF_PASSWORD_EXPIRED) == 0)
2077         self.assertTrue(int(res1[0]["lockoutTime"][0]) == 0)
2078         self.assertTrue(int(res1[0]["pwdLastSet"][0]) == 0)
2079
2080         try:
2081             m = Message()
2082             m.dn = Dn(ldb, "cn=ldaptestuser,cn=users," + self.base_dn)
2083             m["userAccountControl"] = MessageElement(
2084                 str(UF_TEMP_DUPLICATE_ACCOUNT),
2085                 FLAG_MOD_REPLACE, "userAccountControl")
2086             ldb.modify(m)
2087             self.fail()
2088         except LdbError as e66:
2089             (num, _) = e66.args
2090             self.assertEquals(num, ERR_OTHER)
2091
2092         try:
2093             m = Message()
2094             m.dn = Dn(ldb, "cn=ldaptestuser,cn=users," + self.base_dn)
2095             m["userAccountControl"] = MessageElement(
2096                 str(UF_SERVER_TRUST_ACCOUNT),
2097                 FLAG_MOD_REPLACE, "userAccountControl")
2098             ldb.modify(m)
2099             self.fail()
2100         except LdbError as e67:
2101             (num, _) = e67.args
2102             self.assertEquals(num, ERR_UNWILLING_TO_PERFORM)
2103
2104         m = Message()
2105         m.dn = Dn(ldb, "cn=ldaptestuser,cn=users," + self.base_dn)
2106         m["userAccountControl"] = MessageElement(
2107             str(UF_WORKSTATION_TRUST_ACCOUNT),
2108             FLAG_MOD_REPLACE, "userAccountControl")
2109         ldb.modify(m)
2110
2111         try:
2112             m = Message()
2113             m.dn = Dn(ldb, "cn=ldaptestuser,cn=users," + self.base_dn)
2114             m["userAccountControl"] = MessageElement(
2115                 str(UF_WORKSTATION_TRUST_ACCOUNT | UF_PARTIAL_SECRETS_ACCOUNT),
2116                 FLAG_MOD_REPLACE, "userAccountControl")
2117             ldb.modify(m)
2118             self.fail()
2119         except LdbError as e68:
2120             (num, _) = e68.args
2121             self.assertEquals(num, ERR_UNWILLING_TO_PERFORM)
2122
2123         res1 = ldb.search("cn=ldaptestuser,cn=users," + self.base_dn,
2124                           scope=SCOPE_BASE, attrs=["sAMAccountType"])
2125         self.assertTrue(len(res1) == 1)
2126         self.assertEquals(int(res1[0]["sAMAccountType"][0]),
2127                           ATYPE_WORKSTATION_TRUST)
2128
2129         m = Message()
2130         m.dn = Dn(ldb, "cn=ldaptestuser,cn=users," + self.base_dn)
2131         m["userAccountControl"] = MessageElement(
2132             str(UF_NORMAL_ACCOUNT | UF_PASSWD_NOTREQD),
2133             FLAG_MOD_REPLACE, "userAccountControl")
2134         ldb.modify(m)
2135
2136         res1 = ldb.search("cn=ldaptestuser,cn=users," + self.base_dn,
2137                           scope=SCOPE_BASE, attrs=["sAMAccountType"])
2138         self.assertTrue(len(res1) == 1)
2139         self.assertEquals(int(res1[0]["sAMAccountType"][0]),
2140                           ATYPE_NORMAL_ACCOUNT)
2141
2142         try:
2143             m = Message()
2144             m.dn = Dn(ldb, "cn=ldaptestuser,cn=users," + self.base_dn)
2145             m["userAccountControl"] = MessageElement(
2146                 str(UF_INTERDOMAIN_TRUST_ACCOUNT),
2147                 FLAG_MOD_REPLACE, "userAccountControl")
2148             ldb.modify(m)
2149             self.fail()
2150         except LdbError as e69:
2151             (num, _) = e69.args
2152             self.assertEquals(num, ERR_INSUFFICIENT_ACCESS_RIGHTS)
2153
2154         # With a computer object
2155
2156         # Add operation
2157
2158         # As computer you can set a normal account and a server trust account.
2159         # The UF_PASSWD_NOTREQD flag is needed since we haven't requested a
2160         # password yet.
2161         # With SYSTEM rights you can set a interdomain trust account.
2162
2163         ldb.add({
2164             "dn": "cn=ldaptestcomputer,cn=computers," + self.base_dn,
2165             "objectclass": "computer",
2166             "userAccountControl": "0"})
2167
2168         res1 = ldb.search("cn=ldaptestcomputer,cn=computers," + self.base_dn,
2169                           scope=SCOPE_BASE,
2170                           attrs=["sAMAccountType", "userAccountControl"])
2171         self.assertTrue(len(res1) == 1)
2172         self.assertEquals(int(res1[0]["sAMAccountType"][0]),
2173                           ATYPE_NORMAL_ACCOUNT)
2174         self.assertTrue(int(res1[0]["userAccountControl"][0]) & UF_ACCOUNTDISABLE == 0)
2175         self.assertTrue(int(res1[0]["userAccountControl"][0]) & UF_PASSWD_NOTREQD == 0)
2176         delete_force(self.ldb, "cn=ldaptestcomputer,cn=computers," + self.base_dn)
2177
2178         ldb.add({
2179             "dn": "cn=ldaptestcomputer,cn=computers," + self.base_dn,
2180             "objectclass": "computer",
2181             "userAccountControl": str(UF_NORMAL_ACCOUNT)})
2182         delete_force(self.ldb, "cn=ldaptestcomputer,cn=computers," + self.base_dn)
2183
2184         ldb.add({
2185             "dn": "cn=ldaptestcomputer,cn=computers," + self.base_dn,
2186             "objectclass": "computer",
2187             "userAccountControl": str(UF_NORMAL_ACCOUNT | UF_PASSWD_NOTREQD)})
2188
2189         res1 = ldb.search("cn=ldaptestcomputer,cn=computers," + self.base_dn,
2190                           scope=SCOPE_BASE,
2191                           attrs=["sAMAccountType", "userAccountControl"])
2192         self.assertTrue(len(res1) == 1)
2193         self.assertEquals(int(res1[0]["sAMAccountType"][0]),
2194                           ATYPE_NORMAL_ACCOUNT)
2195         self.assertTrue(int(res1[0]["userAccountControl"][0]) & UF_ACCOUNTDISABLE == 0)
2196         delete_force(self.ldb, "cn=ldaptestcomputer,cn=computers," + self.base_dn)
2197
2198         ldb.add({
2199             "dn": "cn=ldaptestcomputer,cn=computers," + self.base_dn,
2200             "objectclass": "computer",
2201             "userAccountControl": str(UF_NORMAL_ACCOUNT | UF_PASSWD_NOTREQD | UF_LOCKOUT | UF_PASSWORD_EXPIRED)})
2202
2203         res1 = ldb.search("cn=ldaptestcomputer,cn=computers," + self.base_dn,
2204                           scope=SCOPE_BASE,
2205                           attrs=["sAMAccountType", "userAccountControl", "lockoutTime", "pwdLastSet"])
2206         self.assertTrue(len(res1) == 1)
2207         self.assertEquals(int(res1[0]["sAMAccountType"][0]),
2208                           ATYPE_NORMAL_ACCOUNT)
2209         self.assertTrue(int(res1[0]["userAccountControl"][0]) & (UF_LOCKOUT | UF_PASSWORD_EXPIRED) == 0)
2210         self.assertFalse("lockoutTime" in res1[0])
2211         self.assertTrue(int(res1[0]["pwdLastSet"][0]) == 0)
2212         delete_force(self.ldb, "cn=ldaptestcomputer,cn=computers," + self.base_dn)
2213
2214         try:
2215             ldb.add({
2216                 "dn": "cn=ldaptestcomputer,cn=computers," + self.base_dn,
2217                 "objectclass": "computer",
2218                 "userAccountControl": str(UF_TEMP_DUPLICATE_ACCOUNT)})
2219             self.fail()
2220         except LdbError as e70:
2221             (num, _) = e70.args
2222             self.assertEquals(num, ERR_OTHER)
2223         delete_force(self.ldb, "cn=ldaptestcomputer,cn=computers," + self.base_dn)
2224
2225         ldb.add({
2226             "dn": "cn=ldaptestcomputer,cn=computers," + self.base_dn,
2227             "objectclass": "computer",
2228             "userAccountControl": str(UF_SERVER_TRUST_ACCOUNT)})
2229
2230         res1 = ldb.search("cn=ldaptestcomputer,cn=computers," + self.base_dn,
2231                           scope=SCOPE_BASE, attrs=["sAMAccountType"])
2232         self.assertTrue(len(res1) == 1)
2233         self.assertEquals(int(res1[0]["sAMAccountType"][0]),
2234                           ATYPE_WORKSTATION_TRUST)
2235         delete_force(self.ldb, "cn=ldaptestcomputer,cn=computers," + self.base_dn)
2236
2237         try:
2238             ldb.add({
2239                 "dn": "cn=ldaptestcomputer,cn=computers," + self.base_dn,
2240                 "objectclass": "computer",
2241                 "userAccountControl": str(UF_WORKSTATION_TRUST_ACCOUNT)})
2242         except LdbError as e71:
2243             (num, _) = e71.args
2244             self.assertEquals(num, ERR_OBJECT_CLASS_VIOLATION)
2245         delete_force(self.ldb, "cn=ldaptestcomputer,cn=computers," + self.base_dn)
2246
2247         try:
2248             ldb.add({
2249                 "dn": "cn=ldaptestcomputer,cn=computers," + self.base_dn,
2250                 "objectclass": "computer",
2251                 "userAccountControl": str(UF_INTERDOMAIN_TRUST_ACCOUNT)})
2252             self.fail()
2253         except LdbError as e72:
2254             (num, _) = e72.args
2255             self.assertEquals(num, ERR_INSUFFICIENT_ACCESS_RIGHTS)
2256         delete_force(self.ldb, "cn=ldaptestcomputer,cn=computers," + self.base_dn)
2257
2258         # Modify operation
2259
2260         ldb.add({
2261             "dn": "cn=ldaptestcomputer,cn=computers," + self.base_dn,
2262             "objectclass": "computer"})
2263
2264         # After creation we should have a normal account
2265         res1 = ldb.search("cn=ldaptestcomputer,cn=computers," + self.base_dn,
2266                           scope=SCOPE_BASE,
2267                           attrs=["sAMAccountType", "userAccountControl"])
2268         self.assertTrue(len(res1) == 1)
2269         self.assertEquals(int(res1[0]["sAMAccountType"][0]),
2270                           ATYPE_NORMAL_ACCOUNT)
2271         self.assertTrue(int(res1[0]["userAccountControl"][0]) & UF_ACCOUNTDISABLE != 0)
2272
2273         # As computer you can switch from a normal account to a workstation
2274         # or server trust account and back (also swapping between trust
2275         # accounts is allowed).
2276         # The UF_PASSWD_NOTREQD flag is needed since we haven't requested a
2277         # password yet.
2278         # With SYSTEM rights you can switch to a interdomain trust account.
2279
2280         # Invalid attribute
2281         try:
2282             m = Message()
2283             m.dn = Dn(ldb, "cn=ldaptestcomputer,cn=computers," + self.base_dn)
2284             m["userAccountControl"] = MessageElement("0",
2285                                                      FLAG_MOD_REPLACE, "userAccountControl")
2286             ldb.modify(m)
2287         except LdbError as e73:
2288             (num, _) = e73.args
2289             self.assertEquals(num, ERR_UNWILLING_TO_PERFORM)
2290
2291         try:
2292             m = Message()
2293             m.dn = Dn(ldb, "cn=ldaptestcomputer,cn=computers," + self.base_dn)
2294             m["userAccountControl"] = MessageElement(
2295                 str(UF_NORMAL_ACCOUNT),
2296                 FLAG_MOD_REPLACE, "userAccountControl")
2297             ldb.modify(m)
2298         except LdbError as e74:
2299             (num, _) = e74.args
2300             self.assertEquals(num, ERR_UNWILLING_TO_PERFORM)
2301
2302         m = Message()
2303         m.dn = Dn(ldb, "cn=ldaptestcomputer,cn=computers," + self.base_dn)
2304         m["userAccountControl"] = MessageElement(
2305             str(UF_NORMAL_ACCOUNT | UF_PASSWD_NOTREQD),
2306             FLAG_MOD_REPLACE, "userAccountControl")
2307         ldb.modify(m)
2308
2309         res1 = ldb.search("cn=ldaptestcomputer,cn=computers," + self.base_dn,
2310                           scope=SCOPE_BASE,
2311                           attrs=["sAMAccountType", "userAccountControl"])
2312         self.assertTrue(len(res1) == 1)
2313         self.assertEquals(int(res1[0]["sAMAccountType"][0]),
2314                           ATYPE_NORMAL_ACCOUNT)
2315         self.assertTrue(int(res1[0]["userAccountControl"][0]) & UF_ACCOUNTDISABLE == 0)
2316
2317         m = Message()
2318         m.dn = Dn(ldb, "cn=ldaptestcomputer,cn=computers," + self.base_dn)
2319         m["userAccountControl"] = MessageElement(
2320             str(UF_ACCOUNTDISABLE),
2321             FLAG_MOD_REPLACE, "userAccountControl")
2322         ldb.modify(m)
2323
2324         res1 = ldb.search("cn=ldaptestcomputer,cn=computers," + self.base_dn,
2325                           scope=SCOPE_BASE,
2326                           attrs=["sAMAccountType", "userAccountControl"])
2327         self.assertTrue(len(res1) == 1)
2328         self.assertEquals(int(res1[0]["sAMAccountType"][0]),
2329                           ATYPE_NORMAL_ACCOUNT)
2330         self.assertTrue(int(res1[0]["userAccountControl"][0]) & UF_NORMAL_ACCOUNT != 0)
2331         self.assertTrue(int(res1[0]["userAccountControl"][0]) & UF_ACCOUNTDISABLE != 0)
2332
2333         m = Message()
2334         m.dn = Dn(ldb, "cn=ldaptestcomputer,cn=computers," + self.base_dn)
2335         m["lockoutTime"] = MessageElement(str(samba.unix2nttime(0)), FLAG_MOD_REPLACE, "lockoutTime")
2336         m["pwdLastSet"] = MessageElement(str(samba.unix2nttime(0)), FLAG_MOD_REPLACE, "pwdLastSet")
2337         ldb.modify(m)
2338
2339         m = Message()
2340         m.dn = Dn(ldb, "cn=ldaptestcomputer,cn=computers," + self.base_dn)
2341         m["userAccountControl"] = MessageElement(
2342             str(UF_LOCKOUT | UF_PASSWORD_EXPIRED),
2343             FLAG_MOD_REPLACE, "userAccountControl")
2344         ldb.modify(m)
2345
2346         res1 = ldb.search("cn=ldaptestcomputer,cn=computers," + self.base_dn,
2347                           scope=SCOPE_BASE,
2348                           attrs=["sAMAccountType", "userAccountControl", "lockoutTime", "pwdLastSet"])
2349         self.assertTrue(len(res1) == 1)
2350         self.assertEquals(int(res1[0]["sAMAccountType"][0]),
2351                           ATYPE_NORMAL_ACCOUNT)
2352         self.assertTrue(int(res1[0]["userAccountControl"][0]) & UF_NORMAL_ACCOUNT != 0)
2353         self.assertTrue(int(res1[0]["userAccountControl"][0]) & (UF_LOCKOUT | UF_PASSWORD_EXPIRED) == 0)
2354         self.assertTrue(int(res1[0]["lockoutTime"][0]) == 0)
2355         self.assertTrue(int(res1[0]["pwdLastSet"][0]) == 0)
2356
2357         try:
2358             m = Message()
2359             m.dn = Dn(ldb, "cn=ldaptestcomputer,cn=computers," + self.base_dn)
2360             m["userAccountControl"] = MessageElement(
2361                 str(UF_TEMP_DUPLICATE_ACCOUNT),
2362                 FLAG_MOD_REPLACE, "userAccountControl")
2363             ldb.modify(m)
2364             self.fail()
2365         except LdbError as e75:
2366             (num, _) = e75.args
2367             self.assertEquals(num, ERR_OTHER)
2368
2369         m = Message()
2370         m.dn = Dn(ldb, "cn=ldaptestcomputer,cn=computers," + self.base_dn)
2371         m["userAccountControl"] = MessageElement(
2372             str(UF_SERVER_TRUST_ACCOUNT),
2373             FLAG_MOD_REPLACE, "userAccountControl")
2374         ldb.modify(m)
2375
2376         res1 = ldb.search("cn=ldaptestcomputer,cn=computers," + self.base_dn,
2377                           scope=SCOPE_BASE, attrs=["sAMAccountType"])
2378         self.assertTrue(len(res1) == 1)
2379         self.assertEquals(int(res1[0]["sAMAccountType"][0]),
2380                           ATYPE_WORKSTATION_TRUST)
2381
2382         m = Message()
2383         m.dn = Dn(ldb, "cn=ldaptestcomputer,cn=computers," + self.base_dn)
2384         m["userAccountControl"] = MessageElement(
2385             str(UF_NORMAL_ACCOUNT | UF_PASSWD_NOTREQD),
2386             FLAG_MOD_REPLACE, "userAccountControl")
2387         ldb.modify(m)
2388
2389         res1 = ldb.search("cn=ldaptestcomputer,cn=computers," + self.base_dn,
2390                           scope=SCOPE_BASE, attrs=["sAMAccountType"])
2391         self.assertTrue(len(res1) == 1)
2392         self.assertEquals(int(res1[0]["sAMAccountType"][0]),
2393                           ATYPE_NORMAL_ACCOUNT)
2394
2395         m = Message()
2396         m.dn = Dn(ldb, "cn=ldaptestcomputer,cn=computers," + self.base_dn)
2397         m["userAccountControl"] = MessageElement(
2398             str(UF_WORKSTATION_TRUST_ACCOUNT),
2399             FLAG_MOD_REPLACE, "userAccountControl")
2400         ldb.modify(m)
2401
2402         res1 = ldb.search("cn=ldaptestcomputer,cn=computers," + self.base_dn,
2403                           scope=SCOPE_BASE, attrs=["sAMAccountType"])
2404         self.assertTrue(len(res1) == 1)
2405         self.assertEquals(int(res1[0]["sAMAccountType"][0]),
2406                           ATYPE_WORKSTATION_TRUST)
2407
2408         m = Message()
2409         m.dn = Dn(ldb, "cn=ldaptestcomputer,cn=computers," + self.base_dn)
2410         m["userAccountControl"] = MessageElement(
2411             str(UF_NORMAL_ACCOUNT | UF_PASSWD_NOTREQD),
2412             FLAG_MOD_REPLACE, "userAccountControl")
2413         ldb.modify(m)
2414
2415         res1 = ldb.search("cn=ldaptestcomputer,cn=computers," + self.base_dn,
2416                           scope=SCOPE_BASE, attrs=["sAMAccountType"])
2417         self.assertTrue(len(res1) == 1)
2418         self.assertEquals(int(res1[0]["sAMAccountType"][0]),
2419                           ATYPE_NORMAL_ACCOUNT)
2420
2421         m = Message()
2422         m.dn = Dn(ldb, "cn=ldaptestcomputer,cn=computers," + self.base_dn)
2423         m["userAccountControl"] = MessageElement(
2424             str(UF_SERVER_TRUST_ACCOUNT),
2425             FLAG_MOD_REPLACE, "userAccountControl")
2426         ldb.modify(m)
2427
2428         res1 = ldb.search("cn=ldaptestcomputer,cn=computers," + self.base_dn,
2429                           scope=SCOPE_BASE, attrs=["sAMAccountType"])
2430         self.assertTrue(len(res1) == 1)
2431         self.assertEquals(int(res1[0]["sAMAccountType"][0]),
2432                           ATYPE_WORKSTATION_TRUST)
2433
2434         m = Message()
2435         m.dn = Dn(ldb, "cn=ldaptestcomputer,cn=computers," + self.base_dn)
2436         m["userAccountControl"] = MessageElement(
2437             str(UF_WORKSTATION_TRUST_ACCOUNT),
2438             FLAG_MOD_REPLACE, "userAccountControl")
2439         ldb.modify(m)
2440
2441         res1 = ldb.search("cn=ldaptestcomputer,cn=computers," + self.base_dn,
2442                           scope=SCOPE_BASE, attrs=["sAMAccountType"])
2443         self.assertTrue(len(res1) == 1)
2444         self.assertEquals(int(res1[0]["sAMAccountType"][0]),
2445                           ATYPE_WORKSTATION_TRUST)
2446
2447         try:
2448             m = Message()
2449             m.dn = Dn(ldb, "cn=ldaptestcomputer,cn=computers," + self.base_dn)
2450             m["userAccountControl"] = MessageElement(
2451                 str(UF_INTERDOMAIN_TRUST_ACCOUNT),
2452                 FLAG_MOD_REPLACE, "userAccountControl")
2453             ldb.modify(m)
2454             self.fail()
2455         except LdbError as e76:
2456             (num, _) = e76.args
2457             self.assertEquals(num, ERR_INSUFFICIENT_ACCESS_RIGHTS)
2458
2459         # "primaryGroupID" does not change if account type remains the same
2460
2461         # For a user account
2462
2463         ldb.add({
2464             "dn": "cn=ldaptestuser2,cn=users," + self.base_dn,
2465             "objectclass": "user",
2466             "userAccountControl": str(UF_NORMAL_ACCOUNT | UF_PASSWD_NOTREQD | UF_ACCOUNTDISABLE)})
2467
2468         res1 = ldb.search("cn=ldaptestuser2,cn=users," + self.base_dn,
2469                           scope=SCOPE_BASE,
2470                           attrs=["userAccountControl"])
2471         self.assertTrue(len(res1) == 1)
2472         self.assertEquals(int(res1[0]["userAccountControl"][0]),
2473                           UF_NORMAL_ACCOUNT | UF_PASSWD_NOTREQD | UF_ACCOUNTDISABLE)
2474
2475         m = Message()
2476         m.dn = Dn(ldb, "<SID=" + ldb.get_domain_sid() + "-" + str(DOMAIN_RID_ADMINS) + ">")
2477         m["member"] = MessageElement(
2478             "cn=ldaptestuser2,cn=users," + self.base_dn, FLAG_MOD_ADD, "member")
2479         ldb.modify(m)
2480
2481         m = Message()
2482         m.dn = Dn(ldb, "cn=ldaptestuser2,cn=users," + self.base_dn)
2483         m["primaryGroupID"] = MessageElement(str(DOMAIN_RID_ADMINS),
2484                                              FLAG_MOD_REPLACE, "primaryGroupID")
2485         ldb.modify(m)
2486
2487         m = Message()
2488         m.dn = Dn(ldb, "cn=ldaptestuser2,cn=users," + self.base_dn)
2489         m["userAccountControl"] = MessageElement(
2490             str(UF_NORMAL_ACCOUNT | UF_PASSWD_NOTREQD),
2491             FLAG_MOD_REPLACE, "userAccountControl")
2492         ldb.modify(m)
2493
2494         res1 = ldb.search("cn=ldaptestuser2,cn=users," + self.base_dn,
2495                           scope=SCOPE_BASE,
2496                           attrs=["userAccountControl", "primaryGroupID"])
2497         self.assertTrue(len(res1) == 1)
2498         self.assertTrue(int(res1[0]["userAccountControl"][0]) & UF_ACCOUNTDISABLE == 0)
2499         self.assertEquals(int(res1[0]["primaryGroupID"][0]), DOMAIN_RID_ADMINS)
2500
2501         # For a workstation account
2502
2503         res1 = ldb.search("cn=ldaptestcomputer,cn=computers," + self.base_dn,
2504                           scope=SCOPE_BASE,
2505                           attrs=["primaryGroupID"])
2506         self.assertTrue(len(res1) == 1)
2507         self.assertEquals(int(res1[0]["primaryGroupID"][0]), DOMAIN_RID_DOMAIN_MEMBERS)
2508
2509         m = Message()
2510         m.dn = Dn(ldb, "<SID=" + ldb.get_domain_sid() + "-" + str(DOMAIN_RID_USERS) + ">")
2511         m["member"] = MessageElement(
2512             "cn=ldaptestcomputer,cn=computers," + self.base_dn, FLAG_MOD_ADD, "member")
2513         ldb.modify(m)
2514
2515         m = Message()
2516         m.dn = Dn(ldb, "cn=ldaptestcomputer,cn=computers," + self.base_dn)
2517         m["primaryGroupID"] = MessageElement(str(DOMAIN_RID_USERS),
2518                                              FLAG_MOD_REPLACE, "primaryGroupID")
2519         ldb.modify(m)
2520
2521         m = Message()
2522         m.dn = Dn(ldb, "cn=ldaptestcomputer,cn=computers," + self.base_dn)
2523         m["userAccountControl"] = MessageElement(
2524             str(UF_WORKSTATION_TRUST_ACCOUNT),
2525             FLAG_MOD_REPLACE, "userAccountControl")
2526         ldb.modify(m)
2527
2528         res1 = ldb.search("cn=ldaptestcomputer,cn=computers," + self.base_dn,
2529                           scope=SCOPE_BASE,
2530                           attrs=["primaryGroupID"])
2531         self.assertTrue(len(res1) == 1)
2532         self.assertEquals(int(res1[0]["primaryGroupID"][0]), DOMAIN_RID_USERS)
2533
2534         delete_force(self.ldb, "cn=ldaptestuser,cn=users," + self.base_dn)
2535         delete_force(self.ldb, "cn=ldaptestuser2,cn=users," + self.base_dn)
2536         delete_force(self.ldb, "cn=ldaptestcomputer,cn=computers," + self.base_dn)
2537
2538     def find_repl_meta_data(self, rpmd, attid):
2539         for i in range(0, rpmd.ctr.count):
2540             m = rpmd.ctr.array[i]
2541             if m.attid == attid:
2542                 return m
2543         return None
2544
2545     def test_smartcard_required1(self):
2546         """Test the UF_SMARTCARD_REQUIRED behaviour"""
2547         print("Testing UF_SMARTCARD_REQUIRED behaviour\n")
2548
2549         delete_force(self.ldb, "cn=ldaptestuser,cn=users," + self.base_dn)
2550
2551         ldb.add({
2552             "dn": "cn=ldaptestuser,cn=users," + self.base_dn,
2553             "objectclass": "user",
2554             "userAccountControl": str(UF_NORMAL_ACCOUNT),
2555             "unicodePwd": "\"thatsAcomplPASS2\"".encode('utf-16-le')
2556         })
2557
2558         res = ldb.search("cn=ldaptestuser,cn=users," + self.base_dn,
2559                          scope=SCOPE_BASE,
2560                          attrs=["sAMAccountType", "userAccountControl",
2561                                 "pwdLastSet", "msDS-KeyVersionNumber",
2562                                 "replPropertyMetaData"])
2563         self.assertTrue(len(res) == 1)
2564         self.assertEqual(int(res[0]["sAMAccountType"][0]),
2565                          ATYPE_NORMAL_ACCOUNT)
2566         self.assertEqual(int(res[0]["userAccountControl"][0]),
2567                          UF_NORMAL_ACCOUNT)
2568         self.assertNotEqual(int(res[0]["pwdLastSet"][0]), 0)
2569         lastset = int(res[0]["pwdLastSet"][0])
2570         self.assertEqual(int(res[0]["msDS-KeyVersionNumber"][0]), 1)
2571         self.assertTrue(len(res[0]["replPropertyMetaData"]) == 1)
2572         rpmd = ndr_unpack(drsblobs.replPropertyMetaDataBlob,
2573                           res[0]["replPropertyMetaData"][0])
2574         lastsetmd = self.find_repl_meta_data(rpmd,
2575                                              drsuapi.DRSUAPI_ATTID_pwdLastSet)
2576         self.assertIsNotNone(lastsetmd)
2577         self.assertEqual(lastsetmd.version, 1)
2578         nthashmd = self.find_repl_meta_data(rpmd,
2579                                             drsuapi.DRSUAPI_ATTID_unicodePwd)
2580         self.assertIsNotNone(nthashmd)
2581         self.assertEqual(nthashmd.version, 1)
2582         nthistmd = self.find_repl_meta_data(rpmd,
2583                                             drsuapi.DRSUAPI_ATTID_ntPwdHistory)
2584         self.assertIsNotNone(nthistmd)
2585         self.assertEqual(nthistmd.version, 1)
2586         lmhashmd = self.find_repl_meta_data(rpmd,
2587                                             drsuapi.DRSUAPI_ATTID_dBCSPwd)
2588         self.assertIsNotNone(lmhashmd)
2589         self.assertEqual(lmhashmd.version, 1)
2590         lmhistmd = self.find_repl_meta_data(rpmd,
2591                                             drsuapi.DRSUAPI_ATTID_lmPwdHistory)
2592         self.assertIsNotNone(lmhistmd)
2593         self.assertEqual(lmhistmd.version, 1)
2594         spcbmd = self.find_repl_meta_data(rpmd,
2595                                           drsuapi.DRSUAPI_ATTID_supplementalCredentials)
2596         self.assertIsNotNone(spcbmd)
2597         self.assertEqual(spcbmd.version, 1)
2598
2599         m = Message()
2600         m.dn = Dn(ldb, "cn=ldaptestuser,cn=users," + self.base_dn)
2601         m["userAccountControl"] = MessageElement(
2602             str(UF_NORMAL_ACCOUNT|UF_SMARTCARD_REQUIRED),
2603             FLAG_MOD_REPLACE, "userAccountControl")
2604         ldb.modify(m)
2605
2606         res = ldb.search("cn=ldaptestuser,cn=users," + self.base_dn,
2607                          scope=SCOPE_BASE,
2608                          attrs=["sAMAccountType", "userAccountControl",
2609                                 "pwdLastSet", "msDS-KeyVersionNumber",
2610                                 "replPropertyMetaData"])
2611         self.assertTrue(len(res) == 1)
2612         self.assertEqual(int(res[0]["sAMAccountType"][0]),
2613                          ATYPE_NORMAL_ACCOUNT)
2614         self.assertEqual(int(res[0]["userAccountControl"][0]),
2615                          UF_NORMAL_ACCOUNT|UF_SMARTCARD_REQUIRED)
2616         self.assertEqual(int(res[0]["pwdLastSet"][0]), lastset)
2617         lastset1 = int(res[0]["pwdLastSet"][0])
2618         self.assertEqual(int(res[0]["msDS-KeyVersionNumber"][0]), 2)
2619         self.assertTrue(len(res[0]["replPropertyMetaData"]) == 1)
2620         rpmd = ndr_unpack(drsblobs.replPropertyMetaDataBlob,
2621                           res[0]["replPropertyMetaData"][0])
2622         lastsetmd = self.find_repl_meta_data(rpmd,
2623                                              drsuapi.DRSUAPI_ATTID_pwdLastSet)
2624         self.assertIsNotNone(lastsetmd)
2625         self.assertEqual(lastsetmd.version, 1)
2626         nthashmd = self.find_repl_meta_data(rpmd,
2627                                             drsuapi.DRSUAPI_ATTID_unicodePwd)
2628         self.assertIsNotNone(nthashmd)
2629         self.assertEqual(nthashmd.version, 2)
2630         nthistmd = self.find_repl_meta_data(rpmd,
2631                                             drsuapi.DRSUAPI_ATTID_ntPwdHistory)
2632         self.assertIsNotNone(nthistmd)
2633         self.assertEqual(nthistmd.version, 2)
2634         lmhashmd = self.find_repl_meta_data(rpmd,
2635                                             drsuapi.DRSUAPI_ATTID_dBCSPwd)
2636         self.assertIsNotNone(lmhashmd)
2637         self.assertEqual(lmhashmd.version, 2)
2638         lmhistmd = self.find_repl_meta_data(rpmd,
2639                                             drsuapi.DRSUAPI_ATTID_lmPwdHistory)
2640         self.assertIsNotNone(lmhistmd)
2641         self.assertEqual(lmhistmd.version, 2)
2642         spcbmd = self.find_repl_meta_data(rpmd,
2643                                           drsuapi.DRSUAPI_ATTID_supplementalCredentials)
2644         self.assertIsNotNone(spcbmd)
2645         self.assertEqual(spcbmd.version, 2)
2646
2647         delete_force(self.ldb, "cn=ldaptestuser,cn=users," + self.base_dn)
2648
2649     def test_smartcard_required2(self):
2650         """Test the UF_SMARTCARD_REQUIRED behaviour"""
2651         print("Testing UF_SMARTCARD_REQUIRED behaviour\n")
2652
2653         delete_force(self.ldb, "cn=ldaptestuser,cn=users," + self.base_dn)
2654
2655         ldb.add({
2656             "dn": "cn=ldaptestuser,cn=users," + self.base_dn,
2657             "objectclass": "user",
2658             "userAccountControl": str(UF_NORMAL_ACCOUNT|UF_ACCOUNTDISABLE),
2659         })
2660
2661         res = ldb.search("cn=ldaptestuser,cn=users," + self.base_dn,
2662                          scope=SCOPE_BASE,
2663                          attrs=["sAMAccountType", "userAccountControl",
2664                                 "pwdLastSet", "msDS-KeyVersionNumber",
2665                                 "replPropertyMetaData"])
2666         self.assertTrue(len(res) == 1)
2667         self.assertEqual(int(res[0]["sAMAccountType"][0]),
2668                          ATYPE_NORMAL_ACCOUNT)
2669         self.assertEqual(int(res[0]["userAccountControl"][0]),
2670                          UF_NORMAL_ACCOUNT|UF_ACCOUNTDISABLE)
2671         self.assertEqual(int(res[0]["pwdLastSet"][0]), 0)
2672         self.assertTrue("msDS-KeyVersionNumber" in res[0])
2673         self.assertEqual(int(res[0]["msDS-KeyVersionNumber"][0]), 1)
2674         self.assertTrue(len(res[0]["replPropertyMetaData"]) == 1)
2675         rpmd = ndr_unpack(drsblobs.replPropertyMetaDataBlob,
2676                           res[0]["replPropertyMetaData"][0])
2677         lastsetmd = self.find_repl_meta_data(rpmd,
2678                                              drsuapi.DRSUAPI_ATTID_pwdLastSet)
2679         self.assertIsNotNone(lastsetmd)
2680         self.assertEqual(lastsetmd.version, 1)
2681         nthashmd = self.find_repl_meta_data(rpmd,
2682                                             drsuapi.DRSUAPI_ATTID_unicodePwd)
2683         self.assertIsNotNone(nthashmd)
2684         self.assertEqual(nthashmd.version, 1)
2685         nthistmd = self.find_repl_meta_data(rpmd,
2686                                             drsuapi.DRSUAPI_ATTID_ntPwdHistory)
2687         self.assertIsNotNone(nthistmd)
2688         self.assertEqual(nthistmd.version, 1)
2689         lmhashmd = self.find_repl_meta_data(rpmd,
2690                                             drsuapi.DRSUAPI_ATTID_dBCSPwd)
2691         self.assertIsNotNone(lmhashmd)
2692         self.assertEqual(lmhashmd.version, 1)
2693         lmhistmd = self.find_repl_meta_data(rpmd,
2694                                             drsuapi.DRSUAPI_ATTID_lmPwdHistory)
2695         self.assertIsNotNone(lmhistmd)
2696         self.assertEqual(lmhistmd.version, 1)
2697         spcbmd = self.find_repl_meta_data(rpmd,
2698                                           drsuapi.DRSUAPI_ATTID_supplementalCredentials)
2699         self.assertIsNone(spcbmd)
2700
2701         m = Message()
2702         m.dn = Dn(ldb, "cn=ldaptestuser,cn=users," + self.base_dn)
2703         m["userAccountControl"] = MessageElement(
2704             str(UF_NORMAL_ACCOUNT|UF_ACCOUNTDISABLE|UF_SMARTCARD_REQUIRED),
2705             FLAG_MOD_REPLACE, "userAccountControl")
2706         ldb.modify(m)
2707
2708         res = ldb.search("cn=ldaptestuser,cn=users," + self.base_dn,
2709                          scope=SCOPE_BASE,
2710                          attrs=["sAMAccountType", "userAccountControl",
2711                                 "pwdLastSet", "msDS-KeyVersionNumber",
2712                                 "replPropertyMetaData"])
2713         self.assertTrue(len(res) == 1)
2714         self.assertEqual(int(res[0]["sAMAccountType"][0]),
2715                          ATYPE_NORMAL_ACCOUNT)
2716         self.assertEqual(int(res[0]["userAccountControl"][0]),
2717                          UF_NORMAL_ACCOUNT|UF_ACCOUNTDISABLE|UF_SMARTCARD_REQUIRED)
2718         self.assertEqual(int(res[0]["pwdLastSet"][0]), 0)
2719         self.assertEqual(int(res[0]["msDS-KeyVersionNumber"][0]), 2)
2720         self.assertTrue(len(res[0]["replPropertyMetaData"]) == 1)
2721         rpmd = ndr_unpack(drsblobs.replPropertyMetaDataBlob,
2722                           res[0]["replPropertyMetaData"][0])
2723         lastsetmd = self.find_repl_meta_data(rpmd,
2724                                              drsuapi.DRSUAPI_ATTID_pwdLastSet)
2725         self.assertIsNotNone(lastsetmd)
2726         self.assertEqual(lastsetmd.version, 1)
2727         nthashmd = self.find_repl_meta_data(rpmd,
2728                                             drsuapi.DRSUAPI_ATTID_unicodePwd)
2729         self.assertIsNotNone(nthashmd)
2730         self.assertEqual(nthashmd.version, 2)
2731         nthistmd = self.find_repl_meta_data(rpmd,
2732                                             drsuapi.DRSUAPI_ATTID_ntPwdHistory)
2733         self.assertIsNotNone(nthistmd)
2734         self.assertEqual(nthistmd.version, 2)
2735         lmhashmd = self.find_repl_meta_data(rpmd,
2736                                             drsuapi.DRSUAPI_ATTID_dBCSPwd)
2737         self.assertIsNotNone(lmhashmd)
2738         self.assertEqual(lmhashmd.version, 2)
2739         lmhistmd = self.find_repl_meta_data(rpmd,
2740                                             drsuapi.DRSUAPI_ATTID_lmPwdHistory)
2741         self.assertIsNotNone(lmhistmd)
2742         self.assertEqual(lmhistmd.version, 2)
2743         spcbmd = self.find_repl_meta_data(rpmd,
2744                                           drsuapi.DRSUAPI_ATTID_supplementalCredentials)
2745         self.assertIsNotNone(spcbmd)
2746         self.assertEqual(spcbmd.version, 1)
2747
2748         m = Message()
2749         m.dn = Dn(ldb, "cn=ldaptestuser,cn=users," + self.base_dn)
2750         m["userAccountControl"] = MessageElement(
2751             str(UF_NORMAL_ACCOUNT|UF_SMARTCARD_REQUIRED),
2752             FLAG_MOD_REPLACE, "userAccountControl")
2753         ldb.modify(m)
2754
2755         res = ldb.search("cn=ldaptestuser,cn=users," + self.base_dn,
2756                          scope=SCOPE_BASE,
2757                          attrs=["sAMAccountType", "userAccountControl",
2758                                 "pwdLastSet", "msDS-KeyVersionNumber",
2759                                 "replPropertyMetaData"])
2760         self.assertTrue(len(res) == 1)
2761         self.assertEqual(int(res[0]["sAMAccountType"][0]),
2762                          ATYPE_NORMAL_ACCOUNT)
2763         self.assertEqual(int(res[0]["userAccountControl"][0]),
2764                          UF_NORMAL_ACCOUNT|UF_SMARTCARD_REQUIRED)
2765         self.assertEqual(int(res[0]["pwdLastSet"][0]), 0)
2766         self.assertEqual(int(res[0]["msDS-KeyVersionNumber"][0]), 2)
2767         self.assertTrue(len(res[0]["replPropertyMetaData"]) == 1)
2768         rpmd = ndr_unpack(drsblobs.replPropertyMetaDataBlob,
2769                           res[0]["replPropertyMetaData"][0])
2770         lastsetmd = self.find_repl_meta_data(rpmd,
2771                                              drsuapi.DRSUAPI_ATTID_pwdLastSet)
2772         self.assertIsNotNone(lastsetmd)
2773         self.assertEqual(lastsetmd.version, 1)
2774         nthashmd = self.find_repl_meta_data(rpmd,
2775                                             drsuapi.DRSUAPI_ATTID_unicodePwd)
2776         self.assertIsNotNone(nthashmd)
2777         self.assertEqual(nthashmd.version, 2)
2778         nthistmd = self.find_repl_meta_data(rpmd,
2779                                             drsuapi.DRSUAPI_ATTID_ntPwdHistory)
2780         self.assertIsNotNone(nthistmd)
2781         self.assertEqual(nthistmd.version, 2)
2782         lmhashmd = self.find_repl_meta_data(rpmd,
2783                                             drsuapi.DRSUAPI_ATTID_dBCSPwd)
2784         self.assertIsNotNone(lmhashmd)
2785         self.assertEqual(lmhashmd.version, 2)
2786         lmhistmd = self.find_repl_meta_data(rpmd,
2787                                             drsuapi.DRSUAPI_ATTID_lmPwdHistory)
2788         self.assertIsNotNone(lmhistmd)
2789         self.assertEqual(lmhistmd.version, 2)
2790         spcbmd = self.find_repl_meta_data(rpmd,
2791                                           drsuapi.DRSUAPI_ATTID_supplementalCredentials)
2792         self.assertIsNotNone(spcbmd)
2793         self.assertEqual(spcbmd.version, 1)
2794
2795         delete_force(self.ldb, "cn=ldaptestuser,cn=users," + self.base_dn)
2796
2797     def test_smartcard_required3(self):
2798         """Test the UF_SMARTCARD_REQUIRED behaviour"""
2799         print("Testing UF_SMARTCARD_REQUIRED behaviour\n")
2800
2801         delete_force(self.ldb, "cn=ldaptestuser,cn=users," + self.base_dn)
2802
2803         ldb.add({
2804             "dn": "cn=ldaptestuser,cn=users," + self.base_dn,
2805             "objectclass": "user",
2806             "userAccountControl": str(UF_NORMAL_ACCOUNT|UF_SMARTCARD_REQUIRED|UF_ACCOUNTDISABLE),
2807         })
2808
2809         res = ldb.search("cn=ldaptestuser,cn=users," + self.base_dn,
2810                          scope=SCOPE_BASE,
2811                          attrs=["sAMAccountType", "userAccountControl",
2812                                 "pwdLastSet", "msDS-KeyVersionNumber",
2813                                 "replPropertyMetaData"])
2814         self.assertTrue(len(res) == 1)
2815         self.assertEqual(int(res[0]["sAMAccountType"][0]),
2816                          ATYPE_NORMAL_ACCOUNT)
2817         self.assertEqual(int(res[0]["userAccountControl"][0]),
2818                          UF_NORMAL_ACCOUNT|UF_SMARTCARD_REQUIRED|UF_ACCOUNTDISABLE)
2819         self.assertEqual(int(res[0]["pwdLastSet"][0]), 0)
2820         self.assertEqual(int(res[0]["msDS-KeyVersionNumber"][0]), 1)
2821         self.assertTrue(len(res[0]["replPropertyMetaData"]) == 1)
2822         rpmd = ndr_unpack(drsblobs.replPropertyMetaDataBlob,
2823                           res[0]["replPropertyMetaData"][0])
2824         lastsetmd = self.find_repl_meta_data(rpmd,
2825                                              drsuapi.DRSUAPI_ATTID_pwdLastSet)
2826         self.assertIsNotNone(lastsetmd)
2827         self.assertEqual(lastsetmd.version, 1)
2828         nthashmd = self.find_repl_meta_data(rpmd,
2829                                             drsuapi.DRSUAPI_ATTID_unicodePwd)
2830         self.assertIsNotNone(nthashmd)
2831         self.assertEqual(nthashmd.version, 1)
2832         nthistmd = self.find_repl_meta_data(rpmd,
2833                                             drsuapi.DRSUAPI_ATTID_ntPwdHistory)
2834         self.assertIsNotNone(nthistmd)
2835         self.assertEqual(nthistmd.version, 1)
2836         lmhashmd = self.find_repl_meta_data(rpmd,
2837                                             drsuapi.DRSUAPI_ATTID_dBCSPwd)
2838         self.assertIsNotNone(lmhashmd)
2839         self.assertEqual(lmhashmd.version, 1)
2840         lmhistmd = self.find_repl_meta_data(rpmd,
2841                                             drsuapi.DRSUAPI_ATTID_lmPwdHistory)
2842         self.assertIsNotNone(lmhistmd)
2843         self.assertEqual(lmhistmd.version, 1)
2844         spcbmd = self.find_repl_meta_data(rpmd,
2845                                           drsuapi.DRSUAPI_ATTID_supplementalCredentials)
2846         self.assertIsNotNone(spcbmd)
2847         self.assertEqual(spcbmd.version, 1)
2848
2849         m = Message()
2850         m.dn = Dn(ldb, "cn=ldaptestuser,cn=users," + self.base_dn)
2851         m["userAccountControl"] = MessageElement(
2852             str(UF_NORMAL_ACCOUNT|UF_SMARTCARD_REQUIRED),
2853             FLAG_MOD_REPLACE, "userAccountControl")
2854         ldb.modify(m)
2855
2856         res = ldb.search("cn=ldaptestuser,cn=users," + self.base_dn,
2857                          scope=SCOPE_BASE,
2858                          attrs=["sAMAccountType", "userAccountControl",
2859                                 "pwdLastSet", "msDS-KeyVersionNumber",
2860                                 "replPropertyMetaData"])
2861         self.assertTrue(len(res) == 1)
2862         self.assertEqual(int(res[0]["sAMAccountType"][0]),
2863                          ATYPE_NORMAL_ACCOUNT)
2864         self.assertEqual(int(res[0]["userAccountControl"][0]),
2865                          UF_NORMAL_ACCOUNT|UF_SMARTCARD_REQUIRED)
2866         self.assertEqual(int(res[0]["pwdLastSet"][0]), 0)
2867         self.assertEqual(int(res[0]["msDS-KeyVersionNumber"][0]), 1)
2868         self.assertTrue(len(res[0]["replPropertyMetaData"]) == 1)
2869         rpmd = ndr_unpack(drsblobs.replPropertyMetaDataBlob,
2870                           res[0]["replPropertyMetaData"][0])
2871         lastsetmd = self.find_repl_meta_data(rpmd,
2872                                              drsuapi.DRSUAPI_ATTID_pwdLastSet)
2873         self.assertIsNotNone(lastsetmd)
2874         self.assertEqual(lastsetmd.version, 1)
2875         nthashmd = self.find_repl_meta_data(rpmd,
2876                                             drsuapi.DRSUAPI_ATTID_unicodePwd)
2877         self.assertIsNotNone(nthashmd)
2878         self.assertEqual(nthashmd.version, 1)
2879         nthistmd = self.find_repl_meta_data(rpmd,
2880                                             drsuapi.DRSUAPI_ATTID_ntPwdHistory)
2881         self.assertIsNotNone(nthistmd)
2882         self.assertEqual(nthistmd.version, 1)
2883         lmhashmd = self.find_repl_meta_data(rpmd,
2884                                             drsuapi.DRSUAPI_ATTID_dBCSPwd)
2885         self.assertIsNotNone(lmhashmd)
2886         self.assertEqual(lmhashmd.version, 1)
2887         lmhistmd = self.find_repl_meta_data(rpmd,
2888                                             drsuapi.DRSUAPI_ATTID_lmPwdHistory)
2889         self.assertIsNotNone(lmhistmd)
2890         self.assertEqual(lmhistmd.version, 1)
2891         spcbmd = self.find_repl_meta_data(rpmd,
2892                                           drsuapi.DRSUAPI_ATTID_supplementalCredentials)
2893         self.assertIsNotNone(spcbmd)
2894         self.assertEqual(spcbmd.version, 1)
2895
2896         delete_force(self.ldb, "cn=ldaptestuser,cn=users," + self.base_dn)
2897
2898     def test_isCriticalSystemObject(self):
2899         """Test the isCriticalSystemObject behaviour"""
2900         print("Testing isCriticalSystemObject behaviour\n")
2901
2902         # Add tests
2903
2904         ldb.add({
2905             "dn": "cn=ldaptestcomputer,cn=computers," + self.base_dn,
2906             "objectclass": "computer"})
2907
2908         res1 = ldb.search("cn=ldaptestcomputer,cn=computers," + self.base_dn,
2909                           scope=SCOPE_BASE,
2910                           attrs=["isCriticalSystemObject"])
2911         self.assertTrue(len(res1) == 1)
2912         self.assertTrue("isCriticalSystemObject" not in res1[0])
2913
2914         delete_force(self.ldb, "cn=ldaptestcomputer,cn=computers," + self.base_dn)
2915
2916         ldb.add({
2917             "dn": "cn=ldaptestcomputer,cn=computers," + self.base_dn,
2918             "objectclass": "computer",
2919             "userAccountControl": str(UF_WORKSTATION_TRUST_ACCOUNT)})
2920
2921         res1 = ldb.search("cn=ldaptestcomputer,cn=computers," + self.base_dn,
2922                           scope=SCOPE_BASE,
2923                           attrs=["isCriticalSystemObject"])
2924         self.assertTrue(len(res1) == 1)
2925         self.assertEquals(res1[0]["isCriticalSystemObject"][0], "FALSE")
2926
2927         delete_force(self.ldb, "cn=ldaptestcomputer,cn=computers," + self.base_dn)
2928
2929         ldb.add({
2930             "dn": "cn=ldaptestcomputer,cn=computers," + self.base_dn,
2931             "objectclass": "computer",
2932             "userAccountControl": str(UF_WORKSTATION_TRUST_ACCOUNT | UF_PARTIAL_SECRETS_ACCOUNT)})
2933
2934         res1 = ldb.search("cn=ldaptestcomputer,cn=computers," + self.base_dn,
2935                           scope=SCOPE_BASE,
2936                           attrs=["isCriticalSystemObject"])
2937         self.assertTrue(len(res1) == 1)
2938         self.assertEquals(res1[0]["isCriticalSystemObject"][0], "TRUE")
2939
2940         delete_force(self.ldb, "cn=ldaptestcomputer,cn=computers," + self.base_dn)
2941
2942         ldb.add({
2943             "dn": "cn=ldaptestcomputer,cn=computers," + self.base_dn,
2944             "objectclass": "computer",
2945             "userAccountControl": str(UF_SERVER_TRUST_ACCOUNT)})
2946
2947         res1 = ldb.search("cn=ldaptestcomputer,cn=computers," + self.base_dn,
2948                           scope=SCOPE_BASE,
2949                           attrs=["isCriticalSystemObject"])
2950         self.assertTrue(len(res1) == 1)
2951         self.assertEquals(res1[0]["isCriticalSystemObject"][0], "TRUE")
2952
2953         # Modification tests
2954
2955         m = Message()
2956         m.dn = Dn(ldb, "cn=ldaptestcomputer,cn=computers," + self.base_dn)
2957         m["userAccountControl"] = MessageElement(str(UF_NORMAL_ACCOUNT | UF_PASSWD_NOTREQD),
2958                                                  FLAG_MOD_REPLACE, "userAccountControl")
2959         ldb.modify(m)
2960
2961         res1 = ldb.search("cn=ldaptestcomputer,cn=computers," + self.base_dn,
2962                           scope=SCOPE_BASE,
2963                           attrs=["isCriticalSystemObject"])
2964         self.assertTrue(len(res1) == 1)
2965         self.assertEquals(res1[0]["isCriticalSystemObject"][0], "TRUE")
2966
2967         m = Message()
2968         m.dn = Dn(ldb, "cn=ldaptestcomputer,cn=computers," + self.base_dn)
2969         m["userAccountControl"] = MessageElement(str(UF_WORKSTATION_TRUST_ACCOUNT),
2970                                                  FLAG_MOD_REPLACE, "userAccountControl")
2971         ldb.modify(m)
2972
2973         res1 = ldb.search("cn=ldaptestcomputer,cn=computers," + self.base_dn,
2974                           scope=SCOPE_BASE,
2975                           attrs=["isCriticalSystemObject"])
2976         self.assertTrue(len(res1) == 1)
2977         self.assertEquals(res1[0]["isCriticalSystemObject"][0], "FALSE")
2978
2979         m = Message()
2980         m.dn = Dn(ldb, "cn=ldaptestcomputer,cn=computers," + self.base_dn)
2981         m["userAccountControl"] = MessageElement(
2982             str(UF_WORKSTATION_TRUST_ACCOUNT | UF_PARTIAL_SECRETS_ACCOUNT),
2983             FLAG_MOD_REPLACE, "userAccountControl")
2984         ldb.modify(m)
2985
2986         res1 = ldb.search("cn=ldaptestcomputer,cn=computers," + self.base_dn,
2987                           scope=SCOPE_BASE,
2988                           attrs=["isCriticalSystemObject"])
2989         self.assertTrue(len(res1) == 1)
2990         self.assertEquals(res1[0]["isCriticalSystemObject"][0], "TRUE")
2991
2992         m = Message()
2993         m.dn = Dn(ldb, "cn=ldaptestcomputer,cn=computers," + self.base_dn)
2994         m["userAccountControl"] = MessageElement(str(UF_NORMAL_ACCOUNT | UF_PASSWD_NOTREQD),
2995                                                  FLAG_MOD_REPLACE, "userAccountControl")
2996         ldb.modify(m)
2997
2998         res1 = ldb.search("cn=ldaptestcomputer,cn=computers," + self.base_dn,
2999                           scope=SCOPE_BASE,
3000                           attrs=["isCriticalSystemObject"])
3001         self.assertTrue(len(res1) == 1)
3002         self.assertEquals(res1[0]["isCriticalSystemObject"][0], "TRUE")
3003
3004         m = Message()
3005         m.dn = Dn(ldb, "cn=ldaptestcomputer,cn=computers," + self.base_dn)
3006         m["userAccountControl"] = MessageElement(str(UF_SERVER_TRUST_ACCOUNT),
3007                                                  FLAG_MOD_REPLACE, "userAccountControl")
3008         ldb.modify(m)
3009
3010         res1 = ldb.search("cn=ldaptestcomputer,cn=computers," + self.base_dn,
3011                           scope=SCOPE_BASE,
3012                           attrs=["isCriticalSystemObject"])
3013         self.assertTrue(len(res1) == 1)
3014         self.assertEquals(res1[0]["isCriticalSystemObject"][0], "TRUE")
3015
3016         m = Message()
3017         m.dn = Dn(ldb, "cn=ldaptestcomputer,cn=computers," + self.base_dn)
3018         m["userAccountControl"] = MessageElement(str(UF_WORKSTATION_TRUST_ACCOUNT),
3019                                                  FLAG_MOD_REPLACE, "userAccountControl")
3020         ldb.modify(m)
3021
3022         res1 = ldb.search("cn=ldaptestcomputer,cn=computers," + self.base_dn,
3023                           scope=SCOPE_BASE,
3024                           attrs=["isCriticalSystemObject"])
3025         self.assertTrue(len(res1) == 1)
3026         self.assertEquals(res1[0]["isCriticalSystemObject"][0], "FALSE")
3027
3028         delete_force(self.ldb, "cn=ldaptestcomputer,cn=computers," + self.base_dn)
3029
3030     def test_service_principal_name_updates(self):
3031         """Test the servicePrincipalNames update behaviour"""
3032         print("Testing servicePrincipalNames update behaviour\n")
3033
3034         ldb.add({
3035             "dn": "cn=ldaptestcomputer,cn=computers," + self.base_dn,
3036             "objectclass": "computer",
3037             "dNSHostName": "testname.testdom"})
3038
3039         res = ldb.search("cn=ldaptestcomputer,cn=computers," + self.base_dn,
3040                          scope=SCOPE_BASE, attrs=["servicePrincipalName"])
3041         self.assertTrue(len(res) == 1)
3042         self.assertFalse("servicePrincipalName" in res[0])
3043
3044         delete_force(self.ldb, "cn=ldaptestcomputer,cn=computers," + self.base_dn)
3045
3046         ldb.add({
3047             "dn": "cn=ldaptestcomputer,cn=computers," + self.base_dn,
3048             "objectclass": "computer",
3049             "servicePrincipalName": "HOST/testname.testdom"})
3050
3051         res = ldb.search("cn=ldaptestcomputer,cn=computers," + self.base_dn,
3052                          scope=SCOPE_BASE, attrs=["dNSHostName"])
3053         self.assertTrue(len(res) == 1)
3054         self.assertFalse("dNSHostName" in res[0])
3055
3056         delete_force(self.ldb, "cn=ldaptestcomputer,cn=computers," + self.base_dn)
3057
3058         ldb.add({
3059             "dn": "cn=ldaptestcomputer,cn=computers," + self.base_dn,
3060             "objectclass": "computer",
3061             "dNSHostName": "testname2.testdom",
3062             "servicePrincipalName": "HOST/testname.testdom"})
3063
3064         res = ldb.search("cn=ldaptestcomputer,cn=computers," + self.base_dn,
3065                          scope=SCOPE_BASE, attrs=["dNSHostName"])
3066         self.assertTrue(len(res) == 1)
3067         self.assertEquals(res[0]["dNSHostName"][0], "testname2.testdom")
3068
3069         res = ldb.search("cn=ldaptestcomputer,cn=computers," + self.base_dn,
3070                          scope=SCOPE_BASE, attrs=["servicePrincipalName"])
3071         self.assertTrue(len(res) == 1)
3072         self.assertEquals(res[0]["servicePrincipalName"][0],
3073                           "HOST/testname.testdom")
3074
3075         m = Message()
3076         m.dn = Dn(ldb, "cn=ldaptestcomputer,cn=computers," + self.base_dn)
3077         m["dNSHostName"] = MessageElement("testname.testdoM",
3078                                           FLAG_MOD_REPLACE, "dNSHostName")
3079         ldb.modify(m)
3080
3081         res = ldb.search("cn=ldaptestcomputer,cn=computers," + self.base_dn,
3082                          scope=SCOPE_BASE, attrs=["servicePrincipalName"])
3083         self.assertTrue(len(res) == 1)
3084         self.assertEquals(res[0]["servicePrincipalName"][0],
3085                           "HOST/testname.testdom")
3086
3087         m = Message()
3088         m.dn = Dn(ldb, "cn=ldaptestcomputer,cn=computers," + self.base_dn)
3089         m["dNSHostName"] = MessageElement("testname2.testdom2",
3090                                           FLAG_MOD_REPLACE, "dNSHostName")
3091         ldb.modify(m)
3092
3093         res = ldb.search("cn=ldaptestcomputer,cn=computers," + self.base_dn,
3094                          scope=SCOPE_BASE, attrs=["servicePrincipalName"])
3095         self.assertTrue(len(res) == 1)
3096         self.assertEquals(res[0]["servicePrincipalName"][0],
3097                           "HOST/testname2.testdom2")
3098
3099         m = Message()
3100         m.dn = Dn(ldb, "cn=ldaptestcomputer,cn=computers," + self.base_dn)
3101         m["dNSHostName"] = MessageElement([],
3102                                           FLAG_MOD_DELETE, "dNSHostName")
3103         ldb.modify(m)
3104
3105         res = ldb.search("cn=ldaptestcomputer,cn=computers," + self.base_dn,
3106                          scope=SCOPE_BASE, attrs=["servicePrincipalName"])
3107         self.assertTrue(len(res) == 1)
3108         self.assertEquals(res[0]["servicePrincipalName"][0],
3109                           "HOST/testname2.testdom2")
3110
3111         m = Message()
3112         m.dn = Dn(ldb, "cn=ldaptestcomputer,cn=computers," + self.base_dn)
3113         m["dNSHostName"] = MessageElement("testname.testdom3",
3114                                           FLAG_MOD_REPLACE, "dNSHostName")
3115         ldb.modify(m)
3116
3117         res = ldb.search("cn=ldaptestcomputer,cn=computers," + self.base_dn,
3118                          scope=SCOPE_BASE, attrs=["servicePrincipalName"])
3119         self.assertTrue(len(res) == 1)
3120         self.assertEquals(res[0]["servicePrincipalName"][0],
3121                           "HOST/testname2.testdom2")
3122
3123         m = Message()
3124         m.dn = Dn(ldb, "cn=ldaptestcomputer,cn=computers," + self.base_dn)
3125         m["dNSHostName"] = MessageElement("testname2.testdom2",
3126                                           FLAG_MOD_REPLACE, "dNSHostName")
3127         ldb.modify(m)
3128
3129         m = Message()
3130         m.dn = Dn(ldb, "cn=ldaptestcomputer,cn=computers," + self.base_dn)
3131         m["dNSHostName"] = MessageElement("testname3.testdom3",
3132                                           FLAG_MOD_REPLACE, "dNSHostName")
3133         m["servicePrincipalName"] = MessageElement("HOST/testname2.testdom2",
3134                                                    FLAG_MOD_REPLACE,
3135                                                    "servicePrincipalName")
3136         ldb.modify(m)
3137
3138         res = ldb.search("cn=ldaptestcomputer,cn=computers," + self.base_dn,
3139                          scope=SCOPE_BASE, attrs=["servicePrincipalName"])
3140         self.assertTrue(len(res) == 1)
3141         self.assertEquals(res[0]["servicePrincipalName"][0],
3142                           "HOST/testname3.testdom3")
3143
3144         m = Message()
3145         m.dn = Dn(ldb, "cn=ldaptestcomputer,cn=computers," + self.base_dn)
3146         m["servicePrincipalName"] = MessageElement("HOST/testname2.testdom2",
3147                                                    FLAG_MOD_REPLACE,
3148                                                    "servicePrincipalName")
3149         m["dNSHostName"] = MessageElement("testname4.testdom4",
3150                                           FLAG_MOD_REPLACE, "dNSHostName")
3151         ldb.modify(m)
3152
3153         res = ldb.search("cn=ldaptestcomputer,cn=computers," + self.base_dn,
3154                          scope=SCOPE_BASE, attrs=["servicePrincipalName"])
3155         self.assertTrue(len(res) == 1)
3156         self.assertEquals(res[0]["servicePrincipalName"][0],
3157                           "HOST/testname2.testdom2")
3158
3159         m = Message()
3160         m.dn = Dn(ldb, "cn=ldaptestcomputer,cn=computers," + self.base_dn)
3161         m["servicePrincipalName"] = MessageElement([],
3162                                                    FLAG_MOD_DELETE,
3163                                                    "servicePrincipalName")
3164         ldb.modify(m)
3165
3166         m = Message()
3167         m.dn = Dn(ldb, "cn=ldaptestcomputer,cn=computers," + self.base_dn)
3168         m["dNSHostName"] = MessageElement("testname2.testdom2",
3169                                           FLAG_MOD_REPLACE, "dNSHostName")
3170         ldb.modify(m)
3171
3172         res = ldb.search("cn=ldaptestcomputer,cn=computers," + self.base_dn,
3173                          scope=SCOPE_BASE, attrs=["servicePrincipalName"])
3174         self.assertTrue(len(res) == 1)
3175         self.assertFalse("servicePrincipalName" in res[0])
3176
3177         delete_force(self.ldb, "cn=ldaptestcomputer,cn=computers," + self.base_dn)
3178
3179         ldb.add({
3180             "dn": "cn=ldaptestcomputer,cn=computers," + self.base_dn,
3181             "objectclass": "computer",
3182             "sAMAccountName": "testname$"})