diff --git a/docs/source/security.rst b/docs/source/security.rst new file mode 100644 index 0000000..7321d5d --- /dev/null +++ b/docs/source/security.rst @@ -0,0 +1,255 @@ +``windows.security`` -- Security Descriptor & related +***************************************************** + +.. warning: + + Foutre les token ici ? + Ca a du sens https://docs.microsoft.com/en-us/windows/desktop/secauthz/access-control-components + + ACCESS CONTROL TOUT CA.. + Les deux vont bien ensemble.. + + +.. module:: windows.security + + +SecurityDescriptor +"""""""""""""""""" + +.. autoclass:: SecurityDescriptor + + +Acl +""" + +.. autoclass:: Acl + :special-members: __len__, __iter__ + + +.. _security_ace: + +Ace +""" + +The ACE are regrouped in two categories. + +The DACL related ACEs: + + - :class:`AccessAllowedACE` + - :class:`AccessDeniedACE` + - :class:`AccessAllowedCallbackACE` + - :class:`AccessDeniedCallbackACE` + - :class:`AccessAllowedObjectACE` + - :class:`AccessDeniedObjectACE` + - :class:`AccessAllowedCallbackObjectACE` + - :class:`AccessDeniedCallbackObjectACE` + +The SACL related ACEs: + + - :class:`SystemAuditACE` + - :class:`SystemAlarmACE` + - :class:`SystemAuditObjectACE` + - :class:`SystemAlarmObjectACE` + - :class:`SystemAuditCallbackACE` + - :class:`SystemAlarmCallbackACE` + - :class:`SystemAuditCallbackObjectACE` + - :class:`SystemAlarmCallbackObjectACE` + - :class:`SystemMandatoryLabelACE` + - :class:`SystemResourceAttributeACE` + - :class:`SystemScopedPolicyIDACE` + - :class:`SystemProcessTrustLabelACE` + + +Ace classes +''''''''''' + +AccessAllowedACE +~~~~~~~~~~~~~~~~ + +.. autoclass:: AccessAllowedACE + :show-inheritance: + :inherited-members: + +AccessDeniedACE +~~~~~~~~~~~~~~~ + +.. autoclass:: AccessDeniedACE + :show-inheritance: + :inherited-members: + + +AccessAllowedCallbackACE +~~~~~~~~~~~~~~~~~~~~~~~~ + +.. autoclass:: AccessAllowedCallbackACE + :show-inheritance: + :inherited-members: + + +AccessDeniedCallbackACE +~~~~~~~~~~~~~~~~~~~~~~~ + +.. autoclass:: AccessDeniedCallbackACE + :show-inheritance: + :inherited-members: + +AccessAllowedObjectACE +~~~~~~~~~~~~~~~~~~~~~~ + +.. autoclass:: AccessAllowedObjectACE + :show-inheritance: + :inherited-members: + +AccessDeniedObjectACE +~~~~~~~~~~~~~~~~~~~~~ + +.. autoclass:: AccessDeniedObjectACE + :show-inheritance: + :inherited-members: + + +AccessAllowedCallbackObjectACE +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. autoclass:: AccessAllowedCallbackObjectACE + :show-inheritance: + :inherited-members: + + +AccessDeniedCallbackObjectACE +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. autoclass:: AccessDeniedCallbackObjectACE + :show-inheritance: + :inherited-members: + + +SystemAuditACE +~~~~~~~~~~~~~~ + +.. autoclass:: SystemAuditACE + :show-inheritance: + :inherited-members: + + +SystemAlarmACE +~~~~~~~~~~~~~~ + +.. autoclass:: SystemAlarmACE + :show-inheritance: + :inherited-members: + +SystemAuditObjectACE +~~~~~~~~~~~~~~~~~~~~ + +.. autoclass:: SystemAuditObjectACE + :show-inheritance: + :inherited-members: + +SystemAlarmObjectACE +~~~~~~~~~~~~~~~~~~~~ + +.. autoclass:: SystemAlarmObjectACE + :show-inheritance: + :inherited-members: + +SystemAuditCallbackACE +~~~~~~~~~~~~~~~~~~~~~~ + +.. autoclass:: SystemAuditCallbackACE + :show-inheritance: + :inherited-members: + + +SystemAlarmCallbackACE +~~~~~~~~~~~~~~~~~~~~~~ + +.. autoclass:: SystemAlarmCallbackACE + :show-inheritance: + :inherited-members: + +SystemAuditCallbackObjectACE +~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. autoclass:: SystemAuditCallbackObjectACE + :show-inheritance: + :inherited-members: + + +SystemAlarmCallbackObjectACE +~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. autoclass:: SystemAlarmCallbackObjectACE + :show-inheritance: + :inherited-members: + + +SystemMandatoryLabelACE +~~~~~~~~~~~~~~~~~~~~~~~ + +.. autoclass:: SystemMandatoryLabelACE + :show-inheritance: + :inherited-members: + + +SystemResourceAttributeACE +~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. autoclass:: SystemResourceAttributeACE + :show-inheritance: + :inherited-members: + + +SystemScopedPolicyIDACE +~~~~~~~~~~~~~~~~~~~~~~~ + +.. autoclass:: SystemScopedPolicyIDACE + :show-inheritance: + :inherited-members: + + +SystemProcessTrustLabelACE +~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. autoclass:: SystemProcessTrustLabelACE + :show-inheritance: + :inherited-members: + + + +Ace common base +""""""""""""""" + +These classes are internals and here for completness sake. +You should not need to instanciate/use them directly. + +AceHeader +''''''''' + +.. autoclass:: AceHeader + +AceBase +''''''' + +.. autoclass:: AceBase + + +MaskAndSidACE +''''''''''''' + +.. autoclass:: MaskAndSidACE + + +CallbackACE +''''''''''' + +.. autoclass:: CallbackACE + + +ObjectRelatedACE +'''''''''''''''' + +.. autoclass:: ObjectRelatedACE + + + diff --git a/samples/security/query_sacl.py b/samples/security/query_sacl.py new file mode 100644 index 0000000..64299a3 --- /dev/null +++ b/samples/security/query_sacl.py @@ -0,0 +1,34 @@ +import sys +import windows.security + +TARGET = r"C:\windows\notepad.exe" # On WIN10 (at least) notepad.exe has a AuditACE + +if not windows.current_process.token.elevated: + print(ValueError("This sample should be run as admin to demonstration SACL access")) + +print("") +print("[NO-PRIV] Querying <{0}> SecurityDescriptor without SACL".format(TARGET)) +sd = windows.security.SecurityDescriptor.from_filename(TARGET) +print("sacl = {0}".format(sd.sacl)) + +print("") +print("[NO-PRIV] Querying <{0}> SecurityDescriptor with SACL".format(TARGET)) +try: + sd = windows.security.SecurityDescriptor.from_filename(TARGET, query_sacl=True) + print("sacl = {0}".format(sd.sacl)) +except WindowsError as e: + print(e) + +print("") +print("Enabling ") +try: + windows.current_process.token.enable_privilege("SeSecurityPrivilege") +except ValueError as e: + print("[ERROR] {0}".format(e)) + exit(1) + +print("") +print("[PRIV] Querying <{0}> SecurityDescriptor with SACL".format(TARGET)) +sd = windows.security.SecurityDescriptor.from_filename(TARGET, query_sacl=True) +print("sacl = {0}".format(sd.sacl)) +print(list(sd.sacl)) \ No newline at end of file diff --git a/samples/security/security_descriptor.py b/samples/security/security_descriptor.py new file mode 100644 index 0000000..510cfd9 --- /dev/null +++ b/samples/security/security_descriptor.py @@ -0,0 +1,24 @@ +import windows.security + +SDDL = "O:BAG:AND:(A;OI;RPWPCCDCLCSWRCWDWOGA;;;S-1-0-0)(D;CIIO;RPWPCCDCLCSWRCWDWOGA;;;S-1-0-0)" + +sd = windows.security.SecurityDescriptor.from_string(SDDL) +print("Security descriptor is: {0}".format(sd)) + +print("Owner: {0}".format(sd.owner)) +print(" - lookup: {0}".format(windows.security.lookup_sid(sd.owner))) +print("Group: {0}".format(sd.group)) +print(" - lookup: {0}".format(windows.security.lookup_sid(sd.group))) + +dacl = sd.dacl +print("Dacl: {0}".format(dacl)) + +for i, ace in enumerate(dacl): + print("") + print(" ACE [{0}]: {1}".format(i, ace)) + print(" - Header-AceType: {0}".format(ace.Header.AceType)) + print(" - Header-AceFlags: {0}".format(ace.Header.AceFlags)) + print(" - Header-flags: {0}".format(ace.Header.flags)) + print(" - Mask: {0}".format(ace.Mask)) + print(" - mask: {0}".format(ace.mask)) + print(" - Sid: {0}".format(ace.sid)) \ No newline at end of file diff --git a/tests/test_security.py b/tests/test_security.py index 9c2f9ea..4bed5f1 100644 --- a/tests/test_security.py +++ b/tests/test_security.py @@ -19,6 +19,13 @@ TEST_SDDL = [ def test_security_descriptor_from_string(sddl): sd = SecurityDescriptor.from_string(sddl) +def test_empty_security_descriptor(): + esd = SecurityDescriptor.from_string("") + assert esd.owner is None # Should NOT be NULL PSID but None + assert esd.group is None # Should NOT be NULL PSID but None + assert esd.dacl is None + assert esd.sacl is None + def test_pacl_object(): SDDL = "O:ANG:S-1-2-3D:(A;;;;;S-1-42-42)(A;;;;;S-1-42-43)(A;;;;;S-1-42-44)" @@ -193,3 +200,4 @@ def test_conditional_ace_applicationdata(sddl, expected_value): assert expected_value in appdata.replace("\x00", "") + diff --git a/windows/security.py b/windows/security.py index 8d18bc1..0c52686 100644 --- a/windows/security.py +++ b/windows/security.py @@ -6,15 +6,171 @@ import windows.generated_def as gdef from windows import winproxy # Temporary ? real API ? +# Mov to utils ? def lookup_sid(psid): usernamesize = gdef.DWORD(0x1000) computernamesize = gdef.DWORD(0x1000) - username = ctypes.c_buffer(usernamesize.value) - computername = ctypes.c_buffer(computernamesize.value) + username = ctypes.create_unicode_buffer(usernamesize.value) + computername = ctypes.create_unicode_buffer(computernamesize.value) peUse = gdef.SID_NAME_USE() - winproxy.LookupAccountSidA(None, psid, username, usernamesize, computername, computernamesize, peUse) + winproxy.LookupAccountSidW(None, psid, username, usernamesize, computername, computernamesize, peUse) return computername[:computernamesize.value], username[:usernamesize.value] +# 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, +) + +SEMAPHORE_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 +) # ACE @@ -47,11 +203,16 @@ class AceHeader(gdef.ACE_HEADER): @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): @@ -71,28 +232,43 @@ class AceHeader(gdef.ACE_HEADER): 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 - # ACE_HEADER Header; - # ACCESS_MASK Mask; - # DWORD SidStart; + """`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): @@ -109,7 +285,12 @@ class MaskAndSidACE(AceBase): class CallbackACE(MaskAndSidACE): @property def application_data(self): - """FROM : https://msdn.microsoft.com/en-us/library/hh877860.aspx""" + """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 @@ -122,17 +303,29 @@ class ObjectRelatedACE(MaskAndSidACE): @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: @@ -220,7 +413,6 @@ class SystemResourceAttributeACE(MaskAndSidACE, gdef.SYSTEM_RESOURCE_ATTRIBUTE_A # 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): @@ -258,7 +450,6 @@ ACE_CLASS_BY_ACE_TYPE = {cls.ACE_TYPE: cls for cls in ( 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 @@ -322,6 +513,7 @@ class ClaimSecurityAttributeRelativeV1(gdef.CLAIM_SECURITY_ATTRIBUTE_RELATIVE_V1 # ACL class Acl(gdef.ACL): + """All the ``ACE`` returned by :class:`Acl` methods/property are described in the :ref:`Ace section `""" @property def size_info(self): size_info = gdef.ACL_SIZE_INFORMATION() @@ -329,6 +521,10 @@ class Acl(gdef.ACL): 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 @@ -336,9 +532,14 @@ class Acl(gdef.ACL): @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): @@ -350,6 +551,9 @@ class Acl(gdef.ACL): 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) @@ -359,11 +563,12 @@ class Acl(gdef.ACL): PAcl = ctypes.POINTER(Acl) # Security descriptor - class SecurityDescriptor(gdef.PSECURITY_DESCRIPTOR): - """TODO: free the underliying buffer when not needed anymore + """A Security Descriptor - for now the underliying memory is never free + .. 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 | @@ -374,14 +579,35 @@ class SecurityDescriptor(gdef.PSECURITY_DESCRIPTOR): gdef.SCOPE_SECURITY_INFORMATION | gdef.PROCESS_TRUST_LABEL_SECURITY_INFORMATION ) + """The default ``flags`` value for functions expecting a + `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`` + + .. 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 SACL sample + """ _close_function = winproxy.LocalFree - # def __init__(self, needs_free=True): - # self._needs_free = needs_free - @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) @@ -389,6 +615,10 @@ class SecurityDescriptor(gdef.PSECURITY_DESCRIPTOR): @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) @@ -396,21 +626,35 @@ class SecurityDescriptor(gdef.PSECURITY_DESCRIPTOR): @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 owner + # 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 group + # 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() @@ -421,6 +665,10 @@ class SecurityDescriptor(gdef.PSECURITY_DESCRIPTOR): @property def sacl(self): + """The SACL of the security descriptor. You may need special attention to retrieve it (see :any:`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() @@ -432,6 +680,16 @@ class SecurityDescriptor(gdef.PSECURITY_DESCRIPTOR): # 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 `_ + """ self = cls() winproxy.ConvertStringSecurityDescriptorToSecurityDescriptorA( sddl, @@ -443,16 +701,16 @@ class SecurityDescriptor(gdef.PSECURITY_DESCRIPTOR): return self @classmethod - def _from_name_and_type(cls, objname, objtype, query_sacl=False, security_infos=DEFAULT_SECURITY_INFORMATION): + def _from_name_and_type(cls, objname, objtype, flags=DEFAULT_SECURITY_INFORMATION, query_sacl=False): self = cls() if query_sacl: - security_infos |= gdef.SACL_SECURITY_INFORMATION + flags |= gdef.SACL_SECURITY_INFORMATION winproxy.GetNamedSecurityInfoA( objname, objtype, - security_infos, + flags, None, None, None, @@ -462,10 +720,48 @@ class SecurityDescriptor(gdef.PSECURITY_DESCRIPTOR): return self @classmethod - def from_filename(cls, filename, query_sacl=False): - return cls._from_name_and_type(filename, gdef.SE_FILE_OBJECT, query_sacl=query_sacl) + 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 + + @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) + + @classmethod + def from_handle(cls, handle, query_sacl=False, flags=DEFAULT_SECURITY_INFORMATION): + """Retrieve the security descriptor for the kernel object described by``handle``""" + return cls._from_handle_and_type(handle, gdef.SE_KERNEL_OBJECT, flags=flags, query_sacl=query_sacl) + + @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, @@ -477,6 +773,7 @@ class SecurityDescriptor(gdef.PSECURITY_DESCRIPTOR): winproxy.LocalFree(result_cstr) return result + # TST # def relative(self):