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