mirror of
https://github.com/reactos/reactos.git
synced 2024-11-01 04:11:30 +00:00
0daa5547d9
This implies that a sample for W10. It has been backported to NT5.2; not sure how it would work on a W2K3 (feel free to test!)
424 lines
11 KiB
C
424 lines
11 KiB
C
/*++
|
||
|
||
Copyright (c) 1989-2000 Microsoft Corporation
|
||
|
||
Module Name:
|
||
|
||
AcChkSup.c
|
||
|
||
Abstract:
|
||
|
||
This module implements the FAT access checking routine
|
||
|
||
|
||
--*/
|
||
|
||
#include "fatprocs.h"
|
||
|
||
//
|
||
// Our debug trace level
|
||
//
|
||
|
||
#define Dbg (DEBUG_TRACE_ACCHKSUP)
|
||
|
||
NTSTATUS
|
||
FatCreateRestrictEveryoneToken(
|
||
IN PACCESS_TOKEN Token,
|
||
OUT PACCESS_TOKEN *RestrictedToken
|
||
);
|
||
|
||
#ifdef ALLOC_PRAGMA
|
||
#pragma alloc_text(PAGE, FatCheckFileAccess)
|
||
#pragma alloc_text(PAGE, FatCheckManageVolumeAccess)
|
||
#pragma alloc_text(PAGE, FatCreateRestrictEveryoneToken)
|
||
#pragma alloc_text(PAGE, FatExplicitDeviceAccessGranted)
|
||
#endif
|
||
|
||
|
||
BOOLEAN
|
||
FatCheckFileAccess (
|
||
PIRP_CONTEXT IrpContext,
|
||
IN UCHAR DirentAttributes,
|
||
IN PACCESS_MASK DesiredAccess
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine checks if a desired access is allowed to a file represented
|
||
by the specified DirentAttriubutes.
|
||
|
||
Arguments:
|
||
|
||
DirentAttributes - Supplies the Dirent attributes to check access for
|
||
|
||
DesiredAccess - Supplies the desired access mask that we are checking for
|
||
|
||
Return Value:
|
||
|
||
BOOLEAN - TRUE if access is allowed and FALSE otherwise
|
||
|
||
--*/
|
||
|
||
{
|
||
BOOLEAN Result;
|
||
|
||
DebugTrace(+1, Dbg, "FatCheckFileAccess\n", 0);
|
||
DebugTrace( 0, Dbg, "DirentAttributes = %8lx\n", DirentAttributes);
|
||
DebugTrace( 0, Dbg, "DesiredAccess = %8lx\n", *DesiredAccess);
|
||
|
||
PAGED_CODE();
|
||
|
||
//
|
||
// This procedures is programmed like a string of filters each
|
||
// filter checks to see if some access is allowed, if it is not allowed
|
||
// the filter return FALSE to the user without further checks otherwise
|
||
// it moves on to the next filter. The filter check is to check for
|
||
// desired access flags that are not allowed for a particular dirent
|
||
//
|
||
|
||
Result = TRUE;
|
||
|
||
_SEH2_TRY {
|
||
|
||
//
|
||
// Check for Volume ID or Device Dirents, these are not allowed user
|
||
// access at all
|
||
//
|
||
|
||
if (FlagOn(DirentAttributes, FAT_DIRENT_ATTR_VOLUME_ID) ||
|
||
FlagOn(DirentAttributes, FAT_DIRENT_ATTR_DEVICE)) {
|
||
|
||
DebugTrace(0, Dbg, "Cannot access volume id or device\n", 0);
|
||
|
||
try_return( Result = FALSE );
|
||
}
|
||
|
||
//
|
||
// Check the desired access for the object - we only blackball that
|
||
// we do not understand. The model of filesystems using ACLs is that
|
||
// they do not type the ACL to the object the ACL is on. Permissions
|
||
// are not checked for consistency vs. the object type - dir/file.
|
||
//
|
||
|
||
if (FlagOn(*DesiredAccess, ~(DELETE |
|
||
READ_CONTROL |
|
||
WRITE_OWNER |
|
||
WRITE_DAC |
|
||
SYNCHRONIZE |
|
||
ACCESS_SYSTEM_SECURITY |
|
||
FILE_WRITE_DATA |
|
||
FILE_READ_EA |
|
||
FILE_WRITE_EA |
|
||
FILE_READ_ATTRIBUTES |
|
||
FILE_WRITE_ATTRIBUTES |
|
||
FILE_LIST_DIRECTORY |
|
||
FILE_TRAVERSE |
|
||
FILE_DELETE_CHILD |
|
||
FILE_APPEND_DATA |
|
||
MAXIMUM_ALLOWED))) {
|
||
|
||
DebugTrace(0, Dbg, "Cannot open object\n", 0);
|
||
|
||
try_return( Result = FALSE );
|
||
}
|
||
|
||
//
|
||
// Check for a read-only Dirent
|
||
//
|
||
|
||
if (FlagOn(DirentAttributes, FAT_DIRENT_ATTR_READ_ONLY)) {
|
||
|
||
//
|
||
// Check the desired access for a read-only dirent. AccessMask will contain
|
||
// the flags we're going to allow.
|
||
//
|
||
|
||
ACCESS_MASK AccessMask = DELETE | READ_CONTROL | WRITE_OWNER | WRITE_DAC |
|
||
SYNCHRONIZE | ACCESS_SYSTEM_SECURITY | FILE_READ_DATA |
|
||
FILE_READ_EA | FILE_WRITE_EA | FILE_READ_ATTRIBUTES |
|
||
FILE_WRITE_ATTRIBUTES | FILE_EXECUTE | FILE_LIST_DIRECTORY |
|
||
FILE_TRAVERSE;
|
||
|
||
//
|
||
// If this is a subdirectory also allow add file/directory and delete.
|
||
//
|
||
|
||
if (FlagOn(DirentAttributes, FAT_DIRENT_ATTR_DIRECTORY)) {
|
||
|
||
AccessMask |= FILE_ADD_SUBDIRECTORY | FILE_ADD_FILE | FILE_DELETE_CHILD;
|
||
}
|
||
|
||
if (FlagOn(*DesiredAccess, ~AccessMask)) {
|
||
|
||
DebugTrace(0, Dbg, "Cannot open readonly\n", 0);
|
||
|
||
try_return( Result = FALSE );
|
||
}
|
||
}
|
||
|
||
try_exit: NOTHING;
|
||
} _SEH2_FINALLY {
|
||
|
||
DebugUnwind( FatCheckFileAccess );
|
||
|
||
DebugTrace(-1, Dbg, "FatCheckFileAccess -> %08lx\n", Result);
|
||
} _SEH2_END;
|
||
|
||
UNREFERENCED_PARAMETER( IrpContext );
|
||
|
||
return Result;
|
||
}
|
||
|
||
|
||
BOOLEAN
|
||
FatCheckManageVolumeAccess (
|
||
_In_ PIRP_CONTEXT IrpContext,
|
||
_In_ PACCESS_STATE AccessState,
|
||
_In_ KPROCESSOR_MODE ProcessorMode
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This function checks whether the SID described in the input access state has
|
||
manage volume privilege.
|
||
|
||
Arguments:
|
||
|
||
AccessState - the access state describing the security context to be checked
|
||
|
||
ProcessorMode - the mode this check should occur against
|
||
|
||
Return Value:
|
||
|
||
BOOLEAN - TRUE if privilege is held and FALSE otherwise
|
||
|
||
--*/
|
||
|
||
{
|
||
PRIVILEGE_SET PrivilegeSet;
|
||
|
||
PAGED_CODE();
|
||
|
||
PrivilegeSet.PrivilegeCount = 1;
|
||
PrivilegeSet.Control = PRIVILEGE_SET_ALL_NECESSARY;
|
||
PrivilegeSet.Privilege[0].Luid = RtlConvertLongToLuid( SE_MANAGE_VOLUME_PRIVILEGE );
|
||
PrivilegeSet.Privilege[0].Attributes = 0;
|
||
|
||
if (SePrivilegeCheck( &PrivilegeSet,
|
||
&AccessState->SubjectSecurityContext,
|
||
ProcessorMode )) {
|
||
|
||
return TRUE;
|
||
}
|
||
|
||
UNREFERENCED_PARAMETER( IrpContext );
|
||
|
||
return FALSE;
|
||
}
|
||
|
||
|
||
NTSTATUS
|
||
FatExplicitDeviceAccessGranted (
|
||
IN PIRP_CONTEXT IrpContext,
|
||
IN PDEVICE_OBJECT DeviceObject,
|
||
IN PACCESS_STATE AccessState,
|
||
IN KPROCESSOR_MODE ProcessorMode
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This function asks whether the SID described in the input access state has
|
||
been granted any explicit access to the given device object. It does this
|
||
by acquiring a token stripped of its ability to acquire access via the
|
||
Everyone SID and re-doing the access check.
|
||
|
||
Arguments:
|
||
|
||
DeviceObject - the device whose ACL will be checked
|
||
|
||
AccessState - the access state describing the security context to be checked
|
||
|
||
ProcessorMode - the mode this check should occur against
|
||
|
||
Return Value:
|
||
|
||
NTSTATUS - Indicating whether explicit access was granted.
|
||
|
||
--*/
|
||
|
||
{
|
||
NTSTATUS Status;
|
||
|
||
PACCESS_TOKEN OriginalAccessToken;
|
||
PACCESS_TOKEN RestrictedAccessToken;
|
||
|
||
PACCESS_TOKEN *EffectiveToken;
|
||
|
||
ACCESS_MASK GrantedAccess;
|
||
|
||
PAGED_CODE();
|
||
|
||
UNREFERENCED_PARAMETER( IrpContext );
|
||
|
||
//
|
||
// If the access state indicates that specific access other
|
||
// than traverse was acquired, either Everyone does have such
|
||
// access or explicit access was granted. In both cases, we're
|
||
// happy to let this proceed.
|
||
//
|
||
|
||
if (AccessState->PreviouslyGrantedAccess & (SPECIFIC_RIGHTS_ALL ^
|
||
FILE_TRAVERSE)) {
|
||
|
||
return STATUS_SUCCESS;
|
||
}
|
||
|
||
//
|
||
// If the manage volume privilege is held, this also permits access.
|
||
//
|
||
|
||
if (FatCheckManageVolumeAccess( IrpContext,
|
||
AccessState,
|
||
ProcessorMode )) {
|
||
|
||
return STATUS_SUCCESS;
|
||
}
|
||
|
||
//
|
||
// Capture the subject context as a prelude to everything below.
|
||
//
|
||
|
||
SeLockSubjectContext( &AccessState->SubjectSecurityContext );
|
||
|
||
//
|
||
// Convert the token in the subject context into one which does not
|
||
// acquire access through the Everyone SID.
|
||
//
|
||
// The logic for deciding which token is effective comes from
|
||
// SeQuerySubjectContextToken; since there is no natural way
|
||
// of getting a pointer to it, do it by hand.
|
||
//
|
||
|
||
if (ARGUMENT_PRESENT( AccessState->SubjectSecurityContext.ClientToken )) {
|
||
EffectiveToken = &AccessState->SubjectSecurityContext.ClientToken;
|
||
} else {
|
||
EffectiveToken = &AccessState->SubjectSecurityContext.PrimaryToken;
|
||
}
|
||
|
||
OriginalAccessToken = *EffectiveToken;
|
||
Status = FatCreateRestrictEveryoneToken( OriginalAccessToken, &RestrictedAccessToken );
|
||
|
||
if (!NT_SUCCESS(Status)) {
|
||
|
||
SeReleaseSubjectContext( &AccessState->SubjectSecurityContext );
|
||
return Status;
|
||
}
|
||
|
||
//
|
||
// Now see if the resulting context has access to the device through
|
||
// its explicitly granted access. We swap in our restricted token
|
||
// for this check as the effective client token.
|
||
//
|
||
|
||
*EffectiveToken = RestrictedAccessToken;
|
||
|
||
#ifdef _MSC_VER
|
||
#pragma prefast( suppress: 28175, "we're a file system, this is ok to touch" )
|
||
#endif
|
||
SeAccessCheck( DeviceObject->SecurityDescriptor,
|
||
&AccessState->SubjectSecurityContext,
|
||
FALSE,
|
||
AccessState->OriginalDesiredAccess,
|
||
0,
|
||
NULL,
|
||
IoGetFileObjectGenericMapping(),
|
||
ProcessorMode,
|
||
&GrantedAccess,
|
||
&Status );
|
||
|
||
*EffectiveToken = OriginalAccessToken;
|
||
|
||
//
|
||
// Cleanup and return.
|
||
//
|
||
|
||
SeUnlockSubjectContext( &AccessState->SubjectSecurityContext );
|
||
ObDereferenceObject( RestrictedAccessToken );
|
||
|
||
return Status;
|
||
}
|
||
|
||
|
||
NTSTATUS
|
||
FatCreateRestrictEveryoneToken (
|
||
IN PACCESS_TOKEN Token,
|
||
OUT PACCESS_TOKEN *RestrictedToken
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This function takes a token as the input and returns a new restricted token
|
||
from which Everyone sid has been disabled. The resulting token may be used
|
||
to find out if access is available to a user-sid by explicit means.
|
||
|
||
Arguments:
|
||
|
||
Token - Input token from which Everyone sid needs to be deactivated.
|
||
|
||
RestrictedToken - Receives the the new restricted token.
|
||
This must be released using ObDereferenceObject(*RestrictedToken);
|
||
|
||
Return Value:
|
||
|
||
NTSTATUS - Returned by SeFilterToken.
|
||
|
||
--*/
|
||
|
||
{
|
||
//
|
||
// Array of sids to disable.
|
||
//
|
||
|
||
TOKEN_GROUPS SidsToDisable;
|
||
|
||
NTSTATUS Status = STATUS_SUCCESS;
|
||
|
||
PAGED_CODE();
|
||
|
||
//
|
||
// Restricted token will contain the original sids with one change:
|
||
// If Everyone sid is present in the token, it will be marked for DenyOnly.
|
||
//
|
||
|
||
*RestrictedToken = NULL;
|
||
|
||
//
|
||
// Put Everyone sid in the array of sids to disable. This will mark it
|
||
// for SE_GROUP_USE_FOR_DENY_ONLY and it'll only be applicable for Deny aces.
|
||
//
|
||
|
||
SidsToDisable.GroupCount = 1;
|
||
SidsToDisable.Groups[0].Attributes = 0;
|
||
SidsToDisable.Groups[0].Sid = SeExports->SeWorldSid;
|
||
|
||
Status = SeFilterToken(
|
||
Token, // Token that needs to be restricted.
|
||
0, // No flags
|
||
&SidsToDisable, // Disable everyone sid
|
||
NULL, // Do not create any restricted sids
|
||
NULL, // Do not delete any privileges
|
||
RestrictedToken // Restricted token
|
||
);
|
||
|
||
return Status;
|
||
}
|
||
|