summaryrefslogtreecommitdiffstats
path: root/source4/dsdb/tests/python
diff options
context:
space:
mode:
authorAndrew Bartlett <abartlet@samba.org>2014-12-08 15:07:59 +1300
committerAndrew Bartlett <abartlet@samba.org>2015-01-22 07:50:06 +0100
commitbf99abb5db91839d8e3589722fe9be000f857691 (patch)
tree079b060dafe0a77b09943cc6b8d06817984d6642 /source4/dsdb/tests/python
parentb995ef3795511fadae3556e706e77abb39f8791c (diff)
downloadsamba-bf99abb5db91839d8e3589722fe9be000f857691.tar.gz
samba-bf99abb5db91839d8e3589722fe9be000f857691.tar.xz
samba-bf99abb5db91839d8e3589722fe9be000f857691.zip
dsdb-tests: Add new test samba4.user_account_control.python
This confirms security behaviour of the userAccountControl attribute as well as the behaviour on ADD as well as MODIFY, for every userAccountControl bit. Bug: https://bugzilla.samba.org/show_bug.cgi?id=10993 Change-Id: I8cd0e0b3c8d40e8b8aea844189703c756cc372f0 Pair-programmed-with: Garming Sam <garming@catalyst.net.nz> Signed-off-by: Andrew Bartlett <abartlet@samba.org> Signed-off-by: Garming Sam <garming@catalyst.net.nz> Reviewed-by: Stefan Metzmacher <metze@samba.org>
Diffstat (limited to 'source4/dsdb/tests/python')
-rw-r--r--source4/dsdb/tests/python/user_account_control.py521
1 files changed, 521 insertions, 0 deletions
diff --git a/source4/dsdb/tests/python/user_account_control.py b/source4/dsdb/tests/python/user_account_control.py
new file mode 100644
index 0000000000..00501bbc33
--- /dev/null
+++ b/source4/dsdb/tests/python/user_account_control.py
@@ -0,0 +1,521 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+# This tests the restrictions on userAccountControl that apply even if write access is permitted
+#
+# Copyright Samuel Cabrero 2014 <samuelcabrero@kernevil.me>
+# Copyright Andrew Bartlett 2014 <abartlet@samba.org>
+#
+# Licenced under the GPLv3
+#
+
+import optparse
+import sys
+import unittest
+import samba
+import samba.getopt as options
+import samba.tests
+import ldb
+import base64
+
+sys.path.insert(0, "bin/python")
+from samba.tests.subunitrun import TestProgram, SubunitOptions
+
+from subunit.run import SubunitTestRunner
+from samba.auth import system_session
+from samba.samdb import SamDB
+from samba.dcerpc import samr, security, lsa
+from samba.credentials import Credentials
+from samba.ndr import ndr_unpack, ndr_pack
+from samba.tests import delete_force
+from samba import gensec, sd_utils
+from samba.credentials import DONT_USE_KERBEROS
+from ldb import SCOPE_SUBTREE, SCOPE_BASE, LdbError
+from ldb import Message, MessageElement, Dn
+from ldb import FLAG_MOD_ADD, FLAG_MOD_REPLACE, FLAG_MOD_DELETE
+from samba.dsdb import UF_SCRIPT, UF_ACCOUNTDISABLE, UF_00000004, UF_HOMEDIR_REQUIRED, \
+ UF_LOCKOUT,UF_PASSWD_NOTREQD, UF_PASSWD_CANT_CHANGE, UF_ENCRYPTED_TEXT_PASSWORD_ALLOWED,\
+ UF_TEMP_DUPLICATE_ACCOUNT, UF_NORMAL_ACCOUNT, UF_00000400, UF_INTERDOMAIN_TRUST_ACCOUNT, \
+ UF_WORKSTATION_TRUST_ACCOUNT, UF_SERVER_TRUST_ACCOUNT, UF_00004000, \
+ UF_00008000, UF_DONT_EXPIRE_PASSWD, UF_MNS_LOGON_ACCOUNT, UF_SMARTCARD_REQUIRED, \
+ UF_TRUSTED_FOR_DELEGATION, UF_NOT_DELEGATED, UF_USE_DES_KEY_ONLY, UF_DONT_REQUIRE_PREAUTH, \
+ UF_PASSWORD_EXPIRED, UF_TRUSTED_TO_AUTHENTICATE_FOR_DELEGATION, UF_NO_AUTH_DATA_REQUIRED, \
+ UF_PARTIAL_SECRETS_ACCOUNT, UF_USE_AES_KEYS
+
+
+parser = optparse.OptionParser("machine_account_privilege.py [options] <host>")
+sambaopts = options.SambaOptions(parser)
+parser.add_option_group(sambaopts)
+parser.add_option_group(options.VersionOptions(parser))
+
+# use command line creds if available
+credopts = options.CredentialsOptions(parser)
+parser.add_option_group(credopts)
+opts, args = parser.parse_args()
+
+if len(args) < 1:
+ parser.print_usage()
+ sys.exit(1)
+host = args[0]
+
+if not "://" in host:
+ ldaphost = "ldap://%s" % host
+else:
+ ldaphost = host
+ start = host.rindex("://")
+ host = host.lstrip(start+3)
+
+lp = sambaopts.get_loadparm()
+creds = credopts.get_credentials(lp)
+creds.set_gensec_features(creds.get_gensec_features() | gensec.FEATURE_SEAL)
+
+bits = [UF_SCRIPT, UF_ACCOUNTDISABLE, UF_00000004, UF_HOMEDIR_REQUIRED,
+ UF_LOCKOUT,UF_PASSWD_NOTREQD, UF_PASSWD_CANT_CHANGE,
+ UF_ENCRYPTED_TEXT_PASSWORD_ALLOWED,
+ UF_TEMP_DUPLICATE_ACCOUNT, UF_NORMAL_ACCOUNT, UF_00000400,
+ UF_INTERDOMAIN_TRUST_ACCOUNT,
+ UF_WORKSTATION_TRUST_ACCOUNT, UF_SERVER_TRUST_ACCOUNT, UF_00004000,
+ UF_00008000, UF_DONT_EXPIRE_PASSWD, UF_MNS_LOGON_ACCOUNT, UF_SMARTCARD_REQUIRED,
+ UF_TRUSTED_FOR_DELEGATION, UF_NOT_DELEGATED, UF_USE_DES_KEY_ONLY,
+ UF_DONT_REQUIRE_PREAUTH,
+ UF_PASSWORD_EXPIRED, UF_TRUSTED_TO_AUTHENTICATE_FOR_DELEGATION,
+ UF_NO_AUTH_DATA_REQUIRED,
+ UF_PARTIAL_SECRETS_ACCOUNT, UF_USE_AES_KEYS,
+ int("0x10000000", 16), int("0x20000000", 16), int("0x40000000", 16), int("0x80000000", 16)]
+
+
+class UserAccountControlTests(samba.tests.TestCase):
+ def add_computer_ldap(self, computername, others=None, samdb=None):
+ if samdb is None:
+ samdb = self.samdb
+ dn = "CN=%s,OU=test_computer_ou1,%s" % (computername, self.base_dn)
+ domainname = ldb.Dn(self.samdb, self.samdb.domain_dn()).canonical_str().replace("/", "")
+ samaccountname = "%s$" % computername
+ dnshostname = "%s.%s" % (computername, domainname)
+ msg_dict = {
+ "dn": dn,
+ "objectclass": "computer"}
+ if others is not None:
+ msg_dict = dict(msg_dict.items() + others.items())
+
+ msg = ldb.Message.from_dict(self.samdb, msg_dict )
+ msg["sAMAccountName"] = samaccountname
+
+ print "Adding computer account %s" % computername
+ samdb.add(msg)
+
+ def get_creds(self, target_username, target_password):
+ creds_tmp = Credentials()
+ creds_tmp.set_username(target_username)
+ creds_tmp.set_password(target_password)
+ creds_tmp.set_domain(creds.get_domain())
+ creds_tmp.set_realm(creds.get_realm())
+ creds_tmp.set_workstation(creds.get_workstation())
+ creds_tmp.set_gensec_features(creds_tmp.get_gensec_features()
+ | gensec.FEATURE_SEAL)
+ creds_tmp.set_kerberos_state(DONT_USE_KERBEROS) # kinit is too expensive to use in a tight loop
+ return creds_tmp
+
+ def setUp(self):
+ super(UserAccountControlTests, self).setUp()
+ self.admin_creds = creds
+ self.admin_samdb = SamDB(url=ldaphost,
+ session_info=system_session(),
+ credentials=self.admin_creds, lp=lp)
+
+ self.unpriv_user = "testuser1"
+ self.unpriv_user_pw = "samba123@"
+ self.unpriv_creds = self.get_creds(self.unpriv_user, self.unpriv_user_pw)
+
+ self.admin_samdb.newuser(self.unpriv_user, self.unpriv_user_pw)
+ res = self.admin_samdb.search("CN=%s,CN=Users,%s" % (self.unpriv_user, self.admin_samdb.domain_dn()),
+ scope=SCOPE_BASE,
+ attrs=["objectSid"])
+ self.assertEqual(1, len(res))
+
+ self.unpriv_user_sid = ndr_unpack(security.dom_sid, res[0]["objectSid"][0])
+ self.unpriv_user_dn = res[0].dn
+
+ self.samdb = SamDB(url=ldaphost, credentials=self.unpriv_creds, lp=lp)
+ self.domain_sid = security.dom_sid(self.samdb.get_domain_sid())
+ self.base_dn = self.samdb.domain_dn()
+
+ self.samr = samr.samr("ncacn_ip_tcp:%s[sign]" % host, lp, self.unpriv_creds)
+ self.samr_handle = self.samr.Connect2(None, security.SEC_FLAG_MAXIMUM_ALLOWED)
+ self.samr_domain = self.samr.OpenDomain(self.samr_handle, security.SEC_FLAG_MAXIMUM_ALLOWED, self.domain_sid)
+
+ self.sd_utils = sd_utils.SDUtils(self.admin_samdb)
+
+ self.admin_samdb.create_ou("OU=test_computer_ou1," + self.base_dn)
+ self.unpriv_user_sid = self.sd_utils.get_object_sid(self.unpriv_user_dn)
+ mod = "(OA;;CC;bf967a86-0de6-11d0-a285-00aa003049e2;;%s)" % str(self.unpriv_user_sid)
+
+ old_sd = self.sd_utils.read_sd_on_dn("OU=test_computer_ou1," + self.base_dn)
+
+ self.sd_utils.dacl_add_ace("OU=test_computer_ou1," + self.base_dn, mod)
+
+ self.add_computer_ldap("testcomputer-t")
+
+ self.sd_utils.modify_sd_on_dn("OU=test_computer_ou1," + self.base_dn, old_sd)
+
+ self.computernames = ["testcomputer-0"]
+
+ # Get the SD of the template account, then force it to match
+ # what we expect for SeMachineAccountPrivilege accounts, so we
+ # can confirm we created the accounts correctly
+ self.sd_reference_cc = self.sd_utils.read_sd_on_dn("CN=testcomputer-t,OU=test_computer_ou1,%s" % (self.base_dn))
+
+ self.sd_reference_modify = self.sd_utils.read_sd_on_dn("CN=testcomputer-t,OU=test_computer_ou1,%s" % (self.base_dn))
+ for ace in self.sd_reference_modify.dacl.aces:
+ if ace.type == security.SEC_ACE_TYPE_ACCESS_ALLOWED and ace.trustee == self.unpriv_user_sid:
+ ace.access_mask = ace.access_mask | security.SEC_ADS_SELF_WRITE | security.SEC_ADS_WRITE_PROP
+
+ # Now reconnect without domain admin rights
+ self.samdb = SamDB(url=ldaphost, credentials=self.unpriv_creds, lp=lp)
+
+
+ def tearDown(self):
+ super(UserAccountControlTests, self).tearDown()
+ for computername in self.computernames:
+ delete_force(self.admin_samdb, "CN=%s,OU=test_computer_ou1,%s" % (computername, self.base_dn))
+ delete_force(self.admin_samdb, "CN=testcomputer-t,OU=test_computer_ou1,%s" % (self.base_dn))
+ delete_force(self.admin_samdb, "OU=test_computer_ou1,%s" % (self.base_dn))
+ delete_force(self.admin_samdb, "CN=%s,CN=Users,%s" % (self.unpriv_user, self.base_dn))
+
+ def test_add_computer_sd_cc(self):
+ user_sid = self.sd_utils.get_object_sid(self.unpriv_user_dn)
+ mod = "(OA;;CC;bf967a86-0de6-11d0-a285-00aa003049e2;;%s)" % str(user_sid)
+
+ old_sd = self.sd_utils.read_sd_on_dn("OU=test_computer_ou1," + self.base_dn)
+
+ self.sd_utils.dacl_add_ace("OU=test_computer_ou1," + self.base_dn, mod)
+
+ computername=self.computernames[0]
+ sd = ldb.MessageElement((ndr_pack(self.sd_reference_modify)),
+ ldb.FLAG_MOD_ADD,
+ "nTSecurityDescriptor")
+ self.add_computer_ldap(computername,
+ others={"nTSecurityDescriptor": sd})
+
+ res = self.admin_samdb.search("%s" % self.base_dn,
+ expression="(&(objectClass=computer)(samAccountName=%s$))" % computername,
+ scope=SCOPE_SUBTREE,
+ attrs=["ntSecurityDescriptor"])
+
+ desc = res[0]["nTSecurityDescriptor"][0]
+ desc = ndr_unpack(security.descriptor, desc, allow_remaining=True)
+
+ sddl = desc.as_sddl(self.domain_sid)
+ self.assertEqual(self.sd_reference_modify.as_sddl(self.domain_sid), sddl)
+
+ m = ldb.Message()
+ m.dn = res[0].dn
+ m["description"]= ldb.MessageElement(
+ ("A description"), ldb.FLAG_MOD_REPLACE,
+ "description")
+ self.samdb.modify(m)
+
+ m = ldb.Message()
+ m.dn = res[0].dn
+ m["userAccountControl"] = ldb.MessageElement(str(samba.dsdb.UF_SERVER_TRUST_ACCOUNT),
+ ldb.FLAG_MOD_REPLACE, "userAccountControl")
+ try:
+ self.samdb.modify(m)
+ self.fail("Unexpectedly able to set userAccountControl to be a DC on %s" % m.dn)
+ except LdbError, (enum, estr):
+ self.assertEqual(ldb.ERR_INSUFFICIENT_ACCESS_RIGHTS, enum)
+
+ m = ldb.Message()
+ m.dn = res[0].dn
+ m["userAccountControl"] = ldb.MessageElement(str(samba.dsdb.UF_WORKSTATION_TRUST_ACCOUNT|samba.dsdb.UF_PARTIAL_SECRETS_ACCOUNT),
+ ldb.FLAG_MOD_REPLACE, "userAccountControl")
+ try:
+ self.samdb.modify(m)
+ self.fail("Unexpectedly able to set userAccountControl to be an RODC on %s" % m.dn)
+ except LdbError, (enum, estr):
+ self.assertEqual(ldb.ERR_INSUFFICIENT_ACCESS_RIGHTS, enum)
+
+ m = ldb.Message()
+ m.dn = res[0].dn
+ m["userAccountControl"] = ldb.MessageElement(str(samba.dsdb.UF_WORKSTATION_TRUST_ACCOUNT),
+ ldb.FLAG_MOD_REPLACE, "userAccountControl")
+ self.samdb.modify(m)
+
+ m = ldb.Message()
+ m.dn = res[0].dn
+ m["primaryGroupID"] = ldb.MessageElement(str(security.DOMAIN_RID_ADMINS),
+ ldb.FLAG_MOD_REPLACE, "primaryGroupID")
+ try:
+ self.samdb.modify(m)
+ except LdbError, (enum, estr):
+ self.assertEqual(ldb.ERR_UNWILLING_TO_PERFORM, enum)
+ return
+ self.fail()
+
+ def test_mod_computer_cc(self):
+ user_sid = self.sd_utils.get_object_sid(self.unpriv_user_dn)
+ mod = "(OA;;CC;bf967a86-0de6-11d0-a285-00aa003049e2;;%s)" % str(user_sid)
+
+ old_sd = self.sd_utils.read_sd_on_dn("OU=test_computer_ou1," + self.base_dn)
+
+ self.sd_utils.dacl_add_ace("OU=test_computer_ou1," + self.base_dn, mod)
+
+ computername=self.computernames[0]
+ self.add_computer_ldap(computername)
+
+ res = self.admin_samdb.search("%s" % self.base_dn,
+ expression="(&(objectClass=computer)(samAccountName=%s$))" % computername,
+ scope=SCOPE_SUBTREE,
+ attrs=[])
+
+ m = ldb.Message()
+ m.dn = res[0].dn
+ m["description"]= ldb.MessageElement(
+ ("A description"), ldb.FLAG_MOD_REPLACE,
+ "description")
+ self.samdb.modify(m)
+
+ m = ldb.Message()
+ m.dn = res[0].dn
+ m["userAccountControl"] = ldb.MessageElement(str(samba.dsdb.UF_WORKSTATION_TRUST_ACCOUNT|samba.dsdb.UF_PARTIAL_SECRETS_ACCOUNT),
+ ldb.FLAG_MOD_REPLACE, "userAccountControl")
+ try:
+ self.samdb.modify(m)
+ self.fail("Unexpectedly able to set userAccountControl on %s" % m.dn)
+ except LdbError, (enum, estr):
+ self.assertEqual(ldb.ERR_INSUFFICIENT_ACCESS_RIGHTS, enum)
+
+ m = ldb.Message()
+ m.dn = res[0].dn
+ m["userAccountControl"] = ldb.MessageElement(str(samba.dsdb.UF_SERVER_TRUST_ACCOUNT),
+ ldb.FLAG_MOD_REPLACE, "userAccountControl")
+ try:
+ self.samdb.modify(m)
+ self.fail()
+ except LdbError, (enum, estr):
+ self.assertEqual(ldb.ERR_INSUFFICIENT_ACCESS_RIGHTS, enum)
+
+ m = ldb.Message()
+ m.dn = res[0].dn
+ m["userAccountControl"] = ldb.MessageElement(str(samba.dsdb.UF_NORMAL_ACCOUNT),
+ ldb.FLAG_MOD_REPLACE, "userAccountControl")
+ self.samdb.modify(m)
+
+ m = ldb.Message()
+ m.dn = res[0].dn
+ m["userAccountControl"] = ldb.MessageElement(str(samba.dsdb.UF_WORKSTATION_TRUST_ACCOUNT),
+ ldb.FLAG_MOD_REPLACE, "userAccountControl")
+ self.samdb.modify(m)
+
+ def test_admin_mod_uac(self):
+ computername=self.computernames[0]
+ self.add_computer_ldap(computername, samdb=self.admin_samdb)
+
+ res = self.admin_samdb.search("%s" % self.base_dn,
+ expression="(&(objectClass=computer)(samAccountName=%s$))" % computername,
+ scope=SCOPE_SUBTREE,
+ attrs=["userAccountControl"])
+
+ self.assertEqual(int(res[0]["userAccountControl"][0]), UF_NORMAL_ACCOUNT|UF_ACCOUNTDISABLE|UF_PASSWD_NOTREQD)
+
+ m = ldb.Message()
+ m.dn = res[0].dn
+ m["userAccountControl"] = ldb.MessageElement(str(UF_WORKSTATION_TRUST_ACCOUNT|UF_PARTIAL_SECRETS_ACCOUNT),
+ ldb.FLAG_MOD_REPLACE, "userAccountControl")
+ self.admin_samdb.modify(m)
+
+ res = self.admin_samdb.search("%s" % self.base_dn,
+ expression="(&(objectClass=computer)(samAccountName=%s$))" % computername,
+ scope=SCOPE_SUBTREE,
+ attrs=["userAccountControl"])
+
+ self.assertEqual(int(res[0]["userAccountControl"][0]), UF_WORKSTATION_TRUST_ACCOUNT|UF_PARTIAL_SECRETS_ACCOUNT)
+ m = ldb.Message()
+ m.dn = res[0].dn
+ m["userAccountControl"] = ldb.MessageElement(str(UF_ACCOUNTDISABLE),
+ ldb.FLAG_MOD_REPLACE, "userAccountControl")
+ self.admin_samdb.modify(m)
+
+ res = self.admin_samdb.search("%s" % self.base_dn,
+ expression="(&(objectClass=computer)(samAccountName=%s$))" % computername,
+ scope=SCOPE_SUBTREE,
+ attrs=["userAccountControl"])
+
+ self.assertEqual(int(res[0]["userAccountControl"][0]), UF_WORKSTATION_TRUST_ACCOUNT| UF_ACCOUNTDISABLE)
+
+
+ def test_uac_bits_set(self):
+ user_sid = self.sd_utils.get_object_sid(self.unpriv_user_dn)
+ mod = "(OA;;CC;bf967a86-0de6-11d0-a285-00aa003049e2;;%s)" % str(user_sid)
+
+ old_sd = self.sd_utils.read_sd_on_dn("OU=test_computer_ou1," + self.base_dn)
+
+ self.sd_utils.dacl_add_ace("OU=test_computer_ou1," + self.base_dn, mod)
+
+ computername=self.computernames[0]
+ self.add_computer_ldap(computername)
+
+ res = self.admin_samdb.search("%s" % self.base_dn,
+ expression="(&(objectClass=computer)(samAccountName=%s$))" % computername,
+ scope=SCOPE_SUBTREE,
+ attrs=[])
+
+ m = ldb.Message()
+ m.dn = res[0].dn
+ m["description"]= ldb.MessageElement(
+ ("A description"), ldb.FLAG_MOD_REPLACE,
+ "description")
+ self.samdb.modify(m)
+
+ # These bits are privileged, but authenticated users have that CAR by default, so this is a pain to test
+ priv_to_auth_users_bits = set([UF_PASSWD_NOTREQD, UF_ENCRYPTED_TEXT_PASSWORD_ALLOWED,
+ UF_DONT_EXPIRE_PASSWD])
+
+ # These bits really are privileged
+ priv_bits = set([UF_INTERDOMAIN_TRUST_ACCOUNT, UF_SERVER_TRUST_ACCOUNT,
+ UF_TRUSTED_FOR_DELEGATION, UF_TRUSTED_TO_AUTHENTICATE_FOR_DELEGATION,
+ UF_PARTIAL_SECRETS_ACCOUNT])
+
+ invalid_bits = set([UF_TEMP_DUPLICATE_ACCOUNT])
+
+ for bit in bits:
+ m = ldb.Message()
+ m.dn = res[0].dn
+ m["userAccountControl"] = ldb.MessageElement(str(bit|UF_PASSWD_NOTREQD),
+ ldb.FLAG_MOD_REPLACE, "userAccountControl")
+ try:
+ self.samdb.modify(m)
+ if (bit in priv_bits):
+ self.fail("Unexpectedly able to set userAccountControl bit 0x%08X on %s" % (bit, m.dn))
+ except LdbError, (enum, estr):
+ if bit in invalid_bits:
+ self.assertEqual(enum, ldb.ERR_OTHER, "was not able to set 0x%08X on %s" % (bit, m.dn))
+ # No point going on, try the next bit
+ continue
+ elif (bit in priv_bits):
+ self.assertEqual(ldb.ERR_INSUFFICIENT_ACCESS_RIGHTS, enum)
+ else:
+ self.fail("Unable to set userAccountControl bit 0x%08X on %s: %s" % (bit, m.dn, estr))
+
+
+ def test_uac_bits_unrelated_modify(self):
+ user_sid = self.sd_utils.get_object_sid(self.unpriv_user_dn)
+ mod = "(OA;;CC;bf967a86-0de6-11d0-a285-00aa003049e2;;%s)" % str(user_sid)
+
+ old_sd = self.sd_utils.read_sd_on_dn("OU=test_computer_ou1," + self.base_dn)
+
+ self.sd_utils.dacl_add_ace("OU=test_computer_ou1," + self.base_dn, mod)
+
+ computername=self.computernames[0]
+ self.add_computer_ldap(computername)
+
+ res = self.admin_samdb.search("%s" % self.base_dn,
+ expression="(&(objectClass=computer)(samAccountName=%s$))" % computername,
+ scope=SCOPE_SUBTREE,
+ attrs=[])
+
+ m = ldb.Message()
+ m.dn = res[0].dn
+ m["description"]= ldb.MessageElement(
+ ("A description"), ldb.FLAG_MOD_REPLACE,
+ "description")
+ self.samdb.modify(m)
+
+ invalid_bits = set([UF_TEMP_DUPLICATE_ACCOUNT])
+
+ super_priv_bits = set([UF_INTERDOMAIN_TRUST_ACCOUNT])
+
+ priv_to_remove_bits = set([UF_TRUSTED_FOR_DELEGATION, UF_TRUSTED_TO_AUTHENTICATE_FOR_DELEGATION])
+
+ for bit in bits:
+ m = ldb.Message()
+ m.dn = res[0].dn
+ m["userAccountControl"] = ldb.MessageElement(str(bit|UF_PASSWD_NOTREQD),
+ ldb.FLAG_MOD_REPLACE, "userAccountControl")
+ try:
+ self.admin_samdb.modify(m)
+ if bit in invalid_bits:
+ self.fail("Should have been unable to set userAccountControl bit 0x%08X on %s" % (bit, m.dn))
+
+ except LdbError, (enum, estr):
+ if bit in invalid_bits:
+ self.assertEqual(enum, ldb.ERR_OTHER)
+ # No point going on, try the next bit
+ continue
+ elif bit in super_priv_bits:
+ self.assertEqual(enum, ldb.ERR_INSUFFICIENT_ACCESS_RIGHTS)
+ # No point going on, try the next bit
+ continue
+ else:
+ self.fail("Unable to set userAccountControl bit 0x%08X on %s: %s" % (bit, m.dn, estr))
+
+ try:
+ m = ldb.Message()
+ m.dn = res[0].dn
+ m["userAccountControl"] = ldb.MessageElement(str(bit|UF_PASSWD_NOTREQD|UF_ACCOUNTDISABLE),
+ ldb.FLAG_MOD_REPLACE, "userAccountControl")
+ self.samdb.modify(m)
+
+ except LdbError, (enum, estr):
+ self.fail("Unable to set userAccountControl bit 0x%08X on %s: %s" % (bit, m.dn, estr))
+
+ try:
+ m = ldb.Message()
+ m.dn = res[0].dn
+ m["userAccountControl"] = ldb.MessageElement(str(UF_PASSWD_NOTREQD|UF_ACCOUNTDISABLE),
+ ldb.FLAG_MOD_REPLACE, "userAccountControl")
+ self.samdb.modify(m)
+ if bit in priv_to_remove_bits:
+ self.fail("Should have been unable to remove userAccountControl bit 0x%08X on %s" % (bit, m.dn))
+
+ except LdbError, (enum, estr):
+ if bit in priv_to_remove_bits:
+ self.assertEqual(enum, ldb.ERR_INSUFFICIENT_ACCESS_RIGHTS)
+ else:
+ self.fail("Unexpectedly able to remove userAccountControl bit 0x%08X on %s: %s" % (bit, m.dn, estr))
+
+ def test_uac_bits_add(self):
+ computername=self.computernames[0]
+
+ user_sid = self.sd_utils.get_object_sid(self.unpriv_user_dn)
+ mod = "(OA;;CC;bf967a86-0de6-11d0-a285-00aa003049e2;;%s)" % str(user_sid)
+
+ old_sd = self.sd_utils.read_sd_on_dn("OU=test_computer_ou1," + self.base_dn)
+
+ self.sd_utils.dacl_add_ace("OU=test_computer_ou1," + self.base_dn, mod)
+
+ invalid_bits = set([UF_TEMP_DUPLICATE_ACCOUNT])
+
+ # These bits are privileged, but authenticated users have that CAR by default, so this is a pain to test
+ priv_to_auth_users_bits = set([UF_PASSWD_NOTREQD, UF_ENCRYPTED_TEXT_PASSWORD_ALLOWED,
+ UF_DONT_EXPIRE_PASSWD])
+
+ # These bits really are privileged
+ priv_bits = set([UF_INTERDOMAIN_TRUST_ACCOUNT, UF_SERVER_TRUST_ACCOUNT,
+ UF_TRUSTED_FOR_DELEGATION, UF_TRUSTED_TO_AUTHENTICATE_FOR_DELEGATION,
+ UF_PARTIAL_SECRETS_ACCOUNT])
+
+ for bit in bits:
+ try:
+ self.add_computer_ldap(computername, others={"userAccountControl": [str(bit)]})
+ delete_force(self.admin_samdb, "CN=%s,OU=test_computer_ou1,%s" % (computername, self.base_dn))
+ if bit in priv_bits:
+ self.fail("Unexpectdly able to set userAccountControl bit 0x%08X on %s" % (bit, computername))
+
+ except LdbError, (enum, estr):
+ if bit in invalid_bits:
+ self.assertEqual(enum, ldb.ERR_OTHER, "Invalid bit 0x%08X was able to be set on %s" % (bit, computername))
+ # No point going on, try the next bit
+ continue
+ elif bit in priv_bits:
+ self.assertEqual(enum, ldb.ERR_INSUFFICIENT_ACCESS_RIGHTS)
+ continue
+ else:
+ self.fail("Unable to set userAccountControl bit 0x%08X on %s: %s" % (bit, computername, estr))
+
+
+
+runner = SubunitTestRunner()
+rc = 0
+if not runner.run(unittest.makeSuite(UserAccountControlTests)).wasSuccessful():
+ rc = 1
+sys.exit(rc)