dsdb: Improve userAccountControl handling
[Samba.git] / source4 / dsdb / tests / python / user_account_control.py
blob69108835096ccb25f4bb8ff62e642f99c8fe2e1f
1 #!/usr/bin/env python
2 # -*- coding: utf-8 -*-
3 # This tests the restrictions on userAccountControl that apply even if write access is permitted
5 # Copyright Samuel Cabrero 2014 <samuelcabrero@kernevil.me>
6 # Copyright Andrew Bartlett 2014 <abartlet@samba.org>
8 # Licenced under the GPLv3
11 import optparse
12 import sys
13 import unittest
14 import samba
15 import samba.getopt as options
16 import samba.tests
17 import ldb
18 import base64
20 sys.path.insert(0, "bin/python")
21 from samba.tests.subunitrun import TestProgram, SubunitOptions
23 from subunit.run import SubunitTestRunner
24 from samba.auth import system_session
25 from samba.samdb import SamDB
26 from samba.dcerpc import samr, security, lsa
27 from samba.credentials import Credentials
28 from samba.ndr import ndr_unpack, ndr_pack
29 from samba.tests import delete_force
30 from samba import gensec, sd_utils
31 from samba.credentials import DONT_USE_KERBEROS
32 from ldb import SCOPE_SUBTREE, SCOPE_BASE, LdbError
33 from ldb import Message, MessageElement, Dn
34 from ldb import FLAG_MOD_ADD, FLAG_MOD_REPLACE, FLAG_MOD_DELETE
35 from samba.dsdb import UF_SCRIPT, UF_ACCOUNTDISABLE, UF_00000004, UF_HOMEDIR_REQUIRED, \
36 UF_LOCKOUT,UF_PASSWD_NOTREQD, UF_PASSWD_CANT_CHANGE, UF_ENCRYPTED_TEXT_PASSWORD_ALLOWED,\
37 UF_TEMP_DUPLICATE_ACCOUNT, UF_NORMAL_ACCOUNT, UF_00000400, UF_INTERDOMAIN_TRUST_ACCOUNT, \
38 UF_WORKSTATION_TRUST_ACCOUNT, UF_SERVER_TRUST_ACCOUNT, UF_00004000, \
39 UF_00008000, UF_DONT_EXPIRE_PASSWD, UF_MNS_LOGON_ACCOUNT, UF_SMARTCARD_REQUIRED, \
40 UF_TRUSTED_FOR_DELEGATION, UF_NOT_DELEGATED, UF_USE_DES_KEY_ONLY, UF_DONT_REQUIRE_PREAUTH, \
41 UF_PASSWORD_EXPIRED, UF_TRUSTED_TO_AUTHENTICATE_FOR_DELEGATION, UF_NO_AUTH_DATA_REQUIRED, \
42 UF_PARTIAL_SECRETS_ACCOUNT, UF_USE_AES_KEYS
45 parser = optparse.OptionParser("machine_account_privilege.py [options] <host>")
46 sambaopts = options.SambaOptions(parser)
47 parser.add_option_group(sambaopts)
48 parser.add_option_group(options.VersionOptions(parser))
50 # use command line creds if available
51 credopts = options.CredentialsOptions(parser)
52 parser.add_option_group(credopts)
53 opts, args = parser.parse_args()
55 if len(args) < 1:
56 parser.print_usage()
57 sys.exit(1)
58 host = args[0]
60 if not "://" in host:
61 ldaphost = "ldap://%s" % host
62 else:
63 ldaphost = host
64 start = host.rindex("://")
65 host = host.lstrip(start+3)
67 lp = sambaopts.get_loadparm()
68 creds = credopts.get_credentials(lp)
69 creds.set_gensec_features(creds.get_gensec_features() | gensec.FEATURE_SEAL)
71 bits = [UF_SCRIPT, UF_ACCOUNTDISABLE, UF_00000004, UF_HOMEDIR_REQUIRED,
72 UF_LOCKOUT,UF_PASSWD_NOTREQD, UF_PASSWD_CANT_CHANGE,
73 UF_ENCRYPTED_TEXT_PASSWORD_ALLOWED,
74 UF_TEMP_DUPLICATE_ACCOUNT, UF_NORMAL_ACCOUNT, UF_00000400,
75 UF_INTERDOMAIN_TRUST_ACCOUNT,
76 UF_WORKSTATION_TRUST_ACCOUNT, UF_SERVER_TRUST_ACCOUNT, UF_00004000,
77 UF_00008000, UF_DONT_EXPIRE_PASSWD, UF_MNS_LOGON_ACCOUNT, UF_SMARTCARD_REQUIRED,
78 UF_TRUSTED_FOR_DELEGATION, UF_NOT_DELEGATED, UF_USE_DES_KEY_ONLY,
79 UF_DONT_REQUIRE_PREAUTH,
80 UF_PASSWORD_EXPIRED, UF_TRUSTED_TO_AUTHENTICATE_FOR_DELEGATION,
81 UF_NO_AUTH_DATA_REQUIRED,
82 UF_PARTIAL_SECRETS_ACCOUNT, UF_USE_AES_KEYS,
83 int("0x10000000", 16), int("0x20000000", 16), int("0x40000000", 16), int("0x80000000", 16)]
86 class UserAccountControlTests(samba.tests.TestCase):
87 def add_computer_ldap(self, computername, others=None, samdb=None):
88 if samdb is None:
89 samdb = self.samdb
90 dn = "CN=%s,OU=test_computer_ou1,%s" % (computername, self.base_dn)
91 domainname = ldb.Dn(self.samdb, self.samdb.domain_dn()).canonical_str().replace("/", "")
92 samaccountname = "%s$" % computername
93 dnshostname = "%s.%s" % (computername, domainname)
94 msg_dict = {
95 "dn": dn,
96 "objectclass": "computer"}
97 if others is not None:
98 msg_dict = dict(msg_dict.items() + others.items())
100 msg = ldb.Message.from_dict(self.samdb, msg_dict )
101 msg["sAMAccountName"] = samaccountname
103 print "Adding computer account %s" % computername
104 samdb.add(msg)
106 def get_creds(self, target_username, target_password):
107 creds_tmp = Credentials()
108 creds_tmp.set_username(target_username)
109 creds_tmp.set_password(target_password)
110 creds_tmp.set_domain(creds.get_domain())
111 creds_tmp.set_realm(creds.get_realm())
112 creds_tmp.set_workstation(creds.get_workstation())
113 creds_tmp.set_gensec_features(creds_tmp.get_gensec_features()
114 | gensec.FEATURE_SEAL)
115 creds_tmp.set_kerberos_state(DONT_USE_KERBEROS) # kinit is too expensive to use in a tight loop
116 return creds_tmp
118 def setUp(self):
119 super(UserAccountControlTests, self).setUp()
120 self.admin_creds = creds
121 self.admin_samdb = SamDB(url=ldaphost,
122 session_info=system_session(),
123 credentials=self.admin_creds, lp=lp)
125 self.unpriv_user = "testuser1"
126 self.unpriv_user_pw = "samba123@"
127 self.unpriv_creds = self.get_creds(self.unpriv_user, self.unpriv_user_pw)
129 self.admin_samdb.newuser(self.unpriv_user, self.unpriv_user_pw)
130 res = self.admin_samdb.search("CN=%s,CN=Users,%s" % (self.unpriv_user, self.admin_samdb.domain_dn()),
131 scope=SCOPE_BASE,
132 attrs=["objectSid"])
133 self.assertEqual(1, len(res))
135 self.unpriv_user_sid = ndr_unpack(security.dom_sid, res[0]["objectSid"][0])
136 self.unpriv_user_dn = res[0].dn
138 self.samdb = SamDB(url=ldaphost, credentials=self.unpriv_creds, lp=lp)
139 self.domain_sid = security.dom_sid(self.samdb.get_domain_sid())
140 self.base_dn = self.samdb.domain_dn()
142 self.samr = samr.samr("ncacn_ip_tcp:%s[sign]" % host, lp, self.unpriv_creds)
143 self.samr_handle = self.samr.Connect2(None, security.SEC_FLAG_MAXIMUM_ALLOWED)
144 self.samr_domain = self.samr.OpenDomain(self.samr_handle, security.SEC_FLAG_MAXIMUM_ALLOWED, self.domain_sid)
146 self.sd_utils = sd_utils.SDUtils(self.admin_samdb)
148 self.admin_samdb.create_ou("OU=test_computer_ou1," + self.base_dn)
149 self.unpriv_user_sid = self.sd_utils.get_object_sid(self.unpriv_user_dn)
150 mod = "(OA;;CC;bf967a86-0de6-11d0-a285-00aa003049e2;;%s)" % str(self.unpriv_user_sid)
152 old_sd = self.sd_utils.read_sd_on_dn("OU=test_computer_ou1," + self.base_dn)
154 self.sd_utils.dacl_add_ace("OU=test_computer_ou1," + self.base_dn, mod)
156 self.add_computer_ldap("testcomputer-t")
158 self.sd_utils.modify_sd_on_dn("OU=test_computer_ou1," + self.base_dn, old_sd)
160 self.computernames = ["testcomputer-0"]
162 # Get the SD of the template account, then force it to match
163 # what we expect for SeMachineAccountPrivilege accounts, so we
164 # can confirm we created the accounts correctly
165 self.sd_reference_cc = self.sd_utils.read_sd_on_dn("CN=testcomputer-t,OU=test_computer_ou1,%s" % (self.base_dn))
167 self.sd_reference_modify = self.sd_utils.read_sd_on_dn("CN=testcomputer-t,OU=test_computer_ou1,%s" % (self.base_dn))
168 for ace in self.sd_reference_modify.dacl.aces:
169 if ace.type == security.SEC_ACE_TYPE_ACCESS_ALLOWED and ace.trustee == self.unpriv_user_sid:
170 ace.access_mask = ace.access_mask | security.SEC_ADS_SELF_WRITE | security.SEC_ADS_WRITE_PROP
172 # Now reconnect without domain admin rights
173 self.samdb = SamDB(url=ldaphost, credentials=self.unpriv_creds, lp=lp)
176 def tearDown(self):
177 super(UserAccountControlTests, self).tearDown()
178 for computername in self.computernames:
179 delete_force(self.admin_samdb, "CN=%s,OU=test_computer_ou1,%s" % (computername, self.base_dn))
180 delete_force(self.admin_samdb, "CN=testcomputer-t,OU=test_computer_ou1,%s" % (self.base_dn))
181 delete_force(self.admin_samdb, "OU=test_computer_ou1,%s" % (self.base_dn))
182 delete_force(self.admin_samdb, "CN=%s,CN=Users,%s" % (self.unpriv_user, self.base_dn))
184 def test_add_computer_sd_cc(self):
185 user_sid = self.sd_utils.get_object_sid(self.unpriv_user_dn)
186 mod = "(OA;;CC;bf967a86-0de6-11d0-a285-00aa003049e2;;%s)" % str(user_sid)
188 old_sd = self.sd_utils.read_sd_on_dn("OU=test_computer_ou1," + self.base_dn)
190 self.sd_utils.dacl_add_ace("OU=test_computer_ou1," + self.base_dn, mod)
192 computername=self.computernames[0]
193 sd = ldb.MessageElement((ndr_pack(self.sd_reference_modify)),
194 ldb.FLAG_MOD_ADD,
195 "nTSecurityDescriptor")
196 self.add_computer_ldap(computername,
197 others={"nTSecurityDescriptor": sd})
199 res = self.admin_samdb.search("%s" % self.base_dn,
200 expression="(&(objectClass=computer)(samAccountName=%s$))" % computername,
201 scope=SCOPE_SUBTREE,
202 attrs=["ntSecurityDescriptor"])
204 desc = res[0]["nTSecurityDescriptor"][0]
205 desc = ndr_unpack(security.descriptor, desc, allow_remaining=True)
207 sddl = desc.as_sddl(self.domain_sid)
208 self.assertEqual(self.sd_reference_modify.as_sddl(self.domain_sid), sddl)
210 m = ldb.Message()
211 m.dn = res[0].dn
212 m["description"]= ldb.MessageElement(
213 ("A description"), ldb.FLAG_MOD_REPLACE,
214 "description")
215 self.samdb.modify(m)
217 m = ldb.Message()
218 m.dn = res[0].dn
219 m["userAccountControl"] = ldb.MessageElement(str(samba.dsdb.UF_SERVER_TRUST_ACCOUNT),
220 ldb.FLAG_MOD_REPLACE, "userAccountControl")
221 try:
222 self.samdb.modify(m)
223 self.fail("Unexpectedly able to set userAccountControl to be a DC on %s" % m.dn)
224 except LdbError, (enum, estr):
225 self.assertEqual(ldb.ERR_INSUFFICIENT_ACCESS_RIGHTS, enum)
227 m = ldb.Message()
228 m.dn = res[0].dn
229 m["userAccountControl"] = ldb.MessageElement(str(samba.dsdb.UF_WORKSTATION_TRUST_ACCOUNT|samba.dsdb.UF_PARTIAL_SECRETS_ACCOUNT),
230 ldb.FLAG_MOD_REPLACE, "userAccountControl")
231 try:
232 self.samdb.modify(m)
233 self.fail("Unexpectedly able to set userAccountControl to be an RODC on %s" % m.dn)
234 except LdbError, (enum, estr):
235 self.assertEqual(ldb.ERR_INSUFFICIENT_ACCESS_RIGHTS, enum)
237 m = ldb.Message()
238 m.dn = res[0].dn
239 m["userAccountControl"] = ldb.MessageElement(str(samba.dsdb.UF_WORKSTATION_TRUST_ACCOUNT),
240 ldb.FLAG_MOD_REPLACE, "userAccountControl")
241 self.samdb.modify(m)
243 m = ldb.Message()
244 m.dn = res[0].dn
245 m["primaryGroupID"] = ldb.MessageElement(str(security.DOMAIN_RID_ADMINS),
246 ldb.FLAG_MOD_REPLACE, "primaryGroupID")
247 try:
248 self.samdb.modify(m)
249 except LdbError, (enum, estr):
250 self.assertEqual(ldb.ERR_UNWILLING_TO_PERFORM, enum)
251 return
252 self.fail()
254 def test_mod_computer_cc(self):
255 user_sid = self.sd_utils.get_object_sid(self.unpriv_user_dn)
256 mod = "(OA;;CC;bf967a86-0de6-11d0-a285-00aa003049e2;;%s)" % str(user_sid)
258 old_sd = self.sd_utils.read_sd_on_dn("OU=test_computer_ou1," + self.base_dn)
260 self.sd_utils.dacl_add_ace("OU=test_computer_ou1," + self.base_dn, mod)
262 computername=self.computernames[0]
263 self.add_computer_ldap(computername)
265 res = self.admin_samdb.search("%s" % self.base_dn,
266 expression="(&(objectClass=computer)(samAccountName=%s$))" % computername,
267 scope=SCOPE_SUBTREE,
268 attrs=[])
270 m = ldb.Message()
271 m.dn = res[0].dn
272 m["description"]= ldb.MessageElement(
273 ("A description"), ldb.FLAG_MOD_REPLACE,
274 "description")
275 self.samdb.modify(m)
277 m = ldb.Message()
278 m.dn = res[0].dn
279 m["userAccountControl"] = ldb.MessageElement(str(samba.dsdb.UF_WORKSTATION_TRUST_ACCOUNT|samba.dsdb.UF_PARTIAL_SECRETS_ACCOUNT),
280 ldb.FLAG_MOD_REPLACE, "userAccountControl")
281 try:
282 self.samdb.modify(m)
283 self.fail("Unexpectedly able to set userAccountControl on %s" % m.dn)
284 except LdbError, (enum, estr):
285 self.assertEqual(ldb.ERR_INSUFFICIENT_ACCESS_RIGHTS, enum)
287 m = ldb.Message()
288 m.dn = res[0].dn
289 m["userAccountControl"] = ldb.MessageElement(str(samba.dsdb.UF_SERVER_TRUST_ACCOUNT),
290 ldb.FLAG_MOD_REPLACE, "userAccountControl")
291 try:
292 self.samdb.modify(m)
293 self.fail()
294 except LdbError, (enum, estr):
295 self.assertEqual(ldb.ERR_INSUFFICIENT_ACCESS_RIGHTS, enum)
297 m = ldb.Message()
298 m.dn = res[0].dn
299 m["userAccountControl"] = ldb.MessageElement(str(samba.dsdb.UF_NORMAL_ACCOUNT),
300 ldb.FLAG_MOD_REPLACE, "userAccountControl")
301 self.samdb.modify(m)
303 m = ldb.Message()
304 m.dn = res[0].dn
305 m["userAccountControl"] = ldb.MessageElement(str(samba.dsdb.UF_WORKSTATION_TRUST_ACCOUNT),
306 ldb.FLAG_MOD_REPLACE, "userAccountControl")
307 self.samdb.modify(m)
309 def test_admin_mod_uac(self):
310 computername=self.computernames[0]
311 self.add_computer_ldap(computername, samdb=self.admin_samdb)
313 res = self.admin_samdb.search("%s" % self.base_dn,
314 expression="(&(objectClass=computer)(samAccountName=%s$))" % computername,
315 scope=SCOPE_SUBTREE,
316 attrs=["userAccountControl"])
318 self.assertEqual(int(res[0]["userAccountControl"][0]), UF_NORMAL_ACCOUNT|UF_ACCOUNTDISABLE|UF_PASSWD_NOTREQD)
320 m = ldb.Message()
321 m.dn = res[0].dn
322 m["userAccountControl"] = ldb.MessageElement(str(UF_WORKSTATION_TRUST_ACCOUNT|UF_PARTIAL_SECRETS_ACCOUNT|UF_TRUSTED_FOR_DELEGATION),
323 ldb.FLAG_MOD_REPLACE, "userAccountControl")
324 try:
325 self.admin_samdb.modify(m)
326 self.fail("Unexpectedly able to set userAccountControl to UF_WORKSTATION_TRUST_ACCOUNT|UF_PARTIAL_SECRETS_ACCOUNT|UF_TRUSTED_FOR_DELEGATION on %s" % m.dn)
327 except LdbError, (enum, estr):
328 self.assertEqual(ldb.ERR_OTHER, enum)
330 m = ldb.Message()
331 m.dn = res[0].dn
332 m["userAccountControl"] = ldb.MessageElement(str(UF_WORKSTATION_TRUST_ACCOUNT|UF_PARTIAL_SECRETS_ACCOUNT),
333 ldb.FLAG_MOD_REPLACE, "userAccountControl")
334 self.admin_samdb.modify(m)
336 res = self.admin_samdb.search("%s" % self.base_dn,
337 expression="(&(objectClass=computer)(samAccountName=%s$))" % computername,
338 scope=SCOPE_SUBTREE,
339 attrs=["userAccountControl"])
341 self.assertEqual(int(res[0]["userAccountControl"][0]), UF_WORKSTATION_TRUST_ACCOUNT|UF_PARTIAL_SECRETS_ACCOUNT)
342 m = ldb.Message()
343 m.dn = res[0].dn
344 m["userAccountControl"] = ldb.MessageElement(str(UF_ACCOUNTDISABLE),
345 ldb.FLAG_MOD_REPLACE, "userAccountControl")
346 self.admin_samdb.modify(m)
348 res = self.admin_samdb.search("%s" % self.base_dn,
349 expression="(&(objectClass=computer)(samAccountName=%s$))" % computername,
350 scope=SCOPE_SUBTREE,
351 attrs=["userAccountControl"])
353 self.assertEqual(int(res[0]["userAccountControl"][0]), UF_NORMAL_ACCOUNT| UF_ACCOUNTDISABLE)
356 def test_uac_bits_set(self):
357 user_sid = self.sd_utils.get_object_sid(self.unpriv_user_dn)
358 mod = "(OA;;CC;bf967a86-0de6-11d0-a285-00aa003049e2;;%s)" % str(user_sid)
360 old_sd = self.sd_utils.read_sd_on_dn("OU=test_computer_ou1," + self.base_dn)
362 self.sd_utils.dacl_add_ace("OU=test_computer_ou1," + self.base_dn, mod)
364 computername=self.computernames[0]
365 self.add_computer_ldap(computername)
367 res = self.admin_samdb.search("%s" % self.base_dn,
368 expression="(&(objectClass=computer)(samAccountName=%s$))" % computername,
369 scope=SCOPE_SUBTREE,
370 attrs=[])
372 m = ldb.Message()
373 m.dn = res[0].dn
374 m["description"]= ldb.MessageElement(
375 ("A description"), ldb.FLAG_MOD_REPLACE,
376 "description")
377 self.samdb.modify(m)
379 # These bits are privileged, but authenticated users have that CAR by default, so this is a pain to test
380 priv_to_auth_users_bits = set([UF_PASSWD_NOTREQD, UF_ENCRYPTED_TEXT_PASSWORD_ALLOWED,
381 UF_DONT_EXPIRE_PASSWD])
383 # These bits really are privileged
384 priv_bits = set([UF_INTERDOMAIN_TRUST_ACCOUNT, UF_SERVER_TRUST_ACCOUNT,
385 UF_TRUSTED_FOR_DELEGATION, UF_TRUSTED_TO_AUTHENTICATE_FOR_DELEGATION])
387 invalid_bits = set([UF_TEMP_DUPLICATE_ACCOUNT, UF_PARTIAL_SECRETS_ACCOUNT])
389 for bit in bits:
390 m = ldb.Message()
391 m.dn = res[0].dn
392 m["userAccountControl"] = ldb.MessageElement(str(bit|UF_PASSWD_NOTREQD),
393 ldb.FLAG_MOD_REPLACE, "userAccountControl")
394 try:
395 self.samdb.modify(m)
396 if (bit in priv_bits):
397 self.fail("Unexpectedly able to set userAccountControl bit 0x%08X on %s" % (bit, m.dn))
398 except LdbError, (enum, estr):
399 if bit in invalid_bits:
400 self.assertEqual(enum, ldb.ERR_OTHER, "was not able to set 0x%08X on %s" % (bit, m.dn))
401 # No point going on, try the next bit
402 continue
403 elif (bit in priv_bits):
404 self.assertEqual(ldb.ERR_INSUFFICIENT_ACCESS_RIGHTS, enum)
405 else:
406 self.fail("Unable to set userAccountControl bit 0x%08X on %s: %s" % (bit, m.dn, estr))
409 def test_uac_bits_unrelated_modify(self):
410 user_sid = self.sd_utils.get_object_sid(self.unpriv_user_dn)
411 mod = "(OA;;CC;bf967a86-0de6-11d0-a285-00aa003049e2;;%s)" % str(user_sid)
413 old_sd = self.sd_utils.read_sd_on_dn("OU=test_computer_ou1," + self.base_dn)
415 self.sd_utils.dacl_add_ace("OU=test_computer_ou1," + self.base_dn, mod)
417 computername=self.computernames[0]
418 self.add_computer_ldap(computername)
420 res = self.admin_samdb.search("%s" % self.base_dn,
421 expression="(&(objectClass=computer)(samAccountName=%s$))" % computername,
422 scope=SCOPE_SUBTREE,
423 attrs=[])
425 m = ldb.Message()
426 m.dn = res[0].dn
427 m["description"]= ldb.MessageElement(
428 ("A description"), ldb.FLAG_MOD_REPLACE,
429 "description")
430 self.samdb.modify(m)
432 invalid_bits = set([UF_TEMP_DUPLICATE_ACCOUNT, UF_PARTIAL_SECRETS_ACCOUNT])
434 super_priv_bits = set([UF_INTERDOMAIN_TRUST_ACCOUNT])
436 priv_to_remove_bits = set([UF_TRUSTED_FOR_DELEGATION, UF_TRUSTED_TO_AUTHENTICATE_FOR_DELEGATION])
438 for bit in bits:
439 m = ldb.Message()
440 m.dn = res[0].dn
441 m["userAccountControl"] = ldb.MessageElement(str(bit|UF_PASSWD_NOTREQD),
442 ldb.FLAG_MOD_REPLACE, "userAccountControl")
443 try:
444 self.admin_samdb.modify(m)
445 if bit in invalid_bits:
446 self.fail("Should have been unable to set userAccountControl bit 0x%08X on %s" % (bit, m.dn))
448 except LdbError, (enum, estr):
449 if bit in invalid_bits:
450 self.assertEqual(enum, ldb.ERR_OTHER)
451 # No point going on, try the next bit
452 continue
453 elif bit in super_priv_bits:
454 self.assertEqual(enum, ldb.ERR_INSUFFICIENT_ACCESS_RIGHTS)
455 # No point going on, try the next bit
456 continue
457 else:
458 self.fail("Unable to set userAccountControl bit 0x%08X on %s: %s" % (bit, m.dn, estr))
460 try:
461 m = ldb.Message()
462 m.dn = res[0].dn
463 m["userAccountControl"] = ldb.MessageElement(str(bit|UF_PASSWD_NOTREQD|UF_ACCOUNTDISABLE),
464 ldb.FLAG_MOD_REPLACE, "userAccountControl")
465 self.samdb.modify(m)
467 except LdbError, (enum, estr):
468 self.fail("Unable to set userAccountControl bit 0x%08X on %s: %s" % (bit, m.dn, estr))
470 try:
471 m = ldb.Message()
472 m.dn = res[0].dn
473 m["userAccountControl"] = ldb.MessageElement(str(UF_PASSWD_NOTREQD|UF_ACCOUNTDISABLE),
474 ldb.FLAG_MOD_REPLACE, "userAccountControl")
475 self.samdb.modify(m)
476 if bit in priv_to_remove_bits:
477 self.fail("Should have been unable to remove userAccountControl bit 0x%08X on %s" % (bit, m.dn))
479 except LdbError, (enum, estr):
480 if bit in priv_to_remove_bits:
481 self.assertEqual(enum, ldb.ERR_INSUFFICIENT_ACCESS_RIGHTS)
482 else:
483 self.fail("Unexpectedly able to remove userAccountControl bit 0x%08X on %s: %s" % (bit, m.dn, estr))
485 def test_uac_bits_add(self):
486 computername=self.computernames[0]
488 user_sid = self.sd_utils.get_object_sid(self.unpriv_user_dn)
489 mod = "(OA;;CC;bf967a86-0de6-11d0-a285-00aa003049e2;;%s)" % str(user_sid)
491 old_sd = self.sd_utils.read_sd_on_dn("OU=test_computer_ou1," + self.base_dn)
493 self.sd_utils.dacl_add_ace("OU=test_computer_ou1," + self.base_dn, mod)
495 invalid_bits = set([UF_TEMP_DUPLICATE_ACCOUNT, UF_PARTIAL_SECRETS_ACCOUNT])
497 # These bits are privileged, but authenticated users have that CAR by default, so this is a pain to test
498 priv_to_auth_users_bits = set([UF_PASSWD_NOTREQD, UF_ENCRYPTED_TEXT_PASSWORD_ALLOWED,
499 UF_DONT_EXPIRE_PASSWD])
501 # These bits really are privileged
502 priv_bits = set([UF_INTERDOMAIN_TRUST_ACCOUNT, UF_SERVER_TRUST_ACCOUNT,
503 UF_TRUSTED_FOR_DELEGATION, UF_TRUSTED_TO_AUTHENTICATE_FOR_DELEGATION])
505 for bit in bits:
506 try:
507 self.add_computer_ldap(computername, others={"userAccountControl": [str(bit)]})
508 delete_force(self.admin_samdb, "CN=%s,OU=test_computer_ou1,%s" % (computername, self.base_dn))
509 if bit in priv_bits:
510 self.fail("Unexpectdly able to set userAccountControl bit 0x%08X on %s" % (bit, computername))
512 except LdbError, (enum, estr):
513 if bit in invalid_bits:
514 self.assertEqual(enum, ldb.ERR_OTHER, "Invalid bit 0x%08X was able to be set on %s" % (bit, computername))
515 # No point going on, try the next bit
516 continue
517 elif bit in priv_bits:
518 self.assertEqual(enum, ldb.ERR_INSUFFICIENT_ACCESS_RIGHTS)
519 continue
520 else:
521 self.fail("Unable to set userAccountControl bit 0x%08X on %s: %s" % (bit, computername, estr))
525 runner = SubunitTestRunner()
526 rc = 0
527 if not runner.run(unittest.makeSuite(UserAccountControlTests)).wasSuccessful():
528 rc = 1
529 sys.exit(rc)