1
0
mirror of https://github.com/samba-team/samba.git synced 2025-06-12 23:17:06 +03:00
samba-mirror/python/samba/tests/security_descriptors.py
Douglas Bagnall 882ad16138 pytest: compare Samba vs Windows SDDL security descriptors
Can Samba understand Windows security descriptors? Does it parse SDDL
the same way?

Here we test on over 7000 SDDL/descriptor pairs and find the answer
is pleasing. In later commits we will add more tests using different
classes of ACE.

The test cases are derived from fuzz seeds, exported to Windows via
the script in the last commit, with the Windows descriptor bytes found
using libcli/security/tests/windows/windows-sddl-test.py.

Signed-off-by: Douglas Bagnall <douglas.bagnall@catalyst.net.nz>
Reviewed-by: Andrew Bartlett <abartlet@samba.org>
2023-08-24 02:53:31 +00:00

148 lines
5.6 KiB
Python

# Unix SMB/CIFS implementation.
# Copyright (C) Volker Lendecke <vl@samba.org> 2021
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
"""These tests compare Windows security descriptors with Samba
descriptors derived from the same SDDL.
They use json and json.gz files in libcli/security/tests/data.
"""
from samba.dcerpc import security
from samba.ndr import ndr_pack, ndr_unpack, ndr_print
from samba.tests import TestCase, DynamicTestCase
from samba.colour import colourdiff
from hashlib import md5
import gzip
import os
import json
from pathlib import Path
TEST_DIR = Path(__name__).parent.parent.parent / 'libcli/security/tests/data'
class SDDLvsDescriptorBase(TestCase):
"""These tests have no explicit cases and no inline data. The actual
data is kept in JSON files in libcli/security/tests/data, so that
it easy to share those files with Windows. To control what tests
are run, set the `json_file` attribute in subclasses, and/or add a
filter_test_cases class method.
"""
maxDiff = 10000
json_file = TEST_DIR / 'conditional_aces.txt.json'
munge_to_v4 = True
domain_sid = security.dom_sid("S-1-5-21-2457507606-2709100691-398136650")
@classmethod
def filter_test_cases(cls, data):
"""Filter out some cases before running the tests.
Like this, for example:
return {k:v for k, v in data.items() if len(k) < 200 and
'(D;;;;;MP)(D;;;;;MP)(D;;;;;MP)' in k}
"""
return data
@classmethod
def setUpDynamicTestCases(cls):
try:
with gzip.open(cls.json_file, 'rt') as f:
data = json.load(f)
except Exception:
with open(cls.json_file) as f:
data = json.load(f)
data = cls.filter_test_cases(data)
for sddl, sdl in data.items():
name = sddl
if len(name) > 130:
tag = md5(sddl.encode()).hexdigest()[:10]
name = f"{name[:100]}+{len(name) - 100}-more-characters-{tag}"
cls.generate_dynamic_test('test_sddl_vs_sd', name, sddl, sdl)
def _test_sddl_vs_sd_with_args(self, sddl, sdl):
sdb_win = bytes(sdl)
try:
sd_sam = security.descriptor.from_sddl(sddl, self.domain_sid)
except (TypeError, ValueError) as e:
self.fail(f"failed to parse {sddl} into SD: {e}")
try:
sdb_sam = ndr_pack(sd_sam)
except RuntimeError as e:
self.fail(f"failed to pack samba SD from {sddl} into bytes: {e}\n"
f"{ndr_print(sd_sam)}")
try:
sd_win = ndr_unpack(security.descriptor, sdb_win)
except RuntimeError as e:
self.fail(f"could not unpack windows descriptor for {sddl}: {e}\n"
f"windows {len(sdb_win)} bytes: {sdb_win.hex(' ')}\n"
f"samba {len(sdb_sam)} bytes: {sdb_sam.hex(' ')}\n"
f"colour diff {colourdiff(sdb_win, sdb_sam)}\n"
f"SAMBA SD is\n{ndr_print(sd_sam)}")
if self.munge_to_v4:
# Force the ACL revisions to match Samba. Windows seems to
# use the lowest possible revision, while Samba uses
# ACL_REVISION_DS when generating from SDDL. The _DS
# version allows more ACE types, but is otherwise the same.
#
# MS-DTYP 2.4.5 ACL:
#
# ACL_REVISION 0x02
#
# When set to 0x02, only AceTypes 0x00, 0x01,
# 0x02, 0x03, 0x11, 0x12, and 0x13 can be present in the ACL.
# An AceType of 0x11 is used for SACLs but not for DACLs. For
# more information about ACE types, see section 2.4.4.1.
#
# ACL_REVISION_DS 0x04
#
# When set to 0x04, AceTypes 0x05, 0x06, 0x07, 0x08, and 0x11
# are allowed. ACLs of revision 0x04 are applicable only to
# directory service objects. An AceType of 0x11 is used for
# SACLs but not for DACLs.
#
# 5, 6, 7, 8 are object ACES.
if sd_win.dacl:
sd_win.dacl.revision = 4
if sd_win.sacl:
sd_win.sacl.revision = 4
if (sd_win != sd_sam):
self.fail(f"Descriptors differ for {sddl}\n"
f"windows {len(sdb_win)} bytes: {sdb_win.hex(' ')}\n"
f"samba {len(sdb_sam)} bytes: {sdb_sam.hex(' ')}\n"
f"colour diff {colourdiff(sdb_win, sdb_sam)}")
@DynamicTestCase
class SDDLvsDescriptorShortOrdinaryAcls(SDDLvsDescriptorBase):
"""These are not conditional ACEs or resource attribute aces, the SDDL
is less than 1000 characters long, and success is expected.
"""
json_file = TEST_DIR / 'short-ordinary-acls.json.gz'
@DynamicTestCase
class SDDLvsDescriptorRegistryObjectRights(SDDLvsDescriptorBase):
"""We'll fail these because we don't recognise 'KA' and related object
rights strings that are used for registry objects."""
json_file = TEST_DIR / 'registry-object-rights.json'