Source code for windows.security

import ctypes
import sys

import windows
import windows.generated_def as gdef
from windows import winproxy
from windows.pycompat import basestring

from windows.winobject.token import Token, KNOW_INTEGRITY_LEVEL


# Specific access right

FILE_ACCESS_RIGHT = gdef.FlagMapper(
    gdef.FILE_READ_DATA,
    gdef.FILE_WRITE_DATA,
    gdef.FILE_APPEND_DATA,
    gdef.FILE_READ_EA,
    gdef.FILE_WRITE_EA,
    gdef.FILE_EXECUTE,
    gdef.FILE_READ_ATTRIBUTES,
    gdef.FILE_WRITE_ATTRIBUTES
)

DIRECTORY_ACCESS_RIGHT = gdef.FlagMapper(
    gdef.FILE_LIST_DIRECTORY,
    gdef.FILE_ADD_FILE,
    gdef.FILE_ADD_SUBDIRECTORY,
    gdef.FILE_READ_EA,
    gdef.FILE_WRITE_EA,
    gdef.FILE_TRAVERSE,
    gdef.FILE_DELETE_CHILD,
    gdef.FILE_READ_ATTRIBUTES,
    gdef.FILE_WRITE_ATTRIBUTES,
)
NAMED_PIPE_ACCESS_RIGHT = gdef.FlagMapper(
    gdef.FILE_READ_DATA,
    gdef.FILE_WRITE_DATA,
    gdef.FILE_CREATE_PIPE_INSTANCE,
    gdef.FILE_READ_ATTRIBUTES,
    gdef.FILE_WRITE_ATTRIBUTES,
)

TOKEN_ACCESS_RIGHT = gdef.FlagMapper(
    gdef.TOKEN_ASSIGN_PRIMARY,
    gdef.TOKEN_DUPLICATE,
    gdef.TOKEN_IMPERSONATE,
    gdef.TOKEN_QUERY,
    gdef.TOKEN_QUERY_SOURCE,
    gdef.TOKEN_ADJUST_PRIVILEGES,
    gdef.TOKEN_ADJUST_GROUPS,
    gdef.TOKEN_ADJUST_DEFAULT,
    gdef.TOKEN_ADJUST_SESSIONID,
)

CLUSTER_API_ACCESS_RIGH = gdef.FlagMapper(
    gdef.CLUSAPI_READ_ACCESS,
    gdef.CLUSAPI_CHANGE_ACCESS,
    gdef.CLUSAPI_NO_ACCESS,
)

FAX_ACCESS_RIGHT = gdef.FlagMapper(
    gdef.FAX_JOB_SUBMIT,
    gdef.FAX_JOB_QUERY,
    gdef.FAX_CONFIG_QUERY,
    gdef.FAX_CONFIG_SET,
    gdef.FAX_PORT_QUERY,
    gdef.FAX_PORT_SET,
    gdef.FAX_JOB_MANAGE,
)

CALLBACK_ACCESS_RIGHT = gdef.FlagMapper(
    gdef.CALLBACK_MODIFY_STATE,
)

MUTANT_ACCESS_RIGHT = gdef.FlagMapper(
    gdef.MUTANT_QUERY_STATE,
)

EVENT_ACCESS_RIGHT = gdef.FlagMapper(
    gdef.EVENT_QUERY_STATE,
    gdef.EVENT_MODIFY_STATE,
)

SEMAPHORE_ACCESS_RIGHT = gdef.FlagMapper(
    gdef.SEMAPHORE_QUERY_STATE,
    gdef.SEMAPHORE_MODIFY_STATE,
)

TIMER_ACCESS_RIGHT = gdef.FlagMapper(
    gdef.TIMER_QUERY_STATE,
    gdef.TIMER_QUERY_STATE,
)

IO_COMPLETION_ACCESS_RIGHT = gdef.FlagMapper(
    gdef.IO_COMPLETION_QUERY_STATE,
    gdef.IO_COMPLETION_MODIFY_STATE,
)

PORT_ACCESS_RIGHT = gdef.FlagMapper(
    gdef.PORT_CONNECT,
)

OBJECT_MANAGER_TYPE_ACCESS_RIGHT = gdef.FlagMapper(
    gdef.OBJECT_TYPE_CREATE
)

OBJECT_MANAGER_DIRECTORY_ACCESS_RIGHT = gdef.FlagMapper(
    gdef.DIRECTORY_QUERY,
    gdef.DIRECTORY_TRAVERSE,
    gdef.DIRECTORY_CREATE_OBJECT,
    gdef.DIRECTORY_CREATE_SUBDIRECTORY,
)

OBJECT_MANAGER_SIMLINK_ACCESS_RIGHT = gdef.FlagMapper(
    gdef.SYMBOLIC_LINK_QUERY,
)

PROCESS_ACCESS_RIGHT = gdef.FlagMapper(
    gdef.PROCESS_TERMINATE                  ,
    gdef.PROCESS_CREATE_THREAD              ,
    gdef.PROCESS_SET_SESSIONID              ,
    gdef.PROCESS_VM_OPERATION               ,
    gdef.PROCESS_VM_READ                    ,
    gdef.PROCESS_VM_WRITE                   ,
    gdef.PROCESS_DUP_HANDLE                 ,
    gdef.PROCESS_CREATE_PROCESS             ,
    gdef.PROCESS_SET_QUOTA                  ,
    gdef.PROCESS_SET_INFORMATION            ,
    gdef.PROCESS_QUERY_INFORMATION          ,
    gdef.PROCESS_SUSPEND_RESUME             ,
    gdef.PROCESS_QUERY_LIMITED_INFORMATION  ,
    gdef.PROCESS_SET_LIMITED_INFORMATION    ,
)

THREAD_ACCESS_RIGHT = gdef.FlagMapper(
    gdef.THREAD_TERMINATE,
    gdef.THREAD_SUSPEND_RESUME,
    gdef.THREAD_GET_CONTEXT,
    gdef.THREAD_SET_CONTEXT,
    gdef.THREAD_QUERY_INFORMATION,
    gdef.THREAD_SET_INFORMATION,
    gdef.THREAD_SET_THREAD_TOKEN,
    gdef.THREAD_IMPERSONATE,
    gdef.THREAD_DIRECT_IMPERSONATION,
)

JOB_ACCESS_RIGHT = gdef.FlagMapper(
    gdef.JOB_OBJECT_ASSIGN_PROCESS,
    gdef.JOB_OBJECT_SET_ATTRIBUTES,
    gdef.JOB_OBJECT_QUERY,
    gdef.JOB_OBJECT_TERMINATE,
    gdef.JOB_OBJECT_SET_SECURITY_ATTRIBUTES,
)

KEY_ACCESS_RIGHT = gdef.FlagMapper(
    gdef.KEY_QUERY_VALUE,
    gdef.KEY_SET_VALUE,
    gdef.KEY_CREATE_SUB_KEY,
    gdef.KEY_ENUMERATE_SUB_KEYS,
    gdef.KEY_NOTIFY,
    gdef.KEY_CREATE_LINK,
    gdef.KEY_WOW64_64KEY,
    gdef.KEY_WOW64_32KEY,
    # KEY_WOW64_RES           (0x0300) # Just a mask of the 2 last
)

SECTION_ACCESS_RIGHT = gdef.FlagMapper(
    gdef.SECTION_QUERY,
    gdef.SECTION_MAP_WRITE,
    gdef.SECTION_MAP_READ,
    gdef.SECTION_MAP_EXECUTE,
    gdef.SECTION_EXTEND_SIZE,
    gdef.SECTION_MAP_EXECUTE_EXPLICIT,
)

COM_ACCESS_RIGHT = gdef.FlagMapper(
    gdef.COM_EXECUTE,
    gdef.COM_EXECUTE_LOCAL,
    gdef.COM_EXECUTE_REMOTE,
    gdef.COM_ACTIVATE_LOCAL,
    gdef.COM_ACTIVATE_REMOTE,
)

SERVICE_ACCESS_RIGHT = gdef.FlagMapper(
    gdef.SERVICE_QUERY_CONFIG,
    gdef.SERVICE_CHANGE_CONFIG,
    gdef.SERVICE_QUERY_STATUS,
    gdef.SERVICE_ENUMERATE_DEPENDENTS,
    gdef.SERVICE_START,
    gdef.SERVICE_STOP,
    gdef.SERVICE_PAUSE_CONTINUE,
    gdef.SERVICE_INTERROGATE,
    gdef.SERVICE_USER_DEFINED_CONTROL,
)

ADS_ACCESS_RIGHT = gdef.FlagMapper(
    gdef.ADS_RIGHT_DS_CREATE_CHILD,
    gdef.ADS_RIGHT_DS_DELETE_CHILD,
    gdef.ADS_RIGHT_ACTRL_DS_LIST,
    gdef.ADS_RIGHT_DS_SELF,
    gdef.ADS_RIGHT_DS_READ_PROP,
    gdef.ADS_RIGHT_DS_WRITE_PROP,
    gdef.ADS_RIGHT_DS_DELETE_TREE,
    gdef.ADS_RIGHT_DS_LIST_OBJECT,
    gdef.ADS_RIGHT_DS_CONTROL_ACCESS,
)


SPECIFIC_ACCESS_RIGTH_BY_TYPE = {
    None: gdef.FlagMapper(),
    "file": FILE_ACCESS_RIGHT,
    "directory": DIRECTORY_ACCESS_RIGHT,
    "named_pipe": NAMED_PIPE_ACCESS_RIGHT,
    "token": TOKEN_ACCESS_RIGHT,
    "cluster_api": CLUSTER_API_ACCESS_RIGH,
    "fax": FAX_ACCESS_RIGHT,
    "callback": CALLBACK_ACCESS_RIGHT,
    "mutant": MUTANT_ACCESS_RIGHT,
    "event": EVENT_ACCESS_RIGHT,
    "semaphore": SEMAPHORE_ACCESS_RIGHT,
    "io_completion": IO_COMPLETION_ACCESS_RIGHT,
    "port": PORT_ACCESS_RIGHT,
    "object_manager_type": OBJECT_MANAGER_TYPE_ACCESS_RIGHT,
    "object_manager_directory": OBJECT_MANAGER_DIRECTORY_ACCESS_RIGHT,
    "object_manager_symlink": OBJECT_MANAGER_SIMLINK_ACCESS_RIGHT,
    "process": PROCESS_ACCESS_RIGHT,
    "thread": THREAD_ACCESS_RIGHT,
    "job": JOB_ACCESS_RIGHT,
    "key": KEY_ACCESS_RIGHT,
    "com": COM_ACCESS_RIGHT,
    "service": SERVICE_ACCESS_RIGHT,
    "ad": ADS_ACCESS_RIGHT,
}

# SD

SD_CONTROL_FLAGS = gdef.FlagMapper(
    gdef.SE_OWNER_DEFAULTED,
    gdef.SE_GROUP_DEFAULTED,
    gdef.SE_DACL_PRESENT,
    gdef.SE_DACL_DEFAULTED,
    gdef.SE_SACL_PRESENT,
    gdef.SE_SACL_DEFAULTED,
    gdef.SE_DACL_UNTRUSTED,
    gdef.SE_SERVER_SECURITY,
    gdef.SE_DACL_AUTO_INHERIT_REQ,
    gdef.SE_SACL_AUTO_INHERIT_REQ,
    gdef.SE_DACL_AUTO_INHERITED,
    gdef.SE_SACL_AUTO_INHERITED,
    gdef.SE_DACL_PROTECTED,
    gdef.SE_SACL_PROTECTED,
    gdef.SE_RM_CONTROL_VALID,
    gdef.SE_SELF_RELATIVE,
)

# ACE

ACE_FLAGS = gdef.FlagMapper(
    gdef.OBJECT_INHERIT_ACE        ,
    gdef.CONTAINER_INHERIT_ACE     ,
    gdef.NO_PROPAGATE_INHERIT_ACE  ,
    gdef.INHERIT_ONLY_ACE          ,
    gdef.INHERITED_ACE             ,
    gdef.VALID_INHERIT_FLAGS       ,
    gdef.SUCCESSFUL_ACCESS_ACE_FLAG,
    gdef.FAILED_ACCESS_ACE_FLAG
)

ACE_MASKS = gdef.FlagMapper(
    gdef.GENERIC_READ                     ,
    gdef.GENERIC_WRITE                    ,
    gdef.GENERIC_EXECUTE                  ,
    gdef.GENERIC_ALL                      ,
    gdef.READ_CONTROL                     ,
    gdef.DELETE                           ,
    gdef.WRITE_DAC                        ,
    gdef.WRITE_OWNER                      ,
    gdef.SYNCHRONIZE                      ,
)

MANDATORY_LABEL_ACE_MASK = gdef.FlagMapper(
    gdef.SYSTEM_MANDATORY_LABEL_NO_WRITE_UP,
    gdef.SYSTEM_MANDATORY_LABEL_NO_READ_UP,
    gdef.SYSTEM_MANDATORY_LABEL_NO_EXECUTE_UP,
)

[docs]class AceHeader(gdef.ACE_HEADER): """Improved ACE_HEADER""" def _to_ace_type(self, ace_type): return ctypes.cast(ctypes.byref(self), ctypes.POINTER(ace_type))[0] @property def AceType(self): """The type of the Ace header""" raw_type = super(AceHeader, self).AceType return ACE_CLASS_TYPE_MAPPER[raw_type] @property def flags(self): """The flags of the Ace header :type: [:class:`int`] - A list of :class:`int` """ return list(self._flags_generator()) def _flags_generator(self): flags = self.AceFlags for i in range(8): # Sizeof(AceFlags) * 8 v = flags & (1 << i) if v: yield ACE_FLAGS[v] def subclass(self): # ACE_CLASS_BY_ACE_TYPE is defined later in this file subcls = ACE_CLASS_BY_ACE_TYPE[self.AceType] return self._to_ace_type(subcls) def __repr__(self): return "<{0} type={1}>".format(type(self).__name__, self.AceType)
[docs]class AceBase(object): # Ca ou mettre flags extraction dans le ctypes generated """Base object for all ``ACE`` classes. provide access to an improved header""" @property def Header(self): # Override the ctypes Header for the struct -> return extended header """The Header of the ``ACE`` :type: :class:`AceHeader` """ addr = ctypes.addressof(self) sheader = super(AceBase, type(self)).Header return AceHeader.from_address(addr + sheader.offset)
[docs]class MaskAndSidACE(AceBase): """`Virtual` ACE for ACE struct with the following layout: - ACE_HEADER Header - ACCESS_MASK Mask - DWORD SidStart """ def _sid_offset(self): return type(self).SidStart.offset @property def sid(self): """The sid described by the ``ACE`` :type: :class:`windows.generated_def.winstructs.PSID` """ return gdef.PSID(ctypes.addressof(self) + self._sid_offset()) @property def mask(self): """The list of flags described by the ``ACE`` :type: [:class:`int`] - A list of :class:`int` """ return list(self._mask_generator()) def _mask_generator(self): mask = self.Mask for i in range(32): # sizeof ACCESS_MASK * 8 v = mask & (1 << i) if v: yield ACE_MASKS[v] def __repr__(self): return "<{0} mask={1}>".format(type(self).__name__, self.Mask)
[docs]class CallbackACE(MaskAndSidACE): @property def application_data(self): """The application-specific data see : https://msdn.microsoft.com/en-us/library/hh877860.aspx :type: :class:`str` """ selfptr = ctypes.cast(ctypes.addressof(self), gdef.PUCHAR) datastart = ctypes.sizeof(self) + self.sid.size - 4 dataend = self.Header.AceSize return selfptr[datastart: dataend] @property def condition(self): buff = windows.utils.BUFFER(gdef.BYTE).from_buffer_copy(self.application_data) resstr = gdef.LPWSTR() winproxy.GetStringConditionFromBinary(buff, StringAceCondition=resstr) condition = resstr.value winproxy.LocalFree(resstr) return condition
[docs]class ObjectRelatedACE(MaskAndSidACE): FLAGS_VALUES = (gdef.ACE_OBJECT_TYPE_PRESENT, gdef.ACE_INHERITED_OBJECT_TYPE_PRESENT) @property def flags(self): """The flags of the ``ACE`` :type: [:class:`int`] - A list of :class:`int` """ flags = self.Flags return [x for x in self.FLAGS_VALUES if flags & x] @property def object_type(self): """The ``ObjectType`` described in the ACE. ``None`` if `ACE`` has no ``ObjectType`` :type: :class:`~windows.generated_def.winstructs.PSID` or ``None`` """ if not self.Flags & gdef.ACE_OBJECT_TYPE_PRESENT: return None return self.ObjectType @property def inherited_object_type(self): """The ``InheritedObjectType`` described in the ACE. ``None`` if `ACE`` has no ``InheritedObjectType`` :type: :class:`~windows.generated_def.winstructs.PSID` or ``None`` """ if not self.Flags & gdef.ACE_INHERITED_OBJECT_TYPE_PRESENT: return None if self.Flags & gdef.ACE_OBJECT_TYPE_PRESENT: # There is an ObjectType so our offset is the good one return self.InheritedObjectType # No ObjectType -> InheritedObjectType is at ObjectType offset # Those are the same type so we can directly use ObjectType return self.ObjectType def _sid_offset(self): base_offset = type(self).SidStart.offset if not self.Flags & gdef.ACE_OBJECT_TYPE_PRESENT: base_offset -= ctypes.sizeof(gdef.GUID) if not self.Flags & gdef.ACE_INHERITED_OBJECT_TYPE_PRESENT: base_offset -= ctypes.sizeof(gdef.GUID) return base_offset
# DACL related ACE # Allow the resolution of Header first
[docs]class AccessAllowedACE(MaskAndSidACE, gdef.ACCESS_ALLOWED_ACE): ACE_TYPE = gdef.ACCESS_ALLOWED_ACE_TYPE
[docs]class AccessDeniedACE(MaskAndSidACE, gdef.ACCESS_DENIED_ACE): ACE_TYPE = gdef.ACCESS_DENIED_ACE_TYPE
[docs]class AccessAllowedCallbackACE(CallbackACE, gdef.ACCESS_ALLOWED_CALLBACK_ACE): ACE_TYPE = gdef.ACCESS_ALLOWED_CALLBACK_ACE_TYPE
[docs]class AccessDeniedCallbackACE(CallbackACE, gdef.ACCESS_DENIED_CALLBACK_ACE): ACE_TYPE = gdef.ACCESS_DENIED_CALLBACK_ACE_TYPE
[docs]class AccessAllowedObjectACE(ObjectRelatedACE, gdef.ACCESS_ALLOWED_OBJECT_ACE): ACE_TYPE = gdef.ACCESS_ALLOWED_OBJECT_ACE_TYPE
[docs]class AccessDeniedObjectACE(ObjectRelatedACE, gdef.ACCESS_DENIED_OBJECT_ACE): ACE_TYPE = gdef.ACCESS_DENIED_OBJECT_ACE_TYPE
[docs]class AccessAllowedCallbackObjectACE(CallbackACE, gdef.ACCESS_ALLOWED_CALLBACK_OBJECT_ACE): ACE_TYPE = gdef.ACCESS_ALLOWED_CALLBACK_OBJECT_ACE_TYPE
# Strangly -> no SDDL for this one
[docs]class AccessDeniedCallbackObjectACE(CallbackACE, gdef.ACCESS_DENIED_CALLBACK_OBJECT_ACE): ACE_TYPE = gdef.ACCESS_DENIED_CALLBACK_OBJECT_ACE_TYPE
# SACL related ACE
[docs]class SystemAuditACE(MaskAndSidACE, gdef.SYSTEM_AUDIT_ACE): ACE_TYPE = gdef.SYSTEM_AUDIT_ACE_TYPE
[docs]class SystemAlarmACE(MaskAndSidACE, gdef.SYSTEM_ALARM_ACE): """reserved for future use.""" ACE_TYPE = gdef.SYSTEM_ALARM_ACE_TYPE
[docs]class SystemAuditObjectACE(ObjectRelatedACE, gdef.SYSTEM_AUDIT_OBJECT_ACE): ACE_TYPE = gdef.SYSTEM_AUDIT_OBJECT_ACE_TYPE
[docs]class SystemAlarmObjectACE(ObjectRelatedACE, gdef.SYSTEM_ALARM_OBJECT_ACE): """reserved for future use.""" ACE_TYPE = gdef.SYSTEM_ALARM_OBJECT_ACE_TYPE
[docs]class SystemAuditCallbackACE(CallbackACE, gdef.SYSTEM_AUDIT_CALLBACK_ACE): ACE_TYPE = gdef.SYSTEM_AUDIT_CALLBACK_ACE_TYPE
[docs]class SystemAlarmCallbackACE(CallbackACE, gdef.SYSTEM_ALARM_CALLBACK_ACE): """reserved for future use.""" ACE_TYPE = gdef.SYSTEM_ALARM_CALLBACK_ACE_TYPE
[docs]class SystemAuditCallbackObjectACE(CallbackACE, gdef.SYSTEM_AUDIT_CALLBACK_OBJECT_ACE): ACE_TYPE = gdef.SYSTEM_AUDIT_CALLBACK_OBJECT_ACE_TYPE
[docs]class SystemAlarmCallbackObjectACE(CallbackACE, gdef.SYSTEM_ALARM_CALLBACK_OBJECT_ACE): """Reserved for future use""" ACE_TYPE = gdef.SYSTEM_ALARM_CALLBACK_OBJECT_ACE_TYPE
[docs]class SystemMandatoryLabelACE(MaskAndSidACE, gdef.SYSTEM_MANDATORY_LABEL_ACE): ACE_TYPE = gdef.SYSTEM_MANDATORY_LABEL_ACE_TYPE
[docs]class SystemResourceAttributeACE(MaskAndSidACE, gdef.SYSTEM_RESOURCE_ATTRIBUTE_ACE): ACE_TYPE = gdef.SYSTEM_RESOURCE_ATTRIBUTE_ACE_TYPE @property def attribute(self): # Sid-size not in the initial struct sid_size_over = self.sid.size - type(self).SidStart.size sec_attr_addr = ctypes.addressof(self) + ctypes.sizeof(self) + sid_size_over return ClaimSecurityAttributeRelativeV1.from_address(sec_attr_addr)
[docs]class SystemScopedPolicyIDACE(MaskAndSidACE, gdef.SYSTEM_SCOPED_POLICY_ID_ACE): ACE_TYPE = gdef.SYSTEM_SCOPED_POLICY_ID_ACE_TYPE
[docs]class SystemProcessTrustLabelACE(MaskAndSidACE, gdef.SYSTEM_PROCESS_TRUST_LABEL_ACE): """Reserved. (from MSDC)""" ACE_TYPE = gdef.SYSTEM_PROCESS_TRUST_LABEL_ACE_TYPE
ACE_CLASS_BY_ACE_TYPE = {cls.ACE_TYPE: cls for cls in ( # DACL AccessAllowedACE, AccessDeniedACE, AccessAllowedCallbackACE, AccessDeniedCallbackACE, AccessAllowedObjectACE, AccessDeniedObjectACE, AccessAllowedCallbackObjectACE, # SACL SystemAuditACE, SystemAlarmACE, # reserved for future use. SystemAuditObjectACE, SystemAlarmObjectACE, # reserved for future use. SystemAuditCallbackACE, SystemAlarmCallbackACE, # reserved for future use. SystemAuditCallbackObjectACE, SystemAlarmCallbackObjectACE, # reserved for future use. SystemMandatoryLabelACE, SystemResourceAttributeACE, SystemScopedPolicyIDACE, SystemProcessTrustLabelACE, )} ACE_CLASS_TYPE_MAPPER = gdef.FlagMapper(*ACE_CLASS_BY_ACE_TYPE.keys()) # CLAIM_SECURITY_ATTRIBUTE_RELATIVE_V1 follow the SYSTEM_RESOURCE_ATTRIBUTE_ACE # For ACE of type SYSTEM_RESOURCE_ATTRIBUTE_ACE_TYPE def retrieve_long64_from_addr(addr): return gdef.LONG64.from_address(addr).value def retrieve_ulong64_from_addr(addr): return gdef.ULONG64.from_address(addr).value def retrieve_wstr_from_addr(addr): return gdef.LPWSTR(addr).value # https://msdn.microsoft.com/en-us/library/hh877847.aspx def retrieve_psid_from_addr(addr): psid_addr = addr + gdef.CLAIM_SECURITY_ATTRIBUTE_OCTET_STRING_RELATIVE.OctetString.offset return gdef.PSID(psid_addr) def retrieve_bool_from_addr(addr): return bool(gdef.ULONG64.from_address(addr).value) def retrieve_octet_string_from_addr(addr): # Good doc: https://msdn.microsoft.com/en-us/library/hh877833.aspx # Doc broken in: https://docs.microsoft.com/en-us/windows/desktop/api/winnt/ns-winnt-_claim_security_attribute_relative_v1 ostring = gdef.CLAIM_SECURITY_ATTRIBUTE_OCTET_STRING_RELATIVE.from_address(addr) # Bypass the array limit return ctypes.cast(ostring.OctetString, gdef.PUCHAR)[:ostring.Length] class ClaimSecurityAttributeRelativeV1(gdef.CLAIM_SECURITY_ATTRIBUTE_RELATIVE_V1): VALUE_ARRAY_PTR_BY_TYPE = { gdef.CLAIM_SECURITY_ATTRIBUTE_TYPE_INT64: ("pInt64", retrieve_long64_from_addr), gdef.CLAIM_SECURITY_ATTRIBUTE_TYPE_UINT64: ("pUint64", retrieve_ulong64_from_addr), gdef.CLAIM_SECURITY_ATTRIBUTE_TYPE_STRING: ("ppString", retrieve_wstr_from_addr), gdef.CLAIM_SECURITY_ATTRIBUTE_TYPE_SID: # ppString is not the good one # But none is doc for PSID ("ppString", retrieve_psid_from_addr), gdef.CLAIM_SECURITY_ATTRIBUTE_TYPE_BOOLEAN: ("pUint64", retrieve_bool_from_addr), gdef.CLAIM_SECURITY_ATTRIBUTE_TYPE_OCTET_STRING: ("pOctetString", retrieve_octet_string_from_addr), } @property def name(self): return gdef.LPWSTR(ctypes.addressof(self) + self.Name).value @property def values(self): array_name, get_value = self.VALUE_ARRAY_PTR_BY_TYPE[self.ValueType] base = ctypes.addressof(self) array = getattr(self.Values, array_name) # The pointer allow us to bypass the array _length_ of 1 array_ptr = ctypes.cast(array, ctypes.POINTER(array._type_)) offsets = array_ptr[:self.ValueCount] # Cast values return tuple(get_value(base + off) for off in offsets) # ACL
[docs]class Acl(gdef.ACL): """All the ``ACE`` returned by :class:`Acl` methods/property are described in the :ref:`Ace section <security_ace>`""" @property def size_info(self): size_info = gdef.ACL_SIZE_INFORMATION() winproxy.GetAclInformation(self, ctypes.byref(size_info), ctypes.sizeof(size_info), gdef.AclSizeInformation) return size_info
[docs] def get_ace(self, i): """Retrieve ``ACE`` number ``i`` :return: :class:`Ace` """ ace = gdef.PVOID() winproxy.GetAce(self, i, ace) # TODO: subclass ACL return AceHeader.from_address(ace.value).subclass()
@property def aces(self): """The list of ``ACE`` in the ACL :class:`Acl` :type: [:class:`Ace`] - A list of ACE """ return list(self)
[docs] def __len__(self): """The number of ``ACE`` in the :class:`Acl`""" return self.AceCount
[docs] def __getitem__(self, i): """Return ``ACE`` nb ``i`` :return: :class:`Ace` """ try: return self.get_ace(i) except WindowsError as e: if e.winerror == gdef.ERROR_INVALID_PARAMETER: raise IndexError("Invalid ACL index {0}".format(i)) raise
[docs] def __iter__(self): """Return an iterable over all the ``ACE`` in the :class:`Acl` :yield: :class:`Ace`""" for i in range(self.AceCount): yield self.get_ace(i)
def __repr__(self): return "<Acl count={0}>".format(self.AceCount)
PAcl = ctypes.POINTER(Acl) # Security descriptor
[docs]class SecurityDescriptor(gdef.PSECURITY_DESCRIPTOR): """A Security Descriptor .. warning:: TODO: free the underliying buffer when not needed anymore for now the underliying memory is never freed. """ DEFAULT_SECURITY_INFORMATION = ( gdef.OWNER_SECURITY_INFORMATION | gdef.GROUP_SECURITY_INFORMATION | gdef.DACL_SECURITY_INFORMATION | gdef.ATTRIBUTE_SECURITY_INFORMATION | gdef.LABEL_SECURITY_INFORMATION | # Integrity # gdef.SACL_SECURITY_INFORMATION | # Need special rights gdef.SCOPE_SECURITY_INFORMATION | gdef.PROCESS_TRUST_LABEL_SECURITY_INFORMATION ) # https://docs.microsoft.com/en-us/windows/win32/api/winsvc/nf-winsvc-queryserviceobjectsecurity # Default flags for services SERVICE_SECURITY_INFORMATION = ( gdef.OWNER_SECURITY_INFORMATION | gdef.GROUP_SECURITY_INFORMATION | gdef.DACL_SECURITY_INFORMATION ) """The default ``flags`` value for functions expecting a `SECURITY_INFORMATION <https://docs.microsoft.com/en-us/windows/desktop/SecAuthZ/security-information>`_. This value regroups the followings flags: - ``OWNER_SECURITY_INFORMATION`` - ``GROUP_SECURITY_INFORMATION`` - ``DACL_SECURITY_INFORMATION`` - ``ATTRIBUTE_SECURITY_INFORMATION`` - ``SCOPE_SECURITY_INFORMATION`` - ``PROCESS_TRUST_LABEL_SECURITY_INFORMATION`` - ``LABEL_SECURITY_INFORMATION`` .. warning:: Note that the value ``SACL_SECURITY_INFORMATION`` needed to access the SACL is not present as it require the ``SeSecurityPrivilege``. To query the SACL enable the ``SeSecurityPrivilege`` and use the parameter ``query_sacl=True`` on the functions expecting a ``flags`` see :ref:`Query SACL sample <sample_security_sacl>` """ _close_function = winproxy.LocalFree @property def control(self): """The security descriptor control :type: :class:`~windows.generated_def.winstructs.SECURITY_DESCRIPTOR_CONTROL` """ lpdwRevision = gdef.DWORD() control = gdef.SECURITY_DESCRIPTOR_CONTROL() winproxy.GetSecurityDescriptorControl(self, control, lpdwRevision) return control.value @property def revision(self): """The security descriptor's revision :type: :class:`int` """ lpdwRevision = gdef.DWORD() control = gdef.SECURITY_DESCRIPTOR_CONTROL() winproxy.GetSecurityDescriptorControl(self, control, lpdwRevision) return lpdwRevision.value @property def owner(self): """The owner of the security descriptor :type: :class:`~windows.generated_def.winstructs.PSID` or ``None`` """ owner = gdef.PSID() lpbOwnerDefaulted = gdef.BOOL() winproxy.GetSecurityDescriptorOwner(self, owner, lpbOwnerDefaulted) # Return None of owner is NULL return owner or None @property def group(self): """The group of the security descriptor :type: :class:`~windows.generated_def.winstructs.PSID` or ``None`` """ group = gdef.PSID() lpbGroupDefaulted = gdef.BOOL() winproxy.GetSecurityDescriptorGroup(self, group, lpbGroupDefaulted) # Return None of group is NULL return group or None @property def dacl(self): """The DACL of the security descriptor. :type: :class:`Acl` or ``None`` if the DACL was ``NULL`` or not present """ dacl_present = gdef.BOOL() pdacl = gdef.PACL() lpbDaclDefaulted = gdef.BOOL() winproxy.GetSecurityDescriptorDacl(self, dacl_present, pdacl, lpbDaclDefaulted) if not dacl_present or not pdacl: return None return ctypes.cast(pdacl, PAcl)[0] @property def sacl(self): """The SACL of the security descriptor. You may need special attention to retrieve it (see :class:`DEFAULT_SECURITY_INFORMATION`) :type: :class:`Acl` or ``None`` if the SACL was ``NULL`` or not present """ sacl_present = gdef.BOOL() psacl = gdef.PACL() lpbSaclDefaulted = gdef.BOOL() winproxy.GetSecurityDescriptorSacl(self, sacl_present, psacl, lpbSaclDefaulted) if not sacl_present or not psacl: return None return ctypes.cast(psacl, PAcl)[0] # Constructors
[docs] @classmethod def from_string(cls, sddl): """Return a new :class:`SecurityDescriptor` from the ``SDDL``. :returns: :class:`SecurityDescriptor` .. warning:: At the moment the underliying buffer is never freed. See `ConvertStringSecurityDescriptorToSecurityDescriptorA <https://docs.microsoft.com/en-us/windows/desktop/api/sddl/nf-sddl-convertstringsecuritydescriptortosecuritydescriptora>`_ """ self = cls() winproxy.ConvertStringSecurityDescriptorToSecurityDescriptorA( sddl.encode("ascii"), gdef.SDDL_REVISION_1, self, None) # TODO: we need to free this buffer.. # Keep track of Security Descritor state ? return self
@classmethod def _from_name_and_type(cls, objname, objtype, flags=DEFAULT_SECURITY_INFORMATION, query_sacl=False): self = cls() if query_sacl: flags |= gdef.SACL_SECURITY_INFORMATION winproxy.GetNamedSecurityInfoA( objname, objtype, flags, None, None, None, None, self ) return self @classmethod def _from_handle_and_type(cls, handle, objtype, flags=DEFAULT_SECURITY_INFORMATION, query_sacl=False): self = cls() if query_sacl: flags |= gdef.SACL_SECURITY_INFORMATION winproxy.GetSecurityInfo( handle, objtype, flags, None, None, None, None, self ) return self def _apply_to_handle_and_type(self, handle, objtype=gdef.SE_KERNEL_OBJECT, flags=None): # Make SecurityDescriptor method has_audit_ace ? if flags is None: flags = gdef.OWNER_SECURITY_INFORMATION | gdef.GROUP_SECURITY_INFORMATION | gdef.DACL_SECURITY_INFORMATION | gdef.LABEL_SECURITY_INFORMATION if (self.sacl and any(ace.Header.AceType != gdef.SYSTEM_MANDATORY_LABEL_ACE_TYPE for ace in self.sacl)): flags |= gdef.SACL_SECURITY_INFORMATION return winproxy.SetSecurityInfo( handle, objtype, flags, self.owner, self.group, self.dacl, self.sacl )
[docs] @classmethod def from_filename(cls, filename, query_sacl=False, flags=DEFAULT_SECURITY_INFORMATION): """Retrieve the security descriptor for the file ``filename``""" return cls._from_name_and_type(filename, gdef.SE_FILE_OBJECT, flags=flags, query_sacl=query_sacl)
[docs] def to_filename(self, filename, flags=0): "Test method: WILL CHANGE" if not flags: if self.owner: flags |= gdef.OWNER_SECURITY_INFORMATION if self.group: flags |= gdef.GROUP_SECURITY_INFORMATION if self.dacl: flags |= gdef.DACL_SECURITY_INFORMATION if self.sacl: flags |= gdef.SACL_SECURITY_INFORMATION winproxy.SetNamedSecurityInfoW(filename, gdef.SE_FILE_OBJECT, flags, self.owner, self.group, self.dacl, self.sacl)
def to_handle(self, handle, flags=0): if not flags: if self.owner: flags |= gdef.OWNER_SECURITY_INFORMATION if self.group: flags |= gdef.GROUP_SECURITY_INFORMATION if self.dacl: flags |= gdef.DACL_SECURITY_INFORMATION if self.sacl: flags |= gdef.SACL_SECURITY_INFORMATION self._apply_to_handle_and_type(handle, gdef.SE_FILE_OBJECT, flags)
[docs] @classmethod def from_service(cls, filename, query_sacl=False, flags=SERVICE_SECURITY_INFORMATION): """Retrieve the security descriptor for the service named ``service``""" sd = cls._from_name_and_type(filename, gdef.SE_SERVICE, flags=flags, query_sacl=query_sacl) sd.obj_type = "service" return sd
[docs] @classmethod def from_handle(cls, handle, query_sacl=False, flags=DEFAULT_SECURITY_INFORMATION, obj_type=None): """Retrieve the security descriptor for the kernel object described by``handle``""" sd = cls._from_handle_and_type(handle, gdef.SE_KERNEL_OBJECT, flags=flags, query_sacl=query_sacl) sd.obj_type = obj_type # Test return sd
[docs] @classmethod def from_binary(cls, data): """Retrieve the security descriptor described by the binary ``data``. Binary security descriptor can be found in the registry for example """ if isinstance(data, basestring): data = ctypes.c_buffer(data) return ctypes.cast(data, cls)
[docs] def to_string(self, security_information=DEFAULT_SECURITY_INFORMATION): """Return the SDDL representation of the security descriptor :type: :class:`str` """ result_cstr = gdef.LPSTR() winproxy.ConvertSecurityDescriptorToStringSecurityDescriptorA( self, gdef.SDDL_REVISION_1, security_information, result_cstr, None) result = result_cstr.value # Retrieve a python-str copy winproxy.LocalFree(result_cstr) return result.decode()
__str__ = to_string # TST # def relative(self): # return bool(self.control & gdef.SE_SELF_RELATIVE) # If we want auto-free we need to handle relf-relative SD # We need to keep-track of sub-object of the SD # Just a ref to SD from SACL / DACL ? # def __del__(self): # if self._needs_free and sys.path is not None: # print("FREE SELF") # self._close_function(self) def explain(self, type=None): if type is None: type = getattr(self, "obj_type", None) return explain_sd(self, sdtype=type)
# Explain SD POC # If ok -> new file def explain_sid(sid): if sid is None: return "None" try: info = u"\\".join(windows.utils.lookup_sid(sid)) except WindowsError as e: print(e) info = u"<lookup failed>" return u"{0} ({1})".format(info, sid) def explain_mask(mask, sdtype): mapper = windows.security.SPECIFIC_ACCESS_RIGTH_BY_TYPE[sdtype] res = [] for i in range(32): # sizeof ACCESS_MASK * 8 v = mask & (1 << i) if v: res.append(mapper[ACE_MASKS[v]]) return res def explain_simple_ace(ace, sdtype): yield u"Type:" yield u" " + str(ace.Header.AceType) yield u"Flags: {0:#x}".format(ace.Header.AceFlags) yield u" {0}".format(ace.Header.flags) yield u"SID:" yield u" " + explain_sid(ace.sid) yield u"Mask: {0:#x}".format(ace.Mask) mapper = windows.security.SPECIFIC_ACCESS_RIGTH_BY_TYPE[sdtype] yield u" " + str(list(mapper[x] for x in ace.mask)) def explain_mandatory_label_ace(ace, sdtype): assert ace.Header.AceType == gdef.SYSTEM_MANDATORY_LABEL_ACE_TYPE yield u"Type:" yield u" {0} (Integrity Level)".format(ace.Header.AceType) yield u"Flags:" yield u" {0:#x}".format(ace.Header.AceFlags) yield u"SID:" sid = ace.sid count = windows.winproxy.GetSidSubAuthorityCount(sid) integrity = windows.winproxy.GetSidSubAuthority(sid, count[0] - 1)[0] yield u" {0} (Integrity Level: {1})".format(sid, KNOW_INTEGRITY_LEVEL[integrity]) yield u"Mask:" mapper = MANDATORY_LABEL_ACE_MASK yield u" " + str(list(mapper[x] for x in ace.mask)) def explain_object_related_ace(ace, sdtype): yield u"Type:" yield u" " + str(ace.Header.AceType) yield u"HeaderFlags:" yield u" {0:#x}".format(ace.Header.AceFlags) yield u"SID:" yield u" " + explain_sid(ace.sid) yield u"Mask:" mapper = windows.security.SPECIFIC_ACCESS_RIGTH_BY_TYPE[sdtype] yield u" " + str(list(mapper[x] for x in ace.mask)) yield u"Flag:" yield u" " + str(ace.flags) object_type = ace.object_type inherited_object_type = ace.inherited_object_type if object_type: yield u"ObjectType" yield u" " + object_type if inherited_object_type: yield u"InheritedObjectType" yield u" " + inherited_object_type def explain_callback_ace(ace, sdtype): yield u"Type:" yield u" " + str(ace.Header.AceType) yield u"HeaderFlags:" yield u" {0:#x}".format(ace.Header.AceFlags) yield u"SID:" yield u" " + explain_sid(ace.sid) yield u"Mask:" mapper = windows.security.SPECIFIC_ACCESS_RIGTH_BY_TYPE[sdtype] yield u" " + str(list(mapper[x] for x in ace.mask)) yield "Application data:" yield " " + repr(ace.application_data.replace("\x00", "")) try: condition = ace.condition yield "Condition:" yield " " + condition except ValueError: pass ACE_EXPLAINER = { gdef.ACCESS_ALLOWED_ACE_TYPE: explain_simple_ace, gdef.ACCESS_DENIED_ACE_TYPE: explain_simple_ace, gdef.SYSTEM_MANDATORY_LABEL_ACE_TYPE: explain_mandatory_label_ace, gdef.SYSTEM_AUDIT_ACE_TYPE: explain_simple_ace, gdef.ACCESS_ALLOWED_OBJECT_ACE_TYPE: explain_object_related_ace, gdef.ACCESS_DENIED_OBJECT_ACE_TYPE: explain_object_related_ace, gdef.ACCESS_ALLOWED_CALLBACK_ACE_TYPE: explain_callback_ace, } def explain_acl(acl, sdtype=None): if acl is None: yield u"None" for ace in acl.aces: yield str(ace) for line in ACE_EXPLAINER[ace.Header.AceType](ace, sdtype): yield u" " + line def explain_sd(sd, sdtype=None): print(u"Security Descriptor: {0}".format(sd)) print(u" Owner:") print(u" " + explain_sid(sd.owner)) print(u" Group:") print(u" " + explain_sid(sd.group)) print(u" Control:") control = sd.control print(" {0}".format([flag for flag in SD_CONTROL_FLAGS if control & flag])) print(u" Dacl: {0}".format(sd.dacl)) if sd.dacl: for line in explain_acl(sd.dacl, sdtype): try: print(u" " + line) except UnicodeError: print(" " + line.encode("cp1252")) print(u" Sacl: {0}".format(sd.sacl)) if sd.sacl: for line in explain_acl(sd.sacl, sdtype): try: print(u" " + line) except UnicodeError: print(" " + line.encode("cp1252"))