windows-nt/Source/XPSP1/NT/base/ntos/se/seastate.c

609 lines
14 KiB
C
Raw Permalink Normal View History

2020-09-26 03:20:57 -05:00
/*++
Module Name:
SeAstate.c
Abstract:
This Module implements the privilege check procedures.
Author:
Robert Reichel (robertre) 20-March-90
Environment:
Kernel Mode
Revision History:
v1: robertre
new file, move Access State related routines here
--*/
#include "pch.h"
#pragma hdrstop
#ifdef ALLOC_PRAGMA
#pragma alloc_text(PAGE,SeCreateAccessState)
#pragma alloc_text(PAGE,SeDeleteAccessState)
#pragma alloc_text(PAGE,SeSetAccessStateGenericMapping)
#pragma alloc_text(PAGE,SeAppendPrivileges)
#pragma alloc_text(PAGE,SepConcatenatePrivileges)
#endif
//
// Define logical sum of all generic accesses.
//
#define GENERIC_ACCESS (GENERIC_READ | GENERIC_WRITE | GENERIC_EXECUTE | GENERIC_ALL)
//
// The PRIVILEGE_SET data structure includes an array including ANYSIZE_ARRAY
// elements. This definition provides the size of an empty PRIVILEGE_SET
// (i.e., one with no privileges in it).
//
#define SEP_PRIVILEGE_SET_HEADER_SIZE \
((ULONG)sizeof(PRIVILEGE_SET) - \
(ANYSIZE_ARRAY * (ULONG)sizeof(LUID_AND_ATTRIBUTES)))
#if 0
NTSTATUS
SeCreateAccessState(
IN PACCESS_STATE AccessState,
IN ACCESS_MASK DesiredAccess,
IN PGENERIC_MAPPING GenericMapping OPTIONAL
)
/*++
Routine Description:
This routine initializes an ACCESS_STATE structure. This consists
of:
- zeroing the entire structure
- mapping generic access types in the passed DesiredAccess
and putting it into the structure
- "capturing" the Subject Context, which must be held for the
duration of the access attempt (at least until auditing is performed).
- Allocating an Operation ID, which is an LUID that will be used
to associate different parts of the access attempt in the audit
log.
Arguments:
AccessState - a pointer to the structure to be initialized.
DesiredAccess - Access mask containing the desired access
GenericMapping - Optionally supplies a pointer to a generic mapping
that may be used to map any generic access requests that may
have been passed in the DesiredAccess parameter.
Note that if this parameter is not supplied, it must be filled
in at some later point. The IO system does this in IopParseDevice.
Return Value:
Error if the attempt to allocate an LUID fails.
Note that this error may be safely ignored if it is known that all
security checks will be performed with PreviousMode == KernelMode.
Know what you're doing if you choose to ignore this.
--*/
{
ACCESS_MASK MappedAccessMask;
PSECURITY_DESCRIPTOR InputSecurityDescriptor = NULL;
PAUX_ACCESS_DATA AuxData;
PAGED_CODE();
//
// Don't modify what he passed in
//
MappedAccessMask = DesiredAccess;
//
// Map generic access to object specific access iff generic access types
// are specified and a generic access mapping table is provided.
//
if ( ((DesiredAccess & GENERIC_ACCESS) != 0) &&
ARGUMENT_PRESENT(GenericMapping) ) {
RtlMapGenericMask(
&MappedAccessMask,
GenericMapping
);
}
RtlZeroMemory(AccessState, sizeof(ACCESS_STATE));
//
// Assume RtlZeroMemory has initialized these fields properly
//
ASSERT( AccessState->SecurityDescriptor == NULL );
ASSERT( AccessState->PrivilegesAllocated == FALSE );
AccessState->AuxData = ExAllocatePool( PagedPool, sizeof( AUX_ACCESS_DATA ));
if (AccessState->AuxData == NULL) {
return( STATUS_NO_MEMORY );
}
AuxData = (PAUX_ACCESS_DATA)AccessState->AuxData;
SeCaptureSubjectContext(&AccessState->SubjectSecurityContext);
if (((PTOKEN)EffectiveToken( &AccessState->SubjectSecurityContext ))->TokenFlags & TOKEN_HAS_TRAVERSE_PRIVILEGE ) {
AccessState->Flags = TOKEN_HAS_TRAVERSE_PRIVILEGE;
}
if (SeTokenIsRestricted(EffectiveToken( &AccessState-SubjectSecurityContext))) {
AccessState->Flags |= TOKEN_IS_RESTRICTED;
}
AccessState->RemainingDesiredAccess = MappedAccessMask;
AccessState->OriginalDesiredAccess = DesiredAccess;
AuxData->PrivilegesUsed = (PPRIVILEGE_SET)((PUCHAR)AccessState +
(FIELD_OFFSET(ACCESS_STATE, Privileges)));
ExAllocateLocallyUniqueId(&AccessState->OperationID);
if (ARGUMENT_PRESENT(GenericMapping)) {
AuxData->GenericMapping = *GenericMapping;
}
return( STATUS_SUCCESS );
}
#endif
NTSTATUS
SeCreateAccessState(
IN PACCESS_STATE AccessState,
IN PAUX_ACCESS_DATA AuxData,
IN ACCESS_MASK DesiredAccess,
IN PGENERIC_MAPPING GenericMapping OPTIONAL
)
/*++
Routine Description:
This routine initializes an ACCESS_STATE structure. This consists
of:
- zeroing the entire structure
- mapping generic access types in the passed DesiredAccess
and putting it into the structure
- "capturing" the Subject Context, which must be held for the
duration of the access attempt (at least until auditing is performed).
- Allocating an Operation ID, which is an LUID that will be used
to associate different parts of the access attempt in the audit
log.
Arguments:
AccessState - a pointer to the structure to be initialized.
AuxData - Supplies a buffer big enough for an AuxData structure
so we don't have to allocate one.
DesiredAccess - Access mask containing the desired access
GenericMapping - Optionally supplies a pointer to a generic mapping
that may be used to map any generic access requests that may
have been passed in the DesiredAccess parameter.
Note that if this parameter is not supplied, it must be filled
in at some later point. The IO system does this in IopParseDevice.
Return Value:
Error if the attempt to allocate an LUID fails.
Note that this error may be safely ignored if it is known that all
security checks will be performed with PreviousMode == KernelMode.
Know what you're doing if you choose to ignore this.
--*/
{
ACCESS_MASK MappedAccessMask;
PSECURITY_DESCRIPTOR InputSecurityDescriptor = NULL;
PAGED_CODE();
//
// Don't modify what he passed in
//
MappedAccessMask = DesiredAccess;
//
// Map generic access to object specific access iff generic access types
// are specified and a generic access mapping table is provided.
//
if ( ((DesiredAccess & GENERIC_ACCESS) != 0) &&
ARGUMENT_PRESENT(GenericMapping) ) {
RtlMapGenericMask(
&MappedAccessMask,
GenericMapping
);
}
RtlZeroMemory(AccessState, sizeof(ACCESS_STATE));
RtlZeroMemory(AuxData, sizeof(AUX_ACCESS_DATA));
//
// Assume RtlZeroMemory has initialized these fields properly
//
ASSERT( AccessState->SecurityDescriptor == NULL );
ASSERT( AccessState->PrivilegesAllocated == FALSE );
AccessState->AuxData = AuxData;
SeCaptureSubjectContext(&AccessState->SubjectSecurityContext);
if (((PTOKEN)EffectiveToken( &AccessState->SubjectSecurityContext ))->TokenFlags & TOKEN_HAS_TRAVERSE_PRIVILEGE ) {
AccessState->Flags = TOKEN_HAS_TRAVERSE_PRIVILEGE;
}
AccessState->RemainingDesiredAccess = MappedAccessMask;
AccessState->OriginalDesiredAccess = MappedAccessMask;
AuxData->PrivilegesUsed = (PPRIVILEGE_SET)((ULONG_PTR)AccessState +
(FIELD_OFFSET(ACCESS_STATE, Privileges)));
ExAllocateLocallyUniqueId(&AccessState->OperationID);
if (ARGUMENT_PRESENT(GenericMapping)) {
AuxData->GenericMapping = *GenericMapping;
}
return( STATUS_SUCCESS );
}
#if 0
VOID
SeDeleteAccessState(
PACCESS_STATE AccessState
)
/*++
Routine Description:
This routine deallocates any memory that may have been allocated as
part of constructing the access state (normally only for an excessive
number of privileges), and frees the Subject Context.
Arguments:
AccessState - a pointer to the ACCESS_STATE structure to be
deallocated.
Return Value:
None.
--*/
{
PAUX_ACCESS_DATA AuxData;
PAGED_CODE();
AuxData = (PAUX_ACCESS_DATA)AccessState->AuxData;
if (AccessState->PrivilegesAllocated) {
ExFreePool( (PVOID)AuxData->PrivilegesUsed );
}
if (AccessState->ObjectName.Buffer != NULL) {
ExFreePool(AccessState->ObjectName.Buffer);
}
if (AccessState->ObjectTypeName.Buffer != NULL) {
ExFreePool(AccessState->ObjectTypeName.Buffer);
}
ExFreePool( AuxData );
SeReleaseSubjectContext(&AccessState->SubjectSecurityContext);
return;
}
#endif
VOID
SeDeleteAccessState(
PACCESS_STATE AccessState
)
/*++
Routine Description:
This routine deallocates any memory that may have been allocated as
part of constructing the access state (normally only for an excessive
number of privileges), and frees the Subject Context.
Arguments:
AccessState - a pointer to the ACCESS_STATE structure to be
deallocated.
Return Value:
None.
--*/
{
PAUX_ACCESS_DATA AuxData;
PAGED_CODE();
AuxData = (PAUX_ACCESS_DATA)AccessState->AuxData;
if (AccessState->PrivilegesAllocated) {
ExFreePool( (PVOID)AuxData->PrivilegesUsed );
}
if (AccessState->ObjectName.Buffer != NULL) {
ExFreePool(AccessState->ObjectName.Buffer);
}
if (AccessState->ObjectTypeName.Buffer != NULL) {
ExFreePool(AccessState->ObjectTypeName.Buffer);
}
SeReleaseSubjectContext(&AccessState->SubjectSecurityContext);
return;
}
VOID
SeSetAccessStateGenericMapping (
PACCESS_STATE AccessState,
PGENERIC_MAPPING GenericMapping
)
/*++
Routine Description:
This routine sets the GenericMapping field in an AccessState structure.
It must be called before access validation is performed if the GenericMapping
is not passed in when the AccessState structure is created.
Arguments:
AccessState - a pointer to the ACCESS_STATE structure to be modified.
GenericMapping - a pointer to the GenericMapping to be copied into the AccessState.
Return Value:
--*/
{
PAUX_ACCESS_DATA AuxData;
PAGED_CODE();
AuxData = (PAUX_ACCESS_DATA)AccessState->AuxData;
AuxData->GenericMapping = *GenericMapping;
return;
}
NTSTATUS
SeAppendPrivileges(
PACCESS_STATE AccessState,
PPRIVILEGE_SET Privileges
)
/*++
Routine Description:
This routine takes a privilege set and adds it to the privilege set
imbedded in an ACCESS_STATE structure.
An AccessState may contain up to three imbedded privileges. To
add more, this routine will allocate a block of memory, copy
the current privileges into it, and append the new privilege
to that block. A bit is set in the AccessState indicating that
the pointer to the privilge set in the structure points to pool
memory and must be deallocated.
Arguments:
AccessState - The AccessState structure representing the current
access attempt.
Privileges - A pointer to a privilege set to be added.
Return Value:
STATUS_INSUFFICIENT_RESOURCES - an attempt to allocate pool memory
failed.
--*/
{
ULONG NewPrivilegeSetSize;
PPRIVILEGE_SET NewPrivilegeSet;
PAUX_ACCESS_DATA AuxData;
PAGED_CODE();
AuxData = (PAUX_ACCESS_DATA)AccessState->AuxData;
if (Privileges->PrivilegeCount + AuxData->PrivilegesUsed->PrivilegeCount >
INITIAL_PRIVILEGE_COUNT) {
//
// Compute the total size of the two privilege sets
//
NewPrivilegeSetSize = SepPrivilegeSetSize( Privileges ) +
SepPrivilegeSetSize( AuxData->PrivilegesUsed );
NewPrivilegeSet = ExAllocatePoolWithTag( PagedPool, NewPrivilegeSetSize, 'rPeS' );
if (NewPrivilegeSet == NULL) {
return( STATUS_INSUFFICIENT_RESOURCES );
}
RtlCopyMemory(
NewPrivilegeSet,
AuxData->PrivilegesUsed,
SepPrivilegeSetSize( AuxData->PrivilegesUsed )
);
//
// Note that this will adjust the privilege count in the
// structure for us.
//
SepConcatenatePrivileges(
NewPrivilegeSet,
NewPrivilegeSetSize,
Privileges
);
if (AccessState->PrivilegesAllocated) {
ExFreePool( AuxData->PrivilegesUsed );
}
AuxData->PrivilegesUsed = NewPrivilegeSet;
//
// Mark that we've allocated memory for the privilege set,
// so we know to free it when we're cleaning up.
//
AccessState->PrivilegesAllocated = TRUE;
} else {
//
// Note that this will adjust the privilege count in the
// structure for us.
//
SepConcatenatePrivileges(
AuxData->PrivilegesUsed,
sizeof(INITIAL_PRIVILEGE_SET),
Privileges
);
}
return( STATUS_SUCCESS );
}
VOID
SepConcatenatePrivileges(
IN PPRIVILEGE_SET TargetPrivilegeSet,
IN ULONG TargetBufferSize,
IN PPRIVILEGE_SET SourcePrivilegeSet
)
/*++
Routine Description:
Takes two privilege sets and appends the second to the end of the
first.
There must be enough space left at the end of the first privilege
set to contain the second.
Arguments:
TargetPrivilegeSet - Supplies a buffer containing a privilege set.
The buffer must be large enough to contain the second privilege
set.
TargetBufferSize - Supplies the size of the target buffer.
SourcePrivilegeSet - Supplies the privilege set to be copied
into the target buffer.
Return Value:
None
--*/
{
PVOID Base;
PVOID Source;
ULONG Length;
PAGED_CODE();
ASSERT( ((ULONG)SepPrivilegeSetSize( TargetPrivilegeSet ) +
(ULONG)SepPrivilegeSetSize( SourcePrivilegeSet ) -
SEP_PRIVILEGE_SET_HEADER_SIZE ) <=
TargetBufferSize
);
Base = (PVOID)((ULONG_PTR)TargetPrivilegeSet + SepPrivilegeSetSize( TargetPrivilegeSet ));
Source = (PVOID) ((ULONG_PTR)SourcePrivilegeSet + SEP_PRIVILEGE_SET_HEADER_SIZE);
Length = SourcePrivilegeSet->PrivilegeCount * sizeof(LUID_AND_ATTRIBUTES);
RtlMoveMemory(
Base,
Source,
Length
);
TargetPrivilegeSet->PrivilegeCount += SourcePrivilegeSet->PrivilegeCount;
}