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,
)

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)


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)


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)


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


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
class AccessAllowedACE(MaskAndSidACE, gdef.ACCESS_ALLOWED_ACE):
    ACE_TYPE = gdef.ACCESS_ALLOWED_ACE_TYPE

class AccessDeniedACE(MaskAndSidACE, gdef.ACCESS_DENIED_ACE):
    ACE_TYPE = gdef.ACCESS_DENIED_ACE_TYPE

class AccessAllowedCallbackACE(CallbackACE, gdef.ACCESS_ALLOWED_CALLBACK_ACE):
    ACE_TYPE = gdef.ACCESS_ALLOWED_CALLBACK_ACE_TYPE

class AccessDeniedCallbackACE(CallbackACE, gdef.ACCESS_DENIED_CALLBACK_ACE):
    ACE_TYPE = gdef.ACCESS_DENIED_CALLBACK_ACE_TYPE

class AccessAllowedObjectACE(ObjectRelatedACE, gdef.ACCESS_ALLOWED_OBJECT_ACE):
    ACE_TYPE = gdef.ACCESS_ALLOWED_OBJECT_ACE_TYPE

class AccessDeniedObjectACE(ObjectRelatedACE, gdef.ACCESS_DENIED_OBJECT_ACE):
    ACE_TYPE = gdef.ACCESS_DENIED_OBJECT_ACE_TYPE

class AccessAllowedCallbackObjectACE(CallbackACE, gdef.ACCESS_ALLOWED_CALLBACK_OBJECT_ACE):
    ACE_TYPE = gdef.ACCESS_ALLOWED_CALLBACK_OBJECT_ACE_TYPE

# Strangly -> no SDDL for this one
class AccessDeniedCallbackObjectACE(CallbackACE, gdef.ACCESS_DENIED_CALLBACK_OBJECT_ACE):
    ACE_TYPE = gdef.ACCESS_DENIED_CALLBACK_OBJECT_ACE_TYPE


# SACL related ACE

class SystemAuditACE(MaskAndSidACE, gdef.SYSTEM_AUDIT_ACE):
    ACE_TYPE = gdef.SYSTEM_AUDIT_ACE_TYPE

class SystemAlarmACE(MaskAndSidACE, gdef.SYSTEM_ALARM_ACE):
    """reserved for future use."""
    ACE_TYPE = gdef.SYSTEM_ALARM_ACE_TYPE

class SystemAuditObjectACE(ObjectRelatedACE, gdef.SYSTEM_AUDIT_OBJECT_ACE):
    ACE_TYPE = gdef.SYSTEM_AUDIT_OBJECT_ACE_TYPE

class SystemAlarmObjectACE(ObjectRelatedACE, gdef.SYSTEM_ALARM_OBJECT_ACE):
    """reserved for future use."""
    ACE_TYPE = gdef.SYSTEM_ALARM_OBJECT_ACE_TYPE

class SystemAuditCallbackACE(CallbackACE, gdef.SYSTEM_AUDIT_CALLBACK_ACE):
    ACE_TYPE = gdef.SYSTEM_AUDIT_CALLBACK_ACE_TYPE

class SystemAlarmCallbackACE(CallbackACE, gdef.SYSTEM_ALARM_CALLBACK_ACE):
    """reserved for future use."""
    ACE_TYPE = gdef.SYSTEM_ALARM_CALLBACK_ACE_TYPE

class SystemAuditCallbackObjectACE(CallbackACE, gdef.SYSTEM_AUDIT_CALLBACK_OBJECT_ACE):
    ACE_TYPE = gdef.SYSTEM_AUDIT_CALLBACK_OBJECT_ACE_TYPE

class SystemAlarmCallbackObjectACE(CallbackACE, gdef.SYSTEM_ALARM_CALLBACK_OBJECT_ACE):
    """Reserved for future use"""
    ACE_TYPE = gdef.SYSTEM_ALARM_CALLBACK_OBJECT_ACE_TYPE

class SystemMandatoryLabelACE(MaskAndSidACE, gdef.SYSTEM_MANDATORY_LABEL_ACE):
    ACE_TYPE = gdef.SYSTEM_MANDATORY_LABEL_ACE_TYPE

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)

class SystemScopedPolicyIDACE(MaskAndSidACE, gdef.SYSTEM_SCOPED_POLICY_ID_ACE):
    ACE_TYPE = gdef.SYSTEM_SCOPED_POLICY_ID_ACE_TYPE

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
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

    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)

    def __len__(self):
        """The number of ``ACE`` in the :class:`Acl`"""
        return self.AceCount

    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

    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
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
    @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
            )

    @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)


    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)

    @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

    @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

    @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)

    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"))
