From bf99abb5db91839d8e3589722fe9be000f857691 Mon Sep 17 00:00:00 2001 From: Andrew Bartlett Date: Mon, 8 Dec 2014 15:07:59 +1300 Subject: [PATCH] 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 Signed-off-by: Andrew Bartlett Signed-off-by: Garming Sam Reviewed-by: Stefan Metzmacher --- source4/dsdb/tests/python/user_account_control.py | 521 ++++++++++++++++++++++ source4/selftest/tests.py | 1 + 2 files changed, 522 insertions(+) create mode 100644 source4/dsdb/tests/python/user_account_control.py 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 00000000000..00501bbc337 --- /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 +# Copyright Andrew Bartlett 2014 +# +# 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] ") +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) diff --git a/source4/selftest/tests.py b/source4/selftest/tests.py index ad2bcec9eb3..a991e1271f4 100755 --- a/source4/selftest/tests.py +++ b/source4/selftest/tests.py @@ -465,6 +465,7 @@ planoldpythontestsuite("plugin_s4_dc", "samba.tests.dcerpc.dnsserver", extra_arg plantestsuite_loadlist("samba4.ldap.python(dc)", "dc", [python, os.path.join(samba4srcdir, "dsdb/tests/python/ldap.py"), '$SERVER', '-U"$USERNAME%$PASSWORD"', '--workgroup=$DOMAIN', '$LOADLIST', '$LISTOPT']) plantestsuite_loadlist("samba4.tokengroups.python(dc)", "dc:local", [python, os.path.join(samba4srcdir, "dsdb/tests/python/token_group.py"), '$SERVER', '-U"$USERNAME%$PASSWORD"', '--workgroup=$DOMAIN', '$LOADLIST', '$LISTOPT']) plantestsuite("samba4.sam.python(dc)", "dc", [python, os.path.join(samba4srcdir, "dsdb/tests/python/sam.py"), '$SERVER', '-U"$USERNAME%$PASSWORD"', '--workgroup=$DOMAIN']) +plantestsuite("samba4.user_account_control.python(dc)", "dc", [python, os.path.join(samba4srcdir, "dsdb/tests/python/user_account_control.py"), '$SERVER', '-U"$USERNAME%$PASSWORD"', '--workgroup=$DOMAIN']) planoldpythontestsuite("dc", "dsdb_schema_info", extra_path=[os.path.join(samba4srcdir, 'dsdb/tests/python')], name="samba4.schemaInfo.python(dc)", -- 2.11.4.GIT