3848 lines
109 KiB
C
3848 lines
109 KiB
C
/*++
|
||
|
||
Copyright (c) 1991 Microsoft Corporation
|
||
|
||
Module Name:
|
||
|
||
SecurSup.c
|
||
|
||
Abstract:
|
||
|
||
This module implements the Ntfs Security Support routines
|
||
|
||
Author:
|
||
|
||
Gary Kimura [GaryKi] 27-Dec-1991
|
||
|
||
Revision History:
|
||
|
||
--*/
|
||
|
||
#include "NtfsProc.h"
|
||
|
||
#define Dbg (DEBUG_TRACE_SECURSUP)
|
||
#define DbgAcl (DEBUG_TRACE_SECURSUP | DEBUG_TRACE_ACLINDEX)
|
||
|
||
//
|
||
// Define a tag for general pool allocations from this module
|
||
//
|
||
|
||
#undef MODULE_POOL_TAG
|
||
#define MODULE_POOL_TAG ('SFtN')
|
||
|
||
UNICODE_STRING FileString = CONSTANT_UNICODE_STRING( L"File" );
|
||
|
||
//
|
||
// Local procedure prototypes
|
||
//
|
||
|
||
VOID
|
||
NtfsLoadSecurityDescriptor (
|
||
PIRP_CONTEXT IrpContext,
|
||
IN PFCB Fcb,
|
||
IN PFCB ParentFcb OPTIONAL
|
||
);
|
||
|
||
VOID
|
||
NtfsStoreSecurityDescriptor (
|
||
PIRP_CONTEXT IrpContext,
|
||
IN PFCB Fcb,
|
||
IN BOOLEAN LogIt
|
||
);
|
||
|
||
#ifdef _CAIRO_
|
||
PSHARED_SECURITY
|
||
NtOfsFindCachedSharedSecurityBySecurityId (
|
||
IN PVCB Vcb,
|
||
IN SECURITY_ID SecurityId
|
||
);
|
||
|
||
PSHARED_SECURITY
|
||
NtOfsFindCachedSharedSecurityByHash (
|
||
IN PVCB Vcb,
|
||
IN PSECURITY_DESCRIPTOR SecurityDescriptor,
|
||
IN ULONG SecurityDescriptorLength,
|
||
IN ULONG Hash
|
||
);
|
||
|
||
VOID
|
||
NtOfsAddCachedSharedSecurity (
|
||
IN PVCB Vcb,
|
||
PSHARED_SECURITY SharedSecurity
|
||
);
|
||
|
||
VOID
|
||
NtOfsMapSecurityIdToSecurityDescriptor (
|
||
IN PIRP_CONTEXT IrpContext,
|
||
IN PVCB Vcb,
|
||
IN SECURITY_ID SecurityId,
|
||
OUT PSECURITY_DESCRIPTOR *SecurityDescriptor,
|
||
OUT PULONG SecurityDescriptorLength,
|
||
OUT PBCB *Bcb
|
||
);
|
||
|
||
NTSTATUS
|
||
NtOfsMatchSecurityHash (
|
||
IN PINDEX_ROW IndexRow,
|
||
IN OUT PVOID MatchData
|
||
);
|
||
|
||
VOID
|
||
NtOfsLookupSecurityDescriptorInIndex (
|
||
PIRP_CONTEXT IrpContext,
|
||
IN OUT PSHARED_SECURITY SharedSecurity
|
||
);
|
||
|
||
SECURITY_ID
|
||
NtOfsGetSecurityIdFromSecurityDescriptor (
|
||
PIRP_CONTEXT IrpContext,
|
||
IN OUT PSHARED_SECURITY SharedSecurity
|
||
);
|
||
#endif // _CAIRO_
|
||
|
||
#ifdef ALLOC_PRAGMA
|
||
#pragma alloc_text(PAGE, NtfsAccessCheck)
|
||
#pragma alloc_text(PAGE, NtfsAssignSecurity)
|
||
#pragma alloc_text(PAGE, NtfsCheckFileForDelete)
|
||
#pragma alloc_text(PAGE, NtfsCheckIndexForAddOrDelete)
|
||
#pragma alloc_text(PAGE, NtfsDereferenceSharedSecurity)
|
||
#pragma alloc_text(PAGE, NtfsLoadSecurityDescriptor)
|
||
#pragma alloc_text(PAGE, NtfsModifySecurity)
|
||
#pragma alloc_text(PAGE, NtfsNotifyTraverseCheck)
|
||
#pragma alloc_text(PAGE, NtfsQuerySecurity)
|
||
#pragma alloc_text(PAGE, NtfsStoreSecurityDescriptor)
|
||
#ifdef _CAIRO_
|
||
#pragma alloc_text(PAGE, NtfsInitializeSecurity)
|
||
#pragma alloc_text(PAGE, NtfsLoadSecurityDescriptorById)
|
||
#pragma alloc_text(PAGE, NtOfsFindCachedSharedSecurityBySecurityId)
|
||
#pragma alloc_text(PAGE, NtOfsFindCachedSharedSecurityByHash)
|
||
#pragma alloc_text(PAGE, NtOfsAddCachedSharedSecurity)
|
||
#pragma alloc_text(PAGE, NtOfsPurgeSecurityCache)
|
||
#pragma alloc_text(PAGE, NtOfsMapSecurityIdToSecurityDescriptor)
|
||
#pragma alloc_text(PAGE, NtOfsMatchSecurityHash)
|
||
#pragma alloc_text(PAGE, NtOfsLookupSecurityDescriptorInIndex)
|
||
#pragma alloc_text(PAGE, NtOfsGetSecurityIdFromSecurityDescriptor)
|
||
#pragma alloc_text(PAGE, NtOfsCollateSecurityHash)
|
||
#endif // _CAIRO_
|
||
#endif
|
||
|
||
|
||
VOID
|
||
NtfsAssignSecurity (
|
||
IN PIRP_CONTEXT IrpContext,
|
||
IN PFCB ParentFcb,
|
||
IN PIRP Irp,
|
||
IN PFCB NewFcb,
|
||
IN PFILE_RECORD_SEGMENT_HEADER FileRecord, // BUGBUG delete
|
||
IN PBCB FileRecordBcb, // BUGBUG delete
|
||
IN LONGLONG FileOffset, // BUGBUG delete
|
||
IN OUT PBOOLEAN LogIt // BUGBUG delete
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine constructs and assigns a new security descriptor to the
|
||
specified file/directory. The new security descriptor is placed both
|
||
on the fcb and on the disk.
|
||
|
||
This will only be called in the context of an open/create operation.
|
||
It currently MUST NOT be called to store a security descriptor for
|
||
an existing file, because it instructs NtfsStoreSecurityDescriptor
|
||
to not log the change.
|
||
|
||
If this is a large security descriptor then it is possible that
|
||
AllocateClusters may be called twice within the call to AddAllocation
|
||
when the attribute is created. If so then the second call will always
|
||
log the changes. In that case we need to log all of the operations to
|
||
create this security attribute and also we must log the current state
|
||
of the file record.
|
||
|
||
It is possible that our caller has already started logging operations against
|
||
this log record. In that case we always log the security changes.
|
||
|
||
Arguments:
|
||
|
||
ParentFcb - Supplies the directory under which the new fcb exists
|
||
|
||
Irp - Supplies the Irp being processed
|
||
|
||
NewFcb - Supplies the fcb that is being assigned a new security descriptor
|
||
|
||
FileRecord - Supplies the file record for this operation. Used if we
|
||
have to log against the file record.
|
||
|
||
FileRecordBcb - Bcb for the file record above.
|
||
|
||
FileOffset - File offset in the Mft for this file record.
|
||
|
||
LogIt - On entry this indicates whether our caller wants this operation
|
||
logged. On exit we return TRUE if we logged the security change.
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
--*/
|
||
|
||
{
|
||
PSECURITY_DESCRIPTOR SecurityDescriptor;
|
||
|
||
NTSTATUS Status;
|
||
BOOLEAN IsDirectory;
|
||
PACCESS_STATE AccessState;
|
||
PIO_STACK_LOCATION IrpSp;
|
||
ULONG SecurityDescLength;
|
||
|
||
ASSERT_IRP_CONTEXT( IrpContext );
|
||
ASSERT_FCB( ParentFcb );
|
||
ASSERT_IRP( Irp );
|
||
ASSERT_FCB( NewFcb );
|
||
|
||
PAGED_CODE();
|
||
|
||
DebugTrace( +1, Dbg, ("NtfsAssignSecurity...\n") );
|
||
|
||
//
|
||
// First decide if we are creating a file or a directory
|
||
//
|
||
|
||
IrpSp = IoGetCurrentIrpStackLocation(Irp);
|
||
if (FlagOn(IrpSp->Parameters.Create.Options, FILE_DIRECTORY_FILE)) {
|
||
|
||
IsDirectory = TRUE;
|
||
|
||
} else {
|
||
|
||
IsDirectory = FALSE;
|
||
}
|
||
|
||
//
|
||
// Extract the parts of the Irp that we need to do our assignment
|
||
//
|
||
|
||
AccessState = IrpSp->Parameters.Create.SecurityContext->AccessState;
|
||
|
||
//
|
||
// Check if we need to load the security descriptor for the parent.
|
||
//
|
||
|
||
if (ParentFcb->SharedSecurity == NULL) {
|
||
|
||
NtfsLoadSecurityDescriptor( IrpContext, ParentFcb, NULL );
|
||
}
|
||
|
||
ASSERT( ParentFcb->SharedSecurity != NULL );
|
||
|
||
//
|
||
// Create a new security descriptor for the file and raise if there is
|
||
// an error
|
||
//
|
||
|
||
if (!NT_SUCCESS( Status = SeAssignSecurity( &ParentFcb->SharedSecurity->SecurityDescriptor,
|
||
AccessState->SecurityDescriptor,
|
||
&SecurityDescriptor,
|
||
IsDirectory,
|
||
&AccessState->SubjectSecurityContext,
|
||
IoGetFileObjectGenericMapping(),
|
||
PagedPool ))) {
|
||
|
||
NtfsRaiseStatus( IrpContext, Status, NULL, NULL );
|
||
}
|
||
|
||
//
|
||
// Load the security descriptor into the Fcb
|
||
//
|
||
|
||
SecurityDescLength = RtlLengthSecurityDescriptor( SecurityDescriptor );
|
||
|
||
//
|
||
// Make sure the length is non-zero.
|
||
//
|
||
|
||
if (SecurityDescLength == 0) {
|
||
|
||
SeDeassignSecurity( &SecurityDescriptor );
|
||
NtfsRaiseStatus( IrpContext, STATUS_INVALID_PARAMETER, NULL, NULL );
|
||
}
|
||
|
||
ASSERT( SeValidSecurityDescriptor( SecurityDescLength, SecurityDescriptor ));
|
||
|
||
NtfsUpdateFcbSecurity( IrpContext,
|
||
NewFcb,
|
||
ParentFcb,
|
||
#ifdef _CAIRO_
|
||
SECURITY_ID_INVALID,
|
||
#endif // _CAIRO_
|
||
SecurityDescriptor,
|
||
SecurityDescLength );
|
||
|
||
//
|
||
// Free the security descriptor created by Se
|
||
//
|
||
|
||
if (!NT_SUCCESS( Status = SeDeassignSecurity( &SecurityDescriptor ))) {
|
||
|
||
NtfsRaiseStatus( IrpContext, Status, NULL, NULL );
|
||
}
|
||
|
||
//
|
||
// BUGBUG begin section to delete when all volumes are cairo
|
||
//
|
||
|
||
#ifdef _CAIRO_
|
||
if (NewFcb->Vcb->SecurityDescriptorStream == NULL) {
|
||
#endif
|
||
//
|
||
// If the security descriptor is large enough that it may cause us to
|
||
// start logging in the StoreSecurity call below then make sure everything
|
||
// is logged.
|
||
//
|
||
|
||
if (!(LogIt) &&
|
||
(SecurityDescLength > BytesFromClusters( NewFcb->Vcb, MAXIMUM_RUNS_AT_ONCE ))) {
|
||
|
||
//
|
||
// Log the current state of the file record.
|
||
//
|
||
|
||
FileRecord->Lsn = NtfsWriteLog( IrpContext,
|
||
NewFcb->Vcb->MftScb,
|
||
FileRecordBcb,
|
||
InitializeFileRecordSegment,
|
||
FileRecord,
|
||
FileRecord->FirstFreeByte,
|
||
Noop,
|
||
NULL,
|
||
0,
|
||
FileOffset,
|
||
0,
|
||
0,
|
||
NewFcb->Vcb->BytesPerFileRecordSegment );
|
||
|
||
*LogIt = TRUE;
|
||
}
|
||
#ifdef _CAIRO_
|
||
}
|
||
#endif // _CAIRO_
|
||
|
||
//
|
||
// BUGBUG end section to delete when all volumes are cairo
|
||
//
|
||
|
||
//
|
||
// Write out the new security descriptor
|
||
//
|
||
|
||
NtfsStoreSecurityDescriptor( IrpContext, NewFcb, *LogIt );
|
||
|
||
//
|
||
// And return to our caller
|
||
//
|
||
|
||
DebugTrace( -1, Dbg, ("NtfsAssignSecurity -> VOID\n") );
|
||
|
||
return;
|
||
}
|
||
|
||
|
||
NTSTATUS
|
||
NtfsModifySecurity (
|
||
IN PIRP_CONTEXT IrpContext,
|
||
IN PFCB Fcb,
|
||
IN PSECURITY_INFORMATION SecurityInformation,
|
||
OUT PSECURITY_DESCRIPTOR SecurityDescriptor
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine modifies an existing security descriptor for a file/directory.
|
||
|
||
Arguments:
|
||
|
||
Fcb - Supplies the Fcb whose security is being modified
|
||
|
||
SecurityInformation - Supplies the security information structure passed to
|
||
the file system by the I/O system.
|
||
|
||
SecurityDescriptor - Supplies the security information structure passed to
|
||
the file system by the I/O system.
|
||
|
||
Return Value:
|
||
|
||
NTSTATUS - Returns an appropriate status value for the function results
|
||
|
||
--*/
|
||
|
||
{
|
||
NTSTATUS Status;
|
||
PSECURITY_DESCRIPTOR DescriptorPtr;
|
||
ULONG DescriptorLength;
|
||
|
||
ASSERT_IRP_CONTEXT( IrpContext );
|
||
ASSERT_FCB( Fcb );
|
||
|
||
PAGED_CODE();
|
||
|
||
DebugTrace( +1, Dbg, ("NtfsModifySecurity...\n") );
|
||
|
||
//
|
||
// First check if we need to load the security descriptor for the file
|
||
//
|
||
|
||
if (Fcb->SharedSecurity == NULL) {
|
||
|
||
NtfsLoadSecurityDescriptor( IrpContext, Fcb, NULL );
|
||
}
|
||
|
||
ASSERT( Fcb->SharedSecurity != NULL);
|
||
|
||
DescriptorPtr = &Fcb->SharedSecurity->SecurityDescriptor;
|
||
|
||
//
|
||
// Do the modify operation. SeSetSecurityDescriptorInfo no longer
|
||
// frees the passed security descriptor.
|
||
//
|
||
|
||
if (!NT_SUCCESS( Status = SeSetSecurityDescriptorInfo( NULL,
|
||
SecurityInformation,
|
||
SecurityDescriptor,
|
||
&DescriptorPtr,
|
||
PagedPool,
|
||
IoGetFileObjectGenericMapping() ))) {
|
||
|
||
NtfsRaiseStatus( IrpContext, Status, NULL, NULL );
|
||
}
|
||
|
||
DescriptorLength = RtlLengthSecurityDescriptor( DescriptorPtr );
|
||
|
||
//
|
||
// Check for a zero length.
|
||
//
|
||
|
||
if (DescriptorLength == 0) {
|
||
|
||
SeDeassignSecurity( &DescriptorPtr );
|
||
NtfsRaiseStatus( IrpContext, STATUS_INVALID_PARAMETER, NULL, NULL );
|
||
}
|
||
|
||
//
|
||
// Update the move the quota to the new owner if necessary.
|
||
//
|
||
|
||
NtfsMoveQuotaOwner( IrpContext, Fcb, DescriptorPtr );
|
||
|
||
NtfsAcquireFcbSecurity( Fcb->Vcb );
|
||
|
||
NtfsDereferenceSharedSecurity( Fcb );
|
||
|
||
NtfsReleaseFcbSecurity( Fcb->Vcb );
|
||
|
||
//
|
||
// Load the security descriptor into the Fcb
|
||
//
|
||
|
||
NtfsUpdateFcbSecurity( IrpContext,
|
||
Fcb,
|
||
NULL,
|
||
#ifdef _CAIRO_
|
||
SECURITY_ID_INVALID,
|
||
#endif // _CAIRO_
|
||
DescriptorPtr,
|
||
DescriptorLength );
|
||
|
||
//
|
||
// Free the security descriptor created by Se
|
||
//
|
||
|
||
if (!NT_SUCCESS( Status = SeDeassignSecurity( &DescriptorPtr ))) {
|
||
|
||
NtfsRaiseStatus( IrpContext, Status, NULL, NULL );
|
||
}
|
||
|
||
//
|
||
// Now we need to store the new security descriptor on disk
|
||
//
|
||
|
||
NtfsStoreSecurityDescriptor( IrpContext, Fcb, TRUE );
|
||
|
||
//
|
||
// Remember that we modified the security on the file.
|
||
//
|
||
|
||
SetFlag( Fcb->InfoFlags, FCB_INFO_MODIFIED_SECURITY );
|
||
|
||
//
|
||
// And return to our caller
|
||
//
|
||
|
||
DebugTrace( -1, Dbg, ("NtfsModifySecurity -> %08lx\n", Status) );
|
||
|
||
return Status;
|
||
}
|
||
|
||
|
||
NTSTATUS
|
||
NtfsQuerySecurity (
|
||
IN PIRP_CONTEXT IrpContext,
|
||
IN PFCB Fcb,
|
||
IN PSECURITY_INFORMATION SecurityInformation,
|
||
OUT PSECURITY_DESCRIPTOR SecurityDescriptor,
|
||
IN OUT PULONG SecurityDescriptorLength
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine is used to query the contents of an existing security descriptor for
|
||
a file/directory.
|
||
|
||
Arguments:
|
||
|
||
Fcb - Supplies the file/directory being queried
|
||
|
||
SecurityInformation - Supplies the security information structure passed to
|
||
the file system by the I/O system.
|
||
|
||
SecurityDescriptor - Supplies the security information structure passed to
|
||
the file system by the I/O system.
|
||
|
||
SecurityDescriptorLength - Supplies the length of the input security descriptor
|
||
buffer in bytes.
|
||
|
||
Return Value:
|
||
|
||
NTSTATUS - Returns an appropriate status value for the function results
|
||
|
||
--*/
|
||
|
||
{
|
||
NTSTATUS Status;
|
||
PSECURITY_DESCRIPTOR LocalPointer;
|
||
|
||
ASSERT_IRP_CONTEXT( IrpContext );
|
||
ASSERT_FCB( Fcb );
|
||
|
||
PAGED_CODE();
|
||
|
||
DebugTrace( +1, Dbg, ("NtfsQuerySecurity...\n") );
|
||
|
||
//
|
||
// First check if we need to load the security descriptor for the file
|
||
//
|
||
|
||
if (Fcb->SharedSecurity == NULL) {
|
||
|
||
NtfsLoadSecurityDescriptor( IrpContext, Fcb, NULL );
|
||
}
|
||
|
||
LocalPointer = &Fcb->SharedSecurity->SecurityDescriptor;
|
||
|
||
//
|
||
// Now with the security descriptor loaded do the query operation but
|
||
// protect ourselves with a exception handler just in case the caller's
|
||
// buffer isn't valid
|
||
//
|
||
|
||
try {
|
||
|
||
Status = SeQuerySecurityDescriptorInfo( SecurityInformation,
|
||
SecurityDescriptor,
|
||
SecurityDescriptorLength,
|
||
&LocalPointer );
|
||
|
||
} except(EXCEPTION_EXECUTE_HANDLER) {
|
||
|
||
ExRaiseStatus( STATUS_INVALID_USER_BUFFER );
|
||
}
|
||
|
||
//
|
||
// And return to our caller
|
||
//
|
||
|
||
DebugTrace( -1, Dbg, ("NtfsQuerySecurity -> %08lx\n", Status) );
|
||
|
||
return Status;
|
||
}
|
||
|
||
|
||
#define NTFS_SE_CONTROL (((SE_DACL_PRESENT | SE_SELF_RELATIVE) << 16) | SECURITY_DESCRIPTOR_REVISION1)
|
||
#define NTFS_DEFAULT_ACCESS_MASK 0x001f01ff
|
||
|
||
ULONG NtfsWorldAclFile[] = {
|
||
0x00000000, // Null Sacl
|
||
0x00000014, // Dacl
|
||
0x001c0002, // Acl header
|
||
0x00000001, // One ACE
|
||
0x00140000, // ACE Header
|
||
NTFS_DEFAULT_ACCESS_MASK,
|
||
0x00000101, // World Sid
|
||
0x01000000,
|
||
0x00000000
|
||
};
|
||
|
||
ULONG NtfsWorldAclDir[] = {
|
||
0x00000000, // Null Sacl
|
||
0x00000014, // Dacl
|
||
0x00300002, // Acl header
|
||
0x00000002, // Two ACEs
|
||
0x00140000, // ACE Header
|
||
NTFS_DEFAULT_ACCESS_MASK,
|
||
0x00000101, // World Sid
|
||
0x01000000,
|
||
0x00000000,
|
||
0x00140b00, // ACE Header
|
||
NTFS_DEFAULT_ACCESS_MASK,
|
||
0x00000101, // World Sid
|
||
0x01000000,
|
||
0x00000000
|
||
};
|
||
|
||
VOID
|
||
NtfsAccessCheck (
|
||
PIRP_CONTEXT IrpContext,
|
||
IN PFCB Fcb,
|
||
IN PFCB ParentFcb OPTIONAL,
|
||
IN PIRP Irp,
|
||
IN ACCESS_MASK DesiredAccess,
|
||
IN BOOLEAN CheckOnly
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine does a general access check for the indicated desired access.
|
||
This will only be called in the context of an open/create operation.
|
||
|
||
If access is granted then control is returned to the caller
|
||
otherwise this function will do the proper Nt security calls to log
|
||
the attempt and then raise an access denied status.
|
||
|
||
Arguments:
|
||
|
||
Fcb - Supplies the file/directory being examined
|
||
|
||
ParentFcb - Optionally supplies the parent of the Fcb being examined
|
||
|
||
Irp - Supplies the Irp being processed
|
||
|
||
DesiredAccess - Supplies a mask of the access being requested
|
||
|
||
CheckOnly - Indicates if this operation is to check the desired access
|
||
only and not accumulate the access granted here. In this case we
|
||
are guaranteed that we have passed in a hard-wired desired access
|
||
and MAXIMUM_ALLOWED will not be one of them.
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
--*/
|
||
|
||
{
|
||
NTSTATUS Status;
|
||
NTSTATUS AccessStatus;
|
||
NTSTATUS AccessStatusError;
|
||
|
||
PACCESS_STATE AccessState;
|
||
|
||
PIO_STACK_LOCATION IrpSp;
|
||
|
||
BOOLEAN AccessGranted;
|
||
ACCESS_MASK GrantedAccess;
|
||
PISECURITY_DESCRIPTOR SecurityDescriptor;
|
||
PPRIVILEGE_SET Privileges;
|
||
PUNICODE_STRING FileName;
|
||
PUNICODE_STRING RelatedFileName;
|
||
PUNICODE_STRING PartialFileName;
|
||
UNICODE_STRING FullFileName;
|
||
PUNICODE_STRING DeviceObjectName;
|
||
USHORT DeviceObjectNameLength;
|
||
BOOLEAN LeadingSlash;
|
||
BOOLEAN RelatedFileNamePresent;
|
||
BOOLEAN PartialFileNamePresent;
|
||
BOOLEAN MaximumRequested;
|
||
BOOLEAN MaximumDeleteAcquired;
|
||
BOOLEAN MaximumReadAttrAcquired;
|
||
BOOLEAN PerformAccessValidation;
|
||
BOOLEAN PerformDeleteAudit;
|
||
|
||
ASSERT_IRP_CONTEXT( IrpContext );
|
||
ASSERT_FCB( Fcb );
|
||
ASSERT_IRP( Irp );
|
||
|
||
PAGED_CODE();
|
||
|
||
DebugTrace( +1, Dbg, ("NtfsAccessCheck...\n") );
|
||
|
||
//
|
||
// First extract the parts of the Irp that we need to do our checking
|
||
//
|
||
|
||
IrpSp = IoGetCurrentIrpStackLocation(Irp);
|
||
AccessState = IrpSp->Parameters.Create.SecurityContext->AccessState;
|
||
|
||
//
|
||
// Check if we need to load the security descriptor for the file
|
||
//
|
||
|
||
if (Fcb->SharedSecurity == NULL) {
|
||
|
||
NtfsLoadSecurityDescriptor( IrpContext, Fcb, ParentFcb );
|
||
}
|
||
|
||
ASSERT( Fcb->SharedSecurity != NULL );
|
||
|
||
SecurityDescriptor = (PISECURITY_DESCRIPTOR) Fcb->SharedSecurity->SecurityDescriptor;
|
||
|
||
//
|
||
// Check to see if auditing is enabled and if this is the default world ACL.
|
||
//
|
||
|
||
if (*((PULONG) SecurityDescriptor) == NTFS_SE_CONTROL &&
|
||
!SeAuditingFileEvents( TRUE, SecurityDescriptor )) {
|
||
|
||
// Directories and files have different default ACLs.
|
||
|
||
if (((Fcb->Info.FileAttributes & DUP_FILE_NAME_INDEX_PRESENT) &&
|
||
RtlEqualMemory(
|
||
&SecurityDescriptor->Sacl,
|
||
NtfsWorldAclDir,
|
||
sizeof(NtfsWorldAclDir))) ||
|
||
RtlEqualMemory(
|
||
&SecurityDescriptor->Sacl,
|
||
NtfsWorldAclFile,
|
||
sizeof(NtfsWorldAclFile))) {
|
||
if (FlagOn( DesiredAccess, MAXIMUM_ALLOWED )) {
|
||
GrantedAccess = NTFS_DEFAULT_ACCESS_MASK;
|
||
} else {
|
||
GrantedAccess = DesiredAccess & NTFS_DEFAULT_ACCESS_MASK;
|
||
}
|
||
|
||
if (!CheckOnly) {
|
||
|
||
SetFlag( AccessState->PreviouslyGrantedAccess, GrantedAccess );
|
||
ClearFlag( AccessState->RemainingDesiredAccess, (GrantedAccess | MAXIMUM_ALLOWED) );
|
||
}
|
||
|
||
return;
|
||
}
|
||
}
|
||
|
||
Privileges = NULL;
|
||
FileName = NULL;
|
||
RelatedFileName = NULL;
|
||
PartialFileName = NULL;
|
||
DeviceObjectName = NULL;
|
||
MaximumRequested = FALSE;
|
||
MaximumDeleteAcquired = FALSE;
|
||
MaximumReadAttrAcquired = FALSE;
|
||
PerformAccessValidation = TRUE;
|
||
PerformDeleteAudit = FALSE;
|
||
|
||
//
|
||
// Check to see if we need to perform access validation
|
||
//
|
||
|
||
ClearFlag( DesiredAccess, AccessState->PreviouslyGrantedAccess );
|
||
|
||
if (DesiredAccess == 0) {
|
||
|
||
//
|
||
// Nothing to check, skip AVR and go straight to auditing
|
||
//
|
||
|
||
PerformAccessValidation = FALSE;
|
||
AccessGranted = TRUE;
|
||
}
|
||
|
||
//
|
||
// Remember the case where MAXIMUM_ALLOWED was requested.
|
||
//
|
||
|
||
if (FlagOn( DesiredAccess, MAXIMUM_ALLOWED )) {
|
||
|
||
MaximumRequested = TRUE;
|
||
}
|
||
|
||
if (FlagOn(IrpSp->Parameters.Create.SecurityContext->FullCreateOptions,FILE_DELETE_ON_CLOSE)) {
|
||
PerformDeleteAudit = TRUE;
|
||
}
|
||
|
||
//
|
||
// Lock the user context, do the access check and then unlock the context
|
||
//
|
||
|
||
SeLockSubjectContext( &AccessState->SubjectSecurityContext );
|
||
|
||
if (PerformAccessValidation) {
|
||
|
||
AccessGranted = SeAccessCheck( &Fcb->SharedSecurity->SecurityDescriptor,
|
||
&AccessState->SubjectSecurityContext,
|
||
TRUE, // Tokens are locked
|
||
DesiredAccess,
|
||
0,
|
||
&Privileges,
|
||
IoGetFileObjectGenericMapping(),
|
||
(KPROCESSOR_MODE)(FlagOn(IrpSp->Flags, SL_FORCE_ACCESS_CHECK) ?
|
||
UserMode : Irp->RequestorMode),
|
||
&GrantedAccess,
|
||
&AccessStatus );
|
||
|
||
if (Privileges != NULL) {
|
||
|
||
Status = SeAppendPrivileges( AccessState, Privileges );
|
||
SeFreePrivileges( Privileges );
|
||
}
|
||
|
||
if (AccessGranted) {
|
||
|
||
ClearFlag( DesiredAccess, GrantedAccess | MAXIMUM_ALLOWED );
|
||
|
||
if (!CheckOnly) {
|
||
|
||
SetFlag( AccessState->PreviouslyGrantedAccess, GrantedAccess );
|
||
|
||
//
|
||
// Remember the case where MAXIMUM_ALLOWED was requested and we
|
||
// got everything requested from the file.
|
||
//
|
||
|
||
if (MaximumRequested) {
|
||
|
||
//
|
||
// Check whether we got DELETE and READ_ATTRIBUTES. Otherwise
|
||
// we will query the parent.
|
||
//
|
||
|
||
if (FlagOn( AccessState->PreviouslyGrantedAccess, DELETE )) {
|
||
|
||
MaximumDeleteAcquired = TRUE;
|
||
}
|
||
|
||
if (FlagOn( AccessState->PreviouslyGrantedAccess, FILE_READ_ATTRIBUTES )) {
|
||
|
||
MaximumReadAttrAcquired = TRUE;
|
||
}
|
||
}
|
||
|
||
ClearFlag( AccessState->RemainingDesiredAccess, (GrantedAccess | MAXIMUM_ALLOWED) );
|
||
}
|
||
|
||
} else {
|
||
|
||
AccessStatusError = AccessStatus;
|
||
}
|
||
|
||
//
|
||
// Check if the access is not granted and if we were given a parent fcb, and
|
||
// if the desired access was asking for delete or file read attributes. If so
|
||
// then we need to do some extra work to decide if the caller does get access
|
||
// based on the parent directories security descriptor. We also do the same
|
||
// work if MAXIMUM_ALLOWED was requested and we didn't get DELETE or
|
||
// FILE_READ_ATTRIBUTES.
|
||
//
|
||
|
||
if ((ParentFcb != NULL)
|
||
&& ((!AccessGranted && FlagOn( DesiredAccess, DELETE | FILE_READ_ATTRIBUTES ))
|
||
|| (MaximumRequested
|
||
&& (!MaximumDeleteAcquired || !MaximumReadAttrAcquired)))) {
|
||
|
||
BOOLEAN DeleteAccessGranted = TRUE;
|
||
BOOLEAN ReadAttributesAccessGranted = TRUE;
|
||
|
||
ACCESS_MASK DeleteChildGrantedAccess = 0;
|
||
ACCESS_MASK ListDirectoryGrantedAccess = 0;
|
||
|
||
//
|
||
// Before we proceed load in the parent security descriptor
|
||
//
|
||
|
||
if (ParentFcb->SharedSecurity == NULL) {
|
||
|
||
NtfsLoadSecurityDescriptor( IrpContext, ParentFcb, NULL );
|
||
}
|
||
|
||
ASSERT( ParentFcb->SharedSecurity != NULL);
|
||
|
||
//
|
||
// Now if the user is asking for delete access then check if the parent
|
||
// will granted delete access to the child, and if so then we munge the
|
||
// desired access
|
||
//
|
||
|
||
if (FlagOn( DesiredAccess, DELETE )
|
||
|| (MaximumRequested && !MaximumDeleteAcquired)) {
|
||
|
||
DeleteAccessGranted = SeAccessCheck( &ParentFcb->SharedSecurity->SecurityDescriptor,
|
||
&AccessState->SubjectSecurityContext,
|
||
TRUE, // Tokens are locked
|
||
FILE_DELETE_CHILD,
|
||
0,
|
||
&Privileges,
|
||
IoGetFileObjectGenericMapping(),
|
||
(KPROCESSOR_MODE)(FlagOn(IrpSp->Flags, SL_FORCE_ACCESS_CHECK) ?
|
||
UserMode : Irp->RequestorMode),
|
||
&DeleteChildGrantedAccess,
|
||
&AccessStatus );
|
||
|
||
if (Privileges != NULL) { SeFreePrivileges( Privileges ); }
|
||
|
||
if (DeleteAccessGranted) {
|
||
|
||
SetFlag( DeleteChildGrantedAccess, DELETE );
|
||
ClearFlag( DeleteChildGrantedAccess, FILE_DELETE_CHILD );
|
||
ClearFlag( DesiredAccess, DELETE );
|
||
|
||
} else {
|
||
|
||
AccessStatusError = AccessStatus;
|
||
}
|
||
}
|
||
|
||
//
|
||
// Do the same test for read attributes and munge the desired access
|
||
// as appropriate
|
||
//
|
||
|
||
if (FlagOn(DesiredAccess, FILE_READ_ATTRIBUTES)
|
||
|| (MaximumRequested && !MaximumReadAttrAcquired)) {
|
||
|
||
ReadAttributesAccessGranted = SeAccessCheck( &ParentFcb->SharedSecurity->SecurityDescriptor,
|
||
&AccessState->SubjectSecurityContext,
|
||
TRUE, // Tokens are locked
|
||
FILE_LIST_DIRECTORY,
|
||
0,
|
||
&Privileges,
|
||
IoGetFileObjectGenericMapping(),
|
||
(KPROCESSOR_MODE)(FlagOn(IrpSp->Flags, SL_FORCE_ACCESS_CHECK) ?
|
||
UserMode : Irp->RequestorMode),
|
||
&ListDirectoryGrantedAccess,
|
||
&AccessStatus );
|
||
|
||
if (Privileges != NULL) { SeFreePrivileges( Privileges ); }
|
||
|
||
if (ReadAttributesAccessGranted) {
|
||
|
||
SetFlag( ListDirectoryGrantedAccess, FILE_READ_ATTRIBUTES );
|
||
ClearFlag( ListDirectoryGrantedAccess, FILE_LIST_DIRECTORY );
|
||
ClearFlag( DesiredAccess, FILE_READ_ATTRIBUTES );
|
||
|
||
} else {
|
||
|
||
AccessStatusError = AccessStatus;
|
||
}
|
||
}
|
||
|
||
if (DesiredAccess == 0) {
|
||
|
||
//
|
||
// If we got either the delete or list directory access then
|
||
// grant access.
|
||
//
|
||
|
||
if (ListDirectoryGrantedAccess != 0 ||
|
||
DeleteChildGrantedAccess != 0) {
|
||
|
||
AccessGranted = TRUE;
|
||
}
|
||
|
||
} else {
|
||
|
||
//
|
||
// Now the desired access has been munged by removing everything the parent
|
||
// has granted so now do the check on the child again
|
||
//
|
||
|
||
AccessGranted = SeAccessCheck( &Fcb->SharedSecurity->SecurityDescriptor,
|
||
&AccessState->SubjectSecurityContext,
|
||
TRUE, // Tokens are locked
|
||
DesiredAccess,
|
||
0,
|
||
&Privileges,
|
||
IoGetFileObjectGenericMapping(),
|
||
(KPROCESSOR_MODE)(FlagOn(IrpSp->Flags, SL_FORCE_ACCESS_CHECK) ?
|
||
UserMode : Irp->RequestorMode),
|
||
&GrantedAccess,
|
||
&AccessStatus );
|
||
|
||
if (Privileges != NULL) {
|
||
|
||
Status = SeAppendPrivileges( AccessState, Privileges );
|
||
SeFreePrivileges( Privileges );
|
||
}
|
||
|
||
//
|
||
// Suppose that we asked for MAXIMUM_ALLOWED and no access was allowed
|
||
// on the file. In that case the call above would fail. It's possible
|
||
// that we were given DELETE or READ_ATTR permission from the
|
||
// parent directory. If we have granted any access and the only remaining
|
||
// desired access is MAXIMUM_ALLOWED then grant this access.
|
||
//
|
||
|
||
if (!AccessGranted) {
|
||
|
||
AccessStatusError = AccessStatus;
|
||
|
||
if (DesiredAccess == MAXIMUM_ALLOWED &&
|
||
(ListDirectoryGrantedAccess != 0 ||
|
||
DeleteChildGrantedAccess != 0)) {
|
||
|
||
GrantedAccess = 0;
|
||
AccessGranted = TRUE;
|
||
}
|
||
}
|
||
}
|
||
|
||
//
|
||
// If we are given access this time then by definition one of the earlier
|
||
// parent checks had to have succeeded, otherwise we would have failed again
|
||
// and we can update the access state
|
||
//
|
||
|
||
if (!CheckOnly && AccessGranted) {
|
||
|
||
SetFlag( AccessState->PreviouslyGrantedAccess,
|
||
(GrantedAccess | DeleteChildGrantedAccess | ListDirectoryGrantedAccess) );
|
||
|
||
ClearFlag( AccessState->RemainingDesiredAccess,
|
||
(GrantedAccess | MAXIMUM_ALLOWED | DeleteChildGrantedAccess | ListDirectoryGrantedAccess) );
|
||
}
|
||
}
|
||
}
|
||
|
||
//
|
||
// Now call a routine that will do the proper open audit/alarm work
|
||
//
|
||
// **** We need to expand the audit alarm code to deal with
|
||
// create and traverse alarms.
|
||
//
|
||
|
||
//
|
||
// First we take a shortcut and see if we should bother setting up
|
||
// and making the audit call.
|
||
//
|
||
|
||
//
|
||
// NOTE: Calling SeAuditingFileEvents below disables per-user auditing functionality.
|
||
// To make per-user auditing work again, it is necessary to change the call below to
|
||
// be SeAuditingFileOrGlobalEvents, which also takes the subject context.
|
||
//
|
||
// The reason for calling SeAuditingFileEvents here is because per-user auditing is
|
||
// not currently exposed to users, and this routine imposes less of a performance
|
||
// penalty than does calling SeAuditingFileOrGlobalEvents.
|
||
//
|
||
|
||
if (SeAuditingFileEvents( AccessGranted, &Fcb->SharedSecurity->SecurityDescriptor )) {
|
||
|
||
BOOLEAN Found;
|
||
ATTRIBUTE_ENUMERATION_CONTEXT Context;
|
||
PFILE_NAME FileNameAttr;
|
||
UNICODE_STRING FileRecordName;
|
||
|
||
NtfsInitializeAttributeContext( &Context );
|
||
|
||
try {
|
||
|
||
//
|
||
// Construct the file name. The file name
|
||
// consists of:
|
||
//
|
||
// The device name out of the Vcb +
|
||
//
|
||
// The contents of the filename in the File Object +
|
||
//
|
||
// The contents of the Related File Object if it
|
||
// is present and the name in the File Object
|
||
// does not start with a '\'
|
||
//
|
||
//
|
||
// Obtain the file name.
|
||
//
|
||
|
||
PartialFileName = &IrpSp->FileObject->FileName;
|
||
|
||
PartialFileNamePresent = (PartialFileName->Length != 0);
|
||
|
||
if (!PartialFileNamePresent &&
|
||
FlagOn(IrpSp->Parameters.Create.Options, FILE_OPEN_BY_FILE_ID) ||
|
||
(IrpSp->FileObject->RelatedFileObject != NULL &&
|
||
IrpSp->FileObject->RelatedFileObject->FsContext2 != NULL &&
|
||
FlagOn(((PCCB) IrpSp->FileObject->RelatedFileObject->FsContext2)->Flags,
|
||
CCB_FLAG_OPEN_BY_FILE_ID))) {
|
||
|
||
//
|
||
// If this file is open by id or the relative file object is
|
||
// then get the first file name out of the file record.
|
||
//
|
||
|
||
Found = NtfsLookupAttributeByCode( IrpContext,
|
||
Fcb,
|
||
&Fcb->FileReference,
|
||
$FILE_NAME,
|
||
&Context );
|
||
|
||
while (Found) {
|
||
|
||
FileNameAttr = (PFILE_NAME) NtfsAttributeValue(
|
||
NtfsFoundAttribute( &Context ));
|
||
|
||
if (FileNameAttr->Flags != FILE_NAME_DOS) {
|
||
|
||
FileRecordName.Length = FileNameAttr->FileNameLength *
|
||
sizeof(WCHAR);
|
||
FileRecordName.MaximumLength = FileRecordName.Length;
|
||
FileRecordName.Buffer = FileNameAttr->FileName;
|
||
|
||
PartialFileNamePresent = TRUE;
|
||
PartialFileName = &FileRecordName;
|
||
break;
|
||
}
|
||
|
||
Found = NtfsLookupNextAttributeByCode( IrpContext,
|
||
Fcb,
|
||
$FILE_NAME,
|
||
&Context );
|
||
}
|
||
}
|
||
|
||
//
|
||
// Obtain the device name.
|
||
//
|
||
|
||
DeviceObjectName = &Fcb->Vcb->DeviceName;
|
||
|
||
DeviceObjectNameLength = DeviceObjectName->Length;
|
||
|
||
//
|
||
// Compute how much space we need for the final name string
|
||
//
|
||
|
||
FullFileName.MaximumLength = DeviceObjectNameLength +
|
||
PartialFileName->Length +
|
||
sizeof( UNICODE_NULL ) +
|
||
sizeof((WCHAR)'\\');
|
||
|
||
//
|
||
// If the partial file name starts with a '\', then don't use
|
||
// whatever may be in the related file name.
|
||
//
|
||
|
||
if (PartialFileNamePresent &&
|
||
((WCHAR)(PartialFileName->Buffer[0]) == L'\\' ||
|
||
PartialFileName == &FileRecordName)) {
|
||
|
||
LeadingSlash = TRUE;
|
||
|
||
} else {
|
||
|
||
//
|
||
// Since PartialFileName either doesn't exist or doesn't
|
||
// start with a '\', examine the RelatedFileName to see
|
||
// if it exists.
|
||
//
|
||
|
||
LeadingSlash = FALSE;
|
||
|
||
if (IrpSp->FileObject->RelatedFileObject != NULL) {
|
||
|
||
RelatedFileName = &IrpSp->FileObject->RelatedFileObject->FileName;
|
||
}
|
||
|
||
if (RelatedFileNamePresent = ((RelatedFileName != NULL) && (RelatedFileName->Length != 0))) {
|
||
|
||
FullFileName.MaximumLength += RelatedFileName->Length;
|
||
}
|
||
}
|
||
|
||
FullFileName.Buffer = NtfsAllocatePool(PagedPool, FullFileName.MaximumLength );
|
||
|
||
} finally {
|
||
|
||
NtfsCleanupAttributeContext( &Context );
|
||
if (AbnormalTermination()) {
|
||
|
||
SeUnlockSubjectContext( &AccessState->SubjectSecurityContext );
|
||
}
|
||
}
|
||
|
||
RtlCopyUnicodeString( &FullFileName, DeviceObjectName );
|
||
|
||
//
|
||
// RelatedFileNamePresent is not initialized if LeadingSlash == TRUE,
|
||
// but in that case we won't even examine it.
|
||
//
|
||
|
||
if (!LeadingSlash && RelatedFileNamePresent) {
|
||
|
||
Status = RtlAppendUnicodeStringToString( &FullFileName, RelatedFileName );
|
||
|
||
ASSERTMSG("RtlAppendUnicodeStringToString of RelatedFileName", NT_SUCCESS( Status ));
|
||
|
||
//
|
||
// RelatedFileName may simply be '\'. Don't append another
|
||
// '\' in this case.
|
||
//
|
||
|
||
if (RelatedFileName->Length != sizeof( WCHAR )) {
|
||
|
||
FullFileName.Buffer[ (FullFileName.Length / sizeof( WCHAR )) ] = L'\\';
|
||
FullFileName.Length += sizeof(WCHAR);
|
||
}
|
||
}
|
||
|
||
if (PartialFileNamePresent) {
|
||
|
||
Status = RtlAppendUnicodeStringToString( &FullFileName, PartialFileName );
|
||
|
||
//
|
||
// This should not fail
|
||
//
|
||
|
||
ASSERTMSG("RtlAppendUnicodeStringToString of PartialFileName failed", NT_SUCCESS( Status ));
|
||
}
|
||
|
||
|
||
if (PerformDeleteAudit) {
|
||
SeOpenObjectForDeleteAuditAlarm( &FileString,
|
||
NULL,
|
||
&FullFileName,
|
||
&Fcb->SharedSecurity->SecurityDescriptor,
|
||
AccessState,
|
||
FALSE,
|
||
AccessGranted,
|
||
(KPROCESSOR_MODE)(FlagOn(IrpSp->Flags, SL_FORCE_ACCESS_CHECK) ?
|
||
UserMode : Irp->RequestorMode),
|
||
&AccessState->GenerateOnClose );
|
||
} else {
|
||
SeOpenObjectAuditAlarm( &FileString,
|
||
NULL,
|
||
&FullFileName,
|
||
&Fcb->SharedSecurity->SecurityDescriptor,
|
||
AccessState,
|
||
FALSE,
|
||
AccessGranted,
|
||
(KPROCESSOR_MODE)(FlagOn(IrpSp->Flags, SL_FORCE_ACCESS_CHECK) ?
|
||
UserMode : Irp->RequestorMode),
|
||
&AccessState->GenerateOnClose );
|
||
|
||
}
|
||
|
||
NtfsFreePool( FullFileName.Buffer );
|
||
}
|
||
|
||
SeUnlockSubjectContext( &AccessState->SubjectSecurityContext );
|
||
|
||
//
|
||
// If access is not granted then we will raise
|
||
//
|
||
|
||
if (!AccessGranted) {
|
||
|
||
DebugTrace( 0, Dbg, ("Access Denied\n") );
|
||
|
||
NtfsRaiseStatus( IrpContext, AccessStatusError, NULL, NULL );
|
||
}
|
||
|
||
//
|
||
// And return to our caller
|
||
//
|
||
|
||
DebugTrace( -1, Dbg, ("NtfsAccessCheck -> VOID\n") );
|
||
|
||
return;
|
||
}
|
||
|
||
|
||
NTSTATUS
|
||
NtfsCheckFileForDelete (
|
||
IN PIRP_CONTEXT IrpContext,
|
||
IN PSCB ParentScb,
|
||
IN PFCB ThisFcb,
|
||
IN BOOLEAN FcbExisted,
|
||
IN PINDEX_ENTRY IndexEntry
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine checks that the caller has permission to delete the target
|
||
file of a rename or set link operation.
|
||
|
||
Arguments:
|
||
|
||
ParentScb - This is the parent directory for this file.
|
||
|
||
ThisFcb - This is the Fcb for the link being removed.
|
||
|
||
FcbExisted - Indicates if this Fcb was just created.
|
||
|
||
IndexEntry - This is the index entry on the disk for this file.
|
||
|
||
Return Value:
|
||
|
||
NTSTATUS - Indicating whether access was granted or the reason access
|
||
was denied.
|
||
|
||
--*/
|
||
|
||
{
|
||
UNICODE_STRING LastComponentFileName;
|
||
PFILE_NAME IndexFileName;
|
||
PLCB ThisLcb;
|
||
PFCB ParentFcb = ParentScb->Fcb;
|
||
|
||
PSCB NextScb = NULL;
|
||
|
||
BOOLEAN LcbExisted;
|
||
|
||
BOOLEAN AccessGranted;
|
||
ACCESS_MASK GrantedAccess;
|
||
NTSTATUS Status = STATUS_SUCCESS;
|
||
|
||
BOOLEAN UnlockSubjectContext = FALSE;
|
||
|
||
PPRIVILEGE_SET Privileges = NULL;
|
||
PAGED_CODE();
|
||
|
||
DebugTrace( +1, Dbg, ("NtfsCheckFileForDelete: Entered\n") );
|
||
|
||
ThisLcb = NULL;
|
||
|
||
IndexFileName = (PFILE_NAME) NtfsFoundIndexEntry( IndexEntry );
|
||
|
||
//
|
||
// If the unclean count is non-zero, we exit with an error.
|
||
//
|
||
|
||
if (ThisFcb->CleanupCount != 0) {
|
||
|
||
DebugTrace( 0, Dbg, ("Unclean count of target is non-zero\n") );
|
||
|
||
return STATUS_ACCESS_DENIED;
|
||
}
|
||
|
||
//
|
||
// We look at the index entry to see if the file is either a directory
|
||
// or a read-only file. We can't delete this for a target directory open.
|
||
//
|
||
|
||
if (IsDirectory( &ThisFcb->Info )
|
||
|| IsReadOnly( &ThisFcb->Info )) {
|
||
|
||
DebugTrace( -1, Dbg, ("NtfsCheckFileForDelete: Read only or directory\n") );
|
||
|
||
return STATUS_ACCESS_DENIED;
|
||
}
|
||
|
||
//
|
||
// We want to scan through all of the Scb for data streams on this file
|
||
// and look for image sections. We must be able to remove the image section
|
||
// in order to delete the file. Otherwise we can get the case where an
|
||
// active image (with no handle) could be deleted and subsequent faults
|
||
// through the image section will return zeroes.
|
||
//
|
||
|
||
if (ThisFcb->LinkCount == 1) {
|
||
|
||
BOOLEAN DecrementScb = FALSE;
|
||
|
||
//
|
||
// We will increment the Scb count to prevent this Scb from going away
|
||
// if the flush call below generates a close. Use a try-finally to
|
||
// restore the count.
|
||
//
|
||
|
||
try {
|
||
|
||
while ((NextScb = NtfsGetNextChildScb( ThisFcb, NextScb )) != NULL) {
|
||
|
||
InterlockedIncrement( &NextScb->CloseCount );
|
||
DecrementScb = TRUE;
|
||
|
||
if (NtfsIsTypeCodeUserData( NextScb->AttributeTypeCode ) &&
|
||
!FlagOn( NextScb->ScbState, SCB_STATE_ATTRIBUTE_DELETED ) &&
|
||
(NextScb->NonpagedScb->SegmentObject.ImageSectionObject != NULL)) {
|
||
|
||
if (!MmFlushImageSection( &NextScb->NonpagedScb->SegmentObject,
|
||
MmFlushForDelete )) {
|
||
|
||
Status = STATUS_ACCESS_DENIED;
|
||
leave;
|
||
}
|
||
}
|
||
|
||
InterlockedDecrement( &NextScb->CloseCount );
|
||
DecrementScb = FALSE;
|
||
}
|
||
|
||
} finally {
|
||
|
||
if (DecrementScb) {
|
||
|
||
InterlockedDecrement( &NextScb->CloseCount );
|
||
}
|
||
}
|
||
|
||
if (Status != STATUS_SUCCESS) {
|
||
|
||
return Status;
|
||
}
|
||
}
|
||
|
||
//
|
||
// We need to check if the link to this file has been deleted. We
|
||
// first check if we definitely know if the link is deleted by
|
||
// looking at the file name flags and the Fcb flags.
|
||
// If that result is uncertain, we need to create an Lcb and
|
||
// check the Lcb flags.
|
||
//
|
||
|
||
if (FcbExisted) {
|
||
|
||
if (FlagOn( IndexFileName->Flags, FILE_NAME_NTFS | FILE_NAME_DOS )) {
|
||
|
||
if (FlagOn( ThisFcb->FcbState, FCB_STATE_PRIMARY_LINK_DELETED )) {
|
||
|
||
DebugTrace( -1, Dbg, ("NtfsCheckFileForDelete: Link is going away\n") );
|
||
return STATUS_DELETE_PENDING;
|
||
}
|
||
|
||
//
|
||
// This is a Posix link. We need to create the link to test it
|
||
// for deletion.
|
||
//
|
||
|
||
} else {
|
||
|
||
LastComponentFileName.MaximumLength
|
||
= LastComponentFileName.Length = IndexFileName->FileNameLength * sizeof( WCHAR );
|
||
|
||
LastComponentFileName.Buffer = (PWCHAR) IndexFileName->FileName;
|
||
|
||
ThisLcb = NtfsCreateLcb( IrpContext,
|
||
ParentScb,
|
||
ThisFcb,
|
||
LastComponentFileName,
|
||
IndexFileName->Flags,
|
||
&LcbExisted );
|
||
|
||
if (FlagOn( ThisLcb->LcbState, LCB_STATE_DELETE_ON_CLOSE )) {
|
||
|
||
DebugTrace( -1, Dbg, ("NtfsCheckFileForDelete: Link is going away\n") );
|
||
|
||
return STATUS_DELETE_PENDING;
|
||
}
|
||
}
|
||
}
|
||
|
||
//
|
||
// Finally call the security package to check for delete access.
|
||
// We check for delete access on the target Fcb. If this succeeds, we
|
||
// are done. Otherwise we will check for delete child access on the
|
||
// the parent. Either is sufficient to perform the delete.
|
||
//
|
||
|
||
//
|
||
// Check if we need to load the security descriptor for the file
|
||
//
|
||
|
||
if (ThisFcb->SharedSecurity == NULL) {
|
||
|
||
NtfsLoadSecurityDescriptor( IrpContext, ThisFcb, ParentFcb );
|
||
}
|
||
|
||
ASSERT( ThisFcb->SharedSecurity != NULL );
|
||
|
||
//
|
||
// Use a try-finally to facilitate cleanup.
|
||
//
|
||
|
||
try {
|
||
|
||
//
|
||
// Lock the user context, do the access check and then unlock the context
|
||
//
|
||
|
||
SeLockSubjectContext( IrpContext->Union.SubjectContext );
|
||
UnlockSubjectContext = TRUE;
|
||
|
||
AccessGranted = SeAccessCheck( &ThisFcb->SharedSecurity->SecurityDescriptor,
|
||
IrpContext->Union.SubjectContext,
|
||
TRUE, // Tokens are locked
|
||
DELETE,
|
||
0,
|
||
&Privileges,
|
||
IoGetFileObjectGenericMapping(),
|
||
UserMode,
|
||
&GrantedAccess,
|
||
&Status );
|
||
|
||
//
|
||
// Check if the access is not granted and if we were given a parent fcb, and
|
||
// if the desired access was asking for delete or file read attributes. If so
|
||
// then we need to do some extra work to decide if the caller does get access
|
||
// based on the parent directories security descriptor
|
||
//
|
||
|
||
if (!AccessGranted) {
|
||
|
||
//
|
||
// Before we proceed load in the parent security descriptor
|
||
//
|
||
|
||
if (ParentFcb->SharedSecurity == NULL) {
|
||
|
||
NtfsLoadSecurityDescriptor( IrpContext, ParentFcb, NULL );
|
||
}
|
||
|
||
ASSERT( ParentFcb->SharedSecurity != NULL);
|
||
|
||
//
|
||
// Now if the user is asking for delete access then check if the parent
|
||
// will granted delete access to the child, and if so then we munge the
|
||
// desired access
|
||
//
|
||
|
||
AccessGranted = SeAccessCheck( &ParentFcb->SharedSecurity->SecurityDescriptor,
|
||
IrpContext->Union.SubjectContext,
|
||
TRUE, // Tokens are locked
|
||
FILE_DELETE_CHILD,
|
||
0,
|
||
&Privileges,
|
||
IoGetFileObjectGenericMapping(),
|
||
UserMode,
|
||
&GrantedAccess,
|
||
&Status );
|
||
}
|
||
|
||
} finally {
|
||
|
||
DebugUnwind( NtfsCheckFileForDelete );
|
||
|
||
if (UnlockSubjectContext) {
|
||
|
||
SeUnlockSubjectContext( IrpContext->Union.SubjectContext );
|
||
}
|
||
|
||
DebugTrace( +1, Dbg, ("NtfsCheckFileForDelete: Exit\n") );
|
||
}
|
||
|
||
return Status;
|
||
}
|
||
|
||
|
||
VOID
|
||
NtfsCheckIndexForAddOrDelete (
|
||
IN PIRP_CONTEXT IrpContext,
|
||
IN PFCB ParentFcb,
|
||
IN ACCESS_MASK DesiredAccess
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine checks if a caller has permission to remove or add a link
|
||
within a directory.
|
||
|
||
Arguments:
|
||
|
||
ParentFcb - This is the parent directory for the add or delete operation.
|
||
|
||
DesiredAccess - Indicates the type of operation. We could be adding or
|
||
removing and entry in the index.
|
||
|
||
Return Value:
|
||
|
||
None - This routine raises on error.
|
||
|
||
--*/
|
||
|
||
{
|
||
BOOLEAN AccessGranted;
|
||
ACCESS_MASK GrantedAccess;
|
||
NTSTATUS Status;
|
||
|
||
BOOLEAN UnlockSubjectContext = FALSE;
|
||
|
||
PPRIVILEGE_SET Privileges = NULL;
|
||
PAGED_CODE();
|
||
|
||
DebugTrace( +1, Dbg, ("NtfsCheckIndexForAddOrDelete: Entered\n") );
|
||
|
||
//
|
||
// Use a try-finally to facilitate cleanup.
|
||
//
|
||
|
||
try {
|
||
|
||
//
|
||
// Finally call the security package to check for delete access.
|
||
// We check for delete access on the target Fcb. If this succeeds, we
|
||
// are done. Otherwise we will check for delete child access on the
|
||
// the parent. Either is sufficient to perform the delete.
|
||
//
|
||
|
||
//
|
||
// Check if we need to load the security descriptor for the file
|
||
//
|
||
|
||
if (ParentFcb->SharedSecurity == NULL) {
|
||
|
||
NtfsLoadSecurityDescriptor( IrpContext, ParentFcb, NULL );
|
||
}
|
||
|
||
ASSERT( ParentFcb->SharedSecurity != NULL );
|
||
|
||
//
|
||
// Capture and lock the user context, do the access check and then unlock the context
|
||
//
|
||
|
||
SeLockSubjectContext( IrpContext->Union.SubjectContext );
|
||
UnlockSubjectContext = TRUE;
|
||
|
||
AccessGranted = SeAccessCheck( &ParentFcb->SharedSecurity->SecurityDescriptor,
|
||
IrpContext->Union.SubjectContext,
|
||
TRUE, // Tokens are locked
|
||
DesiredAccess,
|
||
0,
|
||
&Privileges,
|
||
IoGetFileObjectGenericMapping(),
|
||
UserMode,
|
||
&GrantedAccess,
|
||
&Status );
|
||
|
||
//
|
||
// If access is not granted then we will raise
|
||
//
|
||
|
||
if (!AccessGranted) {
|
||
|
||
DebugTrace( 0, Dbg, ("Access Denied\n") );
|
||
|
||
NtfsRaiseStatus( IrpContext, Status, NULL, NULL );
|
||
}
|
||
|
||
} finally {
|
||
|
||
DebugUnwind( NtfsCheckIndexForAddOrDelete );
|
||
|
||
if (UnlockSubjectContext) {
|
||
|
||
SeUnlockSubjectContext( IrpContext->Union.SubjectContext );
|
||
}
|
||
|
||
DebugTrace( +1, Dbg, ("NtfsCheckIndexForAddOrDelete: Exit\n") );
|
||
}
|
||
|
||
return;
|
||
}
|
||
|
||
|
||
VOID
|
||
NtfsUpdateFcbSecurity (
|
||
IN PIRP_CONTEXT IrpContext,
|
||
IN OUT PFCB Fcb,
|
||
IN PFCB ParentFcb OPTIONAL,
|
||
#ifdef _CAIRO_
|
||
IN SECURITY_ID SecurityId,
|
||
#endif // _CAIRO_
|
||
IN PSECURITY_DESCRIPTOR SecurityDescriptor,
|
||
IN ULONG SecurityDescriptorLength
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine is called to fill in the shared security structure in
|
||
an Fcb. We check the parent if present to determine if we have
|
||
a matching security descriptor and reference the existing one if
|
||
so. This routine must be called while holding the Vcb so we can
|
||
safely access the parent structure.
|
||
|
||
Arguments:
|
||
|
||
Fcb - Supplies the fcb for the file being operated on
|
||
|
||
ParentFcb - Optionally supplies a parent Fcb to examine for a
|
||
match. If not present, we will follow the Lcb chain in the target
|
||
Fcb.
|
||
|
||
SecurityDescriptor - Security Descriptor for this file.
|
||
|
||
SecurityDescriptorLength - Length of security descriptor for this file
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
--*/
|
||
|
||
{
|
||
PSHARED_SECURITY SharedSecurity = NULL;
|
||
PLCB ParentLcb;
|
||
PFCB LastParent = NULL;
|
||
#ifdef _CAIRO_
|
||
ULONG Hash = 0;
|
||
#endif // _CAIRO_
|
||
|
||
PAGED_CODE();
|
||
|
||
//
|
||
// Only continue with the load if the length is greater than zero
|
||
//
|
||
|
||
if (SecurityDescriptorLength == 0) {
|
||
|
||
return;
|
||
}
|
||
|
||
//
|
||
// Make sure the security descriptor we just read in is valid
|
||
//
|
||
|
||
if (!SeValidSecurityDescriptor( SecurityDescriptorLength, SecurityDescriptor )) {
|
||
|
||
SecurityDescriptor = NtfsData.DefaultDescriptor;
|
||
SecurityDescriptorLength = NtfsData.DefaultDescriptorLength;
|
||
|
||
if (!SeValidSecurityDescriptor( SecurityDescriptorLength, SecurityDescriptor )) {
|
||
|
||
NtfsRaiseStatus( IrpContext, STATUS_FILE_CORRUPT_ERROR, NULL, Fcb );
|
||
}
|
||
}
|
||
|
||
#ifdef _CAIRO_
|
||
//
|
||
// Hash security descriptor. This hash must be position independent to
|
||
// allow for multiple instances of the same descriptor. It is assumed
|
||
// that the bits within the security descriptor are all position
|
||
// independent, i.e, no pointers, all offsets.
|
||
//
|
||
// For speed in the hash, we consider the security descriptor as an array
|
||
// of ULONGs. The fragment at the end that is ignored should not affect
|
||
// the collision nature of this hash.
|
||
//
|
||
|
||
{
|
||
PULONG Rover = (PULONG)SecurityDescriptor;
|
||
ULONG Count = SecurityDescriptorLength / 4;
|
||
|
||
while (Count--)
|
||
{
|
||
Hash = ((Hash << 3) | (Hash >> (32-3))) + *Rover++;
|
||
}
|
||
|
||
DebugTrace( 0, Dbg, ("Hash is %08x\n", Hash) );
|
||
}
|
||
#endif // _CAIRO_
|
||
|
||
//
|
||
// Acquire the security event and use a try-finally to insure we release it.
|
||
//
|
||
|
||
NtfsAcquireFcbSecurity( Fcb->Vcb );
|
||
|
||
try {
|
||
|
||
//
|
||
// BUGBUG - since we have a cache based on a hash of security ID's, can
|
||
// we just skip this walk altogether?
|
||
//
|
||
|
||
//
|
||
// If we have a parent then check if this is a matching descriptor.
|
||
//
|
||
|
||
if (!ARGUMENT_PRESENT( ParentFcb )
|
||
&& !IsListEmpty( &Fcb->LcbQueue )) {
|
||
|
||
ParentLcb = CONTAINING_RECORD( Fcb->LcbQueue.Flink,
|
||
LCB,
|
||
FcbLinks );
|
||
|
||
if (ParentLcb != Fcb->Vcb->RootLcb) {
|
||
|
||
ParentFcb = ParentLcb->Scb->Fcb;
|
||
}
|
||
}
|
||
|
||
if (ParentFcb != NULL) {
|
||
|
||
while (TRUE) {
|
||
|
||
PSHARED_SECURITY NextSharedSecurity;
|
||
|
||
//
|
||
// If the target Fcb is an Index then use the security descriptor for
|
||
// our parent. Otherwise use the descriptor for a file on the drive.
|
||
//
|
||
|
||
if (FlagOn( Fcb->Info.FileAttributes, DUP_FILE_NAME_INDEX_PRESENT )) {
|
||
|
||
NextSharedSecurity = ParentFcb->SharedSecurity;
|
||
|
||
} else {
|
||
|
||
NextSharedSecurity = ParentFcb->ChildSharedSecurity;
|
||
}
|
||
|
||
if (NextSharedSecurity != NULL) {
|
||
|
||
if (GetSharedSecurityLength(NextSharedSecurity) == SecurityDescriptorLength
|
||
#ifdef _CAIRO_
|
||
&& NextSharedSecurity->Header.HashKey.Hash == Hash
|
||
#endif // _CAIRO_
|
||
&& RtlEqualMemory( &NextSharedSecurity->SecurityDescriptor,
|
||
SecurityDescriptor,
|
||
SecurityDescriptorLength )) {
|
||
|
||
SharedSecurity = NextSharedSecurity;
|
||
}
|
||
|
||
break;
|
||
}
|
||
|
||
LastParent = ParentFcb;
|
||
|
||
if (!IsListEmpty( &ParentFcb->LcbQueue )) {
|
||
|
||
ParentLcb = CONTAINING_RECORD( ParentFcb->LcbQueue.Flink,
|
||
LCB,
|
||
FcbLinks );
|
||
|
||
if (ParentLcb != Fcb->Vcb->RootLcb) {
|
||
|
||
ParentFcb = ParentLcb->Scb->Fcb;
|
||
|
||
} else {
|
||
|
||
break;
|
||
}
|
||
|
||
} else {
|
||
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
|
||
#ifdef _CAIRO_
|
||
//
|
||
// If we havent't found the security descriptor by walking up the tree then
|
||
// try to find it by hash
|
||
//
|
||
|
||
SharedSecurity =
|
||
NtOfsFindCachedSharedSecurityByHash( Fcb->Vcb,
|
||
SecurityDescriptor,
|
||
SecurityDescriptorLength,
|
||
Hash );
|
||
#endif
|
||
|
||
//
|
||
// If we can't find an existing descriptor allocate new pool and copy
|
||
// security descriptor into it.
|
||
//
|
||
|
||
if (SharedSecurity == NULL) {
|
||
SharedSecurity = NtfsAllocatePool(PagedPool, FIELD_OFFSET( SHARED_SECURITY,
|
||
SecurityDescriptor )
|
||
+ SecurityDescriptorLength );
|
||
|
||
//
|
||
// If this is a file and we have a Parent Fcb without a child
|
||
// descriptor we will store this one with that directory.
|
||
//
|
||
|
||
if (!FlagOn( Fcb->Info.FileAttributes, DUP_FILE_NAME_INDEX_PRESENT )
|
||
&& LastParent != NULL) {
|
||
|
||
SharedSecurity->ParentFcb = LastParent;
|
||
ASSERT( LastParent->ChildSharedSecurity == NULL );
|
||
|
||
LastParent->ChildSharedSecurity = SharedSecurity;
|
||
LastParent->ChildSharedSecurity->ReferenceCount = 1;
|
||
|
||
} else {
|
||
|
||
SharedSecurity->ParentFcb = NULL;
|
||
SharedSecurity->ReferenceCount = 0;
|
||
}
|
||
|
||
#ifdef _CAIRO_
|
||
//
|
||
// Initialize security index data in shared security
|
||
//
|
||
|
||
//
|
||
// Set the security id in the shared structure. If it is not
|
||
// invalid, also cache this shared security structure
|
||
//
|
||
|
||
SharedSecurity->Header.HashKey.SecurityId = SecurityId;
|
||
SharedSecurity->Header.HashKey.Hash = Hash;
|
||
if (SecurityId != SECURITY_ID_INVALID) {
|
||
NtOfsAddCachedSharedSecurity( Fcb->Vcb, SharedSecurity );
|
||
}
|
||
|
||
SetSharedSecurityLength(SharedSecurity, SecurityDescriptorLength);
|
||
SharedSecurity->Header.Offset = (ULONGLONG) 0xFFFFFFFFFFFFFFFFi64;
|
||
#else // _CAIRO_
|
||
SetSharedSecurityLength(SharedSecurity, SecurityDescriptorLength);
|
||
#endif // _CAIRO_
|
||
|
||
RtlCopyMemory( &SharedSecurity->SecurityDescriptor,
|
||
SecurityDescriptor,
|
||
SecurityDescriptorLength );
|
||
|
||
}
|
||
|
||
Fcb->SharedSecurity = SharedSecurity;
|
||
Fcb->SharedSecurity->ReferenceCount++;
|
||
Fcb->CreateSecurityCount++;
|
||
|
||
} finally {
|
||
|
||
DebugUnwind( NtfsUpdateFcbSecurity );
|
||
|
||
NtfsReleaseFcbSecurity( Fcb->Vcb );
|
||
}
|
||
|
||
return;
|
||
}
|
||
|
||
|
||
_inline
|
||
VOID
|
||
NtfsRemoveReferenceSharedSecurity (
|
||
IN OUT PSHARED_SECURITY SharedSecurity
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine is called to manage the reference count on a shared security
|
||
descriptor. If the reference count goes to zero, the shared security is
|
||
freed.
|
||
|
||
Arguments:
|
||
|
||
SharedSecurity - security that is being dereferenced.
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
--*/
|
||
{
|
||
//
|
||
// Note that there will be one less reference shortly
|
||
//
|
||
|
||
SharedSecurity->ReferenceCount--;
|
||
|
||
//
|
||
// If there is another reference to this shared security *AND* this
|
||
// shared security is being shared as a parent's child FCB then
|
||
// decouple it from the parent.
|
||
//
|
||
|
||
if (SharedSecurity->ReferenceCount == 1 && SharedSecurity->ParentFcb != NULL) {
|
||
|
||
//
|
||
// Verify that the parent's child matches this shared security
|
||
//
|
||
|
||
ASSERT( SharedSecurity->ParentFcb->ChildSharedSecurity == SharedSecurity );
|
||
|
||
//
|
||
// Remove reference from parent fcb
|
||
//
|
||
|
||
SharedSecurity->ParentFcb->ChildSharedSecurity = NULL;
|
||
SharedSecurity->ReferenceCount--;
|
||
SharedSecurity->ParentFcb = NULL;
|
||
}
|
||
|
||
if (SharedSecurity->ReferenceCount == 0) {
|
||
NtfsFreePool( SharedSecurity );
|
||
}
|
||
}
|
||
|
||
VOID
|
||
NtfsDereferenceSharedSecurity (
|
||
IN OUT PFCB Fcb
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine is called to dereference the shared security structure in
|
||
an Fcb and deallocate it if possible.
|
||
|
||
Arguments:
|
||
|
||
Fcb - Supplies the fcb for the file being operated on.
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
--*/
|
||
|
||
{
|
||
PSHARED_SECURITY SharedSecurity;
|
||
PAGED_CODE();
|
||
|
||
//
|
||
// Remove the reference and capture the shared security if we are to free it.
|
||
//
|
||
|
||
SharedSecurity = Fcb->SharedSecurity;
|
||
Fcb->SharedSecurity = NULL;
|
||
NtfsRemoveReferenceSharedSecurity( SharedSecurity );
|
||
}
|
||
|
||
BOOLEAN
|
||
NtfsNotifyTraverseCheck (
|
||
IN PCCB Ccb,
|
||
IN PFCB Fcb,
|
||
IN PSECURITY_SUBJECT_CONTEXT SubjectContext
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine is the callback routine provided to the dir notify package
|
||
to check that a caller who is watching a tree has traverse access to
|
||
the directory which has the change. This routine is only called
|
||
when traverse access checking was turned on for the open used to
|
||
perform the watch.
|
||
|
||
Arguments:
|
||
|
||
Ccb - This is the Ccb associated with the directory which is being
|
||
watched.
|
||
|
||
Fcb - This is the Fcb for the directory which contains the file being
|
||
modified. We want to walk up the tree from this point and check
|
||
that the caller has traverse access across that directory.
|
||
If not specified then there is no work to do.
|
||
|
||
SubjectContext - This is the subject context captured at the time the
|
||
dir notify call was made.
|
||
|
||
Return Value:
|
||
|
||
BOOLEAN - TRUE if the caller has traverse access to the file which was
|
||
changed. FALSE otherwise.
|
||
|
||
--*/
|
||
|
||
{
|
||
TOP_LEVEL_CONTEXT TopLevelContext;
|
||
PTOP_LEVEL_CONTEXT ThreadTopLevelContext;
|
||
|
||
PFCB TopFcb;
|
||
|
||
IRP_CONTEXT LocalIrpContext;
|
||
IRP LocalIrp;
|
||
|
||
PIRP_CONTEXT IrpContext;
|
||
|
||
BOOLEAN AccessGranted;
|
||
ACCESS_MASK GrantedAccess;
|
||
NTSTATUS Status = STATUS_SUCCESS;
|
||
|
||
PPRIVILEGE_SET Privileges = NULL;
|
||
PAGED_CODE();
|
||
|
||
//
|
||
// If we have no Fcb then we can return immediately.
|
||
//
|
||
|
||
if (Fcb == NULL) {
|
||
|
||
return TRUE;
|
||
}
|
||
|
||
RtlZeroMemory( &LocalIrpContext, sizeof(LocalIrpContext) );
|
||
RtlZeroMemory( &LocalIrp, sizeof(LocalIrp) );
|
||
|
||
IrpContext = &LocalIrpContext;
|
||
IrpContext->NodeTypeCode = NTFS_NTC_IRP_CONTEXT;
|
||
IrpContext->NodeByteSize = sizeof(IRP_CONTEXT);
|
||
IrpContext->OriginatingIrp = &LocalIrp;
|
||
SetFlag(IrpContext->Flags, IRP_CONTEXT_FLAG_WAIT);
|
||
InitializeListHead( &IrpContext->ExclusiveFcbList );
|
||
IrpContext->Vcb = Fcb->Vcb;
|
||
|
||
//
|
||
// Make sure we don't get any pop-ups
|
||
//
|
||
|
||
ThreadTopLevelContext = NtfsSetTopLevelIrp( &TopLevelContext, TRUE, FALSE );
|
||
ASSERT( ThreadTopLevelContext == &TopLevelContext );
|
||
|
||
NtfsUpdateIrpContextWithTopLevel( IrpContext, &TopLevelContext );
|
||
|
||
TopFcb = Ccb->Lcb->Fcb;
|
||
|
||
//
|
||
// Use a try-except to catch all of the errors.
|
||
//
|
||
|
||
try {
|
||
|
||
//
|
||
// Always lock the subject context.
|
||
//
|
||
|
||
SeLockSubjectContext( SubjectContext );
|
||
|
||
//
|
||
// Use a try-finally to perform local cleanup.
|
||
//
|
||
|
||
try {
|
||
|
||
//
|
||
// We look while walking up the tree.
|
||
//
|
||
|
||
do {
|
||
|
||
PLCB ParentLcb;
|
||
|
||
//
|
||
// Since this is a directory it can have only one parent. So
|
||
// we can use any Lcb to walk upwards.
|
||
//
|
||
|
||
ParentLcb = CONTAINING_RECORD( Fcb->LcbQueue.Flink,
|
||
LCB,
|
||
FcbLinks );
|
||
|
||
Fcb = ParentLcb->Scb->Fcb;
|
||
|
||
//
|
||
// Check if we need to load the security descriptor for the file
|
||
//
|
||
|
||
if (Fcb->SharedSecurity == NULL) {
|
||
|
||
NtfsLoadSecurityDescriptor( IrpContext, Fcb, NULL );
|
||
}
|
||
|
||
AccessGranted = SeAccessCheck( &Fcb->SharedSecurity->SecurityDescriptor,
|
||
SubjectContext,
|
||
TRUE, // Tokens are locked
|
||
FILE_TRAVERSE,
|
||
0,
|
||
&Privileges,
|
||
IoGetFileObjectGenericMapping(),
|
||
UserMode,
|
||
&GrantedAccess,
|
||
&Status );
|
||
|
||
} while ( AccessGranted && Fcb != TopFcb );
|
||
|
||
} finally {
|
||
|
||
SeUnlockSubjectContext( SubjectContext );
|
||
}
|
||
|
||
} except (NtfsExceptionFilter( IrpContext, GetExceptionInformation() )) {
|
||
|
||
NOTHING;
|
||
}
|
||
|
||
NtfsRestoreTopLevelIrp( &TopLevelContext );
|
||
|
||
return AccessGranted;
|
||
}
|
||
|
||
|
||
#ifdef _CAIRO_
|
||
VOID
|
||
NtfsInitializeSecurity (
|
||
IN PIRP_CONTEXT IrpContext,
|
||
IN PVCB Vcb,
|
||
IN PFCB Fcb
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine is called to initialize the security indexes and descriptor
|
||
stream.
|
||
|
||
Arguments:
|
||
|
||
IrpContext - context of call
|
||
|
||
Vcb - Supplies the volume being initialized
|
||
|
||
Fcb - Supplies the file containing the seurity indexes and descriptor
|
||
stream.
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
--*/
|
||
{
|
||
UNICODE_STRING SecurityIdIndexName =
|
||
CONSTANT_UNICODE_STRING( L"$SecurityIdIndex" );
|
||
UNICODE_STRING SecurityDescriptorHashIndexName =
|
||
CONSTANT_UNICODE_STRING( L"$SecurityDescriptorHashIndex" );
|
||
UNICODE_STRING SecurityDescriptorStreamName =
|
||
CONSTANT_UNICODE_STRING( L"$SecurityDescriptorStream" );
|
||
|
||
MAP_HANDLE Map;
|
||
NTSTATUS Status;
|
||
|
||
PAGED_CODE( );
|
||
|
||
//
|
||
// Open/Create the security descriptor stream
|
||
//
|
||
|
||
NtOfsCreateAttribute( IrpContext,
|
||
Fcb,
|
||
SecurityDescriptorStreamName,
|
||
CREATE_OR_OPEN,
|
||
TRUE,
|
||
&Vcb->SecurityDescriptorStream );
|
||
|
||
NtfsAcquireSharedScb( IrpContext, Vcb->SecurityDescriptorStream );
|
||
|
||
//
|
||
// Load the run information for the Security data stream.
|
||
// Note this call must be done after the stream is nonresident.
|
||
//
|
||
|
||
if (!FlagOn( Vcb->SecurityDescriptorStream->ScbState, SCB_STATE_ATTRIBUTE_RESIDENT )) {
|
||
NtfsPreloadAllocation( IrpContext,
|
||
Vcb->SecurityDescriptorStream,
|
||
0,
|
||
MAXLONGLONG );
|
||
}
|
||
|
||
//
|
||
// Open the Security descriptor indexes and storage.
|
||
// BUGBUG: At present, these attributes are stored as part of the
|
||
// QuotaTable file record.
|
||
//
|
||
|
||
NtOfsCreateIndex( IrpContext,
|
||
Fcb,
|
||
SecurityIdIndexName,
|
||
CREATE_OR_OPEN,
|
||
0,
|
||
NtOfsCollateUlong,
|
||
NULL,
|
||
&Vcb->SecurityIdIndex );
|
||
|
||
NtOfsCreateIndex( IrpContext,
|
||
Fcb,
|
||
SecurityDescriptorHashIndexName,
|
||
CREATE_OR_OPEN,
|
||
0,
|
||
NtOfsCollateSecurityHash,
|
||
NULL,
|
||
&Vcb->SecurityDescriptorHashIndex );
|
||
|
||
//
|
||
// Retrieve the next security Id to allocate
|
||
//
|
||
|
||
try {
|
||
|
||
SECURITY_ID LastSecurityId = 0xFFFFFFFF;
|
||
INDEX_KEY LastKey;
|
||
INDEX_ROW LastRow;
|
||
|
||
LastKey.KeyLength = sizeof( SECURITY_ID );
|
||
LastKey.Key = &LastSecurityId;
|
||
|
||
Map.Bcb = NULL;
|
||
|
||
Status = NtOfsFindLastRecord( IrpContext,
|
||
Vcb->SecurityIdIndex,
|
||
&LastKey,
|
||
&LastRow,
|
||
&Map );
|
||
|
||
//
|
||
// If we've found the last key, set the next Id to allocate to be
|
||
// one greater than this last key.
|
||
//
|
||
|
||
if (Status == STATUS_SUCCESS) {
|
||
|
||
ASSERT( LastRow.KeyPart.KeyLength == sizeof( SECURITY_ID ) );
|
||
if (LastRow.KeyPart.KeyLength != sizeof( SECURITY_ID )) {
|
||
|
||
NtfsRaiseStatus( IrpContext, STATUS_DISK_CORRUPT_ERROR, NULL, NULL );
|
||
}
|
||
|
||
DebugTrace( 0, Dbg, ("Found last security Id in index\n") );
|
||
Vcb->NextSecurityId = *(SECURITY_ID *)LastRow.KeyPart.Key + 1;
|
||
|
||
//
|
||
// If the index is empty, then set the next Id to be the beginning of the
|
||
// user range.
|
||
//
|
||
|
||
} else if (Status == STATUS_NO_MATCH) {
|
||
|
||
DebugTrace( 0, Dbg, ("Security Id index is empty\n") );
|
||
Vcb->NextSecurityId = SECURITY_ID_FIRST;
|
||
|
||
} else {
|
||
|
||
NtfsRaiseStatus( IrpContext, Status, NULL, NULL );
|
||
}
|
||
|
||
DebugTrace( 0, Dbg, ("NextSecurityId is %x\n", Vcb->NextSecurityId) );
|
||
|
||
} finally {
|
||
|
||
NtOfsReleaseMap( IrpContext, &Map );
|
||
}
|
||
}
|
||
#endif // _CAIRO_
|
||
|
||
//
|
||
// Local Support routine
|
||
//
|
||
|
||
#ifdef _CAIRO_
|
||
|
||
PSHARED_SECURITY
|
||
NtOfsFindCachedSharedSecurityBySecurityId (
|
||
IN PVCB Vcb,
|
||
IN SECURITY_ID SecurityId
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine maps looks up a shared security structure given the security Id by
|
||
looking in the per-Vcb cache. This routine assumes exclusive access to the
|
||
security cache.
|
||
|
||
Arguments:
|
||
|
||
Vcb - Volume where security Id is cached
|
||
|
||
SecurityId - security Id for descriptor that is being retrieved
|
||
|
||
Return Value:
|
||
|
||
PSHARED_SECURITY of found descriptor. Otherwise, NULL is returned.
|
||
|
||
--*/
|
||
{
|
||
PSHARED_SECURITY SharedSecurity;
|
||
|
||
PAGED_CODE( );
|
||
|
||
SharedSecurity = Vcb->SecurityCacheById[SecurityId % VCB_SECURITY_CACHE_BY_ID_SIZE];
|
||
|
||
//
|
||
// If there is no security descriptor there then no match was found
|
||
//
|
||
|
||
if (SharedSecurity == NULL) {
|
||
return NULL;
|
||
}
|
||
|
||
//
|
||
// If the security Id's don't match then no descriptor was found
|
||
//
|
||
|
||
if (SharedSecurity->Header.HashKey.SecurityId != SecurityId) {
|
||
return NULL;
|
||
}
|
||
|
||
//
|
||
// The shared security was found
|
||
//
|
||
|
||
return SharedSecurity;
|
||
}
|
||
|
||
#endif // _CAIRO_
|
||
|
||
//
|
||
// Local Support routine
|
||
//
|
||
|
||
#ifdef _CAIRO_
|
||
|
||
PSHARED_SECURITY
|
||
NtOfsFindCachedSharedSecurityByHash (
|
||
IN PVCB Vcb,
|
||
IN PSECURITY_DESCRIPTOR SecurityDescriptor,
|
||
IN ULONG SecurityDescriptorLength,
|
||
IN ULONG Hash
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine maps looks up a shared security structure given the Hash by
|
||
looking in the per-Vcb cache. This routine assumes exclusive access to the
|
||
security cache.
|
||
|
||
Arguments:
|
||
|
||
Vcb - Volume where security Id is cached
|
||
|
||
SecurityDescriptor - Security descriptor being retrieved
|
||
|
||
SecurityDescriptorLength - length of descriptor.
|
||
|
||
Hash - Hash for descriptor that is being retrieved
|
||
|
||
Return Value:
|
||
|
||
PSHARED_SECURITY of found shared descriptor. Otherwise, NULL is returned.
|
||
|
||
--*/
|
||
{
|
||
PSHARED_SECURITY *SharedSecurity;
|
||
|
||
PAGED_CODE( );
|
||
|
||
//
|
||
// Hash the hash into the per-volume table
|
||
|
||
SharedSecurity = Vcb->SecurityCacheByHash[Hash % VCB_SECURITY_CACHE_BY_HASH_SIZE];
|
||
|
||
//
|
||
// If there is no shared descriptor there, then no match
|
||
//
|
||
|
||
if (SharedSecurity == NULL || *SharedSecurity == NULL) {
|
||
return NULL;
|
||
}
|
||
|
||
//
|
||
// if the hash doesn't match then no descriptor found
|
||
//
|
||
|
||
if ((*SharedSecurity)->Header.HashKey.Hash != Hash) {
|
||
return NULL;
|
||
}
|
||
|
||
//
|
||
// If the lengths don't match then no descriptor found
|
||
//
|
||
|
||
if (GetSharedSecurityLength( *SharedSecurity ) != SecurityDescriptorLength) {
|
||
return NULL;
|
||
}
|
||
|
||
//
|
||
// If the security descriptor bits don't compare then no match
|
||
//
|
||
|
||
if (!RtlEqualMemory( (*SharedSecurity)->SecurityDescriptor,
|
||
SecurityDescriptor,
|
||
SecurityDescriptorLength) ) {
|
||
return NULL;
|
||
}
|
||
|
||
|
||
//
|
||
// The shared security was found
|
||
//
|
||
|
||
return *SharedSecurity;
|
||
}
|
||
|
||
#endif // _CAIRO_
|
||
|
||
//
|
||
// Local Support routine
|
||
//
|
||
|
||
#ifdef _CAIRO_
|
||
|
||
void
|
||
NtOfsAddCachedSharedSecurity (
|
||
IN PVCB Vcb,
|
||
PSHARED_SECURITY SharedSecurity
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine adds shared security to the Vcb Cache. This routine assumes
|
||
exclusive access to the security cache. The shared security being added
|
||
may have a ref count of one and may already be in the table.
|
||
|
||
Arguments:
|
||
|
||
Vcb - Volume where security Id is cached
|
||
|
||
SharedSecurity - descriptor to be added to the cache
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
--*/
|
||
{
|
||
PSHARED_SECURITY *Bucket;
|
||
PSHARED_SECURITY Old;
|
||
|
||
PAGED_CODE( );
|
||
|
||
//
|
||
// Is there an item already in the hash bucket?
|
||
//
|
||
|
||
Bucket = &Vcb->SecurityCacheById[SharedSecurity->Header.HashKey.SecurityId % VCB_SECURITY_CACHE_BY_ID_SIZE];
|
||
|
||
Old = *Bucket;
|
||
|
||
//
|
||
// Place it into the bucket and reference it
|
||
//
|
||
|
||
*Bucket = SharedSecurity;
|
||
SharedSecurity->ReferenceCount ++;
|
||
|
||
//
|
||
// Set up hash to point to bucket
|
||
//
|
||
|
||
Vcb->SecurityCacheByHash[SharedSecurity->Header.HashKey.Hash % VCB_SECURITY_CACHE_BY_HASH_SIZE] =
|
||
Bucket;
|
||
|
||
//
|
||
// Handle removing the old value from the bucket. We do this after advancing
|
||
// the ReferenceCount above in the case where the item is already in the bucket.
|
||
//
|
||
|
||
if (Old != NULL) {
|
||
//
|
||
// Remove and dereference the item in the bucket
|
||
//
|
||
|
||
// *Bucket = NULL;
|
||
NtfsRemoveReferenceSharedSecurity( Old );
|
||
}
|
||
|
||
}
|
||
|
||
#endif // _CAIRO_
|
||
|
||
|
||
#ifdef _CAIRO_
|
||
|
||
VOID
|
||
NtOfsPurgeSecurityCache (
|
||
IN PVCB Vcb
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine removes all shared security from the per-Vcb cache.
|
||
|
||
Arguments:
|
||
|
||
Vcb - Volume where descriptors are cached
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
--*/
|
||
{
|
||
ULONG i;
|
||
|
||
PAGED_CODE( );
|
||
|
||
//
|
||
// Serialize access to the security cache
|
||
//
|
||
|
||
NtfsAcquireFcbSecurity( Vcb );
|
||
|
||
//
|
||
// Walk through the cache looking for cached security
|
||
//
|
||
|
||
for (i = 0; i < VCB_SECURITY_CACHE_BY_ID_SIZE; i++)
|
||
{
|
||
if (Vcb->SecurityCacheById[i] != NULL) {
|
||
//
|
||
// Remove the reference to the security
|
||
//
|
||
|
||
PSHARED_SECURITY SharedSecurity = Vcb->SecurityCacheById[i];
|
||
Vcb->SecurityCacheById[i] = NULL;
|
||
NtfsRemoveReferenceSharedSecurity( SharedSecurity );
|
||
}
|
||
}
|
||
|
||
//
|
||
// Release access to the cache
|
||
//
|
||
|
||
NtfsReleaseFcbSecurity( Vcb );
|
||
}
|
||
#endif // _CAIRO_
|
||
|
||
|
||
//
|
||
// Local Support routine
|
||
//
|
||
|
||
#ifdef _CAIRO_
|
||
|
||
VOID
|
||
NtOfsMapSecurityIdToSecurityDescriptor (
|
||
IN PIRP_CONTEXT IrpContext,
|
||
IN PVCB Vcb,
|
||
IN SECURITY_ID SecurityId,
|
||
OUT PSECURITY_DESCRIPTOR *SecurityDescriptor,
|
||
OUT PULONG SecurityDescriptorLength,
|
||
OUT PBCB *Bcb
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine maps from a security Id to the descriptor bits stored in the
|
||
security descriptor stream using the security Id index
|
||
|
||
Arguments:
|
||
|
||
IrpContext - Context of the call
|
||
|
||
Vcb - Volume where descriptor is stored
|
||
|
||
SecurityId - security Id for descriptor that is being retrieved
|
||
|
||
SecurityDescriptor - returned security descriptor pointer
|
||
|
||
SecurityDescriptorLength - returned length of security descriptor
|
||
|
||
Bcb - returned mapping control structure
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
--*/
|
||
{
|
||
SECURITY_DESCRIPTOR_HEADER Header;
|
||
NTSTATUS Status;
|
||
MAP_HANDLE Map;
|
||
INDEX_ROW Row;
|
||
INDEX_KEY Key;
|
||
|
||
PAGED_CODE( );
|
||
|
||
DebugTrace( 0, Dbg, ("Mapping security ID %08x\n", SecurityId) );
|
||
|
||
//
|
||
// Lookup descriptor stream position information.
|
||
// The format of the key is simply the ULONG SecurityId
|
||
//
|
||
|
||
Key.KeyLength = sizeof( SecurityId );
|
||
Key.Key = &SecurityId;
|
||
|
||
Status = NtOfsFindRecord( IrpContext,
|
||
Vcb->SecurityIdIndex,
|
||
&Key,
|
||
&Row,
|
||
&Map,
|
||
NULL );
|
||
|
||
DebugTrace( 0, Dbg, ("Security Id lookup status = %08x\n", Status) );
|
||
|
||
//
|
||
// If the security Id is not found, then this volume is corrupt.
|
||
// We raise the error which will force CHKDSK to be run to rebuild
|
||
// the mapping index.
|
||
//
|
||
|
||
if (Status == STATUS_NO_MATCH) {
|
||
DebugTrace( 0, Dbg, ("SecurityId is not found in index\n") );
|
||
NtfsRaiseStatus( IrpContext, STATUS_DISK_CORRUPT_ERROR, NULL, NULL );
|
||
}
|
||
|
||
//
|
||
// Save security descriptor offset and length information
|
||
//
|
||
|
||
Header = *(PSECURITY_DESCRIPTOR_HEADER)Row.DataPart.Data;
|
||
ASSERT( Header.HashKey.SecurityId == SecurityId );
|
||
|
||
//
|
||
// Release mapping information
|
||
//
|
||
|
||
NtOfsReleaseMap( IrpContext, &Map );
|
||
|
||
//
|
||
// Make sure that the data is the correct size
|
||
//
|
||
|
||
ASSERT( Row.DataPart.DataLength == sizeof( SECURITY_DESCRIPTOR_HEADER ) );
|
||
if (Row.DataPart.DataLength != sizeof( SECURITY_DESCRIPTOR_HEADER )) {
|
||
DebugTrace( 0, Dbg, ("SecurityId data doesn't have the correct length\n") );
|
||
NtfsRaiseStatus( IrpContext, STATUS_DISK_CORRUPT_ERROR, NULL, NULL );
|
||
}
|
||
|
||
//
|
||
// Map security descriptor
|
||
//
|
||
|
||
DebugTrace( 0, Dbg, ("Mapping security descriptor stream at %I64x, len %x\n",
|
||
Header.Offset, Header.Length) );
|
||
|
||
NtfsMapStream(
|
||
IrpContext,
|
||
Vcb->SecurityDescriptorStream,
|
||
Header.Offset,
|
||
Header.Length,
|
||
Bcb,
|
||
SecurityDescriptor );
|
||
|
||
//
|
||
// Set return values
|
||
//
|
||
|
||
*SecurityDescriptor =
|
||
(PSECURITY_DESCRIPTOR) Add2Ptr( *SecurityDescriptor,
|
||
sizeof( SECURITY_DESCRIPTOR_HEADER ) );
|
||
*SecurityDescriptorLength =
|
||
GETSECURITYDESCRIPTORLENGTH( &Header );
|
||
}
|
||
|
||
|
||
VOID
|
||
NtfsLoadSecurityDescriptorById (
|
||
IN PIRP_CONTEXT IrpContext,
|
||
IN PFCB Fcb,
|
||
IN PFCB ParentFcb OPTIONAL
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine finds or creates the shared security for the specified
|
||
Fcb by looking in the volume cache or index
|
||
|
||
Arguments:
|
||
|
||
IrpContext - Context of call
|
||
|
||
Fcb - File whose security is to be loaded
|
||
|
||
ParentFcb - FCB of parent when searching upward to find already-cached
|
||
descriptor
|
||
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
--*/
|
||
{
|
||
PSHARED_SECURITY SharedSecurity;
|
||
|
||
PAGED_CODE( );
|
||
|
||
//
|
||
// Serialize access to the security cache
|
||
//
|
||
|
||
NtfsAcquireFcbSecurity( Fcb->Vcb );
|
||
|
||
//
|
||
// First, consult the Vcb cache of security Ids
|
||
//
|
||
|
||
SharedSecurity = NtOfsFindCachedSharedSecurityBySecurityId( Fcb->Vcb, Fcb->SecurityId );
|
||
|
||
//
|
||
// If we found one, store it in the Fcb and we're done
|
||
//
|
||
|
||
if (SharedSecurity != NULL) {
|
||
|
||
Fcb->SharedSecurity = SharedSecurity;
|
||
Fcb->SharedSecurity->ReferenceCount++;
|
||
Fcb->CreateSecurityCount += 1;
|
||
|
||
DebugTrace( 0, DbgAcl, ("Found cached security descriptor %x %x\n",
|
||
SharedSecurity, SharedSecurity->Header.HashKey.SecurityId) );
|
||
|
||
//
|
||
// Release access to security cache
|
||
//
|
||
|
||
NtfsReleaseFcbSecurity( Fcb->Vcb );
|
||
|
||
} else {
|
||
PBCB Bcb = NULL;
|
||
PSECURITY_DESCRIPTOR SecurityDescriptor;
|
||
ULONG SecurityDescriptorLength;
|
||
|
||
//
|
||
// Release access to security cache
|
||
//
|
||
|
||
NtfsReleaseFcbSecurity( Fcb->Vcb );
|
||
DebugTrace( 0, Dbg, ("Looking up security descriptor %x\n", Fcb->SecurityId) );
|
||
|
||
//
|
||
// Lock down the security stream
|
||
//
|
||
|
||
NtfsAcquireSharedScb( IrpContext, Fcb->Vcb->SecurityDescriptorStream );
|
||
|
||
try {
|
||
|
||
//
|
||
// Consult the Vcb index to map to the security descriptor
|
||
//
|
||
|
||
NtOfsMapSecurityIdToSecurityDescriptor( IrpContext,
|
||
Fcb->Vcb,
|
||
Fcb->SecurityId,
|
||
&SecurityDescriptor,
|
||
&SecurityDescriptorLength,
|
||
&Bcb );
|
||
|
||
//
|
||
// Generate the shared security from the security Id and descriptor
|
||
//
|
||
|
||
NtfsUpdateFcbSecurity( IrpContext,
|
||
Fcb,
|
||
ParentFcb,
|
||
Fcb->SecurityId,
|
||
SecurityDescriptor,
|
||
SecurityDescriptorLength );
|
||
|
||
} finally {
|
||
NtfsUnpinBcb( &Bcb );
|
||
NtfsReleaseScb( IrpContext, Fcb->Vcb->SecurityDescriptorStream );
|
||
}
|
||
}
|
||
|
||
}
|
||
#endif // _CAIRO_
|
||
|
||
|
||
//
|
||
// Local Support routine
|
||
//
|
||
|
||
VOID
|
||
NtfsLoadSecurityDescriptor (
|
||
IN PIRP_CONTEXT IrpContext,
|
||
IN PFCB Fcb,
|
||
IN PFCB ParentFcb OPTIONAL
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine loads the shared security descriptor into the fcb for the
|
||
file from disk using either the SecurityId or the $Security_Descriptor
|
||
|
||
Arguments:
|
||
|
||
Fcb - Supplies the fcb for the file being operated on
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
--*/
|
||
|
||
{
|
||
PAGED_CODE();
|
||
|
||
ASSERTMSG("Must only be called with a null value here", Fcb->SharedSecurity == NULL);
|
||
|
||
DebugTrace( +1, Dbg, ("NtfsLoadSecurityDescriptor...\n") );
|
||
|
||
#ifdef _CAIRO_
|
||
//
|
||
// If the file has a valid SecurityId then retrieve the security descriptor
|
||
// from the security descriptor index
|
||
//
|
||
|
||
if (Fcb->SecurityId != SECURITY_ID_INVALID) {
|
||
|
||
NtfsLoadSecurityDescriptorById( IrpContext, Fcb, ParentFcb );
|
||
} else
|
||
#endif // _CAIRO_
|
||
{
|
||
PBCB Bcb = NULL;
|
||
PSHARED_SECURITY SharedSecurity;
|
||
PSECURITY_DESCRIPTOR SecurityDescriptor;
|
||
ULONG SecurityDescriptorLength;
|
||
ATTRIBUTE_ENUMERATION_CONTEXT AttributeContext;
|
||
PATTRIBUTE_RECORD_HEADER Attribute;
|
||
|
||
try {
|
||
//
|
||
// Read in the security descriptor attribute, and it is is not present
|
||
// then there then the file is not protected. In that case we will
|
||
// use the default descriptor.
|
||
//
|
||
|
||
NtfsInitializeAttributeContext( &AttributeContext );
|
||
|
||
if (!NtfsLookupAttributeByCode( IrpContext,
|
||
Fcb,
|
||
&Fcb->FileReference,
|
||
$SECURITY_DESCRIPTOR,
|
||
&AttributeContext )) {
|
||
|
||
DebugTrace( 0, Dbg, ("Security Descriptor attribute does not exist\n") );
|
||
|
||
SecurityDescriptor = NtfsData.DefaultDescriptor;
|
||
SecurityDescriptorLength = NtfsData.DefaultDescriptorLength;
|
||
|
||
} else {
|
||
|
||
//
|
||
// There must be a security descriptor with a non-zero length; only
|
||
// applies for non-resident descriptors with valid data length.
|
||
//
|
||
|
||
Attribute = NtfsFoundAttribute( &AttributeContext );
|
||
|
||
if (NtfsIsAttributeResident( Attribute ) ?
|
||
(Attribute->Form.Resident.ValueLength == 0) :
|
||
(Attribute->Form.Nonresident.ValidDataLength == 0)) {
|
||
|
||
SecurityDescriptor = NtfsData.DefaultDescriptor;
|
||
SecurityDescriptorLength = NtfsData.DefaultDescriptorLength;
|
||
|
||
} else {
|
||
|
||
NtfsMapAttributeValue( IrpContext,
|
||
Fcb,
|
||
(PVOID *)&SecurityDescriptor,
|
||
&SecurityDescriptorLength,
|
||
&Bcb,
|
||
&AttributeContext );
|
||
}
|
||
}
|
||
|
||
NtfsUpdateFcbSecurity( IrpContext,
|
||
Fcb,
|
||
ParentFcb,
|
||
#ifdef _CAIRO_
|
||
SECURITY_ID_INVALID,
|
||
#endif // _CAIRO_
|
||
SecurityDescriptor,
|
||
SecurityDescriptorLength );
|
||
|
||
} finally {
|
||
|
||
DebugUnwind( NtfsLoadSecurityDescriptor );
|
||
|
||
//
|
||
// Cleanup our attribute enumeration context and the Bcb
|
||
//
|
||
|
||
NtfsCleanupAttributeContext( &AttributeContext );
|
||
NtfsUnpinBcb( &Bcb );
|
||
}
|
||
}
|
||
|
||
//
|
||
// And return to our caller
|
||
//
|
||
|
||
DebugTrace( -1, Dbg, ("NtfsLoadSecurityDescriptor -> VOID\n") );
|
||
|
||
return;
|
||
}
|
||
|
||
|
||
//
|
||
// Local Support routine
|
||
//
|
||
|
||
#ifdef _CAIRO_
|
||
|
||
NTSTATUS
|
||
NtOfsMatchSecurityHash (
|
||
IN PINDEX_ROW IndexRow,
|
||
IN OUT PVOID MatchData
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Test whether an index row is worthy of returning based on its contents as
|
||
a row in the SecurityDescriptorHashIndex.
|
||
|
||
Arguments:
|
||
|
||
IndexRow - row that is being tested
|
||
|
||
MatchData - a PVOID that is the hash function we look for.
|
||
|
||
Returns:
|
||
|
||
STATUS_SUCCESS if the IndexRow matches
|
||
STATUS_NO_MATCH if the IndexRow does not match, but the enumeration should
|
||
continue
|
||
STATUS_NO_MORE_MATCHES if the IndexRow does not match, and the enumeration
|
||
should terminate
|
||
|
||
|
||
--*/
|
||
{
|
||
ASSERT(IndexRow->KeyPart.KeyLength == sizeof( SECURITY_HASH_KEY ) );
|
||
|
||
PAGED_CODE( );
|
||
|
||
if (((PSECURITY_HASH_KEY)IndexRow->KeyPart.Key)->Hash == (ULONG) MatchData) {
|
||
return STATUS_SUCCESS;
|
||
} else {
|
||
return STATUS_NO_MORE_MATCHES;
|
||
}
|
||
}
|
||
#endif // _CAIRO_
|
||
|
||
|
||
|
||
//
|
||
// Local Support routine
|
||
//
|
||
|
||
#ifdef _CAIRO_
|
||
|
||
VOID
|
||
NtOfsLookupSecurityDescriptorInIndex (
|
||
PIRP_CONTEXT IrpContext,
|
||
IN OUT PSHARED_SECURITY SharedSecurity
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Look up the security descriptor in the index. If found, return the
|
||
security ID.
|
||
|
||
Arguments:
|
||
|
||
IrpContext - context of the call
|
||
|
||
SharedSecurity - shared security for a file
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
--*/
|
||
{
|
||
PAGED_CODE( );
|
||
|
||
DebugTrace( +1, Dbg, ("NtOfsLookupSecurityDescriptorInIndex...\n") );
|
||
|
||
//
|
||
// For each matching hash record in the index, see if the actual security
|
||
// security descriptor matches.
|
||
//
|
||
{
|
||
INDEX_KEY IndexKey;
|
||
INDEX_ROW FoundRow;
|
||
PSECURITY_DESCRIPTOR_HEADER Header;
|
||
UCHAR HashDescriptorHeader[2 * (sizeof( SECURITY_DESCRIPTOR_HEADER ) + sizeof( ULONG ))];
|
||
|
||
PINDEX_KEY Key = &IndexKey;
|
||
PREAD_CONTEXT ReadContext = NULL;
|
||
ULONG FoundCount = 0;
|
||
PBCB Bcb = NULL;
|
||
|
||
IndexKey.KeyLength = sizeof( SharedSecurity->Header.HashKey );
|
||
IndexKey.Key = &SharedSecurity->Header.HashKey.Hash;
|
||
|
||
try {
|
||
//
|
||
// We keep reading hash records until we find a hash.
|
||
//
|
||
|
||
while (SharedSecurity->Header.HashKey.SecurityId == SECURITY_ID_INVALID)
|
||
{
|
||
//
|
||
// Read next matching SecurityHashIndex record
|
||
//
|
||
|
||
FoundCount = 1;
|
||
NtOfsReadRecords(
|
||
IrpContext,
|
||
IrpContext->Vcb->SecurityDescriptorHashIndex,
|
||
&ReadContext,
|
||
Key,
|
||
NtOfsMatchSecurityHash,
|
||
(PVOID)SharedSecurity->Header.HashKey.Hash,
|
||
&FoundCount,
|
||
&FoundRow,
|
||
sizeof( HashDescriptorHeader ),
|
||
&HashDescriptorHeader[0]);
|
||
|
||
//
|
||
// Set next read to read sequentially rather than explicitly
|
||
// seek.
|
||
//
|
||
|
||
Key = NULL;
|
||
|
||
//
|
||
// If there were no more records found, then go and establish a
|
||
// a new security Id.
|
||
//
|
||
|
||
if (FoundCount == 0) {
|
||
break;
|
||
}
|
||
|
||
//
|
||
// Examine the row to see if the descriptors are
|
||
// the same. Verify the cache contents.
|
||
//
|
||
|
||
ASSERT( FoundRow.DataPart.DataLength == sizeof( SECURITY_DESCRIPTOR_HEADER ) );
|
||
if (FoundRow.DataPart.DataLength != sizeof( SECURITY_DESCRIPTOR_HEADER )) {
|
||
DebugTrace( 0, Dbg, ("Found row has a bad size\n") );
|
||
NtfsRaiseStatus( IrpContext,
|
||
STATUS_DISK_CORRUPT_ERROR,
|
||
NULL, NULL );
|
||
}
|
||
|
||
Header = (PSECURITY_DESCRIPTOR_HEADER)FoundRow.DataPart.Data;
|
||
|
||
//
|
||
// If the length of the security descriptor in the stream is NOT
|
||
// the same as the current security descriptor, then a match is
|
||
// not possible
|
||
//
|
||
|
||
if (SharedSecurity->Header.Length != Header->Length) {
|
||
continue;
|
||
}
|
||
|
||
//
|
||
// Map security descriptor given descriptor stream position.
|
||
//
|
||
|
||
try {
|
||
PSECURITY_DESCRIPTOR_HEADER TestHeader;
|
||
|
||
NtfsMapStream(
|
||
IrpContext,
|
||
IrpContext->Vcb->SecurityDescriptorStream,
|
||
Header->Offset,
|
||
Header->Length,
|
||
&Bcb,
|
||
&TestHeader);
|
||
|
||
//
|
||
// Make sure index data matches stream data
|
||
//
|
||
|
||
ASSERT( TestHeader->HashKey.Hash == Header->HashKey.Hash &&
|
||
TestHeader->HashKey.SecurityId == Header->HashKey.SecurityId &&
|
||
TestHeader->Length == Header->Length );
|
||
|
||
//
|
||
// Compare byte-for-byte the security descriptors. We do not
|
||
// perform any rearranging of descriptors into canonical forms.
|
||
//
|
||
|
||
if (RtlEqualMemory( SharedSecurity->SecurityDescriptor,
|
||
TestHeader + 1,
|
||
GetSharedSecurityLength( SharedSecurity )) ) {
|
||
//
|
||
// We have a match. Save the found header
|
||
//
|
||
|
||
SharedSecurity->Header = *TestHeader;
|
||
DebugTrace( 0, DbgAcl, ("Reusing indexed security Id %x\n",
|
||
TestHeader->HashKey.SecurityId) );
|
||
}
|
||
} finally {
|
||
NtfsUnpinBcb( &Bcb );
|
||
}
|
||
}
|
||
|
||
} finally {
|
||
if (ReadContext != NULL) {
|
||
NtOfsFreeReadContext( ReadContext );
|
||
}
|
||
}
|
||
}
|
||
}
|
||
#endif // _CAIRO_
|
||
|
||
|
||
|
||
//
|
||
// Local Support routine
|
||
//
|
||
|
||
#ifdef _CAIRO_
|
||
|
||
SECURITY_ID
|
||
NtOfsGetSecurityIdFromSecurityDescriptor (
|
||
PIRP_CONTEXT IrpContext,
|
||
IN OUT PSHARED_SECURITY SharedSecurity
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Return the security Id associated with a given security descriptor. If
|
||
there is an existing Id, return it. If no Id exists, create one.
|
||
|
||
Arguments:
|
||
|
||
IrpContext - context of the call
|
||
|
||
SharedSecurity - Shared security used by file
|
||
|
||
Return Value:
|
||
|
||
SECURITY_ID corresponding to the unique instantiation of the security
|
||
descriptor on the volume.
|
||
|
||
--*/
|
||
{
|
||
SECURITY_ID SavedSecurityId;
|
||
|
||
PAGED_CODE( );
|
||
|
||
DebugTrace( +1, Dbg, ("NtOfsGetSecurityIdFromSecurityDescriptor...\n") );
|
||
|
||
//
|
||
// Make sure the data structures don't change underneath us
|
||
//
|
||
|
||
NtfsAcquireSharedScb( IrpContext, IrpContext->Vcb->SecurityDescriptorStream );
|
||
|
||
//
|
||
// Save next Security Id. This is used if we fail to find the security
|
||
// descriptor in the descriptor stream.
|
||
//
|
||
|
||
SavedSecurityId = IrpContext->Vcb->NextSecurityId;
|
||
|
||
//
|
||
// Find descriptor in indexes/stream
|
||
//
|
||
|
||
try {
|
||
NtOfsLookupSecurityDescriptorInIndex( IrpContext, SharedSecurity );
|
||
|
||
//
|
||
// If we've found the security descriptor in the stream we're done.
|
||
//
|
||
|
||
if (SharedSecurity->Header.HashKey.SecurityId != SECURITY_ID_INVALID) {
|
||
leave;
|
||
}
|
||
|
||
//
|
||
// The security descriptor is not found. Reacquire the security
|
||
// stream exclusive since we are about to modify it.
|
||
//
|
||
|
||
NtfsReleaseScb( IrpContext, IrpContext->Vcb->SecurityDescriptorStream );
|
||
NtfsAcquireExclusiveScb( IrpContext, IrpContext->Vcb->SecurityDescriptorStream );
|
||
|
||
//
|
||
// During the short interval above, we did not own the security stream.
|
||
// It is possible that another thread has gotten in and created this
|
||
// descriptor. Therefore, we must probe the indexes again.
|
||
//
|
||
// Rather than perform this expensive test *always*, we saved the next
|
||
// security id to be allocated above. Now that we've obtained the stream
|
||
// exclusive we can check to see if the saved one is the same as the next
|
||
// one. If so, then we need to probe the indexes. Otherwise
|
||
// we know that no modifications have taken place.
|
||
//
|
||
|
||
if (SavedSecurityId != IrpContext->Vcb->NextSecurityId) {
|
||
DebugTrace( 0, DbgAcl, ("SecurityId changed, rescanning\n") );
|
||
|
||
//
|
||
// The descriptor cache has been edited. We must search again
|
||
//
|
||
|
||
NtOfsLookupSecurityDescriptorInIndex( IrpContext, SharedSecurity );
|
||
|
||
//
|
||
// If the Id was found this time, simply return it
|
||
//
|
||
|
||
if (SharedSecurity->Header.HashKey.SecurityId != SECURITY_ID_INVALID) {
|
||
leave;
|
||
}
|
||
}
|
||
|
||
//
|
||
// allocate security id. This does not need to be logged since we only
|
||
// increment this and initialize this from the max key in the index at
|
||
// mount time.
|
||
//
|
||
|
||
SharedSecurity->Header.HashKey.SecurityId =
|
||
IrpContext->Vcb->NextSecurityId++;
|
||
|
||
//
|
||
// Determine allocation location in descriptor stream. The alignment
|
||
// requirements for security descriptors within the stream are:
|
||
//
|
||
// DWORD alignment
|
||
// Not spanning a VACB_MAPPING_GRANULARITY boundary
|
||
//
|
||
|
||
//
|
||
// Get current EOF for descriptor stream
|
||
//
|
||
|
||
SharedSecurity->Header.Offset =
|
||
IrpContext->Vcb->SecurityDescriptorStream->Header.FileSize.QuadPart;
|
||
|
||
//
|
||
// Align to big boundary
|
||
//
|
||
|
||
SharedSecurity->Header.Offset =
|
||
(SharedSecurity->Header.Offset + 0xF) & 0xFFFFFFFFFFFFFFF0i64;
|
||
|
||
DebugTrace( 0, DbgAcl, ("Allocating SecurityId %x at %016I64x\n",
|
||
SharedSecurity->Header.HashKey.SecurityId,
|
||
SharedSecurity->Header.Offset) );
|
||
|
||
//
|
||
// Make sure we don't span a VACB_MAPPING_GRANULARITY boundary
|
||
//
|
||
|
||
if ((SharedSecurity->Header.Offset & (VACB_MAPPING_GRANULARITY - 1)) +
|
||
SharedSecurity->Header.Length >= VACB_MAPPING_GRANULARITY) {
|
||
SharedSecurity->Header.Offset =
|
||
(SharedSecurity->Header.Offset + VACB_MAPPING_GRANULARITY - 1) &
|
||
~(VACB_MAPPING_GRANULARITY - 1);
|
||
}
|
||
|
||
|
||
//
|
||
// Grow security stream to make room for new descriptor and header
|
||
//
|
||
|
||
NtOfsSetLength( IrpContext, IrpContext->Vcb->SecurityDescriptorStream,
|
||
SharedSecurity->Header.Offset +
|
||
SharedSecurity->Header.Length);
|
||
|
||
|
||
//
|
||
// Put the new descriptor into the stream
|
||
//
|
||
|
||
NtOfsPutData( IrpContext, IrpContext->Vcb->SecurityDescriptorStream,
|
||
SharedSecurity->Header.Offset,
|
||
SharedSecurity->Header.Length,
|
||
&SharedSecurity->Header );
|
||
|
||
|
||
//
|
||
// add id->data map
|
||
//
|
||
|
||
{
|
||
INDEX_ROW Row;
|
||
|
||
Row.KeyPart.KeyLength =
|
||
sizeof( SharedSecurity->Header.HashKey.SecurityId );
|
||
Row.KeyPart.Key = &SharedSecurity->Header.HashKey.SecurityId;
|
||
|
||
Row.DataPart.DataLength = sizeof( SharedSecurity->Header );
|
||
Row.DataPart.Data = &SharedSecurity->Header;
|
||
|
||
NtOfsAddRecords(
|
||
IrpContext,
|
||
IrpContext->Vcb->SecurityIdIndex,
|
||
1,
|
||
&Row,
|
||
FALSE );
|
||
}
|
||
|
||
//
|
||
// add hash|id->data map
|
||
//
|
||
|
||
{
|
||
INDEX_ROW Row;
|
||
|
||
Row.KeyPart.KeyLength =
|
||
sizeof( SharedSecurity->Header.HashKey );
|
||
Row.KeyPart.Key = &SharedSecurity->Header.HashKey;
|
||
|
||
Row.DataPart.DataLength = sizeof( SharedSecurity->Header );
|
||
Row.DataPart.Data = &SharedSecurity->Header;
|
||
|
||
NtOfsAddRecords(
|
||
IrpContext,
|
||
IrpContext->Vcb->SecurityDescriptorHashIndex,
|
||
1,
|
||
&Row,
|
||
FALSE );
|
||
}
|
||
} finally {
|
||
NtfsReleaseScb( IrpContext, IrpContext->Vcb->SecurityDescriptorStream );
|
||
}
|
||
|
||
DebugTrace(-1, Dbg, ("NtOfsGetSecurityIdFromSecurityDescriptor returns %08x\n",
|
||
SharedSecurity->Header.HashKey.SecurityId));
|
||
|
||
return SharedSecurity->Header.HashKey.SecurityId;
|
||
}
|
||
#endif // _CAIRO_
|
||
|
||
|
||
//
|
||
// Local Support routine
|
||
//
|
||
|
||
VOID
|
||
NtfsStoreSecurityDescriptor (
|
||
PIRP_CONTEXT IrpContext,
|
||
IN PFCB Fcb,
|
||
IN BOOLEAN LogIt
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine stores a new security descriptor already stored in the fcb
|
||
from memory onto the disk.
|
||
|
||
Arguments:
|
||
|
||
Fcb - Supplies the fcb for the file being operated on
|
||
|
||
LogIt - Supplies whether or not the creation of a new security descriptor
|
||
should/ be logged or not. Modifications are always logged. This
|
||
parameter must only be specified as FALSE for a file which is currently
|
||
being created.
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
--*/
|
||
|
||
{
|
||
ATTRIBUTE_ENUMERATION_CONTEXT AttributeContext;
|
||
|
||
ATTRIBUTE_ENUMERATION_CONTEXT StdInfoContext;
|
||
BOOLEAN CleanupStdInfoContext = FALSE;
|
||
|
||
PAGED_CODE();
|
||
|
||
DebugTrace( +1, Dbg, ("NtfsStoreSecurityDescriptor...\n") );
|
||
|
||
//
|
||
// Initialize the attribute and find the security attribute
|
||
//
|
||
|
||
NtfsInitializeAttributeContext( &AttributeContext );
|
||
try {
|
||
#ifdef _CAIRO_
|
||
//
|
||
// BUGBUG - remove the following IF statement when all volumes get security
|
||
// descriptor streams.
|
||
//
|
||
|
||
if (Fcb->Vcb->SecurityDescriptorStream != NULL) {
|
||
//
|
||
// If the shared security pointer is null, then we are deleting the
|
||
// security descriptor altogether. If so, and we have a security
|
||
// attribute, indicated by NOT having large standard info, then we
|
||
// must delete the security attribute.
|
||
//
|
||
|
||
if (Fcb->SharedSecurity == NULL) {
|
||
if (!FlagOn( Fcb->FcbState, FCB_STATE_LARGE_STD_INFO )) {
|
||
DebugTrace( 0, Dbg, ("Security Descriptor is null\n") );
|
||
|
||
//
|
||
// Read in the security descriptor attribute if it already
|
||
// doesn't exist then we're done, otherwise simply delete
|
||
// the attribute
|
||
//
|
||
|
||
if (NtfsLookupAttributeByCode( IrpContext,
|
||
Fcb,
|
||
&Fcb->FileReference,
|
||
$SECURITY_DESCRIPTOR,
|
||
&AttributeContext )) {
|
||
|
||
DebugTrace( 0, Dbg, ("Delete existing Security Descriptor\n") );
|
||
|
||
NtfsDeleteAttributeRecord( IrpContext,
|
||
Fcb,
|
||
TRUE,
|
||
FALSE,
|
||
&AttributeContext );
|
||
}
|
||
}
|
||
|
||
leave;
|
||
}
|
||
|
||
//
|
||
// We are called to replace an existing security descriptor. In the
|
||
// event that we have a downlevel $STANDARD_INFORMATION attribute, we
|
||
// must convert it to large form before we store the ACL efficiently.
|
||
//
|
||
|
||
if (!FlagOn( Fcb->FcbState, FCB_STATE_LARGE_STD_INFO) ) {
|
||
DebugTrace( 0, Dbg, ("Growing standard information\n") );
|
||
|
||
NtfsGrowStandardInformation( IrpContext, Fcb );
|
||
|
||
DebugTrace( 0, Dbg, ("Security Descriptor is null\n") );
|
||
|
||
//
|
||
// Read in the security descriptor attribute if it already
|
||
// doesn't exist then we're done, otherwise simply delete the
|
||
// attribute
|
||
//
|
||
|
||
if (NtfsLookupAttributeByCode( IrpContext,
|
||
Fcb,
|
||
&Fcb->FileReference,
|
||
$SECURITY_DESCRIPTOR,
|
||
&AttributeContext )) {
|
||
|
||
DebugTrace( 0, Dbg, ("Delete existing Security Descriptor\n") );
|
||
|
||
NtfsDeleteAttributeRecord( IrpContext,
|
||
Fcb,
|
||
TRUE,
|
||
FALSE,
|
||
&AttributeContext );
|
||
}
|
||
}
|
||
|
||
//
|
||
// If the shared security descriptor already has an ID assigned, then
|
||
// use it
|
||
//
|
||
|
||
if (Fcb->SharedSecurity->Header.HashKey.SecurityId != SECURITY_ID_INVALID) {
|
||
Fcb->SecurityId = Fcb->SharedSecurity->Header.HashKey.SecurityId;
|
||
DebugTrace( 0, DbgAcl, ("Reusing cached security Id %x\n", Fcb->SecurityId) );
|
||
} else {
|
||
//
|
||
// Find unique SecurityId for descriptor and set SecurityId in Fcb.
|
||
//
|
||
|
||
Fcb->SecurityId = NtOfsGetSecurityIdFromSecurityDescriptor( IrpContext,
|
||
Fcb->SharedSecurity );
|
||
|
||
//
|
||
// By serializing allocation of Id's, we have a tiny race in here
|
||
// where two threads could be setting the same security Id into
|
||
// the shared security.
|
||
//
|
||
|
||
ASSERT( Fcb->SharedSecurity->Header.HashKey.SecurityId == SECURITY_ID_INVALID ||
|
||
Fcb->SharedSecurity->Header.HashKey.SecurityId == Fcb->SecurityId );
|
||
Fcb->SharedSecurity->Header.HashKey.SecurityId = Fcb->SecurityId;
|
||
|
||
//
|
||
// Serialize access to the security cache
|
||
//
|
||
|
||
NtfsAcquireFcbSecurity( Fcb->Vcb );
|
||
|
||
//
|
||
// Cache this shared security for faster access
|
||
//
|
||
|
||
NtOfsAddCachedSharedSecurity( Fcb->Vcb, Fcb->SharedSecurity );
|
||
|
||
//
|
||
// Release access to security cache
|
||
//
|
||
|
||
NtfsReleaseFcbSecurity( Fcb->Vcb );
|
||
}
|
||
|
||
|
||
//
|
||
// We've changed the standard information for this file. We now must
|
||
// update the disk to make sure things are consistent.
|
||
//
|
||
|
||
|
||
leave;
|
||
}
|
||
#endif // _CAIRO_
|
||
|
||
//
|
||
// Check if the attribute is first being modified or deleted, a null
|
||
// value means that we are deleting the security descriptor
|
||
//
|
||
|
||
if (Fcb->SharedSecurity == NULL) {
|
||
|
||
DebugTrace( 0, Dbg, ("Security Descriptor is null\n") );
|
||
|
||
//
|
||
// If it already doesn't exist then we're done, otherwise simply
|
||
// delete the attribute
|
||
//
|
||
|
||
if (NtfsLookupAttributeByCode( IrpContext,
|
||
Fcb,
|
||
&Fcb->FileReference,
|
||
$SECURITY_DESCRIPTOR,
|
||
&AttributeContext )) {
|
||
|
||
DebugTrace( 0, Dbg, ("Delete existing Security Descriptor\n") );
|
||
|
||
NtfsDeleteAttributeRecord( IrpContext,
|
||
Fcb,
|
||
TRUE,
|
||
FALSE,
|
||
&AttributeContext );
|
||
}
|
||
|
||
leave;
|
||
}
|
||
|
||
//
|
||
// At this point we are modifying the security descriptor so read in the
|
||
// security descriptor, if it does not exist then we will need to create
|
||
// one.
|
||
//
|
||
|
||
if (!NtfsLookupAttributeByCode( IrpContext,
|
||
Fcb,
|
||
&Fcb->FileReference,
|
||
$SECURITY_DESCRIPTOR,
|
||
&AttributeContext )) {
|
||
|
||
DebugTrace( 0, Dbg, ("Create a new Security Descriptor\n") );
|
||
|
||
NtfsCleanupAttributeContext( &AttributeContext );
|
||
NtfsInitializeAttributeContext( &AttributeContext );
|
||
|
||
NtfsCreateAttributeWithValue( IrpContext,
|
||
Fcb,
|
||
$SECURITY_DESCRIPTOR,
|
||
NULL, // attribute name
|
||
&Fcb->SharedSecurity->SecurityDescriptor,
|
||
GetSharedSecurityLength(Fcb->SharedSecurity),
|
||
0, // attribute flags
|
||
NULL, // where indexed
|
||
LogIt, // logit
|
||
&AttributeContext );
|
||
|
||
//
|
||
// We may be modifying the security descriptor of an NT 5.0 volume.
|
||
// We want to store a SecurityID in the standard information field so
|
||
// that if we reboot on 5.0 NTFS will know where to find the most
|
||
// recent security descriptor.
|
||
//
|
||
|
||
if (FlagOn( Fcb->FcbState, FCB_STATE_LARGE_STD_INFO )) {
|
||
|
||
LARGE_STANDARD_INFORMATION StandardInformation;
|
||
|
||
//
|
||
// Initialize the context structure.
|
||
//
|
||
|
||
NtfsInitializeAttributeContext( &StdInfoContext );
|
||
CleanupStdInfoContext = TRUE;
|
||
|
||
//
|
||
// Locate the standard information, it must be there.
|
||
//
|
||
|
||
if (!NtfsLookupAttributeByCode( IrpContext,
|
||
Fcb,
|
||
&Fcb->FileReference,
|
||
$STANDARD_INFORMATION,
|
||
&StdInfoContext )) {
|
||
|
||
DebugTrace( 0, Dbg, ("Can't find standard information\n") );
|
||
|
||
NtfsRaiseStatus( IrpContext, STATUS_FILE_CORRUPT_ERROR, NULL, Fcb );
|
||
}
|
||
|
||
ASSERT( NtfsFoundAttribute( &StdInfoContext )->Form.Resident.ValueLength >= sizeof( LARGE_STANDARD_INFORMATION ));
|
||
|
||
//
|
||
// Copy the existing standard information to our buffer.
|
||
//
|
||
|
||
RtlCopyMemory( &StandardInformation,
|
||
NtfsAttributeValue( NtfsFoundAttribute( &StdInfoContext )),
|
||
sizeof( LARGE_STANDARD_INFORMATION ));
|
||
|
||
StandardInformation.SecurityId = SECURITY_ID_INVALID;
|
||
StandardInformation.OwnerId = 0;
|
||
|
||
//
|
||
// Call to change the attribute value.
|
||
//
|
||
|
||
NtfsChangeAttributeValue( IrpContext,
|
||
Fcb,
|
||
0,
|
||
&StandardInformation,
|
||
sizeof( LARGE_STANDARD_INFORMATION ),
|
||
FALSE,
|
||
FALSE,
|
||
FALSE,
|
||
FALSE,
|
||
&StdInfoContext );
|
||
}
|
||
|
||
} else {
|
||
|
||
DebugTrace( 0, Dbg, ("Change an existing Security Descriptor\n") );
|
||
|
||
NtfsChangeAttributeValue( IrpContext,
|
||
Fcb,
|
||
0, // Value offset
|
||
&Fcb->SharedSecurity->SecurityDescriptor,
|
||
GetSharedSecurityLength( Fcb->SharedSecurity ),
|
||
TRUE, // logit
|
||
TRUE,
|
||
FALSE,
|
||
FALSE,
|
||
&AttributeContext );
|
||
}
|
||
|
||
} finally {
|
||
|
||
DebugUnwind( NtfsStoreSecurityDescriptor );
|
||
|
||
//
|
||
// Cleanup our attribute enumeration context
|
||
//
|
||
|
||
NtfsCleanupAttributeContext( &AttributeContext );
|
||
|
||
if (CleanupStdInfoContext) {
|
||
|
||
NtfsCleanupAttributeContext( &StdInfoContext );
|
||
}
|
||
}
|
||
|
||
//
|
||
// And return to our caller
|
||
//
|
||
|
||
DebugTrace( -1, Dbg, ("NtfsStoreSecurityDescriptor -> VOID\n") );
|
||
|
||
return;
|
||
}
|
||
|
||
|
||
|
||
/*++
|
||
|
||
Routine Descriptions:
|
||
|
||
Collation routines for security hash index. Collation occurs by Hash first,
|
||
then security Id
|
||
|
||
Arguments:
|
||
|
||
Key1 - First key to compare.
|
||
|
||
Key2 - Second key to compare.
|
||
|
||
CollationData - Optional data to support the collation.
|
||
|
||
Return Value:
|
||
|
||
LessThan, EqualTo, or Greater than, for how Key1 compares
|
||
with Key2.
|
||
|
||
--*/
|
||
|
||
#ifdef _CAIRO_
|
||
FSRTL_COMPARISON_RESULT
|
||
NtOfsCollateSecurityHash (
|
||
IN PINDEX_KEY Key1,
|
||
IN PINDEX_KEY Key2,
|
||
IN PVOID CollationData
|
||
)
|
||
|
||
{
|
||
PSECURITY_HASH_KEY HashKey1 = (PSECURITY_HASH_KEY) Key1->Key;
|
||
PSECURITY_HASH_KEY HashKey2 = (PSECURITY_HASH_KEY) Key2->Key;
|
||
|
||
UNREFERENCED_PARAMETER(CollationData);
|
||
|
||
PAGED_CODE( );
|
||
|
||
ASSERT( Key1->KeyLength == sizeof( SECURITY_HASH_KEY ) );
|
||
ASSERT( Key2->KeyLength == sizeof( SECURITY_HASH_KEY ) );
|
||
|
||
if (HashKey1->Hash < HashKey2->Hash) {
|
||
return LessThan;
|
||
} else if (HashKey1->Hash > HashKey2->Hash) {
|
||
return GreaterThan;
|
||
} else if (HashKey1->SecurityId < HashKey2->SecurityId) {
|
||
return LessThan;
|
||
} else if (HashKey1->SecurityId > HashKey2->SecurityId) {
|
||
return GreaterThan;
|
||
} else {
|
||
return EqualTo;
|
||
}
|
||
}
|
||
#endif // _CAIRO_
|
||
|
||
|