mirror of
https://github.com/samba-team/samba.git
synced 2025-12-18 08:23:51 +03:00
This is a helper module to construct conditional ACEs that can't be created from SDDL. There is a semi-infinite number of valid conditional ACEs that don't have SDDL representations, and an even larger number of invalid (or borderline invalid) ACEs. This allows us to create those ACEs without having to deal with too many array of numbers. The next commit provides an example of its use. Signed-off-by: Douglas Bagnall <douglas.bagnall@catalyst.net.nz> Reviewed-by: Andrew Bartlett <abartlet@samba.org>
197 lines
5.2 KiB
Python
197 lines
5.2 KiB
Python
# Unix SMB/CIFS implementation.
|
|
# Copyright © Catalyst IT 2023
|
|
#
|
|
# 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/>.
|
|
#
|
|
"""Fine-grained control over conditional ACE contents.
|
|
|
|
This deliberately allows you to do broken things that SDDL doesn't.
|
|
|
|
- token sequences that make no real sense
|
|
- sequences that make sense which SDDL can't encode
|
|
- strings that aren't proper utf-16
|
|
- etc.
|
|
"""
|
|
|
|
import struct
|
|
from samba.dcerpc import security, conditional_ace as ca
|
|
from samba.ndr import ndr_pack
|
|
|
|
|
|
class Composite:
|
|
token = ca.CONDITIONAL_ACE_TOKEN_COMPOSITE
|
|
|
|
def __init__(self, *tokens):
|
|
self.members = []
|
|
for t in tokens:
|
|
self.members.append(dwim_one_token(t))
|
|
|
|
def __bytes__(self):
|
|
v = []
|
|
for x in self.members:
|
|
v.extend(bytes(x))
|
|
|
|
return (bytes([self.token]) +
|
|
struct.pack("<I", len(v)) +
|
|
bytes(v))
|
|
|
|
|
|
class Int:
|
|
def __init__(self, value,
|
|
bits=ca.CONDITIONAL_ACE_TOKEN_INT64,
|
|
base=ca.CONDITIONAL_ACE_INT_BASE_10,
|
|
sign=ca.CONDITIONAL_ACE_INT_SIGN_NONE):
|
|
self.value = value
|
|
self.bits = int(bits)
|
|
self.base = int(base)
|
|
self.sign = int(sign)
|
|
|
|
def __bytes__(self):
|
|
n = struct.pack('<q', self.value)
|
|
return bytes([self.bits]) + n + bytes([self.sign, self.base])
|
|
|
|
|
|
class String:
|
|
"""A string is decoded as UTF-16.
|
|
Other iterables allows the insertion of arbitrary raw bytes."""
|
|
token = ca.CONDITIONAL_ACE_TOKEN_UNICODE
|
|
|
|
def __init__(self, value):
|
|
if isinstance(value, str):
|
|
value = value.encode('utf-16-le')
|
|
self.value = list(value)
|
|
|
|
def __bytes__(self):
|
|
header = struct.pack('<BI', self.token, len(self.value))
|
|
return header + bytes(self.value)
|
|
|
|
|
|
class LocalAttr(String):
|
|
token = ca.CONDITIONAL_ACE_LOCAL_ATTRIBUTE
|
|
|
|
|
|
class UserAttr(String):
|
|
token = ca.CONDITIONAL_ACE_USER_ATTRIBUTE
|
|
|
|
|
|
class DeviceAttr(String):
|
|
token = ca.CONDITIONAL_ACE_DEVICE_ATTRIBUTE
|
|
|
|
|
|
class ResourceAttr(String):
|
|
token = ca.CONDITIONAL_ACE_RESOURCE_ATTRIBUTE
|
|
|
|
|
|
class ByteString:
|
|
"""takes an iterable of 8-bit numbers, or a string."""
|
|
token = ca.CONDITIONAL_ACE_TOKEN_OCTET_STRING
|
|
|
|
def __init__(self, value):
|
|
if isinstance(value, str):
|
|
value = value.encode()
|
|
self.value = bytes(value)
|
|
if max(self.value) > 255 or min(self.value) < 0:
|
|
raise ValueError("bytes do need to be bytes (0-255)")
|
|
|
|
def __bytes__(self):
|
|
header = struct.pack('<BI', self.token, len(self.value))
|
|
return header + self.value
|
|
|
|
|
|
class SID:
|
|
token = ca.CONDITIONAL_ACE_TOKEN_SID
|
|
|
|
def __init__(self, sidstring):
|
|
self.sid = security.domsid(sidstring)
|
|
|
|
def __bytes__(self):
|
|
value = ndr_pack(self.sid)
|
|
header = struct.pack('B<I', self.token, len(value))
|
|
return header + value
|
|
|
|
|
|
class Token:
|
|
"""To add a raw byte, like
|
|
Token(ca.CONDITIONAL_ACE_TOKEN_COMPOSITE)
|
|
"""
|
|
def __init__(self, v):
|
|
self.token = v
|
|
|
|
def __bytes__(self):
|
|
return bytes([self.token])
|
|
|
|
|
|
def _add_tokens():
|
|
for tok in dir(ca):
|
|
if not tok[:22] == 'CONDITIONAL_ACE_TOKEN_':
|
|
continue
|
|
k = tok[22:]
|
|
globals()[k] = Token(getattr(ca, tok))
|
|
|
|
_add_tokens()
|
|
|
|
|
|
def dwim_one_token(t):
|
|
if isinstance(t, int):
|
|
return Int(t)
|
|
if isinstance(t, str):
|
|
return String(t)
|
|
if isinstance(t, tuple):
|
|
return Composite(*t)
|
|
if isinstance(t, bytes):
|
|
return ByteString(t)
|
|
|
|
return t
|
|
|
|
|
|
def assemble(*tokens):
|
|
program = b'artx'
|
|
if len(tokens) == 1 and isinstance(tokens, (list, tuple, set)):
|
|
print("WARNING: single argument container will become a composite. "
|
|
"you might have meant 'assemble(*args)', not 'assemble(args)'")
|
|
|
|
for t in tokens:
|
|
t = dwim_one_token(t)
|
|
program += bytes(t)
|
|
|
|
program += b'\x00\x00\x00'
|
|
program = program[:-(len(program) & 3)]
|
|
|
|
return program
|
|
|
|
|
|
def assemble_ace(tokens=[],
|
|
type=security.SEC_ACE_TYPE_ACCESS_ALLOWED_CALLBACK,
|
|
trustee=None,
|
|
flags=None,
|
|
object=None,
|
|
access_mask=None):
|
|
type_strings = {
|
|
'XA': security.SEC_ACE_TYPE_ACCESS_ALLOWED_CALLBACK,
|
|
'XD': security.SEC_ACE_TYPE_ACCESS_DENIED_CALLBACK,
|
|
'ZA': security.SEC_ACE_TYPE_ACCESS_ALLOWED_CALLBACK_OBJECT,
|
|
}
|
|
|
|
a = security.ace()
|
|
a.type = type_strings.get(type, type)
|
|
if trustee is not None:
|
|
a.trustee = trustee
|
|
if flags is not None:
|
|
a.flags = flags
|
|
if object is not None:
|
|
a.object = object
|
|
|
|
a.coda = assemble(*tokens)
|
|
return a
|