Skip to content

Commit

Permalink
Manage NTSTATUS errors in SecurityDescriptor.evolve (PR #26, issue #25)
Browse files Browse the repository at this point in the history
  • Loading branch information
vxgmichel authored Aug 7, 2020
2 parents 01ea855 + 863b0ef commit a6690b7
Show file tree
Hide file tree
Showing 12 changed files with 194 additions and 73 deletions.
5 changes: 2 additions & 3 deletions src/winfspy/__init__.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@
from ._version import __version__
from .plumbing.bindings import enable_debug_log
from .plumbing.winstuff import FILE_ATTRIBUTE, CREATE_FILE_CREATE_OPTIONS
from .plumbing import enable_debug_log, FILE_ATTRIBUTE, CREATE_FILE_CREATE_OPTIONS
from .file_system import FileSystem
from .operations import BaseFileSystemOperations, BaseFileContext
from .exceptions import (
from .plumbing.exceptions import (
WinFSPyError,
FileSystemAlreadyStarted,
FileSystemNotStarted,
Expand Down
6 changes: 2 additions & 4 deletions src/winfspy/file_system.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
from .plumbing.bindings import ffi, lib
from .plumbing.file_system_interface import file_system_interface_trampoline_factory
from .plumbing.winstuff import cook_ntstatus, nt_success
from .exceptions import WinFSPyError, FileSystemAlreadyStarted, FileSystemNotStarted
from .plumbing import ffi, lib, cook_ntstatus, nt_success, file_system_interface_trampoline_factory
from .plumbing import WinFSPyError, FileSystemAlreadyStarted, FileSystemNotStarted
from .operations import BaseFileSystemOperations


Expand Down
3 changes: 2 additions & 1 deletion src/winfspy/memfs.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,8 @@
NTStatusEndOfFile,
NTStatusMediaWriteProtected,
)
from winfspy.plumbing.winstuff import filetime_now, SecurityDescriptor
from winfspy.plumbing.win32_filetime import filetime_now
from winfspy.plumbing.security_descriptor import SecurityDescriptor


def operation(fn):
Expand Down
5 changes: 2 additions & 3 deletions src/winfspy/operations.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,8 @@
from typing import List
from functools import wraps

from .plumbing.winstuff import NTSTATUS, SecurityDescriptor
from .plumbing.bindings import lib, ffi
from .exceptions import NTStatusError
from .plumbing import NTSTATUS, SecurityDescriptor, lib, ffi
from .plumbing import NTStatusError


logger = logging.getLogger("winfspy")
Expand Down
61 changes: 61 additions & 0 deletions src/winfspy/plumbing/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
from .bindings import ffi, lib, enable_debug_log
from .status import NTSTATUS, cook_ntstatus, posix_to_ntstatus, ntstatus_to_posix, nt_success
from .file_attribute import FILE_ATTRIBUTE, CREATE_FILE_CREATE_OPTIONS
from .win32_filetime import dt_to_filetime, filetime_to_dt, filetime_now
from .file_system_interface import file_system_interface_trampoline_factory
from .security_descriptor import SecurityDescriptor
from .get_winfsp_dir import get_winfsp_dir, get_winfsp_bin_dir, get_winfsp_library_name
from .exceptions import (
WinFSPyError,
FileSystemAlreadyStarted,
FileSystemNotStarted,
NTStatusError,
NTStatusObjectNameNotFound,
NTStatusObjectNameCollision,
NTStatusAccessDenied,
NTStatusNotADirectory,
NTStatusEndOfFile,
NTStatusDirectoryNotEmpty,
NTStatusMediaWriteProtected,
)


__all__ = (
# Bindings
"ffi",
"lib",
"enable_debug_log",
# Status
"NTSTATUS",
"nt_success",
"cook_ntstatus",
"posix_to_ntstatus",
"ntstatus_to_posix",
# File attribute
"FILE_ATTRIBUTE",
"CREATE_FILE_CREATE_OPTIONS",
# Filetime
"dt_to_filetime",
"filetime_to_dt",
"filetime_now",
# File system interface
"file_system_interface_trampoline_factory",
# Security descriptor
"SecurityDescriptor",
# Get winfsp directory
"get_winfsp_dir",
"get_winfsp_bin_dir",
"get_winfsp_library_name",
# Exception
"WinFSPyError",
"FileSystemAlreadyStarted",
"FileSystemNotStarted",
"NTStatusError",
"NTStatusObjectNameNotFound",
"NTStatusObjectNameCollision",
"NTStatusAccessDenied",
"NTStatusNotADirectory",
"NTStatusEndOfFile",
"NTStatusDirectoryNotEmpty",
"NTStatusMediaWriteProtected",
)
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
from .plumbing.winstuff import NTSTATUS
from .status import NTSTATUS


class WinFSPyError(Exception):
Expand Down
62 changes: 62 additions & 0 deletions src/winfspy/plumbing/file_attribute.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
import enum

from .bindings import lib

__all__ = (
"FILE_ATTRIBUTE",
"CREATE_FILE_CREATE_OPTIONS",
)


class FILE_ATTRIBUTE(enum.IntEnum):
"""
NT File attributes
see https://docs.microsoft.com/en-us/dotnet/api/system.io.fileattributes
"""

FILE_ATTRIBUTE_ARCHIVE = 0x20
FILE_ATTRIBUTE_COMPRESSED = 0x800
FILE_ATTRIBUTE_DEVICE = 0x40
FILE_ATTRIBUTE_DIRECTORY = 0x10
FILE_ATTRIBUTE_ENCRYPTED = 0x4000
FILE_ATTRIBUTE_HIDDEN = 0x2
FILE_ATTRIBUTE_INTEGRITY_STREAM = 0x8000
FILE_ATTRIBUTE_NORMAL = 0x80
FILE_ATTRIBUTE_NOT_CONTENT_INDEXED = 0x2000
FILE_ATTRIBUTE_NO_SCRUB_DATA = 0x20000
FILE_ATTRIBUTE_OFFLINE = 0x1000
FILE_ATTRIBUTE_READONLY = 0x1
FILE_ATTRIBUTE_RECALL_ON_DATA_ACCESS = 0x400000
FILE_ATTRIBUTE_RECALL_ON_OPEN = 0x40000
FILE_ATTRIBUTE_REPARSE_POINT = 0x400
FILE_ATTRIBUTE_SPARSE_FILE = 0x200
FILE_ATTRIBUTE_SYSTEM = 0x4
FILE_ATTRIBUTE_TEMPORARY = 0x100
FILE_ATTRIBUTE_VIRTUAL = 0x10000

INVALID_FILE_ATTRIBUTES = lib.WFSPY_FILE_ATTRIBUTE_INVALID_FILE_ATTRIBUTES


class CREATE_FILE_CREATE_OPTIONS(enum.IntEnum):
"""
`NtCreateFile`'s CreateOptions flags
see https://docs.microsoft.com/en-us/windows/desktop/api/winternl/nf-winternl-ntcreatefile
"""

FILE_DIRECTORY_FILE = lib.WFSPY_FILE_DIRECTORY_FILE
FILE_NON_DIRECTORY_FILE = lib.WFSPY_FILE_NON_DIRECTORY_FILE
FILE_WRITE_THROUGH = lib.WFSPY_FILE_WRITE_THROUGH
FILE_SEQUENTIAL_ONLY = lib.WFSPY_FILE_SEQUENTIAL_ONLY
FILE_RANDOM_ACCESS = lib.WFSPY_FILE_RANDOM_ACCESS
FILE_NO_INTERMEDIATE_BUFFERING = lib.WFSPY_FILE_NO_INTERMEDIATE_BUFFERING
FILE_SYNCHRONOUS_IO_ALERT = lib.WFSPY_FILE_SYNCHRONOUS_IO_ALERT
FILE_SYNCHRONOUS_IO_NONALERT = lib.WFSPY_FILE_SYNCHRONOUS_IO_NONALERT
FILE_CREATE_TREE_CONNECTION = lib.WFSPY_FILE_CREATE_TREE_CONNECTION
FILE_NO_EA_KNOWLEDGE = lib.WFSPY_FILE_NO_EA_KNOWLEDGE
FILE_OPEN_REPARSE_POINT = lib.WFSPY_FILE_OPEN_REPARSE_POINT
FILE_DELETE_ON_CLOSE = lib.WFSPY_FILE_DELETE_ON_CLOSE
FILE_OPEN_BY_FILE_ID = lib.WFSPY_FILE_OPEN_BY_FILE_ID
FILE_OPEN_FOR_BACKUP_INTENT = lib.WFSPY_FILE_OPEN_FOR_BACKUP_INTENT
FILE_RESERVE_OPFILTER = lib.WFSPY_FILE_RESERVE_OPFILTER
FILE_OPEN_REQUIRING_OPLOCK = lib.WFSPY_FILE_OPEN_REQUIRING_OPLOCK
FILE_COMPLETE_IF_OPLOCKED = lib.WFSPY_FILE_COMPLETE_IF_OPLOCKED
58 changes: 58 additions & 0 deletions src/winfspy/plumbing/security_descriptor.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
from typing import NamedTuple, Any

from .status import NTSTATUS, cook_ntstatus
from .exceptions import NTStatusError

# Security descriptor conversion
# see https://docs.microsoft.com/en-us/windows/desktop/api/sddl/nf-sddl-convertstringsecuritydescriptortosecuritydescriptorw
from .bindings import lib, ffi


__all__ = ["SecurityDescriptor"]


class SecurityDescriptor(NamedTuple):

handle: Any
size: int

@classmethod
def from_cpointer(cls, handle):
if handle == ffi.NULL:
return cls(ffi.NULL, 0)
size = lib.GetSecurityDescriptorLength(handle)
pointer = lib.malloc(size)
new_handle = ffi.cast("SECURITY_DESCRIPTOR*", pointer)
ffi.memmove(new_handle, handle, size)
return cls(new_handle, size)

@classmethod
def from_string(cls, string_format):
# see https://docs.microsoft.com/fr-fr/windows/desktop/SecAuthZ/security-descriptor-string-format
psd = ffi.new("SECURITY_DESCRIPTOR**")
psd_size = ffi.new("ULONG*")
if not lib.ConvertStringSecurityDescriptorToSecurityDescriptorW(
string_format, lib.WFSPY_STRING_SECURITY_DESCRIPTOR_REVISION, psd, psd_size
):
raise RuntimeError(
f"Cannot create security descriptor `{string_format}`: "
f"{cook_ntstatus(lib.GetLastError())}"
)
return cls(psd[0], psd_size[0])

def evolve(self, security_information, modification_descriptor):
psd = ffi.new("SECURITY_DESCRIPTOR**")
status = lib.FspSetSecurityDescriptor(
self.handle, security_information, modification_descriptor, psd
)
if status != NTSTATUS.STATUS_SUCCESS:
raise NTStatusError(status)
handle = psd[0]
size = lib.GetSecurityDescriptorLength(handle)
return type(self)(handle, size)

def is_valid(self):
return bool(lib.IsValidSecurityDescriptor(self.handle))

def __del__(self):
lib.LocalFree(self.handle)
2 changes: 1 addition & 1 deletion src/winfspy/plumbing/service.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
import threading

from .bindings import ffi, lib
from .winstuff import NTSTATUS
from .status import NTSTATUS


# TODO: finish this...
Expand Down
Original file line number Diff line number Diff line change
@@ -1,20 +1,8 @@
import enum
from typing import NamedTuple, Any

from .win32_filetime import dt_to_filetime, filetime_to_dt, filetime_now

# Security descriptor conversion
# see https://docs.microsoft.com/en-us/windows/desktop/api/sddl/nf-sddl-convertstringsecuritydescriptortosecuritydescriptorw
from .bindings import ffi, lib

from .bindings import lib

__all__ = (
"dt_to_filetime",
"filetime_to_dt",
"filetime_now",
"SecurityDescriptor",
"FILE_ATTRIBUTE",
"CREATE_FILE_CREATE_OPTIONS",
"NTSTATUS",
"nt_success",
"nt_information",
Expand All @@ -26,51 +14,6 @@
)


class SecurityDescriptor(NamedTuple):

handle: Any
size: int

@classmethod
def from_cpointer(cls, handle):
if handle == ffi.NULL:
return cls(ffi.NULL, 0)
size = lib.GetSecurityDescriptorLength(handle)
pointer = lib.malloc(size)
new_handle = ffi.cast("SECURITY_DESCRIPTOR*", pointer)
ffi.memmove(new_handle, handle, size)
return cls(new_handle, size)

@classmethod
def from_string(cls, string_format):
# see https://docs.microsoft.com/fr-fr/windows/desktop/SecAuthZ/security-descriptor-string-format
psd = ffi.new("SECURITY_DESCRIPTOR**")
psd_size = ffi.new("ULONG*")
if not lib.ConvertStringSecurityDescriptorToSecurityDescriptorW(
string_format, lib.WFSPY_STRING_SECURITY_DESCRIPTOR_REVISION, psd, psd_size
):
raise RuntimeError(
f"Cannot create security descriptor `{string_format}`: "
f"{cook_ntstatus(lib.GetLastError())}"
)
return cls(psd[0], psd_size[0])

def evolve(self, security_information, modification_descriptor):
psd = ffi.new("SECURITY_DESCRIPTOR**")
lib.FspSetSecurityDescriptor(
self.handle, security_information, modification_descriptor, psd
)
handle = psd[0]
size = lib.GetSecurityDescriptorLength(handle)
return type(self)(handle, size)

def is_valid(self):
return bool(lib.IsValidSecurityDescriptor(self.handle))

def __del__(self):
lib.LocalFree(self.handle)


class FILE_ATTRIBUTE(enum.IntEnum):
"""
NT File attributes
Expand Down
2 changes: 1 addition & 1 deletion src/winfspy/tests/test_bindings.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
import sys
import subprocess

from winfspy.plumbing.get_winfsp_dir import get_winfsp_dir, get_winfsp_library_name
from winfspy.plumbing import get_winfsp_dir, get_winfsp_library_name


def run_import_winfspy(env):
Expand Down
2 changes: 1 addition & 1 deletion src/winfspy/tests/winfsp_tests/test_winfsp_tests.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
import pytest
import pathlib

from winfspy.plumbing.get_winfsp_dir import get_winfsp_bin_dir
from winfspy.plumbing import get_winfsp_bin_dir
from . import WINFSP_TESTS_EXECUTABLE

MEMFS_XFAIL_LIST = [
Expand Down

0 comments on commit a6690b7

Please sign in to comment.