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
15 import samba.getopt as options
20 sys.path.insert(0, "bin/python")
21 from samba.tests.subunitrun import TestProgram, SubunitOptions
23 from samba.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("user_account_control.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()
61 ldaphost = "ldap://%s" % 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)]
85 account_types = set([UF_NORMAL_ACCOUNT, UF_WORKSTATION_TRUST_ACCOUNT, UF_SERVER_TRUST_ACCOUNT])
88 class UserAccountControlTests(samba.tests.TestCase):
89 def add_computer_ldap(self, computername, others=None, samdb=None):
92 dn = "CN=%s,OU=test_computer_ou1,%s" % (computername, self.base_dn)
93 domainname = ldb.Dn(self.samdb, self.samdb.domain_dn()).canonical_str().replace("/", "")
94 samaccountname = "%s$" % computername
95 dnshostname = "%s.%s" % (computername, domainname)
98 "objectclass": "computer"}
99 if others is not None:
100 msg_dict = dict(msg_dict.items() + others.items())
102 msg = ldb.Message.from_dict(self.samdb, msg_dict )
103 msg["sAMAccountName"] = samaccountname
105 print "Adding computer account %s" % computername
108 def get_creds(self, target_username, target_password):
109 creds_tmp = Credentials()
110 creds_tmp.set_username(target_username)
111 creds_tmp.set_password(target_password)
112 creds_tmp.set_domain(creds.get_domain())
113 creds_tmp.set_realm(creds.get_realm())
114 creds_tmp.set_workstation(creds.get_workstation())
115 creds_tmp.set_gensec_features(creds_tmp.get_gensec_features()
116 | gensec.FEATURE_SEAL)
117 creds_tmp.set_kerberos_state(DONT_USE_KERBEROS) # kinit is too expensive to use in a tight loop
121 super(UserAccountControlTests, self).setUp()
122 self.admin_creds = creds
123 self.admin_samdb = SamDB(url=ldaphost,
124 session_info=system_session(),
125 credentials=self.admin_creds, lp=lp)
126 self.domain_sid = security.dom_sid(self.admin_samdb.get_domain_sid())
127 self.base_dn = self.admin_samdb.domain_dn()
129 self.unpriv_user = "testuser1"
130 self.unpriv_user_pw = "samba123@"
131 self.unpriv_creds = self.get_creds(self.unpriv_user, self.unpriv_user_pw)
133 delete_force(self.admin_samdb, "CN=testcomputer-t,OU=test_computer_ou1,%s" % (self.base_dn))
134 delete_force(self.admin_samdb, "OU=test_computer_ou1,%s" % (self.base_dn))
135 delete_force(self.admin_samdb, "CN=%s,CN=Users,%s" % (self.unpriv_user, self.base_dn))
137 self.admin_samdb.newuser(self.unpriv_user, self.unpriv_user_pw)
138 res = self.admin_samdb.search("CN=%s,CN=Users,%s" % (self.unpriv_user, self.admin_samdb.domain_dn()),
141 self.assertEqual(1, len(res))
143 self.unpriv_user_sid = ndr_unpack(security.dom_sid, res[0]["objectSid"][0])
144 self.unpriv_user_dn = res[0].dn
146 self.samdb = SamDB(url=ldaphost, credentials=self.unpriv_creds, lp=lp)
148 self.samr = samr.samr("ncacn_ip_tcp:%s[seal]" % host, lp, self.unpriv_creds)
149 self.samr_handle = self.samr.Connect2(None, security.SEC_FLAG_MAXIMUM_ALLOWED)
150 self.samr_domain = self.samr.OpenDomain(self.samr_handle, security.SEC_FLAG_MAXIMUM_ALLOWED, self.domain_sid)
152 self.sd_utils = sd_utils.SDUtils(self.admin_samdb)
154 self.admin_samdb.create_ou("OU=test_computer_ou1," + self.base_dn)
155 self.unpriv_user_sid = self.sd_utils.get_object_sid(self.unpriv_user_dn)
156 mod = "(OA;;CC;bf967a86-0de6-11d0-a285-00aa003049e2;;%s)" % str(self.unpriv_user_sid)
158 old_sd = self.sd_utils.read_sd_on_dn("OU=test_computer_ou1," + self.base_dn)
160 self.sd_utils.dacl_add_ace("OU=test_computer_ou1," + self.base_dn, mod)
162 self.add_computer_ldap("testcomputer-t")
164 self.sd_utils.modify_sd_on_dn("OU=test_computer_ou1," + self.base_dn, old_sd)
166 self.computernames = ["testcomputer-0"]
168 # Get the SD of the template account, then force it to match
169 # what we expect for SeMachineAccountPrivilege accounts, so we
170 # can confirm we created the accounts correctly
171 self.sd_reference_cc = self.sd_utils.read_sd_on_dn("CN=testcomputer-t,OU=test_computer_ou1,%s" % (self.base_dn))
173 self.sd_reference_modify = self.sd_utils.read_sd_on_dn("CN=testcomputer-t,OU=test_computer_ou1,%s" % (self.base_dn))
174 for ace in self.sd_reference_modify.dacl.aces:
175 if ace.type == security.SEC_ACE_TYPE_ACCESS_ALLOWED and ace.trustee == self.unpriv_user_sid:
176 ace.access_mask = ace.access_mask | security.SEC_ADS_SELF_WRITE | security.SEC_ADS_WRITE_PROP
178 # Now reconnect without domain admin rights
179 self.samdb = SamDB(url=ldaphost, credentials=self.unpriv_creds, lp=lp)
183 super(UserAccountControlTests, self).tearDown()
184 for computername in self.computernames:
185 delete_force(self.admin_samdb, "CN=%s,OU=test_computer_ou1,%s" % (computername, self.base_dn))
186 delete_force(self.admin_samdb, "CN=testcomputer-t,OU=test_computer_ou1,%s" % (self.base_dn))
187 delete_force(self.admin_samdb, "OU=test_computer_ou1,%s" % (self.base_dn))
188 delete_force(self.admin_samdb, "CN=%s,CN=Users,%s" % (self.unpriv_user, self.base_dn))
190 def test_add_computer_sd_cc(self):
191 user_sid = self.sd_utils.get_object_sid(self.unpriv_user_dn)
192 mod = "(OA;;CC;bf967a86-0de6-11d0-a285-00aa003049e2;;%s)" % str(user_sid)
194 old_sd = self.sd_utils.read_sd_on_dn("OU=test_computer_ou1," + self.base_dn)
196 self.sd_utils.dacl_add_ace("OU=test_computer_ou1," + self.base_dn, mod)
198 computername=self.computernames[0]
199 sd = ldb.MessageElement((ndr_pack(self.sd_reference_modify)),
201 "nTSecurityDescriptor")
202 self.add_computer_ldap(computername,
203 others={"nTSecurityDescriptor": sd})
205 res = self.admin_samdb.search("%s" % self.base_dn,
206 expression="(&(objectClass=computer)(samAccountName=%s$))" % computername,
208 attrs=["ntSecurityDescriptor"])
210 desc = res[0]["nTSecurityDescriptor"][0]
211 desc = ndr_unpack(security.descriptor, desc, allow_remaining=True)
213 sddl = desc.as_sddl(self.domain_sid)
214 self.assertEqual(self.sd_reference_modify.as_sddl(self.domain_sid), sddl)
218 m["description"]= ldb.MessageElement(
219 ("A description"), ldb.FLAG_MOD_REPLACE,
225 m["userAccountControl"] = ldb.MessageElement(str(samba.dsdb.UF_SERVER_TRUST_ACCOUNT),
226 ldb.FLAG_MOD_REPLACE, "userAccountControl")
229 self.fail("Unexpectedly able to set userAccountControl to be a DC on %s" % m.dn)
230 except LdbError as e5:
231 (enum, estr) = e5.args
232 self.assertEqual(ldb.ERR_INSUFFICIENT_ACCESS_RIGHTS, enum)
236 m["userAccountControl"] = ldb.MessageElement(str(samba.dsdb.UF_WORKSTATION_TRUST_ACCOUNT|samba.dsdb.UF_PARTIAL_SECRETS_ACCOUNT),
237 ldb.FLAG_MOD_REPLACE, "userAccountControl")
240 self.fail("Unexpectedly able to set userAccountControl to be an RODC on %s" % m.dn)
241 except LdbError as e6:
242 (enum, estr) = e6.args
243 self.assertEqual(ldb.ERR_INSUFFICIENT_ACCESS_RIGHTS, enum)
247 m["userAccountControl"] = ldb.MessageElement(str(samba.dsdb.UF_WORKSTATION_TRUST_ACCOUNT),
248 ldb.FLAG_MOD_REPLACE, "userAccountControl")
251 self.fail("Unexpectedly able to set userAccountControl to be an Workstation on %s" % m.dn)
252 except LdbError as e7:
253 (enum, estr) = e7.args
254 self.assertEqual(ldb.ERR_INSUFFICIENT_ACCESS_RIGHTS, enum)
258 m["userAccountControl"] = ldb.MessageElement(str(samba.dsdb.UF_NORMAL_ACCOUNT),
259 ldb.FLAG_MOD_REPLACE, "userAccountControl")
264 m["primaryGroupID"] = ldb.MessageElement(str(security.DOMAIN_RID_ADMINS),
265 ldb.FLAG_MOD_REPLACE, "primaryGroupID")
268 except LdbError as e8:
269 (enum, estr) = e8.args
270 self.assertEqual(ldb.ERR_UNWILLING_TO_PERFORM, enum)
274 def test_mod_computer_cc(self):
275 user_sid = self.sd_utils.get_object_sid(self.unpriv_user_dn)
276 mod = "(OA;;CC;bf967a86-0de6-11d0-a285-00aa003049e2;;%s)" % str(user_sid)
278 old_sd = self.sd_utils.read_sd_on_dn("OU=test_computer_ou1," + self.base_dn)
280 self.sd_utils.dacl_add_ace("OU=test_computer_ou1," + self.base_dn, mod)
282 computername=self.computernames[0]
283 self.add_computer_ldap(computername)
285 res = self.admin_samdb.search("%s" % self.base_dn,
286 expression="(&(objectClass=computer)(samAccountName=%s$))" % computername,
292 m["description"]= ldb.MessageElement(
293 ("A description"), ldb.FLAG_MOD_REPLACE,
299 m["userAccountControl"] = ldb.MessageElement(str(samba.dsdb.UF_WORKSTATION_TRUST_ACCOUNT|samba.dsdb.UF_PARTIAL_SECRETS_ACCOUNT),
300 ldb.FLAG_MOD_REPLACE, "userAccountControl")
303 self.fail("Unexpectedly able to set userAccountControl on %s" % m.dn)
304 except LdbError as e9:
305 (enum, estr) = e9.args
306 self.assertEqual(ldb.ERR_INSUFFICIENT_ACCESS_RIGHTS, enum)
310 m["userAccountControl"] = ldb.MessageElement(str(samba.dsdb.UF_SERVER_TRUST_ACCOUNT),
311 ldb.FLAG_MOD_REPLACE, "userAccountControl")
315 except LdbError as e10:
316 (enum, estr) = e10.args
317 self.assertEqual(ldb.ERR_INSUFFICIENT_ACCESS_RIGHTS, enum)
321 m["userAccountControl"] = ldb.MessageElement(str(samba.dsdb.UF_NORMAL_ACCOUNT),
322 ldb.FLAG_MOD_REPLACE, "userAccountControl")
327 m["userAccountControl"] = ldb.MessageElement(str(samba.dsdb.UF_WORKSTATION_TRUST_ACCOUNT),
328 ldb.FLAG_MOD_REPLACE, "userAccountControl")
331 self.fail("Unexpectedly able to set userAccountControl to be an Workstation on %s" % m.dn)
332 except LdbError as e11:
333 (enum, estr) = e11.args
334 self.assertEqual(ldb.ERR_INSUFFICIENT_ACCESS_RIGHTS, enum)
337 def test_admin_mod_uac(self):
338 computername=self.computernames[0]
339 self.add_computer_ldap(computername, samdb=self.admin_samdb)
341 res = self.admin_samdb.search("%s" % self.base_dn,
342 expression="(&(objectClass=computer)(samAccountName=%s$))" % computername,
344 attrs=["userAccountControl"])
346 self.assertEqual(int(res[0]["userAccountControl"][0]), UF_NORMAL_ACCOUNT|UF_ACCOUNTDISABLE|UF_PASSWD_NOTREQD)
350 m["userAccountControl"] = ldb.MessageElement(str(UF_WORKSTATION_TRUST_ACCOUNT|UF_PARTIAL_SECRETS_ACCOUNT|UF_TRUSTED_FOR_DELEGATION),
351 ldb.FLAG_MOD_REPLACE, "userAccountControl")
353 self.admin_samdb.modify(m)
354 self.fail("Unexpectedly able to set userAccountControl to UF_WORKSTATION_TRUST_ACCOUNT|UF_PARTIAL_SECRETS_ACCOUNT|UF_TRUSTED_FOR_DELEGATION on %s" % m.dn)
355 except LdbError as e12:
356 (enum, estr) = e12.args
357 self.assertEqual(ldb.ERR_OTHER, enum)
361 m["userAccountControl"] = ldb.MessageElement(str(UF_WORKSTATION_TRUST_ACCOUNT|UF_PARTIAL_SECRETS_ACCOUNT),
362 ldb.FLAG_MOD_REPLACE, "userAccountControl")
363 self.admin_samdb.modify(m)
365 res = self.admin_samdb.search("%s" % self.base_dn,
366 expression="(&(objectClass=computer)(samAccountName=%s$))" % computername,
368 attrs=["userAccountControl"])
370 self.assertEqual(int(res[0]["userAccountControl"][0]), UF_WORKSTATION_TRUST_ACCOUNT|UF_PARTIAL_SECRETS_ACCOUNT)
373 m["userAccountControl"] = ldb.MessageElement(str(UF_ACCOUNTDISABLE),
374 ldb.FLAG_MOD_REPLACE, "userAccountControl")
375 self.admin_samdb.modify(m)
377 res = self.admin_samdb.search("%s" % self.base_dn,
378 expression="(&(objectClass=computer)(samAccountName=%s$))" % computername,
380 attrs=["userAccountControl"])
382 self.assertEqual(int(res[0]["userAccountControl"][0]), UF_NORMAL_ACCOUNT| UF_ACCOUNTDISABLE)
385 def test_uac_bits_set(self):
386 user_sid = self.sd_utils.get_object_sid(self.unpriv_user_dn)
387 mod = "(OA;;CC;bf967a86-0de6-11d0-a285-00aa003049e2;;%s)" % str(user_sid)
389 old_sd = self.sd_utils.read_sd_on_dn("OU=test_computer_ou1," + self.base_dn)
391 self.sd_utils.dacl_add_ace("OU=test_computer_ou1," + self.base_dn, mod)
393 computername=self.computernames[0]
394 self.add_computer_ldap(computername)
396 res = self.admin_samdb.search("%s" % self.base_dn,
397 expression="(&(objectClass=computer)(samAccountName=%s$))" % computername,
403 m["description"]= ldb.MessageElement(
404 ("A description"), ldb.FLAG_MOD_REPLACE,
408 # These bits are privileged, but authenticated users have that CAR by default, so this is a pain to test
409 priv_to_auth_users_bits = set([UF_PASSWD_NOTREQD, UF_ENCRYPTED_TEXT_PASSWORD_ALLOWED,
410 UF_DONT_EXPIRE_PASSWD])
412 # These bits really are privileged, or can't be changed from UF_NORMAL as a non-admin
413 priv_bits = set([UF_INTERDOMAIN_TRUST_ACCOUNT, UF_SERVER_TRUST_ACCOUNT,
414 UF_TRUSTED_FOR_DELEGATION, UF_TRUSTED_TO_AUTHENTICATE_FOR_DELEGATION,
415 UF_WORKSTATION_TRUST_ACCOUNT])
417 invalid_bits = set([UF_TEMP_DUPLICATE_ACCOUNT, UF_PARTIAL_SECRETS_ACCOUNT])
422 m["userAccountControl"] = ldb.MessageElement(str(bit|UF_PASSWD_NOTREQD),
423 ldb.FLAG_MOD_REPLACE, "userAccountControl")
426 if (bit in priv_bits):
427 self.fail("Unexpectedly able to set userAccountControl bit 0x%08X on %s" % (bit, m.dn))
428 except LdbError as e:
429 (enum, estr) = e.args
430 if bit in invalid_bits:
431 self.assertEqual(enum, ldb.ERR_OTHER, "was not able to set 0x%08X on %s" % (bit, m.dn))
432 # No point going on, try the next bit
434 elif (bit in priv_bits):
435 self.assertEqual(ldb.ERR_INSUFFICIENT_ACCESS_RIGHTS, enum)
437 self.fail("Unable to set userAccountControl bit 0x%08X on %s: %s" % (bit, m.dn, estr))
440 def uac_bits_unrelated_modify_helper(self, account_type):
441 user_sid = self.sd_utils.get_object_sid(self.unpriv_user_dn)
442 mod = "(OA;;CC;bf967a86-0de6-11d0-a285-00aa003049e2;;%s)" % str(user_sid)
444 old_sd = self.sd_utils.read_sd_on_dn("OU=test_computer_ou1," + self.base_dn)
446 self.sd_utils.dacl_add_ace("OU=test_computer_ou1," + self.base_dn, mod)
448 computername=self.computernames[0]
449 self.add_computer_ldap(computername, others={"userAccountControl": [str(account_type)]})
451 res = self.admin_samdb.search("%s" % self.base_dn,
452 expression="(&(objectClass=computer)(samAccountName=%s$))" % computername,
454 attrs=["userAccountControl"])
455 self.assertEqual(int(res[0]["userAccountControl"][0]), account_type)
459 m["description"]= ldb.MessageElement(
460 ("A description"), ldb.FLAG_MOD_REPLACE,
464 invalid_bits = set([UF_TEMP_DUPLICATE_ACCOUNT, UF_PARTIAL_SECRETS_ACCOUNT])
466 # UF_LOCKOUT isn't actually ignored, it changes other
467 # attributes but does not stick here. See MS-SAMR 2.2.1.13
468 # UF_FLAG Codes clarification that UF_SCRIPT and
469 # UF_PASSWD_CANT_CHANGE are simply ignored by both clients and
470 # servers. Other bits are ignored as they are undefined, or
471 # are not set into the attribute (instead triggering other
473 ignored_bits = set([UF_SCRIPT, UF_00000004, UF_LOCKOUT, UF_PASSWD_CANT_CHANGE,
474 UF_00000400, UF_00004000, UF_00008000, UF_PASSWORD_EXPIRED,
475 int("0x10000000", 16), int("0x20000000", 16), int("0x40000000", 16), int("0x80000000", 16)])
476 super_priv_bits = set([UF_INTERDOMAIN_TRUST_ACCOUNT])
478 priv_to_remove_bits = set([UF_TRUSTED_FOR_DELEGATION, UF_TRUSTED_TO_AUTHENTICATE_FOR_DELEGATION, UF_WORKSTATION_TRUST_ACCOUNT])
481 # Reset this to the initial position, just to be sure
484 m["userAccountControl"] = ldb.MessageElement(str(account_type),
485 ldb.FLAG_MOD_REPLACE, "userAccountControl")
486 self.admin_samdb.modify(m)
488 res = self.admin_samdb.search("%s" % self.base_dn,
489 expression="(&(objectClass=computer)(samAccountName=%s$))" % computername,
491 attrs=["userAccountControl"])
493 self.assertEqual(int(res[0]["userAccountControl"][0]), account_type)
497 m["userAccountControl"] = ldb.MessageElement(str(bit|UF_PASSWD_NOTREQD),
498 ldb.FLAG_MOD_REPLACE, "userAccountControl")
500 self.admin_samdb.modify(m)
501 if bit in invalid_bits:
502 self.fail("Should have been unable to set userAccountControl bit 0x%08X on %s" % (bit, m.dn))
504 except LdbError as e1:
505 (enum, estr) = e1.args
506 if bit in invalid_bits:
507 self.assertEqual(enum, ldb.ERR_OTHER)
508 # No point going on, try the next bit
510 elif bit in super_priv_bits:
511 self.assertEqual(enum, ldb.ERR_INSUFFICIENT_ACCESS_RIGHTS)
512 # No point going on, try the next bit
515 self.fail("Unable to set userAccountControl bit 0x%08X on %s: %s" % (bit, m.dn, estr))
517 res = self.admin_samdb.search("%s" % self.base_dn,
518 expression="(&(objectClass=computer)(samAccountName=%s$))" % computername,
520 attrs=["userAccountControl"])
522 if bit in ignored_bits:
523 self.assertEqual(int(res[0]["userAccountControl"][0]), UF_NORMAL_ACCOUNT|UF_PASSWD_NOTREQD, "Bit 0x%08x shouldn't stick" % bit)
525 if bit in account_types:
526 self.assertEqual(int(res[0]["userAccountControl"][0]), bit|UF_PASSWD_NOTREQD, "Bit 0x%08x didn't stick" % bit)
528 self.assertEqual(int(res[0]["userAccountControl"][0]), bit|UF_NORMAL_ACCOUNT|UF_PASSWD_NOTREQD, "Bit 0x%08x didn't stick" % bit)
533 m["userAccountControl"] = ldb.MessageElement(str(bit|UF_PASSWD_NOTREQD|UF_ACCOUNTDISABLE),
534 ldb.FLAG_MOD_REPLACE, "userAccountControl")
537 except LdbError as e2:
538 (enum, estr) = e2.args
539 self.fail("Unable to set userAccountControl bit 0x%08X on %s: %s" % (bit, m.dn, estr))
541 res = self.admin_samdb.search("%s" % self.base_dn,
542 expression="(&(objectClass=computer)(samAccountName=%s$))" % computername,
544 attrs=["userAccountControl"])
546 if bit in account_types:
547 self.assertEqual(int(res[0]["userAccountControl"][0]),
548 bit|UF_ACCOUNTDISABLE|UF_PASSWD_NOTREQD,
549 "bit 0X%08x should have been added (0X%08x vs 0X%08x)"
550 % (bit, int(res[0]["userAccountControl"][0]),
551 bit|UF_ACCOUNTDISABLE|UF_PASSWD_NOTREQD))
552 elif bit in ignored_bits:
553 self.assertEqual(int(res[0]["userAccountControl"][0]),
554 UF_NORMAL_ACCOUNT|UF_ACCOUNTDISABLE|UF_PASSWD_NOTREQD,
555 "bit 0X%08x should have been added (0X%08x vs 0X%08x)"
556 % (bit, int(res[0]["userAccountControl"][0]),
557 UF_NORMAL_ACCOUNT|UF_ACCOUNTDISABLE|UF_PASSWD_NOTREQD))
560 self.assertEqual(int(res[0]["userAccountControl"][0]),
561 bit|UF_NORMAL_ACCOUNT|UF_ACCOUNTDISABLE|UF_PASSWD_NOTREQD,
562 "bit 0X%08x should have been added (0X%08x vs 0X%08x)"
563 % (bit, int(res[0]["userAccountControl"][0]),
564 bit|UF_NORMAL_ACCOUNT|UF_ACCOUNTDISABLE|UF_PASSWD_NOTREQD))
569 m["userAccountControl"] = ldb.MessageElement(str(UF_PASSWD_NOTREQD|UF_ACCOUNTDISABLE),
570 ldb.FLAG_MOD_REPLACE, "userAccountControl")
572 if bit in priv_to_remove_bits:
573 self.fail("Should have been unable to remove userAccountControl bit 0x%08X on %s" % (bit, m.dn))
575 except LdbError as e3:
576 (enum, estr) = e3.args
577 if bit in priv_to_remove_bits:
578 self.assertEqual(enum, ldb.ERR_INSUFFICIENT_ACCESS_RIGHTS)
580 self.fail("Unexpectedly unable to remove userAccountControl bit 0x%08X on %s: %s" % (bit, m.dn, estr))
582 res = self.admin_samdb.search("%s" % self.base_dn,
583 expression="(&(objectClass=computer)(samAccountName=%s$))" % computername,
585 attrs=["userAccountControl"])
587 if bit in priv_to_remove_bits:
588 if bit in account_types:
589 self.assertEqual(int(res[0]["userAccountControl"][0]),
590 bit|UF_ACCOUNTDISABLE|UF_PASSWD_NOTREQD,
591 "bit 0X%08x should not have been removed" % bit)
593 self.assertEqual(int(res[0]["userAccountControl"][0]),
594 bit|UF_NORMAL_ACCOUNT|UF_ACCOUNTDISABLE|UF_PASSWD_NOTREQD,
595 "bit 0X%08x should not have been removed" % bit)
597 self.assertEqual(int(res[0]["userAccountControl"][0]),
598 UF_NORMAL_ACCOUNT|UF_ACCOUNTDISABLE|UF_PASSWD_NOTREQD,
599 "bit 0X%08x should have been removed" % bit)
601 def test_uac_bits_unrelated_modify_normal(self):
602 self.uac_bits_unrelated_modify_helper(UF_NORMAL_ACCOUNT)
604 def test_uac_bits_unrelated_modify_workstation(self):
605 self.uac_bits_unrelated_modify_helper(UF_WORKSTATION_TRUST_ACCOUNT)
607 def test_uac_bits_add(self):
608 computername=self.computernames[0]
610 user_sid = self.sd_utils.get_object_sid(self.unpriv_user_dn)
611 mod = "(OA;;CC;bf967a86-0de6-11d0-a285-00aa003049e2;;%s)" % str(user_sid)
613 old_sd = self.sd_utils.read_sd_on_dn("OU=test_computer_ou1," + self.base_dn)
615 self.sd_utils.dacl_add_ace("OU=test_computer_ou1," + self.base_dn, mod)
617 invalid_bits = set([UF_TEMP_DUPLICATE_ACCOUNT, UF_PARTIAL_SECRETS_ACCOUNT])
618 # These bits are privileged, but authenticated users have that CAR by default, so this is a pain to test
619 priv_to_auth_users_bits = set([UF_PASSWD_NOTREQD, UF_ENCRYPTED_TEXT_PASSWORD_ALLOWED,
620 UF_DONT_EXPIRE_PASSWD])
622 # These bits really are privileged
623 priv_bits = set([UF_INTERDOMAIN_TRUST_ACCOUNT, UF_SERVER_TRUST_ACCOUNT,
624 UF_TRUSTED_FOR_DELEGATION, UF_TRUSTED_TO_AUTHENTICATE_FOR_DELEGATION])
628 self.add_computer_ldap(computername, others={"userAccountControl": [str(bit)]})
629 delete_force(self.admin_samdb, "CN=%s,OU=test_computer_ou1,%s" % (computername, self.base_dn))
631 self.fail("Unexpectdly able to set userAccountControl bit 0x%08X on %s" % (bit, computername))
633 except LdbError as e4:
634 (enum, estr) = e4.args
635 if bit in invalid_bits:
636 self.assertEqual(enum, ldb.ERR_OTHER, "Invalid bit 0x%08X was able to be set on %s" % (bit, computername))
637 # No point going on, try the next bit
639 elif bit in priv_bits:
640 self.assertEqual(enum, ldb.ERR_INSUFFICIENT_ACCESS_RIGHTS)
643 self.fail("Unable to set userAccountControl bit 0x%08X on %s: %s" % (bit, computername, estr))
645 def test_primarygroupID_cc_add(self):
646 computername=self.computernames[0]
648 user_sid = self.sd_utils.get_object_sid(self.unpriv_user_dn)
649 mod = "(OA;;CC;bf967a86-0de6-11d0-a285-00aa003049e2;;%s)" % str(user_sid)
651 old_sd = self.sd_utils.read_sd_on_dn("OU=test_computer_ou1," + self.base_dn)
653 self.sd_utils.dacl_add_ace("OU=test_computer_ou1," + self.base_dn, mod)
655 # When creating a new object, you can not ever set the primaryGroupID
656 self.add_computer_ldap(computername, others={"primaryGroupID": [str(security.DOMAIN_RID_ADMINS)]})
657 self.fail("Unexpectedly able to set primaryGruopID to be an admin on %s" % computername)
658 except LdbError as e13:
659 (enum, estr) = e13.args
660 self.assertEqual(enum, ldb.ERR_UNWILLING_TO_PERFORM)
663 def test_primarygroupID_priv_DC_modify(self):
664 computername=self.computernames[0]
666 self.add_computer_ldap(computername,
667 others={"userAccountControl": [str(UF_SERVER_TRUST_ACCOUNT)]},
668 samdb=self.admin_samdb)
669 res = self.admin_samdb.search("%s" % self.base_dn,
670 expression="(&(objectClass=computer)(samAccountName=%s$))" % computername,
676 m.dn = ldb.Dn(self.admin_samdb, "<SID=%s-%d>" % (str(self.domain_sid),
677 security.DOMAIN_RID_USERS))
678 m["member"]= ldb.MessageElement(
679 [str(res[0].dn)], ldb.FLAG_MOD_ADD,
681 self.admin_samdb.modify(m)
685 m["primaryGroupID"]= ldb.MessageElement(
686 [str(security.DOMAIN_RID_USERS)], ldb.FLAG_MOD_REPLACE,
689 self.admin_samdb.modify(m)
691 # When creating a new object, you can not ever set the primaryGroupID
692 self.fail("Unexpectedly able to set primaryGroupID to be other than DCS on %s" % computername)
693 except LdbError as e14:
694 (enum, estr) = e14.args
695 self.assertEqual(enum, ldb.ERR_UNWILLING_TO_PERFORM)
697 def test_primarygroupID_priv_member_modify(self):
698 computername=self.computernames[0]
700 self.add_computer_ldap(computername,
701 others={"userAccountControl": [str(UF_WORKSTATION_TRUST_ACCOUNT|UF_PARTIAL_SECRETS_ACCOUNT)]},
702 samdb=self.admin_samdb)
703 res = self.admin_samdb.search("%s" % self.base_dn,
704 expression="(&(objectClass=computer)(samAccountName=%s$))" % computername,
710 m.dn = ldb.Dn(self.admin_samdb, "<SID=%s-%d>" % (str(self.domain_sid),
711 security.DOMAIN_RID_USERS))
712 m["member"]= ldb.MessageElement(
713 [str(res[0].dn)], ldb.FLAG_MOD_ADD,
715 self.admin_samdb.modify(m)
719 m["primaryGroupID"]= ldb.MessageElement(
720 [str(security.DOMAIN_RID_USERS)], ldb.FLAG_MOD_REPLACE,
723 self.admin_samdb.modify(m)
725 # When creating a new object, you can not ever set the primaryGroupID
726 self.fail("Unexpectedly able to set primaryGroupID to be other than DCS on %s" % computername)
727 except LdbError as e15:
728 (enum, estr) = e15.args
729 self.assertEqual(enum, ldb.ERR_UNWILLING_TO_PERFORM)
732 def test_primarygroupID_priv_user_modify(self):
733 computername=self.computernames[0]
735 self.add_computer_ldap(computername,
736 others={"userAccountControl": [str(UF_WORKSTATION_TRUST_ACCOUNT)]},
737 samdb=self.admin_samdb)
738 res = self.admin_samdb.search("%s" % self.base_dn,
739 expression="(&(objectClass=computer)(samAccountName=%s$))" % computername,
745 m.dn = ldb.Dn(self.admin_samdb, "<SID=%s-%d>" % (str(self.domain_sid),
746 security.DOMAIN_RID_ADMINS))
747 m["member"]= ldb.MessageElement(
748 [str(res[0].dn)], ldb.FLAG_MOD_ADD,
750 self.admin_samdb.modify(m)
754 m["primaryGroupID"]= ldb.MessageElement(
755 [str(security.DOMAIN_RID_ADMINS)], ldb.FLAG_MOD_REPLACE,
757 self.admin_samdb.modify(m)
760 runner = SubunitTestRunner()
762 if not runner.run(unittest.makeSuite(UserAccountControlTests)).wasSuccessful():