1
0
mirror of https://github.com/samba-team/samba.git synced 2025-01-22 22:04:08 +03:00

CVE-2020-25722 dsdb: Improve privileged and unprivileged tests for objectclass/doller/UAC

This helps ensure we cover off all the cases that matter
for objectclass/trailing-doller/userAccountControl

BUG: https://bugzilla.samba.org/show_bug.cgi?id=14753

Signed-off-by: Andrew Bartlett <abartlet@samba.org>
Reviewed-by: Douglas Bagnall <douglas.bagnall@catalyst.net.nz>
This commit is contained in:
Andrew Bartlett 2021-10-22 15:42:08 +13:00 committed by Jule Anger
parent a32ff3ba26
commit 45a7506af6
4 changed files with 141 additions and 59 deletions

View File

@ -0,0 +1 @@
^samba4.user_account_control.python\(.*\).__main__.UserAccountControlTests.test_objectclass_uac_dollar_lock_UF_WORKSTATION_TRUST_ACCOUNT_computer_cc_plain

View File

@ -13,6 +13,22 @@
^samba4.user_account_control.python\(ad_dc_default\).__main__.UserAccountControlTests.test_add_computer_sd_cc\(ad_dc_default\)
^samba4.user_account_control.python\(ad_dc_default\).__main__.UserAccountControlTests.test_admin_mod_uac\(ad_dc_default\)
^samba4.user_account_control.python\(ad_dc_default\).__main__.UserAccountControlTests.test_mod_computer_cc\(ad_dc_default\)
^samba4.user_account_control.python\(ad_dc_default\).__main__.UserAccountControlTests.test_objectclass_mod_lock_UF_NORMAL_ACCOUNT_computer_replace\(ad_dc_default\)
^samba4.user_account_control.python\(ad_dc_default\).__main__.UserAccountControlTests.test_objectclass_mod_lock_UF_NORMAL_ACCOUNT_user_replace\(ad_dc_default\)
^samba4.user_account_control.python\(ad_dc_default\).__main__.UserAccountControlTests.test_objectclass_mod_lock_UF_SERVER_TRUST_ACCOUNT_computer_replace\(ad_dc_default\)
^samba4.user_account_control.python\(ad_dc_default\).__main__.UserAccountControlTests.test_objectclass_mod_lock_UF_WORKSTATION_TRUST_ACCOUNT_computer_replace\(ad_dc_default\)
^samba4.user_account_control.python\(ad_dc_default\).__main__.UserAccountControlTests.test_uac_bits_set_UF_WORKSTATION_TRUST_ACCOUNT\(ad_dc_default\)
^samba4.user_account_control.python\(ad_dc_default\).__main__.UserAccountControlTests.test_uac_bits_unrelated_modify_UF_NORMAL_ACCOUNT\(ad_dc_default\)
^samba4.user_account_control.python\(ad_dc_default\).__main__.UserAccountControlTests.test_objectclass_uac_dollar_lock_UF_NORMAL_ACCOUNT_computer_cc_plain\(ad_dc_default\)
^samba4.user_account_control.python\(ad_dc_default\).__main__.UserAccountControlTests.test_objectclass_uac_dollar_lock_UF_NORMAL_ACCOUNT_computer_cc_withdollar\(ad_dc_default\)
^samba4.user_account_control.python\(ad_dc_default\).__main__.UserAccountControlTests.test_objectclass_uac_dollar_lock_UF_SERVER_TRUST_ACCOUNT_user_cc_plain\(ad_dc_default\)
^samba4.user_account_control.python\(ad_dc_default\).__main__.UserAccountControlTests.test_objectclass_uac_dollar_lock_UF_SERVER_TRUST_ACCOUNT_user_cc_withdollar\(ad_dc_default\)
^samba4.user_account_control.python\(ad_dc_default\).__main__.UserAccountControlTests.test_objectclass_uac_mod_lock_UF_NORMAL_ACCOUNT_UF_WORKSTATION_TRUST_ACCOUNT_deladd_wp\(ad_dc_default\)
^samba4.user_account_control.python\(ad_dc_default\).__main__.UserAccountControlTests.test_objectclass_uac_mod_lock_UF_NORMAL_ACCOUNT_UF_WORKSTATION_TRUST_ACCOUNT_replace_wp\(ad_dc_default\)
^samba4.user_account_control.python\(ad_dc_default\).__main__.UserAccountControlTests.test_objectclass_uac_mod_lock_UF_SERVER_TRUST_ACCOUNT_UF_NORMAL_ACCOUNT_deladd_wp\(ad_dc_default\)
^samba4.user_account_control.python\(ad_dc_default\).__main__.UserAccountControlTests.test_objectclass_uac_mod_lock_UF_SERVER_TRUST_ACCOUNT_UF_NORMAL_ACCOUNT_replace_wp\(ad_dc_default\)
^samba4.user_account_control.python\(ad_dc_default\).__main__.UserAccountControlTests.test_objectclass_uac_mod_lock_UF_WORKSTATION_TRUST_ACCOUNT_UF_NORMAL_ACCOUNT_deladd_wp\(ad_dc_default\)
^samba4.user_account_control.python\(ad_dc_default\).__main__.UserAccountControlTests.test_objectclass_uac_mod_lock_UF_WORKSTATION_TRUST_ACCOUNT_UF_NORMAL_ACCOUNT_replace_wp\(ad_dc_default\)
^samba4.user_account_control.python\(ad_dc_default\).__main__.UserAccountControlTests.test_uac_bits_set_0x10000000\(ad_dc_default\)
^samba4.user_account_control.python\(ad_dc_default\).__main__.UserAccountControlTests.test_uac_bits_set_0x20000000\(ad_dc_default\)
^samba4.user_account_control.python\(ad_dc_default\).__main__.UserAccountControlTests.test_uac_bits_set_0x40000000\(ad_dc_default\)
@ -38,5 +54,3 @@
^samba4.user_account_control.python\(ad_dc_default\).__main__.UserAccountControlTests.test_uac_bits_set_UF_SMARTCARD_REQUIRED\(ad_dc_default\)
^samba4.user_account_control.python\(ad_dc_default\).__main__.UserAccountControlTests.test_uac_bits_set_UF_USE_AES_KEYS\(ad_dc_default\)
^samba4.user_account_control.python\(ad_dc_default\).__main__.UserAccountControlTests.test_uac_bits_set_UF_USE_DES_KEY_ONLY\(ad_dc_default\)
^samba4.user_account_control.python\(ad_dc_default\).__main__.UserAccountControlTests.test_uac_bits_set_UF_WORKSTATION_TRUST_ACCOUNT\(ad_dc_default\)
^samba4.user_account_control.python\(ad_dc_default\).__main__.UserAccountControlTests.test_uac_bits_unrelated_modify_UF_NORMAL_ACCOUNT\(ad_dc_default\)

View File

@ -1,11 +0,0 @@
# We do not want user account control account type swapping, so we mark these as knownfail
^samba4.user_account_control.python\(.*\).__main__.UserAccountControlTests.test_objectclass_mod_lock_UF_NORMAL_ACCOUNT_computer_replace
^samba4.user_account_control.python\(.*\).__main__.UserAccountControlTests.test_objectclass_mod_lock_UF_NORMAL_ACCOUNT_user_replace
^samba4.user_account_control.python\(.*\).__main__.UserAccountControlTests.test_objectclass_mod_lock_UF_SERVER_TRUST_ACCOUNT_computer_replace
^samba4.user_account_control.python\(.*\).__main__.UserAccountControlTests.test_objectclass_mod_lock_UF_WORKSTATION_TRUST_ACCOUNT_computer_replace
^samba4.user_account_control.python\(.*\).__main__.UserAccountControlTests.test_objectclass_uac_mod_lock_UF_NORMAL_ACCOUNT_UF_WORKSTATION_TRUST_ACCOUNT_deladd
^samba4.user_account_control.python\(.*\).__main__.UserAccountControlTests.test_objectclass_uac_mod_lock_UF_NORMAL_ACCOUNT_UF_WORKSTATION_TRUST_ACCOUNT_replace
^samba4.user_account_control.python\(.*\).__main__.UserAccountControlTests.test_objectclass_uac_mod_lock_UF_SERVER_TRUST_ACCOUNT_UF_NORMAL_ACCOUNT_deladd
^samba4.user_account_control.python\(.*\).__main__.UserAccountControlTests.test_objectclass_uac_mod_lock_UF_SERVER_TRUST_ACCOUNT_UF_NORMAL_ACCOUNT_replace
^samba4.user_account_control.python\(.*\).__main__.UserAccountControlTests.test_objectclass_uac_mod_lock_UF_WORKSTATION_TRUST_ACCOUNT_UF_NORMAL_ACCOUNT_deladd
^samba4.user_account_control.python\(.*\).__main__.UserAccountControlTests.test_objectclass_uac_mod_lock_UF_WORKSTATION_TRUST_ACCOUNT_UF_NORMAL_ACCOUNT_replace

View File

@ -91,32 +91,43 @@ account_types = set([UF_NORMAL_ACCOUNT, UF_WORKSTATION_TRUST_ACCOUNT, UF_SERVER_
class UserAccountControlTests(samba.tests.TestCase):
@classmethod
def setUpDynamicTestCases(cls):
for account_type in [UF_NORMAL_ACCOUNT,
UF_WORKSTATION_TRUST_ACCOUNT,
UF_SERVER_TRUST_ACCOUNT]:
account_type_str = dsdb.user_account_control_flag_bit_to_string(account_type)
for objectclass in ["computer", "user"]:
test_name = f"{account_type_str}_{objectclass}"
cls.generate_dynamic_test("test_objectclass_uac_lock",
test_name,
account_type,
objectclass)
for priv in [(True, "priv"), (False, "cc")]:
for account_type in [UF_NORMAL_ACCOUNT,
UF_WORKSTATION_TRUST_ACCOUNT,
UF_SERVER_TRUST_ACCOUNT]:
account_type_str = dsdb.user_account_control_flag_bit_to_string(account_type)
for objectclass in ["computer", "user"]:
for name in [("oc_uac_lock$", "withdollar"), \
("oc_uac_lock", "plain")]:
test_name = f"{account_type_str}_{objectclass}_{priv[1]}_{name[1]}"
cls.generate_dynamic_test("test_objectclass_uac_dollar_lock",
test_name,
account_type,
objectclass,
name[0],
priv[0])
for priv in [(True, "priv"), (False, "wp")]:
for account_type in [UF_NORMAL_ACCOUNT,
UF_WORKSTATION_TRUST_ACCOUNT,
UF_SERVER_TRUST_ACCOUNT]:
account_type_str = dsdb.user_account_control_flag_bit_to_string(account_type)
for account_type2 in [UF_NORMAL_ACCOUNT,
UF_WORKSTATION_TRUST_ACCOUNT,
UF_SERVER_TRUST_ACCOUNT]:
for how in ["replace", "deladd"]:
account_type2_str = dsdb.user_account_control_flag_bit_to_string(account_type2)
test_name = f"{account_type_str}_{account_type2_str}_{how}_{priv[1]}"
cls.generate_dynamic_test("test_objectclass_uac_mod_lock",
test_name,
account_type,
account_type2,
how,
priv[0])
for account_type in [UF_NORMAL_ACCOUNT,
UF_WORKSTATION_TRUST_ACCOUNT,
UF_SERVER_TRUST_ACCOUNT]:
account_type_str = dsdb.user_account_control_flag_bit_to_string(account_type)
for account_type2 in [UF_NORMAL_ACCOUNT,
UF_WORKSTATION_TRUST_ACCOUNT,
UF_SERVER_TRUST_ACCOUNT]:
for how in ["replace", "deladd"]:
account_type2_str = dsdb.user_account_control_flag_bit_to_string(account_type2)
test_name = f"{account_type_str}_{account_type2_str}_{how}"
cls.generate_dynamic_test("test_objectclass_uac_mod_lock",
test_name,
account_type,
account_type2,
how)
for objectclass in ["user", "computer"]:
for how in ["replace", "deladd"]:
test_name = f"{account_type_str}_{objectclass}_{how}"
@ -895,10 +906,11 @@ class UserAccountControlTests(samba.tests.TestCase):
"primaryGroupID")
self.admin_samdb.modify(m)
def _test_objectclass_uac_lock_with_args(self,
account_type,
objectclass):
name = "oc_uac_lock$"
def _test_objectclass_uac_dollar_lock_with_args(self,
account_type,
objectclass,
name,
priv):
dn = "CN=%s,%s" % (name, self.OU)
msg_dict = {
"dn": dn,
@ -910,25 +922,57 @@ class UserAccountControlTests(samba.tests.TestCase):
print(f"Adding account {name} as {account_type_str} with objectclass {objectclass}")
if (objectclass == "user" \
and account_type == UF_NORMAL_ACCOUNT):
self.admin_samdb.add(msg_dict)
elif objectclass == "computer":
try:
self.admin_samdb.add(msg_dict)
except ldb.LdbError as e:
(num, msg) = e.args
self.fail("Failed to create {objectclass} account "
"with {account_type_string}")
if priv:
samdb = self.admin_samdb
else:
self.assertRaisesLdbError(ldb.ERR_OBJECT_CLASS_VIOLATION,
"Should have been unable to {account_type_str} on {objectclass}",
self.admin_samdb.add, msg_dict)
user_sid = self.sd_utils.get_object_sid(self.unpriv_user_dn)
mod = "(OA;;CC;;;%s)" % str(user_sid)
self.sd_utils.dacl_add_ace(self.OU, mod)
samdb = self.samdb
enum = ldb.SUCCESS
try:
samdb.add(msg_dict)
except ldb.LdbError as e:
(enum, msg) = e.args
if (account_type == UF_SERVER_TRUST_ACCOUNT
and objectclass != "computer"):
self.assertEqual(enum, ldb.ERR_OBJECT_CLASS_VIOLATION)
return
if priv == False and account_type == UF_SERVER_TRUST_ACCOUNT:
self.assertEqual(enum, ldb.ERR_INSUFFICIENT_ACCESS_RIGHTS)
return
if (objectclass == "user"
and account_type != UF_NORMAL_ACCOUNT):
self.assertEqual(enum, ldb.ERR_OBJECT_CLASS_VIOLATION)
return
if (not priv and objectclass == "computer"
and account_type == UF_NORMAL_ACCOUNT):
self.assertEqual(enum, ldb.ERR_OBJECT_CLASS_VIOLATION)
return
if priv and account_type == UF_NORMAL_ACCOUNT:
self.assertEqual(enum, 0)
return
if (priv == False and
account_type != UF_NORMAL_ACCOUNT and
name[-1] != '$'):
self.assertEqual(enum, ldb.ERR_UNWILLING_TO_PERFORM)
return
self.assertEqual(enum, 0)
def _test_objectclass_uac_mod_lock_with_args(self,
account_type,
account_type2,
how):
how,
priv):
name = "uac_mod_lock$"
dn = "CN=%s,%s" % (name, self.OU)
if account_type == UF_NORMAL_ACCOUNT:
@ -949,10 +993,25 @@ class UserAccountControlTests(samba.tests.TestCase):
print(f"Adding account {name} as {account_type_str} with objectclass {objectclass}")
if priv:
samdb = self.admin_samdb
else:
samdb = self.samdb
user_sid = self.sd_utils.get_object_sid(self.unpriv_user_dn)
# Create the object as admin
self.admin_samdb.add(msg_dict)
# We want to test what the underlying rules for non-admins
# regardless of security descriptors are, so set this very,
# dangerously, broadly
mod = "(OA;;WP;;;%s)" % str(user_sid)
self.sd_utils.dacl_add_ace(dn, mod)
m = ldb.Message()
m.dn = ldb.Dn(self.admin_samdb, dn)
m.dn = ldb.Dn(samdb, dn)
if how == "replace":
m["userAccountControl"] = ldb.MessageElement(str(account_type2 | UF_PASSWD_NOTREQD),
ldb.FLAG_MOD_REPLACE, "userAccountControl")
@ -964,17 +1023,36 @@ class UserAccountControlTests(samba.tests.TestCase):
else:
raise ValueError(f"{how} was not a valid argument")
if (account_type in [UF_SERVER_TRUST_ACCOUNT,
UF_WORKSTATION_TRUST_ACCOUNT]) and \
if (account_type == account_type2):
samdb.modify(m)
elif (account_type == UF_NORMAL_ACCOUNT) and \
(account_type2 == UF_SERVER_TRUST_ACCOUNT) and not priv:
self.assertRaisesLdbError(ldb.ERR_INSUFFICIENT_ACCESS_RIGHTS,
f"Should have been unable to change {account_type_str} to {account_type2_str}",
samdb.modify, m)
elif (account_type == UF_NORMAL_ACCOUNT) and \
(account_type2 == UF_SERVER_TRUST_ACCOUNT) and priv:
self.assertRaisesLdbError(ldb.ERR_UNWILLING_TO_PERFORM,
f"Should have been unable to change {account_type_str} to {account_type2_str}",
samdb.modify, m)
elif (account_type == UF_WORKSTATION_TRUST_ACCOUNT) and \
(account_type2 == UF_SERVER_TRUST_ACCOUNT) and not priv:
self.assertRaisesLdbError(ldb.ERR_INSUFFICIENT_ACCESS_RIGHTS,
f"Should have been unable to change {account_type_str} to {account_type2_str}",
samdb.modify, m)
elif priv:
samdb.modify(m)
elif (account_type in [UF_SERVER_TRUST_ACCOUNT,
UF_WORKSTATION_TRUST_ACCOUNT]) and \
(account_type2 in [UF_SERVER_TRUST_ACCOUNT,
UF_WORKSTATION_TRUST_ACCOUNT]):
self.admin_samdb.modify(m)
samdb.modify(m)
elif (account_type == account_type2):
self.admin_samdb.modify(m)
samdb.modify(m)
else:
self.assertRaisesLdbError(ldb.ERR_UNWILLING_TO_PERFORM,
self.assertRaisesLdbError(ldb.ERR_OBJECT_CLASS_VIOLATION,
f"Should have been unable to change {account_type_str} to {account_type2_str}",
self.admin_samdb.modify, m)
samdb.modify, m)
def _test_objectclass_mod_lock_with_args(self,
account_type,