////////////////////////////////////////////////////////////////////
// Copyright (C) Alexander Telyatnikov, Ivan Keliukh, Yegor Anchishkin, SKIF Software, 1999-2013. Kiev, Ukraine
// All rights reserved
// This file was released under the GPLv2 on June 2015.
////////////////////////////////////////////////////////////////////
/*************************************************************************
*
* File: SecurSup.cpp
*
* Module: UDF File System Driver (Kernel mode execution only)
*
* Description:
*   Contains code to handle the "Get/Set Security" dispatch entry points.
*
*************************************************************************/

#include            "udffs.h"

// define the file specific bug-check id
#define         UDF_BUG_CHECK_ID                UDF_FILE_SECURITY

#ifdef UDF_ENABLE_SECURITY

NTSTATUS UDFConvertToSelfRelative(
    IN OUT PSECURITY_DESCRIPTOR* SecurityDesc);

/*UCHAR FullControlSD[] = {
0x01, 0x00, 0x04, 0x80, 0x4c, 0x00, 0x00, 0x00,
0x58, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x14, 0x00, 0x00, 0x00, 0x02, 0x00, 0x38, 0x00,
0x02, 0x00, 0x00, 0x00, 0x00, 0x09, 0x18, 0x00,
0x00, 0x00, 0x00, 0x10, 0x01, 0x01, 0x00, 0x00,
0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x18, 0x00,
0xff, 0x01, 0x1f, 0x00, 0x01, 0x01, 0x00, 0x00,
0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00,
0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00,
0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
0x00, 0x00, 0x00, 0x00
};*/

/*************************************************************************
*
* Function: UDFGetSecurity()
*
* Description:
*
* Expected Interrupt Level (for execution) :
*
*  IRQL_PASSIVE_LEVEL
*
* Return Value: Irrelevant.
*
*************************************************************************/
NTSTATUS
UDFGetSecurity(
    IN PDEVICE_OBJECT DeviceObject,       // the logical volume device object
    IN PIRP           Irp)                // I/O Request Packet
{
    NTSTATUS            RC = STATUS_SUCCESS;
    PtrUDFIrpContext    PtrIrpContext = NULL;
    BOOLEAN             AreWeTopLevel = FALSE;

    UDFPrint(("UDFGetSecurity\n"));
//    BrutePoint();

    FsRtlEnterFileSystem();
    ASSERT(DeviceObject);
    ASSERT(Irp);

    // set the top level context
    AreWeTopLevel = UDFIsIrpTopLevel(Irp);
    ASSERT(!UDFIsFSDevObj(DeviceObject));
    //  Call the common Lock Control routine, with blocking allowed if
    //  synchronous
    _SEH2_TRY {

        // get an IRP context structure and issue the request
        PtrIrpContext = UDFAllocateIrpContext(Irp, DeviceObject);
        if(PtrIrpContext) {
            RC = UDFCommonGetSecurity(PtrIrpContext, Irp);
        } else {
            RC = STATUS_INSUFFICIENT_RESOURCES;
            Irp->IoStatus.Status = RC;
            Irp->IoStatus.Information = 0;
            // complete the IRP
            IoCompleteRequest(Irp, IO_DISK_INCREMENT);
        }

    } __except (UDFExceptionFilter(PtrIrpContext, GetExceptionInformation())) {

        RC = UDFExceptionHandler(PtrIrpContext, Irp);

        UDFLogEvent(UDF_ERROR_INTERNAL_ERROR, RC);
    }

    if (AreWeTopLevel) {
        IoSetTopLevelIrp(NULL);
    }

    FsRtlExitFileSystem();

    return(RC);
} // end UDFGetSecurity()


/*************************************************************************
*
* Function: UDFCommonGetSecurity()
*
* Description:
*  This is the common routine for getting Security (ACL) called
*  by both the fsd and fsp threads
*
* Expected Interrupt Level (for execution) :
*
*  IRQL_PASSIVE_LEVEL
*
* Return Value: Irrelevant
*
*************************************************************************/
NTSTATUS
UDFCommonGetSecurity(
    IN PtrUDFIrpContext PtrIrpContext,
    IN PIRP             Irp)
{
    NTSTATUS            RC = STATUS_SUCCESS;
    PIO_STACK_LOCATION  IrpSp = NULL;
    BOOLEAN             PostRequest = FALSE;
    BOOLEAN             CanWait = FALSE;
    PtrUDFNTRequiredFCB NtReqFcb = NULL;
    BOOLEAN             AcquiredFCB = FALSE;
    PFILE_OBJECT        FileObject = NULL;
    PtrUDFFCB           Fcb = NULL;
    PtrUDFCCB           Ccb = NULL;
    PVOID               PtrSystemBuffer = NULL;
    ULONG               BufferLength = 0;

    UDFPrint(("UDFCommonGetSecurity\n"));

    _SEH2_TRY {

        // First, get a pointer to the current I/O stack location.
        IrpSp = IoGetCurrentIrpStackLocation(Irp);
        ASSERT(IrpSp);

        FileObject = IrpSp->FileObject;
        ASSERT(FileObject);

        // Get the FCB and CCB pointers.
        Ccb = (PtrUDFCCB)(FileObject->FsContext2);
        ASSERT(Ccb);
        Fcb = Ccb->Fcb;
        ASSERT(Fcb);

/*        if(!Fcb->Vcb->ReadSecurity)
            try_return(RC = STATUS_NOT_IMPLEMENTED);*/

        NtReqFcb = Fcb->NTRequiredFCB;
        CanWait = ((PtrIrpContext->IrpContextFlags & UDF_IRP_CONTEXT_CAN_BLOCK) ? TRUE : FALSE);

        // Acquire the FCB resource shared
        UDF_CHECK_PAGING_IO_RESOURCE(NtReqFcb);
        if (!UDFAcquireResourceExclusive(&(NtReqFcb->MainResource), CanWait)) {
//        if (!UDFAcquireResourceShared(&(NtReqFcb->MainResource), CanWait)) {
            PostRequest = TRUE;
            try_return(RC = STATUS_PENDING);
        }
        AcquiredFCB = TRUE;

        PtrSystemBuffer = UDFGetCallersBuffer(PtrIrpContext, Irp);
        if(!PtrSystemBuffer)
            try_return(RC = STATUS_INVALID_USER_BUFFER);
        BufferLength = IrpSp->Parameters.QuerySecurity.Length;

        if(!NtReqFcb->SecurityDesc) {
            RC = UDFAssignAcl(Fcb->Vcb, FileObject, Fcb, NtReqFcb);
            if(!NT_SUCCESS(RC))
                try_return(RC);
        }

        _SEH2_TRY {
            RC = SeQuerySecurityDescriptorInfo(&(IrpSp->Parameters.QuerySecurity.SecurityInformation),
                                          (PSECURITY_DESCRIPTOR)PtrSystemBuffer,
                                          &BufferLength,
                                          &(NtReqFcb->SecurityDesc) );
        } _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) {
            RC = STATUS_BUFFER_TOO_SMALL;
        }

try_exit: NOTHING;

    } _SEH2_FINALLY {

        // Release the FCB resources if acquired.
        if (AcquiredFCB) {
            UDF_CHECK_PAGING_IO_RESOURCE(NtReqFcb);
            UDFReleaseResource(&(NtReqFcb->MainResource));
            AcquiredFCB = FALSE;
        }

        if (PostRequest) {
            // Perform appropriate post related processing here
            RC = UDFPostRequest(PtrIrpContext, Irp);
        } else
        if(!AbnormalTermination()) {
            Irp->IoStatus.Status = RC;
            Irp->IoStatus.Information = BufferLength;
            // Free up the Irp Context
            UDFReleaseIrpContext(PtrIrpContext);
            // complete the IRP
            IoCompleteRequest(Irp, IO_DISK_INCREMENT);
        }

    } // end of "__finally" processing

    return(RC);
}

#ifndef UDF_READ_ONLY_BUILD
/*************************************************************************
*
* Function: UDFSetSecurity()
*
* Description:
*
* Expected Interrupt Level (for execution) :
*
*  IRQL_PASSIVE_LEVEL
*
* Return Value: Irrelevant.
*
*************************************************************************/
NTSTATUS
UDFSetSecurity(
    IN PDEVICE_OBJECT DeviceObject,       // the logical volume device object
    IN PIRP           Irp)                // I/O Request Packet
{
    NTSTATUS            RC = STATUS_SUCCESS;
    PtrUDFIrpContext    PtrIrpContext = NULL;
    BOOLEAN             AreWeTopLevel = FALSE;

    UDFPrint(("UDFSetSecurity\n"));
//    BrutePoint();

    FsRtlEnterFileSystem();
    ASSERT(DeviceObject);
    ASSERT(Irp);

    // set the top level context
    AreWeTopLevel = UDFIsIrpTopLevel(Irp);
    //  Call the common Lock Control routine, with blocking allowed if
    //  synchronous
    _SEH2_TRY {

        // get an IRP context structure and issue the request
        PtrIrpContext = UDFAllocateIrpContext(Irp, DeviceObject);
        if(PtrIrpContext) {
            RC = UDFCommonSetSecurity(PtrIrpContext, Irp);
        } else {
            RC = STATUS_INSUFFICIENT_RESOURCES;
            Irp->IoStatus.Status = RC;
            Irp->IoStatus.Information = 0;
            // complete the IRP
            IoCompleteRequest(Irp, IO_DISK_INCREMENT);
        }

    } __except (UDFExceptionFilter(PtrIrpContext, GetExceptionInformation())) {

        RC = UDFExceptionHandler(PtrIrpContext, Irp);

        UDFLogEvent(UDF_ERROR_INTERNAL_ERROR, RC);
    }

    if (AreWeTopLevel) {
        IoSetTopLevelIrp(NULL);
    }

    FsRtlExitFileSystem();

    return(RC);
} // end UDFSetSecurity()


/*************************************************************************
*
* Function: UDFCommonSetSecurity()
*
* Description:
*  This is the common routine for getting Security (ACL) called
*  by both the fsd and fsp threads
*
* Expected Interrupt Level (for execution) :
*
*  IRQL_PASSIVE_LEVEL
*
* Return Value: Irrelevant
*
*************************************************************************/
NTSTATUS
UDFCommonSetSecurity(
    IN PtrUDFIrpContext PtrIrpContext,
    IN PIRP             Irp)
{
    NTSTATUS            RC = STATUS_SUCCESS;
    PIO_STACK_LOCATION  IrpSp = NULL;
    BOOLEAN             PostRequest = FALSE;
    BOOLEAN             CanWait = FALSE;
    PtrUDFNTRequiredFCB NtReqFcb = NULL;
    BOOLEAN             AcquiredFCB = FALSE;
    PFILE_OBJECT        FileObject = NULL;
    PtrUDFFCB           Fcb = NULL;
    PtrUDFCCB           Ccb = NULL;
    ACCESS_MASK         DesiredAccess = 0;

    UDFPrint(("UDFCommonSetSecurity\n"));

    _SEH2_TRY {

        // First, get a pointer to the current I/O stack location.
        IrpSp = IoGetCurrentIrpStackLocation(Irp);
        ASSERT(IrpSp);

        FileObject = IrpSp->FileObject;
        ASSERT(FileObject);

        // Get the FCB and CCB pointers.
        Ccb = (PtrUDFCCB)(FileObject->FsContext2);
        ASSERT(Ccb);
        Fcb = Ccb->Fcb;
        ASSERT(Fcb);

        if(!Fcb->Vcb->WriteSecurity)
            try_return(RC = STATUS_NOT_IMPLEMENTED);

        NtReqFcb = Fcb->NTRequiredFCB;
        CanWait = ((PtrIrpContext->IrpContextFlags & UDF_IRP_CONTEXT_CAN_BLOCK) ? TRUE : FALSE);

        // Acquire the FCB resource exclusive
        UDF_CHECK_PAGING_IO_RESOURCE(NtReqFcb);
        if (!UDFAcquireResourceExclusive(&(NtReqFcb->MainResource), CanWait)) {
            PostRequest = TRUE;
            try_return(RC = STATUS_PENDING);
        }
        AcquiredFCB = TRUE;

//OWNER_SECURITY_INFORMATION
        if(IrpSp->Parameters.SetSecurity.SecurityInformation & OWNER_SECURITY_INFORMATION)
            DesiredAccess |= WRITE_OWNER;
//GROUP_SECURITY_INFORMATION
        if(IrpSp->Parameters.SetSecurity.SecurityInformation & GROUP_SECURITY_INFORMATION)
            DesiredAccess |= WRITE_OWNER;
//DACL_SECURITY_INFORMATION
        if(IrpSp->Parameters.SetSecurity.SecurityInformation & DACL_SECURITY_INFORMATION)
            DesiredAccess |= WRITE_DAC;
//SACL_SECURITY_INFORMATION
        if(IrpSp->Parameters.SetSecurity.SecurityInformation & SACL_SECURITY_INFORMATION)
            DesiredAccess |= ACCESS_SYSTEM_SECURITY;

        _SEH2_TRY {
            UDFConvertToSelfRelative(&(NtReqFcb->SecurityDesc));

            KdDump(NtReqFcb->SecurityDesc, RtlLengthSecurityDescriptor(NtReqFcb->SecurityDesc));
            UDFPrint(("\n"));

            RC = SeSetSecurityDescriptorInfo(/*FileObject*/ NULL,
                                          &(IrpSp->Parameters.SetSecurity.SecurityInformation),
                                          IrpSp->Parameters.SetSecurity.SecurityDescriptor,
                                          &(NtReqFcb->SecurityDesc),
                                          NonPagedPool,
                                          IoGetFileObjectGenericMapping() );

            KdDump(NtReqFcb->SecurityDesc, RtlLengthSecurityDescriptor(NtReqFcb->SecurityDesc));

        } _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) {
            RC = STATUS_INVALID_PARAMETER;
        }
        if(NT_SUCCESS(RC)) {
            NtReqFcb->NtReqFCBFlags |= UDF_NTREQ_FCB_SD_MODIFIED;

            UDFNotifyFullReportChange( Fcb->Vcb, Fcb->FileInfo,
                                       FILE_NOTIFY_CHANGE_SECURITY,
                                       FILE_ACTION_MODIFIED);
        }

try_exit: NOTHING;

    } _SEH2_FINALLY {

        // Release the FCB resources if acquired.
        if (AcquiredFCB) {
            UDF_CHECK_PAGING_IO_RESOURCE(NtReqFcb);
            UDFReleaseResource(&(NtReqFcb->MainResource));
            AcquiredFCB = FALSE;
        }

        if (PostRequest) {
            // Perform appropriate post related processing here
            RC = UDFPostRequest(PtrIrpContext, Irp);
        } else
        if(!AbnormalTermination()) {
            Irp->IoStatus.Status = RC;
            Irp->IoStatus.Information = 0;
            // Free up the Irp Context
            UDFReleaseIrpContext(PtrIrpContext);
            // complete the IRP
            IoCompleteRequest(Irp, IO_DISK_INCREMENT);
        }

    } // end of "__finally" processing

    return(RC);
} // ens UDFCommonSetSecurity()

#endif //UDF_READ_ONLY_BUILD
#endif //UDF_ENABLE_SECURITY

NTSTATUS
UDFReadSecurity(
    IN PVCB Vcb,
    IN PtrUDFFCB Fcb,
    IN PSECURITY_DESCRIPTOR* SecurityDesc
    )
{
#ifdef UDF_ENABLE_SECURITY
    PUDF_FILE_INFO FileInfo = NULL;
    PUDF_FILE_INFO SDirInfo = NULL;
    PUDF_FILE_INFO AclInfo = NULL;
    NTSTATUS RC;
    ULONG NumberBytesRead;
    PERESOURCE Res1 = NULL;

    UDFPrint(("UDFReadSecurity\n"));

    _SEH2_TRY {

        FileInfo = Fcb->FileInfo;
        ASSERT(FileInfo);
        if(!FileInfo) {
            UDFPrint(("  Volume Security\n"));
            try_return(RC = STATUS_NO_SECURITY_ON_OBJECT);
        }
        if(Vcb->VCBFlags & UDF_VCB_FLAGS_RAW_DISK) {
            UDFPrint(("  No Security on blank volume\n"));
            try_return(RC = STATUS_NO_SECURITY_ON_OBJECT);
        }

        // Open Stream Directory
        RC = UDFOpenStreamDir__(Vcb, FileInfo, &SDirInfo);

        if(RC == STATUS_NOT_FOUND)
            try_return(RC = STATUS_NO_SECURITY_ON_OBJECT);
        if(!NT_SUCCESS(RC)) {
            if(UDFCleanUpFile__(Vcb, SDirInfo)) {
                if(SDirInfo) MyFreePool__(SDirInfo);
            }
            SDirInfo = NULL;
            try_return(RC);
        }
        // Acquire SDir exclusively if Fcb present
        if(SDirInfo->Fcb) {
            BrutePoint();
            UDF_CHECK_PAGING_IO_RESOURCE(SDirInfo->Fcb->NTRequiredFCB);
            UDFAcquireResourceExclusive(Res1 = &(SDirInfo->Fcb->NTRequiredFCB->MainResource),TRUE);
        }

        // Open Acl Stream
        RC = UDFOpenFile__(Vcb,
                           FALSE,TRUE,&(UDFGlobalData.AclName),
                           SDirInfo,&AclInfo,NULL);
        if(RC == STATUS_OBJECT_NAME_NOT_FOUND)
            try_return(RC = STATUS_NO_SECURITY_ON_OBJECT);
        if(!NT_SUCCESS(RC)) {
            if(UDFCleanUpFile__(Vcb, AclInfo)) {
                if(AclInfo) MyFreePool__(AclInfo);
            }
            AclInfo = NULL;
            try_return(RC);
        }

        NumberBytesRead = (ULONG)UDFGetFileSize(AclInfo);
        (*SecurityDesc) = DbgAllocatePool(NonPagedPool, NumberBytesRead);
        if(!(*SecurityDesc))
            try_return(RC = STATUS_INSUFFICIENT_RESOURCES);
        RC = UDFReadFile__(Vcb, AclInfo, 0, NumberBytesRead,
                       FALSE, (PCHAR)(*SecurityDesc), &NumberBytesRead);
        if(!NT_SUCCESS(RC))
            try_return(RC);

        RC = RtlValidSecurityDescriptor(*SecurityDesc);

try_exit: NOTHING;

    } _SEH2_FINALLY {

        if(AclInfo) {
            UDFCloseFile__(Vcb, AclInfo);
            if(UDFCleanUpFile__(Vcb, AclInfo))
                MyFreePool__(AclInfo);
        }

        if(SDirInfo) {
            UDFCloseFile__(Vcb, SDirInfo);
            if(UDFCleanUpFile__(Vcb, SDirInfo))
                MyFreePool__(SDirInfo);
        }

        if(!NT_SUCCESS(RC) && (*SecurityDesc)) {
            DbgFreePool(*SecurityDesc);
            (*SecurityDesc) = NULL;
        }
        if(Res1)
            UDFReleaseResource(Res1);
    }

    return RC;
#else
    return STATUS_NO_SECURITY_ON_OBJECT;
#endif //UDF_ENABLE_SECURITY

} // end UDFReadSecurity()

#ifdef UDF_ENABLE_SECURITY
NTSTATUS
UDFConvertToSelfRelative(
    IN OUT PSECURITY_DESCRIPTOR* SecurityDesc
    )
{
    NTSTATUS RC;
    SECURITY_INFORMATION SecurityInformation;
    PSECURITY_DESCRIPTOR NewSD;
    ULONG Len;

    UDFPrint(("  UDFConvertToSelfRelative\n"));

    if(!(*SecurityDesc))
        return STATUS_NO_SECURITY_ON_OBJECT;

    SecurityInformation = FULL_SECURITY_INFORMATION;
    Len = RtlLengthSecurityDescriptor(*SecurityDesc);
    ASSERT(Len <= 1024);
    NewSD = (PSECURITY_DESCRIPTOR)DbgAllocatePool(NonPagedPool, Len);
    if(!NewSD)
        return STATUS_INSUFFICIENT_RESOURCES;
    _SEH2_TRY {
        RC = SeQuerySecurityDescriptorInfo(&SecurityInformation, NewSD, &Len, SecurityDesc);
    } _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) {
        RC = STATUS_INSUFFICIENT_RESOURCES;
    }

    if(NT_SUCCESS(RC)) {
        DbgFreePool(*SecurityDesc);
        *SecurityDesc = NewSD;
    } else {
        DbgFreePool(NewSD);
    }
    return RC;
} // end UDFConvertToSelfRelative()

NTSTATUS
UDFInheritAcl(
    IN PVCB Vcb,
    IN PSECURITY_DESCRIPTOR* ParentSecurityDesc,
    IN OUT PSECURITY_DESCRIPTOR* SecurityDesc
    )
{
    NTSTATUS RC;
    SECURITY_INFORMATION SecurityInformation;
    ULONG Len;

    UDFPrint(("  UDFInheritAcl\n"));

    if(!(*ParentSecurityDesc)) {
        *SecurityDesc = NULL;
        return STATUS_SUCCESS;
    }

    SecurityInformation = FULL_SECURITY_INFORMATION;
    Len = RtlLengthSecurityDescriptor(*ParentSecurityDesc);
    *SecurityDesc = (PSECURITY_DESCRIPTOR)DbgAllocatePool(NonPagedPool, Len);
    if(!(*SecurityDesc))
        return STATUS_INSUFFICIENT_RESOURCES;
    _SEH2_TRY {
        RC = SeQuerySecurityDescriptorInfo(&SecurityInformation, *SecurityDesc, &Len, ParentSecurityDesc);
    } _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) {
        RC = STATUS_INSUFFICIENT_RESOURCES;
    }

    if(!NT_SUCCESS(RC)) {
        DbgFreePool(*SecurityDesc);
        *SecurityDesc = NULL;
    }
    return RC;
} // end UDFInheritAcl()

NTSTATUS
UDFBuildEmptyAcl(
    IN PVCB Vcb,
    IN PSECURITY_DESCRIPTOR* SecurityDesc
    )
{
    NTSTATUS RC;
    ULONG Len = 2 * (sizeof(ACL) + sizeof(ACCESS_ALLOWED_ACE) + sizeof(ULONG)*4 /*RtlLengthSid(SeExports->SeWorldSid)*/);

    UDFPrint(("  UDFBuildEmptyAcl\n"));
    // Create Security Descriptor
    (*SecurityDesc) = (PSECURITY_DESCRIPTOR)DbgAllocatePool(NonPagedPool,
           sizeof(SECURITY_DESCRIPTOR) + Len);
    if(!(*SecurityDesc))
        return STATUS_INSUFFICIENT_RESOURCES;

    RC = RtlCreateSecurityDescriptor(*SecurityDesc, SECURITY_DESCRIPTOR_REVISION);

    if(!NT_SUCCESS(RC)) {
        DbgFreePool(*SecurityDesc);
        *((PULONG)SecurityDesc) = NULL;
    }
    return RC;
} // end UDFBuildEmptyAcl()

NTSTATUS
UDFBuildFullControlAcl(
    IN PVCB Vcb,
    IN PSECURITY_DESCRIPTOR* SecurityDesc
    )
{
    NTSTATUS RC;
    PACL Acl;
    ULONG Len = sizeof(ACL) + 2*(sizeof(ACCESS_ALLOWED_ACE) + sizeof(ULONG)*4 /*- sizeof(ULONG)*/ /*+ RtlLengthSid(SeExports->SeWorldSid)*/);

    UDFPrint(("  UDFBuildFullControlAcl\n"));
    // Create Security Descriptor
    RC = UDFBuildEmptyAcl(Vcb, SecurityDesc);
    if(!NT_SUCCESS(RC))
        return RC;

    // Set Owner
    RC = RtlSetOwnerSecurityDescriptor(*SecurityDesc, SeExports->SeWorldSid, FALSE);
    if(!NT_SUCCESS(RC)) {
        DbgFreePool(*SecurityDesc);
        *((PULONG)SecurityDesc) = NULL;
        return RC;
    }

    // Set Group
    RC = RtlSetGroupSecurityDescriptor(*SecurityDesc, SeExports->SeWorldSid, FALSE);
    if(!NT_SUCCESS(RC)) {
        DbgFreePool(*SecurityDesc);
        *((PULONG)SecurityDesc) = NULL;
        return RC;
    }

    // Create empty Acl
    Acl = (PACL)DbgAllocatePool(NonPagedPool, Len);
    if(!Acl) {
        DbgFreePool(*SecurityDesc);
        *((PULONG)SecurityDesc) = NULL;
        return RC;
    }
    RtlZeroMemory(Acl, Len);

    RC = RtlCreateAcl(Acl, Len, ACL_REVISION);
    if(!NT_SUCCESS(RC)) {
        DbgFreePool(Acl);
        DbgFreePool(*SecurityDesc);
        *((PULONG)SecurityDesc) = NULL;
        return RC;
    }

    // Add (All)(All) access for Everyone
/*    RC = RtlAddAccessAllowedAce(Acl, ACL_REVISION,
                                GENERIC_ALL,
                                SeExports->SeWorldSid);*/

    RC = RtlAddAccessAllowedAce(Acl, ACL_REVISION,
                                FILE_ALL_ACCESS,
                                SeExports->SeWorldSid);

    if(!NT_SUCCESS(RC)) {
        DbgFreePool(Acl);
        DbgFreePool(*SecurityDesc);
        *((PULONG)SecurityDesc) = NULL;
        return RC;
    }

    // Add Acl to Security Descriptor
    RC = RtlSetDaclSecurityDescriptor(*SecurityDesc, TRUE, Acl, FALSE);
    if(!NT_SUCCESS(RC)) {
        DbgFreePool(Acl);
        DbgFreePool(*SecurityDesc);
        *((PULONG)SecurityDesc) = NULL;
        return RC;
    }

    RC = UDFConvertToSelfRelative(SecurityDesc);

    DbgFreePool(Acl);

    return RC;
} // end UDFBuildFullControlAcl()

#endif // UDF_ENABLE_SECURITY

NTSTATUS
UDFAssignAcl(
    IN PVCB Vcb,
    IN PFILE_OBJECT FileObject, // OPTIONAL
    IN PtrUDFFCB Fcb,
    IN PtrUDFNTRequiredFCB NtReqFcb
    )
{
    NTSTATUS RC = STATUS_SUCCESS;
#ifdef UDF_ENABLE_SECURITY
//    SECURITY_INFORMATION SecurityInformation;

//    UDFPrint(("  UDFAssignAcl\n"));
    if(!NtReqFcb->SecurityDesc) {

        PSECURITY_DESCRIPTOR ExplicitSecurity = NULL;

        if(UDFIsAStreamDir(Fcb->FileInfo) || UDFIsAStream(Fcb->FileInfo)) {
            // Stream/SDir security
            NtReqFcb->SecurityDesc = Fcb->FileInfo->ParentFile->Dloc->CommonFcb->SecurityDesc;
            return STATUS_SUCCESS;
        } else
        if(!Fcb->FileInfo) {
            // Volume security
            if(Vcb->RootDirFCB &&
               Vcb->RootDirFCB->FileInfo &&
               Vcb->RootDirFCB->FileInfo->Dloc &&
               Vcb->RootDirFCB->FileInfo->Dloc->CommonFcb) {
                RC = UDFInheritAcl(Vcb, &(Vcb->RootDirFCB->FileInfo->Dloc->CommonFcb->SecurityDesc), &ExplicitSecurity);
            } else {
                NtReqFcb->SecurityDesc = NULL;
                RC = STATUS_NO_SECURITY_ON_OBJECT;
            }
            return RC;
        }

        RC = UDFReadSecurity(Vcb, Fcb, &ExplicitSecurity);
        if(RC == STATUS_NO_SECURITY_ON_OBJECT) {
            if(!Fcb->FileInfo->ParentFile) {
                RC = UDFBuildFullControlAcl(Vcb, &ExplicitSecurity);
            } else {
                RC = UDFInheritAcl(Vcb, &(Fcb->FileInfo->ParentFile->Dloc->CommonFcb->SecurityDesc), &ExplicitSecurity);
            }
/*            if(NT_SUCCESS(RC)) {
                NtReqFcb->NtReqFCBFlags |= UDF_NTREQ_FCB_SD_MODIFIED;
            }*/
        }
        if(NT_SUCCESS(RC)) {

//            SecurityInformation = FULL_SECURITY_INFORMATION;
            NtReqFcb->SecurityDesc = ExplicitSecurity;

/*            RC = SeSetSecurityDescriptorInfo(FileObject,
                                          &SecurityInformation,
                                          ExplicitSecurity,
                                          &(NtReqFcb->SecurityDesc),
                                          NonPagedPool,
                                          IoGetFileObjectGenericMapping() );*/

        }
    }
#endif //UDF_ENABLE_SECURITY
    return RC;
} // end UDFAssignAcl()


VOID
UDFDeassignAcl(
    IN PtrUDFNTRequiredFCB NtReqFcb,
    IN BOOLEAN AutoInherited
    )
{
#ifdef UDF_ENABLE_SECURITY
//    NTSTATUS RC = STATUS_SUCCESS;

//    UDFPrint(("  UDFDeassignAcl\n"));
    if(!NtReqFcb->SecurityDesc)
        return;

    if(AutoInherited) {
        NtReqFcb->SecurityDesc = NULL;
        return;
    }

    SeDeassignSecurity(&(NtReqFcb->SecurityDesc));
    NtReqFcb->SecurityDesc = NULL; // HA BCRK CLU4
#endif //UDF_ENABLE_SECURITY
    return;
} // end UDFDeassignAcl()

NTSTATUS
UDFWriteSecurity(
    IN PVCB Vcb,
    IN PtrUDFFCB Fcb,
    IN PSECURITY_DESCRIPTOR* SecurityDesc
    )
{
#ifdef UDF_ENABLE_SECURITY
    PUDF_FILE_INFO FileInfo = NULL;
    PUDF_FILE_INFO SDirInfo = NULL;
    PUDF_FILE_INFO AclInfo = NULL;
    PERESOURCE Res1 = NULL;
    NTSTATUS RC;
    ULONG NumberBytesRead;

//    UDFPrint(("UDFWriteSecurity\n"));

#if !defined(UDF_READ_ONLY_BUILD)

    if(!Vcb->WriteSecurity ||
       (Vcb->VCBFlags & (UDF_VCB_FLAGS_VOLUME_READ_ONLY |
                         UDF_VCB_FLAGS_MEDIA_READ_ONLY)))

#endif //!defined(UDF_READ_ONLY_BUILD)

        return STATUS_SUCCESS;

#if !defined(UDF_READ_ONLY_BUILD)

    _SEH2_TRY {

        FileInfo = Fcb->FileInfo;
        ASSERT(FileInfo);
        if(!FileInfo) {
            UDFPrint(("  Volume Security\n"));
            try_return(RC = STATUS_SUCCESS);
        }

        if(!(Fcb->NTRequiredFCB->NtReqFCBFlags & UDF_NTREQ_FCB_SD_MODIFIED))
            try_return(RC = STATUS_SUCCESS);

        // Open Stream Directory
        RC = UDFOpenStreamDir__(Vcb, FileInfo, &SDirInfo);

        if(RC == STATUS_NOT_FOUND) {
            RC = UDFCreateStreamDir__(Vcb, FileInfo, &SDirInfo);
        }
        if(!NT_SUCCESS(RC)) {
            if(UDFCleanUpFile__(Vcb, SDirInfo)) {
                if(SDirInfo) MyFreePool__(SDirInfo);
            }
            SDirInfo = NULL;
            try_return(RC);
        }
        // Acquire SDir exclusively if Fcb present
        if(SDirInfo->Fcb) {
            BrutePoint();
            UDF_CHECK_PAGING_IO_RESOURCE(SDirInfo->Fcb->NTRequiredFCB);
            UDFAcquireResourceExclusive(Res1 = &(SDirInfo->Fcb->NTRequiredFCB->MainResource),TRUE);
        }

        // Open Acl Stream
        RC = UDFOpenFile__(Vcb,
                           FALSE,TRUE,&(UDFGlobalData.AclName),
                           SDirInfo,&AclInfo,NULL);
        if(RC == STATUS_OBJECT_NAME_NOT_FOUND) {
            RC = UDFCreateFile__(Vcb, FALSE, &(UDFGlobalData.AclName),
                               0, 0, FALSE, FALSE, SDirInfo, &AclInfo);
        }
        if(!NT_SUCCESS(RC)) {
            if(UDFCleanUpFile__(Vcb, AclInfo)) {
                if(AclInfo) MyFreePool__(AclInfo);
            }
            AclInfo = NULL;
            try_return(RC);
        }

        if(!(*SecurityDesc)) {
            UDFFlushFile__(Vcb, AclInfo);
            RC = UDFUnlinkFile__(Vcb, AclInfo, TRUE);
            try_return(RC);
        }
        NumberBytesRead = RtlLengthSecurityDescriptor(*SecurityDesc);

        RC = UDFWriteFile__(Vcb, AclInfo, 0, NumberBytesRead,
                       FALSE, (PCHAR)(*SecurityDesc), &NumberBytesRead);
        if(!NT_SUCCESS(RC))
            try_return(RC);

        Fcb->NTRequiredFCB->NtReqFCBFlags &= ~UDF_NTREQ_FCB_SD_MODIFIED;

try_exit: NOTHING;

    } _SEH2_FINALLY {

        if(AclInfo) {
            UDFCloseFile__(Vcb, AclInfo);
            if(UDFCleanUpFile__(Vcb, AclInfo))
                MyFreePool__(AclInfo);
        }

        if(SDirInfo) {
            UDFCloseFile__(Vcb, SDirInfo);
            if(UDFCleanUpFile__(Vcb, SDirInfo))
                MyFreePool__(SDirInfo);
        }
        if(Res1)
            UDFReleaseResource(Res1);
    }

    return RC;

#endif //!defined(UDF_READ_ONLY_BUILD)
#endif //UDF_ENABLE_SECURITY

    return STATUS_SUCCESS;

} // end UDFWriteSecurity()

PSECURITY_DESCRIPTOR
UDFLookUpAcl(
    IN PVCB      Vcb,
    PFILE_OBJECT FileObject, // OPTIONAL
    IN PtrUDFFCB Fcb
    )
{
    UDFAssignAcl(Vcb, FileObject, Fcb, Fcb->NTRequiredFCB);
    return (Fcb->NTRequiredFCB->SecurityDesc);
} // end UDFLookUpAcl()


NTSTATUS
UDFCheckAccessRights(
    PFILE_OBJECT FileObject, // OPTIONAL
    PACCESS_STATE AccessState,
    PtrUDFFCB    Fcb,
    PtrUDFCCB    Ccb,        // OPTIONAL
    ACCESS_MASK  DesiredAccess,
    USHORT       ShareAccess
    )
{
    NTSTATUS RC;
    BOOLEAN ROCheck = FALSE;
#ifdef UDF_ENABLE_SECURITY
    BOOLEAN SecurityCheck;
    PSECURITY_DESCRIPTOR SecDesc;
    SECURITY_SUBJECT_CONTEXT SubjectContext;
    ACCESS_MASK LocalAccessMask;
#endif //UDF_ENABLE_SECURITY

    // Check attr compatibility
    ASSERT(Fcb);
    ASSERT(Fcb->Vcb);
#ifdef UDF_READ_ONLY_BUILD
    goto treat_as_ro;
#endif //UDF_READ_ONLY_BUILD

    if(Fcb->FCBFlags & UDF_FCB_READ_ONLY) {
        ROCheck = TRUE;
    } else
    if((Fcb->Vcb->origIntegrityType == INTEGRITY_TYPE_OPEN) &&
        Ccb && !(Ccb->CCBFlags & UDF_CCB_VOLUME_OPEN) &&
       (Fcb->Vcb->CompatFlags & UDF_VCB_IC_DIRTY_RO)) {
        AdPrint(("force R/O on dirty\n"));
        ROCheck = TRUE;
    }
    if(ROCheck) {
#ifdef UDF_READ_ONLY_BUILD
treat_as_ro:
#endif //UDF_READ_ONLY_BUILD
        ACCESS_MASK  DesiredAccessMask = 0;

        if(Fcb->Vcb->CompatFlags & UDF_VCB_IC_WRITE_IN_RO_DIR) {
            if(Fcb->FCBFlags & UDF_FCB_DIRECTORY) {
                DesiredAccessMask = (FILE_WRITE_EA |
                                     DELETE);
            } else {
                DesiredAccessMask = (FILE_WRITE_DATA |
                                     FILE_APPEND_DATA |
                                     FILE_WRITE_EA |
                                     DELETE);
            }
        } else {
                DesiredAccessMask = (FILE_WRITE_DATA |
                                     FILE_APPEND_DATA |
                                     FILE_WRITE_EA |
                                     FILE_DELETE_CHILD |
                                     FILE_ADD_SUBDIRECTORY |
                                     FILE_ADD_FILE |
                                     DELETE);
        }
        if(DesiredAccess & DesiredAccessMask)
            return STATUS_ACCESS_DENIED;
    }
#ifdef UDF_ENABLE_SECURITY
    // Check Security
    // NOTE: we should not perform security check if an empty DesiredAccess
    // was specified. AFAIU, SeAccessCheck() will return FALSE in this case.
    SecDesc = UDFLookUpAcl(Fcb->Vcb, FileObject, Fcb);
    if(SecDesc && DesiredAccess) {
        SeCaptureSubjectContext(&SubjectContext);
        SecurityCheck =
            SeAccessCheck(SecDesc,
                          &SubjectContext,
                          FALSE,
                          DesiredAccess,
                          Ccb ? Ccb->PreviouslyGrantedAccess : 0,
                          NULL,
                          IoGetFileObjectGenericMapping(),
                          UserMode,
                          Ccb ? &(Ccb->PreviouslyGrantedAccess) : &LocalAccessMask,
                          &RC);
        SeReleaseSubjectContext(&SubjectContext);

        if(!SecurityCheck) {
            return RC;
        } else
#endif //UDF_ENABLE_SECURITY
        if(DesiredAccess & ACCESS_SYSTEM_SECURITY) {
            if (!SeSinglePrivilegeCheck(SeExports->SeSecurityPrivilege, UserMode))
                return STATUS_ACCESS_DENIED;
            Ccb->PreviouslyGrantedAccess |= ACCESS_SYSTEM_SECURITY;
        }
#ifdef UDF_ENABLE_SECURITY
    }
#endif //UDF_ENABLE_SECURITY
    if(FileObject) {
        if (Fcb->OpenHandleCount) {
            // The FCB is currently in use by some thread.
            // We must check whether the requested access/share access
            // conflicts with the existing open operations.
            RC = IoCheckShareAccess(DesiredAccess, ShareAccess, FileObject,
                                            &(Fcb->NTRequiredFCB->FCBShareAccess), TRUE);
#ifndef UDF_ENABLE_SECURITY
            if(Ccb)
                Ccb->PreviouslyGrantedAccess |= DesiredAccess;
            IoUpdateShareAccess(FileObject, &(Fcb->NTRequiredFCB->FCBShareAccess));
#endif //UDF_ENABLE_SECURITY
        } else {
            IoSetShareAccess(DesiredAccess, ShareAccess, FileObject, &(Fcb->NTRequiredFCB->FCBShareAccess));
#ifndef UDF_ENABLE_SECURITY
            if(Ccb)
                Ccb->PreviouslyGrantedAccess = DesiredAccess;
#endif //UDF_ENABLE_SECURITY
            RC = STATUS_SUCCESS;
        }
    } else {
        // we get here if given file was opened for internal purposes
        RC = STATUS_SUCCESS;
    }
    return RC;
} // end UDFCheckAccessRights()

NTSTATUS
UDFSetAccessRights(
    PFILE_OBJECT FileObject,
    PACCESS_STATE AccessState,
    PtrUDFFCB    Fcb,
    PtrUDFCCB    Ccb,
    ACCESS_MASK  DesiredAccess,
    USHORT       ShareAccess
    )
{
#ifndef UDF_ENABLE_SECURITY
    ASSERT(Ccb);
    ASSERT(Fcb->FileInfo);

    return UDFCheckAccessRights(FileObject, AccessState, Fcb, Ccb, DesiredAccess, ShareAccess);

#else //UDF_ENABLE_SECURITY

    NTSTATUS RC;
    // Set Security on Object
    PSECURITY_DESCRIPTOR SecDesc;
    SECURITY_SUBJECT_CONTEXT SubjectContext;
    BOOLEAN AutoInherit;

    ASSERT(Ccb);
    ASSERT(Fcb->FileInfo);

    SecDesc = UDFLookUpAcl(Fcb->Vcb, FileObject, Fcb);
    AutoInherit = UDFIsAStreamDir(Fcb->FileInfo) || UDFIsAStream(Fcb->FileInfo);

    if(SecDesc && !AutoInherit) {
        // Get caller's User/Primary Group info
        SeCaptureSubjectContext(&SubjectContext);
        RC = SeAssignSecurity(
                         Fcb->FileInfo->ParentFile->Dloc->CommonFcb->SecurityDesc,
//                         NULL,
                         AccessState->SecurityDescriptor,
                         &(Fcb->NTRequiredFCB->SecurityDesc),
                         UDFIsADirectory(Fcb->FileInfo),
                         &SubjectContext,
                         IoGetFileObjectGenericMapping(),
                         NonPagedPool);
        SeReleaseSubjectContext(&SubjectContext);
        UDFConvertToSelfRelative(&(Fcb->NTRequiredFCB->SecurityDesc));

        if(!NT_SUCCESS(RC)) {
Clean_Up_SD:
            UDFDeassignAcl(Fcb->NTRequiredFCB, AutoInherit);
            return RC;
        }
    }

    RC = UDFCheckAccessRights(FileObject, AccessState, Fcb, Ccb, DesiredAccess, ShareAccess);
    if(!NT_SUCCESS(RC))
        goto Clean_Up_SD;
    return RC;

#endif //UDF_ENABLE_SECURITY

} // end UDFSetAccessRights()