2902 lines
71 KiB
C
2902 lines
71 KiB
C
/*++
|
||
|
||
Copyright (c) 1989 Microsoft Corporation
|
||
|
||
Module Name:
|
||
|
||
Capture.c
|
||
|
||
Abstract:
|
||
|
||
This Module implements the security data structure capturing routines.
|
||
There are corresponding Release routines for the data structures that
|
||
are captured into allocated pool.
|
||
|
||
Author:
|
||
|
||
Gary Kimura (GaryKi) 9-Nov-1989
|
||
Jim Kelly (JimK) 1-Feb-1990
|
||
|
||
Environment:
|
||
|
||
Kernel Mode
|
||
|
||
Revision History:
|
||
|
||
--*/
|
||
|
||
#include "pch.h"
|
||
|
||
#pragma hdrstop
|
||
|
||
|
||
#ifdef ALLOC_PRAGMA
|
||
#pragma alloc_text(PAGE,SeCaptureSecurityDescriptor)
|
||
#pragma alloc_text(PAGE,SeReleaseSecurityDescriptor)
|
||
#pragma alloc_text(PAGE,SepCopyProxyData)
|
||
#pragma alloc_text(PAGE,SepFreeProxyData)
|
||
#pragma alloc_text(PAGE,SepProbeAndCaptureQosData)
|
||
#pragma alloc_text(PAGE,SeFreeCapturedSecurityQos)
|
||
#pragma alloc_text(PAGE,SeCaptureSecurityQos)
|
||
#pragma alloc_text(PAGE,SeCaptureSid)
|
||
#pragma alloc_text(PAGE,SeReleaseSid)
|
||
#pragma alloc_text(PAGE,SeCaptureAcl)
|
||
#pragma alloc_text(PAGE,SeReleaseAcl)
|
||
#pragma alloc_text(PAGE,SeCaptureLuidAndAttributesArray)
|
||
#pragma alloc_text(PAGE,SeReleaseLuidAndAttributesArray)
|
||
#pragma alloc_text(PAGE,SeCaptureSidAndAttributesArray)
|
||
#pragma alloc_text(PAGE,SeReleaseSidAndAttributesArray)
|
||
#pragma alloc_text(PAGE,SeCaptureAuditPolicy)
|
||
#pragma alloc_text(PAGE,SeReleaseAuditPolicy)
|
||
#pragma alloc_text(PAGE,SeComputeQuotaInformationSize)
|
||
#pragma alloc_text(PAGE,SeValidSecurityDescriptor)
|
||
#endif
|
||
|
||
#define LongAligned( ptr ) (LongAlignPtr(ptr) == (ptr))
|
||
|
||
|
||
NTSTATUS
|
||
SeCaptureSecurityDescriptor (
|
||
IN PSECURITY_DESCRIPTOR InputSecurityDescriptor,
|
||
IN KPROCESSOR_MODE RequestorMode,
|
||
IN POOL_TYPE PoolType,
|
||
IN BOOLEAN ForceCapture,
|
||
OUT PSECURITY_DESCRIPTOR *OutputSecurityDescriptor
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine probes and captures a copy of the security descriptor based
|
||
upon the following tests.
|
||
|
||
if the requestor mode is not kernel mode then
|
||
|
||
probe and capture the input descriptor
|
||
(the captured descriptor is self-relative)
|
||
|
||
if the requstor mode is kernel mode then
|
||
|
||
if force capture is true then
|
||
|
||
do not probe the input descriptor, but do capture it.
|
||
(the captured descriptor is self-relative)
|
||
|
||
else
|
||
|
||
do nothing
|
||
(the input descriptor is expected to be self-relative)
|
||
|
||
Arguments:
|
||
|
||
InputSecurityDescriptor - Supplies the security descriptor to capture.
|
||
This parameter is assumed to have been provided by the mode specified
|
||
in RequestorMode.
|
||
|
||
RequestorMode - Specifies the caller's access mode.
|
||
|
||
PoolType - Specifies which pool type to allocate the captured
|
||
descriptor from
|
||
|
||
ForceCapture - Specifies whether the input descriptor should always be
|
||
captured
|
||
|
||
OutputSecurityDescriptor - Supplies the address of a pointer to the
|
||
output security descriptor. The captured descriptor will be
|
||
self-relative format.
|
||
|
||
Return Value:
|
||
|
||
STATUS_SUCCESS if the operation is successful.
|
||
|
||
STATUS_INVALID_SID - An SID within the security descriptor is not
|
||
a valid SID.
|
||
|
||
STATUS_INVALID_ACL - An ACL within the security descriptor is not
|
||
a valid ACL.
|
||
|
||
STATUS_UNKNOWN_REVISION - The revision level of the security descriptor
|
||
is not one known to this revision of the capture routine.
|
||
--*/
|
||
|
||
{
|
||
#define SEP_USHORT_OVERFLOW ((ULONG) ((USHORT) -1))
|
||
|
||
SECURITY_DESCRIPTOR Captured;
|
||
SECURITY_DESCRIPTOR_RELATIVE *PIOutputSecurityDescriptor;
|
||
PCHAR DescriptorOffset;
|
||
|
||
ULONG SaclSize;
|
||
ULONG NewSaclSize;
|
||
|
||
ULONG DaclSize;
|
||
ULONG NewDaclSize;
|
||
|
||
ULONG OwnerSubAuthorityCount=0;
|
||
ULONG OwnerSize=0;
|
||
ULONG NewOwnerSize;
|
||
|
||
ULONG GroupSubAuthorityCount=0;
|
||
ULONG GroupSize=0;
|
||
ULONG NewGroupSize;
|
||
|
||
ULONG Size;
|
||
|
||
PAGED_CODE();
|
||
|
||
//
|
||
// if the security descriptor is null then there is really nothing to
|
||
// capture
|
||
//
|
||
|
||
if (InputSecurityDescriptor == NULL) {
|
||
|
||
(*OutputSecurityDescriptor) = NULL;
|
||
|
||
return STATUS_SUCCESS;
|
||
|
||
}
|
||
|
||
//
|
||
// check if the requestors mode is kernel mode and we are not
|
||
// to force a capture
|
||
//
|
||
|
||
if ((RequestorMode == KernelMode) && (ForceCapture == FALSE)) {
|
||
|
||
//
|
||
// Yes it is so we don't need to do any work and can simply
|
||
// return a pointer to the input descriptor
|
||
//
|
||
|
||
(*OutputSecurityDescriptor) = InputSecurityDescriptor;
|
||
|
||
return STATUS_SUCCESS;
|
||
|
||
}
|
||
|
||
|
||
//
|
||
// We need to probe and capture the descriptor.
|
||
// To do this we need to probe the main security descriptor record
|
||
// first.
|
||
//
|
||
|
||
if (RequestorMode != KernelMode) {
|
||
|
||
//
|
||
// Capture of UserMode SecurityDescriptor.
|
||
//
|
||
|
||
try {
|
||
|
||
//
|
||
// Probe the main record of the input SecurityDescriptor
|
||
//
|
||
|
||
ProbeForReadSmallStructure( InputSecurityDescriptor,
|
||
sizeof(SECURITY_DESCRIPTOR_RELATIVE),
|
||
sizeof(ULONG) );
|
||
|
||
//
|
||
// Capture the SecurityDescriptor main record.
|
||
//
|
||
|
||
RtlCopyMemory( (&Captured),
|
||
InputSecurityDescriptor,
|
||
sizeof(SECURITY_DESCRIPTOR_RELATIVE) );
|
||
|
||
//
|
||
// Verify the alignment is correct for absolute case. This is
|
||
// only needed when pointer are 64 bits.
|
||
//
|
||
|
||
if (!(Captured.Control & SE_SELF_RELATIVE)) {
|
||
|
||
if ((ULONG_PTR) InputSecurityDescriptor & (sizeof(ULONG_PTR) - 1)) {
|
||
ExRaiseDatatypeMisalignment();
|
||
}
|
||
}
|
||
|
||
|
||
} except(EXCEPTION_EXECUTE_HANDLER) {
|
||
return GetExceptionCode();
|
||
}
|
||
|
||
} else {
|
||
|
||
//
|
||
// Force capture of kernel mode SecurityDescriptor.
|
||
//
|
||
// Capture the SecurityDescriptor main record.
|
||
// It doesn't need probing because requestor mode is kernel.
|
||
//
|
||
|
||
RtlCopyMemory( (&Captured),
|
||
InputSecurityDescriptor,
|
||
sizeof(SECURITY_DESCRIPTOR_RELATIVE) );
|
||
|
||
}
|
||
|
||
//
|
||
// Make sure it is a revision we recognize
|
||
//
|
||
|
||
if (Captured.Revision != SECURITY_DESCRIPTOR_REVISION) {
|
||
return STATUS_UNKNOWN_REVISION;
|
||
}
|
||
|
||
|
||
//
|
||
// In case the input security descriptor is self-relative, change the
|
||
// captured main record to appear as an absolute form so we can use
|
||
// common code for both cases below.
|
||
//
|
||
// Note that the fields of Captured are left pointing to user
|
||
// space addresses. Treat them carefully.
|
||
//
|
||
|
||
try {
|
||
|
||
Captured.Owner = RtlpOwnerAddrSecurityDescriptor(
|
||
(SECURITY_DESCRIPTOR *)InputSecurityDescriptor
|
||
);
|
||
Captured.Group = RtlpGroupAddrSecurityDescriptor(
|
||
(SECURITY_DESCRIPTOR *)InputSecurityDescriptor
|
||
);
|
||
Captured.Sacl = RtlpSaclAddrSecurityDescriptor (
|
||
(SECURITY_DESCRIPTOR *)InputSecurityDescriptor
|
||
);
|
||
Captured.Dacl = RtlpDaclAddrSecurityDescriptor (
|
||
(SECURITY_DESCRIPTOR *)InputSecurityDescriptor
|
||
);
|
||
Captured.Control &= ~SE_SELF_RELATIVE;
|
||
|
||
} except(EXCEPTION_EXECUTE_HANDLER) {
|
||
return GetExceptionCode();
|
||
}
|
||
|
||
|
||
|
||
//
|
||
// Indicate the size we are going to need to allocate for the captured
|
||
// acls
|
||
//
|
||
|
||
SaclSize = 0;
|
||
DaclSize = 0;
|
||
|
||
NewSaclSize = 0;
|
||
NewDaclSize = 0;
|
||
NewGroupSize = 0;
|
||
NewOwnerSize = 0;
|
||
|
||
//
|
||
// Probe (if necessary) and capture each of the components of a
|
||
// SECURITY_DESCRIPTOR.
|
||
//
|
||
|
||
//
|
||
// System ACL first
|
||
//
|
||
|
||
if ((Captured.Control & SE_SACL_PRESENT) &&
|
||
(Captured.Sacl != NULL) ) {
|
||
|
||
if (RequestorMode != KernelMode) {
|
||
|
||
try {
|
||
SaclSize = ProbeAndReadUshort( &(Captured.Sacl->AclSize) );
|
||
ProbeForRead( Captured.Sacl,
|
||
SaclSize,
|
||
sizeof(ULONG) );
|
||
} except(EXCEPTION_EXECUTE_HANDLER) {
|
||
return GetExceptionCode();
|
||
}
|
||
|
||
} else {
|
||
|
||
SaclSize = Captured.Sacl->AclSize;
|
||
|
||
}
|
||
|
||
NewSaclSize = (ULONG)LongAlignSize( SaclSize );
|
||
|
||
//
|
||
// Make sure that we do not have an overflow.
|
||
//
|
||
|
||
if (NewSaclSize > SEP_USHORT_OVERFLOW) {
|
||
return STATUS_INVALID_ACL;
|
||
}
|
||
|
||
} else {
|
||
//
|
||
// Force the SACL to null if the bit is off
|
||
//
|
||
Captured.Sacl = NULL;
|
||
}
|
||
|
||
//
|
||
// Discretionary ACL
|
||
//
|
||
|
||
if ((Captured.Control & SE_DACL_PRESENT) &&
|
||
(Captured.Dacl != NULL) ) {
|
||
|
||
if (RequestorMode != KernelMode) {
|
||
|
||
try {
|
||
DaclSize = ProbeAndReadUshort( &(Captured.Dacl->AclSize) );
|
||
ProbeForRead( Captured.Dacl,
|
||
DaclSize,
|
||
sizeof(ULONG) );
|
||
} except(EXCEPTION_EXECUTE_HANDLER) {
|
||
return GetExceptionCode();
|
||
}
|
||
|
||
} else {
|
||
|
||
DaclSize = Captured.Dacl->AclSize;
|
||
|
||
}
|
||
|
||
NewDaclSize = (ULONG)LongAlignSize( DaclSize );
|
||
|
||
//
|
||
// Make sure that we do not have an overflow.
|
||
//
|
||
|
||
if (NewDaclSize > SEP_USHORT_OVERFLOW) {
|
||
return STATUS_INVALID_ACL;
|
||
}
|
||
|
||
} else {
|
||
//
|
||
// Force the DACL to null if it is not present
|
||
//
|
||
Captured.Dacl = NULL;
|
||
}
|
||
|
||
//
|
||
// Owner SID
|
||
//
|
||
|
||
if (Captured.Owner != NULL) {
|
||
|
||
if (RequestorMode != KernelMode) {
|
||
|
||
try {
|
||
OwnerSubAuthorityCount =
|
||
ProbeAndReadUchar( &(((SID *)(Captured.Owner))->SubAuthorityCount) );
|
||
OwnerSize = RtlLengthRequiredSid( OwnerSubAuthorityCount );
|
||
ProbeForRead( Captured.Owner,
|
||
OwnerSize,
|
||
sizeof(ULONG) );
|
||
} except(EXCEPTION_EXECUTE_HANDLER) {
|
||
return GetExceptionCode();
|
||
}
|
||
|
||
} else {
|
||
|
||
OwnerSubAuthorityCount = ((SID *)(Captured.Owner))->SubAuthorityCount;
|
||
OwnerSize = RtlLengthRequiredSid( OwnerSubAuthorityCount );
|
||
|
||
}
|
||
|
||
NewOwnerSize = (ULONG)LongAlignSize( OwnerSize );
|
||
|
||
}
|
||
|
||
//
|
||
// Group SID
|
||
//
|
||
|
||
if (Captured.Group != NULL) {
|
||
|
||
if (RequestorMode != KernelMode) {
|
||
|
||
try {
|
||
GroupSubAuthorityCount =
|
||
ProbeAndReadUchar( &(((SID *)(Captured.Group))->SubAuthorityCount) );
|
||
GroupSize = RtlLengthRequiredSid( GroupSubAuthorityCount );
|
||
ProbeForRead( Captured.Group,
|
||
GroupSize,
|
||
sizeof(ULONG) );
|
||
} except(EXCEPTION_EXECUTE_HANDLER) {
|
||
return GetExceptionCode();
|
||
}
|
||
|
||
} else {
|
||
|
||
GroupSubAuthorityCount = ((SID *)(Captured.Group))->SubAuthorityCount;
|
||
GroupSize = RtlLengthRequiredSid( GroupSubAuthorityCount );
|
||
|
||
}
|
||
|
||
NewGroupSize = (ULONG)LongAlignSize( GroupSize );
|
||
|
||
}
|
||
|
||
|
||
|
||
//
|
||
// Now allocate enough pool to hold the descriptor
|
||
//
|
||
|
||
Size = sizeof(SECURITY_DESCRIPTOR_RELATIVE) +
|
||
NewSaclSize +
|
||
NewDaclSize +
|
||
NewOwnerSize +
|
||
NewGroupSize;
|
||
|
||
(PIOutputSecurityDescriptor) = (SECURITY_DESCRIPTOR_RELATIVE *)ExAllocatePoolWithTag( PoolType,
|
||
Size,
|
||
'cSeS' );
|
||
|
||
if ( PIOutputSecurityDescriptor == NULL ) {
|
||
return( STATUS_INSUFFICIENT_RESOURCES );
|
||
}
|
||
|
||
(*OutputSecurityDescriptor) = (PSECURITY_DESCRIPTOR)PIOutputSecurityDescriptor;
|
||
DescriptorOffset = (PCHAR)(PIOutputSecurityDescriptor);
|
||
|
||
|
||
//
|
||
// Copy the main security descriptor record over
|
||
//
|
||
|
||
RtlCopyMemory( DescriptorOffset,
|
||
&Captured,
|
||
sizeof(SECURITY_DESCRIPTOR_RELATIVE) );
|
||
DescriptorOffset += sizeof(SECURITY_DESCRIPTOR_RELATIVE);
|
||
|
||
//
|
||
// Indicate the output descriptor is self-relative
|
||
//
|
||
|
||
PIOutputSecurityDescriptor->Control |= SE_SELF_RELATIVE;
|
||
|
||
//
|
||
// If there is a System Acl, copy it over and set
|
||
// the output descriptor's offset to point to the newly captured copy.
|
||
//
|
||
|
||
if ((Captured.Control & SE_SACL_PRESENT) && (Captured.Sacl != NULL)) {
|
||
|
||
|
||
try {
|
||
RtlCopyMemory( DescriptorOffset,
|
||
Captured.Sacl,
|
||
SaclSize );
|
||
|
||
|
||
} except(EXCEPTION_EXECUTE_HANDLER) {
|
||
ExFreePool( PIOutputSecurityDescriptor );
|
||
return GetExceptionCode();
|
||
}
|
||
|
||
if ((RequestorMode != KernelMode) &&
|
||
(!SepCheckAcl( (PACL) DescriptorOffset, SaclSize )) ) {
|
||
|
||
ExFreePool( PIOutputSecurityDescriptor );
|
||
return STATUS_INVALID_ACL;
|
||
}
|
||
|
||
//
|
||
// Change pointer to offset
|
||
//
|
||
|
||
PIOutputSecurityDescriptor->Sacl =
|
||
RtlPointerToOffset( PIOutputSecurityDescriptor,
|
||
DescriptorOffset
|
||
);
|
||
|
||
((PACL) DescriptorOffset)->AclSize = (USHORT) NewSaclSize;
|
||
DescriptorOffset += NewSaclSize;
|
||
} else {
|
||
PIOutputSecurityDescriptor->Sacl = 0;
|
||
}
|
||
|
||
//
|
||
// If there is a Discretionary Acl, copy it over and set
|
||
// the output descriptor's offset to point to the newly captured copy.
|
||
//
|
||
|
||
if ((Captured.Control & SE_DACL_PRESENT) && (Captured.Dacl != NULL)) {
|
||
|
||
|
||
try {
|
||
RtlCopyMemory( DescriptorOffset,
|
||
Captured.Dacl,
|
||
DaclSize );
|
||
} except(EXCEPTION_EXECUTE_HANDLER) {
|
||
ExFreePool( PIOutputSecurityDescriptor );
|
||
return GetExceptionCode();
|
||
}
|
||
|
||
if ((RequestorMode != KernelMode) &&
|
||
(!SepCheckAcl( (PACL) DescriptorOffset, DaclSize )) ) {
|
||
|
||
ExFreePool( PIOutputSecurityDescriptor );
|
||
return STATUS_INVALID_ACL;
|
||
}
|
||
|
||
//
|
||
// Change pointer to offset
|
||
//
|
||
|
||
PIOutputSecurityDescriptor->Dacl =
|
||
RtlPointerToOffset(
|
||
PIOutputSecurityDescriptor,
|
||
DescriptorOffset
|
||
);
|
||
|
||
((PACL) DescriptorOffset)->AclSize = (USHORT) NewDaclSize;
|
||
DescriptorOffset += NewDaclSize;
|
||
} else {
|
||
PIOutputSecurityDescriptor->Dacl = 0;
|
||
}
|
||
|
||
//
|
||
// If there is an Owner SID, copy it over and set
|
||
// the output descriptor's offset to point to the newly captured copy.
|
||
//
|
||
|
||
if (Captured.Owner != NULL) {
|
||
|
||
|
||
try {
|
||
RtlCopyMemory( DescriptorOffset,
|
||
Captured.Owner,
|
||
OwnerSize );
|
||
((SID *) (DescriptorOffset))->SubAuthorityCount = (UCHAR) OwnerSubAuthorityCount;
|
||
|
||
} except(EXCEPTION_EXECUTE_HANDLER) {
|
||
ExFreePool( PIOutputSecurityDescriptor );
|
||
return GetExceptionCode();
|
||
}
|
||
|
||
if ((RequestorMode != KernelMode) &&
|
||
(!RtlValidSid( (PSID) DescriptorOffset )) ) {
|
||
|
||
ExFreePool( PIOutputSecurityDescriptor );
|
||
return STATUS_INVALID_SID;
|
||
}
|
||
|
||
//
|
||
// Change pointer to offset
|
||
//
|
||
|
||
PIOutputSecurityDescriptor->Owner =
|
||
RtlPointerToOffset(
|
||
PIOutputSecurityDescriptor,
|
||
DescriptorOffset
|
||
);
|
||
|
||
DescriptorOffset += NewOwnerSize;
|
||
|
||
} else {
|
||
PIOutputSecurityDescriptor->Owner = 0;
|
||
}
|
||
|
||
//
|
||
// If there is a group SID, copy it over and set
|
||
// the output descriptor's offset to point to the newly captured copy.
|
||
//
|
||
|
||
if (Captured.Group != NULL) {
|
||
|
||
|
||
try {
|
||
RtlCopyMemory( DescriptorOffset,
|
||
Captured.Group,
|
||
GroupSize );
|
||
|
||
((SID *) DescriptorOffset)->SubAuthorityCount = (UCHAR) GroupSubAuthorityCount;
|
||
} except(EXCEPTION_EXECUTE_HANDLER) {
|
||
ExFreePool( PIOutputSecurityDescriptor );
|
||
return GetExceptionCode();
|
||
}
|
||
|
||
if ((RequestorMode != KernelMode) &&
|
||
(!RtlValidSid( (PSID) DescriptorOffset )) ) {
|
||
|
||
ExFreePool( PIOutputSecurityDescriptor );
|
||
return STATUS_INVALID_SID;
|
||
}
|
||
|
||
//
|
||
// Change pointer to offset
|
||
//
|
||
|
||
PIOutputSecurityDescriptor->Group =
|
||
RtlPointerToOffset(
|
||
PIOutputSecurityDescriptor,
|
||
DescriptorOffset
|
||
);
|
||
|
||
DescriptorOffset += NewGroupSize;
|
||
} else {
|
||
PIOutputSecurityDescriptor->Group = 0;
|
||
}
|
||
|
||
//
|
||
// And return to our caller
|
||
//
|
||
|
||
return STATUS_SUCCESS;
|
||
|
||
}
|
||
|
||
|
||
VOID
|
||
SeReleaseSecurityDescriptor (
|
||
IN PSECURITY_DESCRIPTOR CapturedSecurityDescriptor,
|
||
IN KPROCESSOR_MODE RequestorMode,
|
||
IN BOOLEAN ForceCapture
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine releases a previously captured security descriptor.
|
||
Only
|
||
|
||
Arguments:
|
||
|
||
CapturedSecurityDescriptor - Supplies the security descriptor to release.
|
||
|
||
RequestorMode - The processor mode specified when the descriptor was
|
||
captured.
|
||
|
||
ForceCapture - The ForceCapture value specified when the descriptor was
|
||
captured.
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
--*/
|
||
|
||
{
|
||
//
|
||
// We only have something to deallocate if the requestor was user
|
||
// mode or kernel mode requesting ForceCapture.
|
||
//
|
||
|
||
PAGED_CODE();
|
||
|
||
if ( ((RequestorMode == KernelMode) && (ForceCapture == TRUE)) ||
|
||
(RequestorMode == UserMode ) ) {
|
||
if ( CapturedSecurityDescriptor ) {
|
||
ExFreePool(CapturedSecurityDescriptor);
|
||
}
|
||
}
|
||
|
||
return;
|
||
|
||
}
|
||
|
||
|
||
NTSTATUS
|
||
SepCopyProxyData (
|
||
OUT PSECURITY_TOKEN_PROXY_DATA * DestProxyData,
|
||
IN PSECURITY_TOKEN_PROXY_DATA SourceProxyData
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine copies a token proxy data structure from one token to another.
|
||
|
||
Arguments:
|
||
|
||
DestProxyData - Receives a pointer to a new proxy data structure.
|
||
|
||
SourceProxyData - Supplies a pointer to an already existing proxy data structure.
|
||
|
||
Return Value:
|
||
|
||
STATUS_INSUFFICIENT_RESOURCES on failure.
|
||
|
||
--*/
|
||
|
||
{
|
||
|
||
PAGED_CODE();
|
||
|
||
*DestProxyData = ExAllocatePoolWithTag( PagedPool, PtrAlignSize(sizeof( SECURITY_TOKEN_PROXY_DATA )) + SourceProxyData->PathInfo.Length, 'dPoT' );
|
||
|
||
if (*DestProxyData == NULL) {
|
||
return( STATUS_INSUFFICIENT_RESOURCES );
|
||
}
|
||
|
||
(*DestProxyData)->PathInfo.Buffer = (PWSTR)(((PUCHAR)(*DestProxyData)) + PtrAlignSize(sizeof( SECURITY_TOKEN_PROXY_DATA )));
|
||
|
||
(*DestProxyData)->Length = SourceProxyData->Length;
|
||
(*DestProxyData)->ProxyClass = SourceProxyData->ProxyClass;
|
||
(*DestProxyData)->PathInfo.MaximumLength =
|
||
(*DestProxyData)->PathInfo.Length = SourceProxyData->PathInfo.Length;
|
||
(*DestProxyData)->ContainerMask = SourceProxyData->ContainerMask;
|
||
(*DestProxyData)->ObjectMask = SourceProxyData->ObjectMask;
|
||
|
||
RtlCopyUnicodeString( &(*DestProxyData)->PathInfo, &SourceProxyData->PathInfo );
|
||
|
||
return( STATUS_SUCCESS );
|
||
}
|
||
|
||
VOID
|
||
SepFreeProxyData (
|
||
IN PSECURITY_TOKEN_PROXY_DATA ProxyData
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine frees a SECURITY_TOKEN_PROXY_DATA structure and all sub structures.
|
||
|
||
Arguments:
|
||
|
||
ProxyData - Supplies a pointer to an existing proxy data structure.
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
--*/
|
||
{
|
||
PAGED_CODE();
|
||
|
||
if (ProxyData != NULL) {
|
||
|
||
ExFreePool( ProxyData );
|
||
}
|
||
}
|
||
|
||
|
||
|
||
|
||
NTSTATUS
|
||
SepProbeAndCaptureQosData(
|
||
IN PSECURITY_ADVANCED_QUALITY_OF_SERVICE CapturedSecurityQos
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine probes and captures the imbedded structures in a
|
||
Security Quality of Service structure.
|
||
|
||
This routine assumes that it is being called under an existing
|
||
try-except clause.
|
||
|
||
Arguments:
|
||
|
||
CapturedSecurityQos - Points to the captured body of a QOS
|
||
structure. The pointers in this structure are presumed
|
||
not to be probed or captured at this point.
|
||
|
||
Return Value:
|
||
|
||
STATUS_SUCCESS indicates no exceptions were encountered.
|
||
|
||
Any access violations encountered will be returned.
|
||
|
||
--*/
|
||
{
|
||
NTSTATUS Status;
|
||
PSECURITY_TOKEN_PROXY_DATA CapturedProxyData;
|
||
PSECURITY_TOKEN_AUDIT_DATA CapturedAuditData;
|
||
SECURITY_TOKEN_PROXY_DATA StackProxyData;
|
||
PAGED_CODE();
|
||
|
||
CapturedProxyData = CapturedSecurityQos->ProxyData;
|
||
CapturedSecurityQos->ProxyData = NULL;
|
||
CapturedAuditData = CapturedSecurityQos->AuditData;
|
||
CapturedSecurityQos->AuditData = NULL;
|
||
|
||
if (ARGUMENT_PRESENT( CapturedProxyData )) {
|
||
|
||
//
|
||
// Make sure the body of the proxy data is ok to read.
|
||
//
|
||
|
||
ProbeForReadSmallStructure(
|
||
CapturedProxyData,
|
||
sizeof(SECURITY_TOKEN_PROXY_DATA),
|
||
sizeof(ULONG)
|
||
);
|
||
|
||
StackProxyData = *CapturedProxyData;
|
||
|
||
if (StackProxyData.Length != sizeof( SECURITY_TOKEN_PROXY_DATA )) {
|
||
return( STATUS_INVALID_PARAMETER );
|
||
}
|
||
|
||
|
||
//
|
||
// Probe the passed pathinfo buffer
|
||
//
|
||
|
||
ProbeForRead(
|
||
StackProxyData.PathInfo.Buffer,
|
||
StackProxyData.PathInfo.Length,
|
||
sizeof( UCHAR )
|
||
);
|
||
|
||
Status = SepCopyProxyData( &CapturedSecurityQos->ProxyData, &StackProxyData );
|
||
|
||
if (!NT_SUCCESS(Status)) {
|
||
|
||
if (CapturedSecurityQos->ProxyData != NULL) {
|
||
SepFreeProxyData( CapturedSecurityQos->ProxyData );
|
||
CapturedSecurityQos->ProxyData = NULL;
|
||
}
|
||
|
||
return( Status );
|
||
}
|
||
|
||
}
|
||
|
||
if (ARGUMENT_PRESENT( CapturedAuditData )) {
|
||
|
||
PSECURITY_TOKEN_AUDIT_DATA LocalAuditData;
|
||
|
||
//
|
||
// Probe the audit data structure and make sure it looks ok
|
||
//
|
||
|
||
ProbeForReadSmallStructure(
|
||
CapturedAuditData,
|
||
sizeof( SECURITY_TOKEN_AUDIT_DATA ),
|
||
sizeof( ULONG )
|
||
);
|
||
|
||
|
||
LocalAuditData = ExAllocatePool( PagedPool, sizeof( SECURITY_TOKEN_AUDIT_DATA ));
|
||
|
||
if (LocalAuditData == NULL) {
|
||
|
||
//
|
||
// Cleanup any proxy data we may have allocated.
|
||
//
|
||
|
||
SepFreeProxyData( CapturedSecurityQos->ProxyData );
|
||
CapturedSecurityQos->ProxyData = NULL;
|
||
|
||
return( STATUS_INSUFFICIENT_RESOURCES );
|
||
|
||
}
|
||
|
||
//
|
||
// Copy the data to the local buffer. Note: we do this in this
|
||
// order so that if the final assignment fails the caller will
|
||
// still be able to free the allocated pool.
|
||
//
|
||
|
||
CapturedSecurityQos->AuditData = LocalAuditData;
|
||
|
||
*CapturedSecurityQos->AuditData = *CapturedAuditData;
|
||
|
||
if ( LocalAuditData->Length != sizeof( SECURITY_TOKEN_AUDIT_DATA ) ) {
|
||
SepFreeProxyData( CapturedSecurityQos->ProxyData );
|
||
CapturedSecurityQos->ProxyData = NULL;
|
||
ExFreePool(CapturedSecurityQos->AuditData);
|
||
CapturedSecurityQos->AuditData = NULL;
|
||
return( STATUS_INVALID_PARAMETER );
|
||
}
|
||
}
|
||
|
||
return( STATUS_SUCCESS );
|
||
|
||
}
|
||
|
||
|
||
VOID
|
||
SeFreeCapturedSecurityQos(
|
||
IN PVOID SecurityQos
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine frees the data associated with a captured SecurityQos
|
||
structure. It does not free the body of the structure, just whatever
|
||
its internal fields point to.
|
||
|
||
Arguments:
|
||
|
||
SecurityQos - Points to a captured security QOS structure.
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
--*/
|
||
|
||
{
|
||
PSECURITY_ADVANCED_QUALITY_OF_SERVICE IAdvancedSecurityQos;
|
||
|
||
PAGED_CODE();
|
||
|
||
IAdvancedSecurityQos = (PSECURITY_ADVANCED_QUALITY_OF_SERVICE)SecurityQos;
|
||
|
||
if (IAdvancedSecurityQos->Length == sizeof( SECURITY_ADVANCED_QUALITY_OF_SERVICE )) {
|
||
|
||
if (IAdvancedSecurityQos->AuditData != NULL) {
|
||
ExFreePool( IAdvancedSecurityQos->AuditData );
|
||
}
|
||
|
||
SepFreeProxyData( IAdvancedSecurityQos->ProxyData );
|
||
}
|
||
|
||
return;
|
||
}
|
||
|
||
|
||
NTSTATUS
|
||
SeCaptureSecurityQos (
|
||
IN POBJECT_ATTRIBUTES ObjectAttributes OPTIONAL,
|
||
IN KPROCESSOR_MODE RequestorMode,
|
||
OUT PBOOLEAN SecurityQosPresent,
|
||
OUT PSECURITY_ADVANCED_QUALITY_OF_SERVICE CapturedSecurityQos
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine probes and captures a copy of any security quality
|
||
of service parameters that might have been provided via the
|
||
ObjectAttributes argument.
|
||
|
||
Arguments:
|
||
|
||
ObjectAttributes - The object attributes from which the QOS
|
||
information is to be retrieved.
|
||
|
||
RequestorMode - Indicates the processor mode by which the access
|
||
is being requested.
|
||
|
||
SecurityQosPresent - Receives a boolean value indicating whether
|
||
or not the optional security QOS information was available
|
||
and copied.
|
||
|
||
CapturedSecurityQos - Receives the security QOS information if available.
|
||
|
||
Return Value:
|
||
|
||
STATUS_SUCCESS indicates no exceptions were encountered.
|
||
|
||
Any access violations encountered will be returned.
|
||
|
||
--*/
|
||
|
||
{
|
||
|
||
PSECURITY_QUALITY_OF_SERVICE LocalSecurityQos;
|
||
ULONG LocalQosLength;
|
||
PSECURITY_ADVANCED_QUALITY_OF_SERVICE LocalAdvancedSecurityQos;
|
||
NTSTATUS Status;
|
||
BOOLEAN CapturedQos;
|
||
|
||
PAGED_CODE();
|
||
|
||
CapturedQos = FALSE;
|
||
//
|
||
// Set default return
|
||
//
|
||
|
||
(*SecurityQosPresent) = FALSE;
|
||
|
||
//
|
||
// check if the requestors mode is kernel mode
|
||
//
|
||
|
||
if (RequestorMode != KernelMode) {
|
||
try {
|
||
|
||
if ( ARGUMENT_PRESENT(ObjectAttributes) ) {
|
||
|
||
ProbeForReadSmallStructure( ObjectAttributes,
|
||
sizeof(OBJECT_ATTRIBUTES),
|
||
sizeof(ULONG)
|
||
);
|
||
|
||
LocalSecurityQos =
|
||
(PSECURITY_QUALITY_OF_SERVICE)ObjectAttributes->SecurityQualityOfService;
|
||
|
||
if ( ARGUMENT_PRESENT(LocalSecurityQos) ) {
|
||
|
||
ProbeForReadSmallStructure(
|
||
LocalSecurityQos,
|
||
sizeof(SECURITY_QUALITY_OF_SERVICE),
|
||
sizeof(ULONG)
|
||
);
|
||
|
||
LocalQosLength = LocalSecurityQos->Length;
|
||
|
||
//
|
||
// Check the length and see if this is a QOS or Advanced QOS
|
||
// structure.
|
||
//
|
||
|
||
if (LocalQosLength == sizeof( SECURITY_QUALITY_OF_SERVICE )) {
|
||
|
||
//
|
||
// It's a downlevel QOS, copy what's there and leave.
|
||
//
|
||
|
||
(*SecurityQosPresent) = TRUE;
|
||
RtlCopyMemory( CapturedSecurityQos, LocalSecurityQos, sizeof( SECURITY_QUALITY_OF_SERVICE ));
|
||
CapturedSecurityQos->ProxyData = NULL;
|
||
CapturedSecurityQos->AuditData = NULL;
|
||
CapturedSecurityQos->Length = LocalQosLength;
|
||
|
||
} else {
|
||
|
||
if (LocalQosLength == sizeof( SECURITY_ADVANCED_QUALITY_OF_SERVICE )) {
|
||
|
||
LocalAdvancedSecurityQos =
|
||
(PSECURITY_ADVANCED_QUALITY_OF_SERVICE)ObjectAttributes->SecurityQualityOfService;
|
||
|
||
ProbeForReadSmallStructure(
|
||
LocalAdvancedSecurityQos,
|
||
sizeof(SECURITY_ADVANCED_QUALITY_OF_SERVICE),
|
||
sizeof(ULONG)
|
||
);
|
||
|
||
(*SecurityQosPresent) = TRUE;
|
||
*CapturedSecurityQos = *LocalAdvancedSecurityQos;
|
||
CapturedSecurityQos->Length = LocalQosLength;
|
||
|
||
//
|
||
// Capture the proxy and audit data, if necessary.
|
||
//
|
||
|
||
if ( ARGUMENT_PRESENT(CapturedSecurityQos->ProxyData) || ARGUMENT_PRESENT( CapturedSecurityQos->AuditData ) ) {
|
||
|
||
CapturedQos = TRUE;
|
||
Status = SepProbeAndCaptureQosData( CapturedSecurityQos );
|
||
|
||
if (!NT_SUCCESS( Status )) {
|
||
|
||
return( Status );
|
||
}
|
||
}
|
||
|
||
} else {
|
||
|
||
return( STATUS_INVALID_PARAMETER );
|
||
}
|
||
}
|
||
|
||
} // end_if
|
||
|
||
|
||
} // end_if
|
||
|
||
} except(EXCEPTION_EXECUTE_HANDLER) {
|
||
|
||
|
||
//
|
||
// If we captured any proxy data, we need to free it now.
|
||
//
|
||
|
||
if ( CapturedQos ) {
|
||
|
||
SepFreeProxyData( CapturedSecurityQos->ProxyData );
|
||
|
||
if ( CapturedSecurityQos->AuditData != NULL ) {
|
||
ExFreePool( CapturedSecurityQos->AuditData );
|
||
}
|
||
}
|
||
|
||
return GetExceptionCode();
|
||
} // end_try
|
||
|
||
|
||
} else {
|
||
|
||
if ( ARGUMENT_PRESENT(ObjectAttributes) ) {
|
||
if ( ARGUMENT_PRESENT(ObjectAttributes->SecurityQualityOfService) ) {
|
||
(*SecurityQosPresent) = TRUE;
|
||
|
||
if (((PSECURITY_QUALITY_OF_SERVICE)(ObjectAttributes->SecurityQualityOfService))->Length == sizeof( SECURITY_QUALITY_OF_SERVICE )) {
|
||
|
||
RtlCopyMemory( CapturedSecurityQos, ObjectAttributes->SecurityQualityOfService, sizeof( SECURITY_QUALITY_OF_SERVICE ));
|
||
CapturedSecurityQos->ProxyData = NULL;
|
||
CapturedSecurityQos->AuditData = NULL;
|
||
|
||
} else {
|
||
|
||
(*CapturedSecurityQos) =
|
||
(*(SECURITY_ADVANCED_QUALITY_OF_SERVICE *)(ObjectAttributes->SecurityQualityOfService));
|
||
}
|
||
|
||
|
||
} // end_if
|
||
} // end_if
|
||
|
||
} // end_if
|
||
|
||
return STATUS_SUCCESS;
|
||
}
|
||
|
||
NTSTATUS
|
||
SeCaptureSid (
|
||
IN PSID InputSid,
|
||
IN KPROCESSOR_MODE RequestorMode,
|
||
IN PVOID CaptureBuffer OPTIONAL,
|
||
IN ULONG CaptureBufferLength,
|
||
IN POOL_TYPE PoolType,
|
||
IN BOOLEAN ForceCapture,
|
||
OUT PSID *CapturedSid
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine probes and captures a copy of the specified SID.
|
||
The SID is either captured into a provided buffer, or pool
|
||
allocated to receive the SID.
|
||
|
||
|
||
if the requestor mode is not kernel mode then
|
||
|
||
probe and capture the input SID
|
||
|
||
if the requstor mode is kernel mode then
|
||
|
||
if force capture is true then
|
||
|
||
do not probe the input SID, but do capture it
|
||
|
||
else
|
||
|
||
return address of original, but don't copy
|
||
|
||
Arguments:
|
||
|
||
InputSid - Supplies the SID to capture. This parameter is assumed
|
||
to have been provided by the mode specified in RequestorMode.
|
||
|
||
RequestorMode - Specifies the caller's access mode.
|
||
|
||
CaptureBuffer - Specifies a buffer into which the SID is to be
|
||
captured. If this parameter is not provided, pool will be allocated
|
||
to hold the captured data.
|
||
|
||
CaptureBufferLength - Indicates the length, in bytes, of the capture
|
||
buffer.
|
||
|
||
PoolType - Specifies which pool type to allocate to capture the
|
||
SID into. This parameter is ignored if CaptureBuffer is provided.
|
||
|
||
ForceCapture - Specifies whether the SID should be captured even if
|
||
requestor mode is kernel.
|
||
|
||
CapturedSid - Supplies the address of a pointer to an SID.
|
||
The pointer will be set to point to the captured (or uncaptured) SID.
|
||
|
||
AlignedSidSize - Supplies the address of a ULONG to receive the length
|
||
of the SID rounded up to the next longword boundary.
|
||
|
||
Return Value:
|
||
|
||
STATUS_SUCCESS indicates the capture was successful.
|
||
|
||
STATUS_BUFFER_TOO_SMALL - indicates the buffer provided to capture the SID
|
||
into wasn't large enough to hold the SID.
|
||
|
||
Any access violations encountered will be returned.
|
||
|
||
--*/
|
||
|
||
{
|
||
|
||
|
||
|
||
ULONG GetSidSubAuthorityCount;
|
||
ULONG SidSize;
|
||
|
||
PAGED_CODE();
|
||
|
||
//
|
||
// check if the requestors mode is kernel mode and we are not
|
||
// to force a capture.
|
||
//
|
||
|
||
if ((RequestorMode == KernelMode) && (ForceCapture == FALSE)) {
|
||
|
||
//
|
||
// We don't need to do any work and can simply
|
||
// return a pointer to the input SID
|
||
//
|
||
|
||
(*CapturedSid) = InputSid;
|
||
|
||
return STATUS_SUCCESS;
|
||
}
|
||
|
||
|
||
//
|
||
// Get the length needed to hold the SID
|
||
//
|
||
|
||
if (RequestorMode != KernelMode) {
|
||
|
||
try {
|
||
GetSidSubAuthorityCount =
|
||
ProbeAndReadUchar( &(((SID *)(InputSid))->SubAuthorityCount) );
|
||
SidSize = RtlLengthRequiredSid( GetSidSubAuthorityCount );
|
||
ProbeForRead( InputSid,
|
||
SidSize,
|
||
sizeof(ULONG) );
|
||
} except(EXCEPTION_EXECUTE_HANDLER) {
|
||
return GetExceptionCode();
|
||
}
|
||
|
||
} else {
|
||
|
||
GetSidSubAuthorityCount = ((SID *)(InputSid))->SubAuthorityCount;
|
||
SidSize = RtlLengthRequiredSid( GetSidSubAuthorityCount );
|
||
|
||
}
|
||
|
||
|
||
//
|
||
// If a buffer was provided, compare lengths.
|
||
// Otherwise, allocate a buffer.
|
||
//
|
||
|
||
if (ARGUMENT_PRESENT(CaptureBuffer)) {
|
||
|
||
if (SidSize > CaptureBufferLength) {
|
||
return STATUS_BUFFER_TOO_SMALL;
|
||
} else {
|
||
|
||
(*CapturedSid) = CaptureBuffer;
|
||
}
|
||
|
||
} else {
|
||
|
||
(*CapturedSid) = (PSID)ExAllocatePoolWithTag(PoolType, SidSize, 'iSeS');
|
||
|
||
if ( *CapturedSid == NULL ) {
|
||
return( STATUS_INSUFFICIENT_RESOURCES );
|
||
}
|
||
|
||
}
|
||
|
||
//
|
||
// Now copy the SID and validate it
|
||
//
|
||
|
||
try {
|
||
|
||
RtlCopyMemory( (*CapturedSid), InputSid, SidSize );
|
||
((SID *)(*CapturedSid))->SubAuthorityCount = (UCHAR) GetSidSubAuthorityCount;
|
||
|
||
} except(EXCEPTION_EXECUTE_HANDLER) {
|
||
if (!ARGUMENT_PRESENT(CaptureBuffer)) {
|
||
ExFreePool( (*CapturedSid) );
|
||
*CapturedSid = NULL;
|
||
}
|
||
|
||
return GetExceptionCode();
|
||
}
|
||
|
||
if ((!RtlValidSid( (*CapturedSid) )) ) {
|
||
|
||
if (!ARGUMENT_PRESENT(CaptureBuffer)) {
|
||
ExFreePool( (*CapturedSid) );
|
||
*CapturedSid = NULL;
|
||
}
|
||
|
||
return STATUS_INVALID_SID;
|
||
}
|
||
|
||
return STATUS_SUCCESS;
|
||
|
||
}
|
||
|
||
|
||
VOID
|
||
SeReleaseSid (
|
||
IN PSID CapturedSid,
|
||
IN KPROCESSOR_MODE RequestorMode,
|
||
IN BOOLEAN ForceCapture
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine releases a previously captured SID.
|
||
|
||
This routine should NOT be called if the SID was captured into a
|
||
provided CaptureBuffer (see SeCaptureSid).
|
||
|
||
Arguments:
|
||
|
||
CapturedSid - Supplies the SID to release.
|
||
|
||
RequestorMode - The processor mode specified when the SID was captured.
|
||
|
||
ForceCapture - The ForceCapture value specified when the SID was
|
||
captured.
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
--*/
|
||
|
||
{
|
||
//
|
||
// We only have something to deallocate if the requestor was user
|
||
// mode or kernel mode requesting ForceCapture.
|
||
//
|
||
|
||
PAGED_CODE();
|
||
|
||
if ( ((RequestorMode == KernelMode) && (ForceCapture == TRUE)) ||
|
||
(RequestorMode == UserMode ) ) {
|
||
|
||
ExFreePool(CapturedSid);
|
||
|
||
}
|
||
|
||
return;
|
||
|
||
}
|
||
|
||
NTSTATUS
|
||
SeCaptureAcl (
|
||
IN PACL InputAcl,
|
||
IN KPROCESSOR_MODE RequestorMode,
|
||
IN PVOID CaptureBuffer OPTIONAL,
|
||
IN ULONG CaptureBufferLength,
|
||
IN POOL_TYPE PoolType,
|
||
IN BOOLEAN ForceCapture,
|
||
OUT PACL *CapturedAcl,
|
||
OUT PULONG AlignedAclSize
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine probes and captures a copy of the specified ACL.
|
||
The ACL is either captured into a provided buffer, or pool
|
||
allocated to receive the ACL.
|
||
|
||
Any ACL captured will have its structure validated.
|
||
|
||
|
||
if the requestor mode is not kernel mode then
|
||
|
||
probe and capture the input ACL
|
||
|
||
if the requstor mode is kernel mode then
|
||
|
||
if force capture is true then
|
||
|
||
do not probe the input ACL, but do capture it
|
||
|
||
else
|
||
|
||
return address of original, but don't copy
|
||
|
||
Arguments:
|
||
|
||
InputAcl - Supplies the ACL to capture. This parameter is assumed
|
||
to have been provided by the mode specified in RequestorMode.
|
||
|
||
RequestorMode - Specifies the caller's access mode.
|
||
|
||
CaptureBuffer - Specifies a buffer into which the ACL is to be
|
||
captured. If this parameter is not provided, pool will be allocated
|
||
to hold the captured data.
|
||
|
||
CaptureBufferLength - Indicates the length, in bytes, of the capture
|
||
buffer.
|
||
|
||
PoolType - Specifies which pool type to allocate to capture the
|
||
ACL into. This parameter is ignored if CaptureBuffer is provided.
|
||
|
||
ForceCapture - Specifies whether the ACL should be captured even if
|
||
requestor mode is kernel.
|
||
|
||
CapturedAcl - Supplies the address of a pointer to an ACL.
|
||
The pointer will be set to point to the captured (or uncaptured) ACL.
|
||
|
||
AlignedAclSize - Supplies the address of a ULONG to receive the length
|
||
of the ACL rounded up to the next longword boundary.
|
||
|
||
Return Value:
|
||
|
||
STATUS_SUCCESS indicates the capture was successful.
|
||
|
||
STATUS_BUFFER_TOO_SMALL - indicates the buffer provided to capture the ACL
|
||
into wasn't large enough to hold the ACL.
|
||
|
||
Any access violations encountered will be returned.
|
||
|
||
--*/
|
||
|
||
{
|
||
|
||
ULONG AclSize;
|
||
|
||
PAGED_CODE();
|
||
|
||
//
|
||
// check if the requestors mode is kernel mode and we are not
|
||
// to force a capture.
|
||
//
|
||
|
||
if ((RequestorMode == KernelMode) && (ForceCapture == FALSE)) {
|
||
|
||
//
|
||
// We don't need to do any work and can simply
|
||
// return a pointer to the input ACL
|
||
//
|
||
|
||
(*CapturedAcl) = InputAcl;
|
||
|
||
return STATUS_SUCCESS;
|
||
}
|
||
|
||
|
||
//
|
||
// Get the length needed to hold the ACL
|
||
//
|
||
|
||
if (RequestorMode != KernelMode) {
|
||
|
||
try {
|
||
|
||
AclSize = ProbeAndReadUshort( &(InputAcl->AclSize) );
|
||
|
||
ProbeForRead( InputAcl,
|
||
AclSize,
|
||
sizeof(ULONG) );
|
||
|
||
} except(EXCEPTION_EXECUTE_HANDLER) {
|
||
return GetExceptionCode();
|
||
}
|
||
|
||
} else {
|
||
|
||
AclSize = InputAcl->AclSize;
|
||
|
||
}
|
||
|
||
//
|
||
// If the passed pointer is non-null, it has better at least
|
||
// point to a well formed ACL
|
||
//
|
||
|
||
if (AclSize < sizeof(ACL)) {
|
||
return( STATUS_INVALID_ACL );
|
||
}
|
||
|
||
(*AlignedAclSize) = (ULONG)LongAlignSize( AclSize );
|
||
|
||
|
||
//
|
||
// If a buffer was provided, compare lengths.
|
||
// Otherwise, allocate a buffer.
|
||
//
|
||
|
||
if (ARGUMENT_PRESENT(CaptureBuffer)) {
|
||
|
||
if (AclSize > CaptureBufferLength) {
|
||
return STATUS_BUFFER_TOO_SMALL;
|
||
} else {
|
||
|
||
(*CapturedAcl) = CaptureBuffer;
|
||
}
|
||
|
||
} else {
|
||
|
||
(*CapturedAcl) = (PACL)ExAllocatePoolWithTag(PoolType, AclSize, 'cAeS');
|
||
|
||
if ( *CapturedAcl == NULL ) {
|
||
return( STATUS_INSUFFICIENT_RESOURCES );
|
||
}
|
||
|
||
}
|
||
|
||
//
|
||
// Now copy the ACL and validate it
|
||
//
|
||
|
||
try {
|
||
|
||
RtlCopyMemory( (*CapturedAcl), InputAcl, AclSize );
|
||
|
||
} except(EXCEPTION_EXECUTE_HANDLER) {
|
||
if (!ARGUMENT_PRESENT(CaptureBuffer)) {
|
||
ExFreePool( (*CapturedAcl) );
|
||
}
|
||
|
||
*CapturedAcl = NULL;
|
||
return GetExceptionCode();
|
||
}
|
||
|
||
if ( (!SepCheckAcl( (*CapturedAcl), AclSize )) ) {
|
||
|
||
if (!ARGUMENT_PRESENT(CaptureBuffer)) {
|
||
ExFreePool( (*CapturedAcl) );
|
||
}
|
||
|
||
*CapturedAcl = NULL;
|
||
return STATUS_INVALID_ACL;
|
||
}
|
||
|
||
return STATUS_SUCCESS;
|
||
|
||
}
|
||
|
||
|
||
VOID
|
||
SeReleaseAcl (
|
||
IN PACL CapturedAcl,
|
||
IN KPROCESSOR_MODE RequestorMode,
|
||
IN BOOLEAN ForceCapture
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine releases a previously captured ACL.
|
||
|
||
This routine should NOT be called if the ACL was captured into a
|
||
provided CaptureBuffer (see SeCaptureAcl).
|
||
|
||
Arguments:
|
||
|
||
CapturedAcl - Supplies the ACL to release.
|
||
|
||
RequestorMode - The processor mode specified when the ACL was captured.
|
||
|
||
ForceCapture - The ForceCapture value specified when the ACL was
|
||
captured.
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
--*/
|
||
|
||
{
|
||
//
|
||
// We only have something to deallocate if the requestor was user
|
||
// mode or kernel mode requesting ForceCapture.
|
||
//
|
||
|
||
PAGED_CODE();
|
||
|
||
if ( ((RequestorMode == KernelMode) && (ForceCapture == TRUE)) ||
|
||
(RequestorMode == UserMode ) ) {
|
||
|
||
ExFreePool(CapturedAcl);
|
||
|
||
}
|
||
|
||
}
|
||
|
||
|
||
NTSTATUS
|
||
SeCaptureLuidAndAttributesArray (
|
||
IN PLUID_AND_ATTRIBUTES InputArray,
|
||
IN ULONG ArrayCount,
|
||
IN KPROCESSOR_MODE RequestorMode,
|
||
IN PVOID CaptureBuffer OPTIONAL,
|
||
IN ULONG CaptureBufferLength,
|
||
IN POOL_TYPE PoolType,
|
||
IN BOOLEAN ForceCapture,
|
||
OUT PLUID_AND_ATTRIBUTES *CapturedArray,
|
||
OUT PULONG AlignedArraySize
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine probes and captures a copy of the specified
|
||
LUID_AND_ATTRIBUTES array.
|
||
|
||
The array is either captured into a provided buffer, or pool
|
||
allocated to receive the array.
|
||
|
||
|
||
if the requestor mode is not kernel mode then
|
||
|
||
probe and capture the input array
|
||
|
||
if the requstor mode is kernel mode then
|
||
|
||
if force capture is true then
|
||
|
||
do not probe the input array, but do capture it
|
||
|
||
else
|
||
|
||
return address of original, but don't copy
|
||
|
||
Arguments:
|
||
|
||
InputArray - Supplies the array to capture. This parameter is assumed
|
||
to have been provided by the mode specified in RequestorMode.
|
||
|
||
ArrayCount - Indicates the number of elements in the array to capture.
|
||
|
||
RequestorMode - Specifies the caller's access mode.
|
||
|
||
CaptureBuffer - Specifies a buffer into which the array is to be
|
||
captured. If this parameter is not provided, pool will be allocated
|
||
to hold the captured data.
|
||
|
||
CaptureBufferLength - Indicates the length, in bytes, of the capture
|
||
buffer.
|
||
|
||
PoolType - Specifies which pool type to allocate to capture the
|
||
array into. This parameter is ignored if CaptureBuffer is provided.
|
||
|
||
ForceCapture - Specifies whether the array should be captured even if
|
||
requestor mode is kernel.
|
||
|
||
CapturedArray - Supplies the address of a pointer to an array.
|
||
The pointer will be set to point to the captured (or uncaptured) array.
|
||
|
||
AlignedArraySize - Supplies the address of a ULONG to receive the length
|
||
of the array rounded up to the next longword boundary.
|
||
|
||
Return Value:
|
||
|
||
STATUS_SUCCESS indicates the capture was successful.
|
||
|
||
STATUS_BUFFER_TOO_SMALL - indicates the buffer provided to capture the array
|
||
into wasn't large enough to hold the array.
|
||
|
||
Any access violations encountered will be returned.
|
||
|
||
--*/
|
||
|
||
{
|
||
|
||
ULONG ArraySize;
|
||
|
||
PAGED_CODE();
|
||
|
||
//
|
||
// Make sure the array isn't empty
|
||
//
|
||
|
||
if (ArrayCount == 0) {
|
||
(*CapturedArray) = NULL;
|
||
(*AlignedArraySize) = 0;
|
||
return STATUS_SUCCESS;
|
||
}
|
||
|
||
//
|
||
// If there are too many LUIDs, return failure
|
||
//
|
||
|
||
if (ArrayCount > SEP_MAX_PRIVILEGE_COUNT) {
|
||
return(STATUS_INVALID_PARAMETER);
|
||
}
|
||
|
||
//
|
||
// check if the requestors mode is kernel mode and we are not
|
||
// to force a capture.
|
||
//
|
||
|
||
if ((RequestorMode == KernelMode) && (ForceCapture == FALSE)) {
|
||
|
||
//
|
||
// We don't need to do any work and can simply
|
||
// return a pointer to the input array
|
||
//
|
||
|
||
(*CapturedArray) = InputArray;
|
||
|
||
return STATUS_SUCCESS;
|
||
}
|
||
|
||
|
||
//
|
||
// Get the length needed to hold the array
|
||
//
|
||
|
||
ArraySize = ArrayCount * (ULONG)sizeof(LUID_AND_ATTRIBUTES);
|
||
(*AlignedArraySize) = (ULONG)LongAlignSize( ArraySize );
|
||
|
||
if (RequestorMode != KernelMode) {
|
||
|
||
try {
|
||
|
||
|
||
ProbeForRead( InputArray,
|
||
ArraySize,
|
||
sizeof(ULONG) );
|
||
|
||
} except(EXCEPTION_EXECUTE_HANDLER) {
|
||
return GetExceptionCode();
|
||
}
|
||
|
||
}
|
||
|
||
|
||
|
||
//
|
||
// If a buffer was provided, compare lengths.
|
||
// Otherwise, allocate a buffer.
|
||
//
|
||
|
||
if (ARGUMENT_PRESENT(CaptureBuffer)) {
|
||
|
||
if (ArraySize > CaptureBufferLength) {
|
||
return STATUS_BUFFER_TOO_SMALL;
|
||
} else {
|
||
|
||
(*CapturedArray) = CaptureBuffer;
|
||
}
|
||
|
||
} else {
|
||
|
||
(*CapturedArray) =
|
||
(PLUID_AND_ATTRIBUTES)ExAllocatePoolWithTag(PoolType, ArraySize, 'uLeS');
|
||
|
||
if ( *CapturedArray == NULL ) {
|
||
return( STATUS_INSUFFICIENT_RESOURCES );
|
||
}
|
||
|
||
}
|
||
|
||
//
|
||
// Now copy the array
|
||
//
|
||
|
||
try {
|
||
|
||
RtlCopyMemory( (*CapturedArray), InputArray, ArraySize );
|
||
|
||
} except(EXCEPTION_EXECUTE_HANDLER) {
|
||
if (!ARGUMENT_PRESENT(CaptureBuffer)) {
|
||
ExFreePool( (*CapturedArray) );
|
||
}
|
||
|
||
return GetExceptionCode();
|
||
}
|
||
|
||
return STATUS_SUCCESS;
|
||
|
||
}
|
||
|
||
|
||
VOID
|
||
SeReleaseLuidAndAttributesArray (
|
||
IN PLUID_AND_ATTRIBUTES CapturedArray,
|
||
IN KPROCESSOR_MODE RequestorMode,
|
||
IN BOOLEAN ForceCapture
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine releases a previously captured array of LUID_AND_ATTRIBUTES.
|
||
|
||
This routine should NOT be called if the array was captured into a
|
||
provided CaptureBuffer (see SeCaptureLuidAndAttributesArray).
|
||
|
||
Arguments:
|
||
|
||
CapturedArray - Supplies the array to release.
|
||
|
||
RequestorMode - The processor mode specified when the array was captured.
|
||
|
||
ForceCapture - The ForceCapture value specified when the array was
|
||
captured.
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
--*/
|
||
|
||
{
|
||
//
|
||
// We only have something to deallocate if the requestor was user
|
||
// mode or kernel mode requesting ForceCapture.
|
||
//
|
||
|
||
PAGED_CODE();
|
||
|
||
if ( ((RequestorMode == KernelMode) && (ForceCapture == TRUE)) ||
|
||
(RequestorMode == UserMode )) {
|
||
//
|
||
// the capture routine returns success with a null pointer for zero elements.
|
||
//
|
||
if (CapturedArray != NULL)
|
||
ExFreePool(CapturedArray);
|
||
|
||
}
|
||
|
||
return;
|
||
|
||
}
|
||
|
||
NTSTATUS
|
||
SeCaptureSidAndAttributesArray (
|
||
IN PSID_AND_ATTRIBUTES InputArray,
|
||
IN ULONG ArrayCount,
|
||
IN KPROCESSOR_MODE RequestorMode,
|
||
IN PVOID CaptureBuffer OPTIONAL,
|
||
IN ULONG CaptureBufferLength,
|
||
IN POOL_TYPE PoolType,
|
||
IN BOOLEAN ForceCapture,
|
||
OUT PSID_AND_ATTRIBUTES *CapturedArray,
|
||
OUT PULONG AlignedArraySize
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine probes and captures a copy of the specified
|
||
SID_AND_ATTRIBUTES array, along with the SID values pointed
|
||
to.
|
||
|
||
The array is either captured into a provided buffer, or pool
|
||
allocated to receive the array.
|
||
|
||
The format of the captured information is an array of SID_AND_ATTRIBUTES
|
||
data structures followed by the SID values. THIS MAY NOT BE THE CASE
|
||
FOR KERNEL MODE UNLESS A FORCE CAPTURE IS SPECIFIED.
|
||
|
||
|
||
if the requestor mode is not kernel mode then
|
||
|
||
probe and capture the input array
|
||
|
||
if the requstor mode is kernel mode then
|
||
|
||
if force capture is true then
|
||
|
||
do not probe the input array, but do capture it
|
||
|
||
else
|
||
|
||
return address of original, but don't copy
|
||
|
||
Arguments:
|
||
|
||
InputArray - Supplies the array to capture. This parameter is assumed
|
||
to have been provided by the mode specified in RequestorMode.
|
||
|
||
ArrayCount - Indicates the number of elements in the array to capture.
|
||
|
||
RequestorMode - Specifies the caller's access mode.
|
||
|
||
CaptureBuffer - Specifies a buffer into which the array is to be
|
||
captured. If this parameter is not provided, pool will be allocated
|
||
to hold the captured data.
|
||
|
||
CaptureBufferLength - Indicates the length, in bytes, of the capture
|
||
buffer.
|
||
|
||
PoolType - Specifies which pool type to allocate to capture the
|
||
array into. This parameter is ignored if CaptureBuffer is provided.
|
||
|
||
ForceCapture - Specifies whether the array should be captured even if
|
||
requestor mode is kernel.
|
||
|
||
CapturedArray - Supplies the address of a pointer to an array.
|
||
The pointer will be set to point to the captured (or uncaptured) array.
|
||
|
||
AlignedArraySize - Supplies the address of a ULONG to receive the length
|
||
of the array rounded up to the next longword boundary.
|
||
|
||
Return Value:
|
||
|
||
STATUS_SUCCESS indicates the capture was successful.
|
||
|
||
STATUS_BUFFER_TOO_SMALL - indicates the buffer provided to capture the array
|
||
into wasn't large enough to hold the array.
|
||
|
||
Any access violations encountered will be returned.
|
||
|
||
--*/
|
||
|
||
{
|
||
|
||
typedef struct _TEMP_ARRAY_ELEMENT {
|
||
PISID Sid;
|
||
ULONG SidLength;
|
||
} TEMP_ARRAY_ELEMENT;
|
||
|
||
|
||
TEMP_ARRAY_ELEMENT *TempArray = NULL;
|
||
|
||
NTSTATUS CompletionStatus = STATUS_SUCCESS;
|
||
|
||
ULONG ArraySize;
|
||
ULONG AlignedLengthRequired;
|
||
|
||
ULONG NextIndex;
|
||
|
||
PSID_AND_ATTRIBUTES NextElement;
|
||
PVOID NextBufferLocation;
|
||
|
||
ULONG GetSidSubAuthorityCount;
|
||
ULONG SidSize;
|
||
ULONG AlignedSidSize;
|
||
|
||
PAGED_CODE();
|
||
|
||
//
|
||
// Make sure the array isn't empty
|
||
//
|
||
|
||
if (ArrayCount == 0) {
|
||
(*CapturedArray) = NULL;
|
||
(*AlignedArraySize) = 0;
|
||
return STATUS_SUCCESS;
|
||
}
|
||
|
||
//
|
||
// Check there aren't too many SIDs
|
||
//
|
||
|
||
if (ArrayCount > SEP_MAX_GROUP_COUNT) {
|
||
return(STATUS_INVALID_PARAMETER);
|
||
}
|
||
//
|
||
// check if the requestor's mode is kernel mode and we are not
|
||
// to force a capture.
|
||
//
|
||
|
||
if ((RequestorMode == KernelMode) && (ForceCapture == FALSE)) {
|
||
|
||
//
|
||
// We don't need to do any work and can simply
|
||
// return a pointer to the input array
|
||
//
|
||
|
||
(*CapturedArray) = InputArray;
|
||
|
||
return STATUS_SUCCESS;
|
||
}
|
||
|
||
|
||
//
|
||
// ---------- For RequestorMode == UserMode ----------------------
|
||
//
|
||
// the algorithm for capturing an SID_AND_ATTRIBUTES array is somewhat
|
||
// convoluted to avoid problems that could occur if the data is
|
||
// being changed while being captured.
|
||
//
|
||
// The algorithm uses two loops.
|
||
//
|
||
// Allocate a temporary buffer to house the fixed length data.
|
||
//
|
||
// 1st loop:
|
||
// For each SID:
|
||
// Capture the Pointers to the SID and the length of the SID.
|
||
//
|
||
// Allocate a buffer large enough to hold all of the data.
|
||
//
|
||
// 2nd loop:
|
||
// For each SID:
|
||
// Capture the Attributes.
|
||
// Capture the SID.
|
||
// Set the pointer to the SID.
|
||
//
|
||
// Deallocate temporary buffer.
|
||
//
|
||
// ------------ For RequestorMode == KernelMode --------------------
|
||
//
|
||
// There is no need to capture the length and address of the SIDs
|
||
// in the first loop (since the kernel can be trusted not to change
|
||
// them while they are being copied.) So for kernel mode, the first
|
||
// loop just adds up the length needed. Kernel mode, thus, avoids
|
||
// having to allocate a temporary buffer.
|
||
//
|
||
|
||
//
|
||
// Get the length needed to hold the array elements.
|
||
//
|
||
|
||
ArraySize = ArrayCount * (ULONG)sizeof(TEMP_ARRAY_ELEMENT);
|
||
AlignedLengthRequired = (ULONG)LongAlignSize( ArraySize );
|
||
|
||
if (RequestorMode != KernelMode) {
|
||
|
||
//
|
||
// Allocate a temporary array to capture the array elements into
|
||
//
|
||
|
||
TempArray =
|
||
(TEMP_ARRAY_ELEMENT *)ExAllocatePoolWithTag(PoolType, AlignedLengthRequired, 'aTeS');
|
||
|
||
if ( TempArray == NULL ) {
|
||
return( STATUS_INSUFFICIENT_RESOURCES );
|
||
}
|
||
|
||
|
||
try {
|
||
|
||
//
|
||
// Make sure we can read each SID_AND_ATTRIBUTE
|
||
//
|
||
|
||
ProbeForRead( InputArray,
|
||
ArraySize,
|
||
sizeof(ULONG) );
|
||
|
||
//
|
||
// Probe and capture the length and address of each SID
|
||
//
|
||
|
||
NextIndex = 0;
|
||
while (NextIndex < ArrayCount) {
|
||
PSID TempSid;
|
||
|
||
TempSid = InputArray[NextIndex].Sid;
|
||
GetSidSubAuthorityCount =
|
||
ProbeAndReadUchar( &((PISID)TempSid)->SubAuthorityCount);
|
||
|
||
if (GetSidSubAuthorityCount > SID_MAX_SUB_AUTHORITIES) {
|
||
CompletionStatus = STATUS_INVALID_SID;
|
||
break;
|
||
}
|
||
|
||
TempArray[NextIndex].Sid = ((PISID)(TempSid));
|
||
TempArray[NextIndex].SidLength =
|
||
RtlLengthRequiredSid( GetSidSubAuthorityCount );
|
||
|
||
ProbeForRead( TempArray[NextIndex].Sid,
|
||
TempArray[NextIndex].SidLength,
|
||
sizeof(ULONG) );
|
||
|
||
AlignedLengthRequired +=
|
||
(ULONG)LongAlignSize( TempArray[NextIndex].SidLength );
|
||
|
||
NextIndex += 1;
|
||
|
||
} //end while
|
||
|
||
} except(EXCEPTION_EXECUTE_HANDLER) {
|
||
|
||
ExFreePool( TempArray );
|
||
return GetExceptionCode();
|
||
}
|
||
|
||
if (!NT_SUCCESS(CompletionStatus)) {
|
||
ExFreePool( TempArray );
|
||
return(CompletionStatus);
|
||
}
|
||
|
||
} else {
|
||
|
||
//
|
||
// No need to capture anything.
|
||
// But, we do need to add up the lengths of the SIDs
|
||
// so we can allocate a buffer (or check the size of one provided).
|
||
//
|
||
|
||
NextIndex = 0;
|
||
|
||
while (NextIndex < ArrayCount) {
|
||
|
||
GetSidSubAuthorityCount =
|
||
((PISID)(InputArray[NextIndex].Sid))->SubAuthorityCount;
|
||
|
||
AlignedLengthRequired +=
|
||
(ULONG)LongAlignSize(RtlLengthRequiredSid(GetSidSubAuthorityCount));
|
||
|
||
NextIndex += 1;
|
||
|
||
} //end while
|
||
|
||
}
|
||
|
||
|
||
//
|
||
// Now we know how much memory we need.
|
||
// Return this value in the output parameter.
|
||
//
|
||
|
||
(*AlignedArraySize) = AlignedLengthRequired;
|
||
|
||
//
|
||
// If a buffer was provided, make sure it is long enough.
|
||
// Otherwise, allocate a buffer.
|
||
//
|
||
|
||
if (ARGUMENT_PRESENT(CaptureBuffer)) {
|
||
|
||
if (AlignedLengthRequired > CaptureBufferLength) {
|
||
|
||
if (RequestorMode != KernelMode) {
|
||
ExFreePool( TempArray );
|
||
}
|
||
|
||
return STATUS_BUFFER_TOO_SMALL;
|
||
|
||
} else {
|
||
|
||
(*CapturedArray) = CaptureBuffer;
|
||
}
|
||
|
||
} else {
|
||
|
||
(*CapturedArray) =
|
||
(PSID_AND_ATTRIBUTES)ExAllocatePoolWithTag(PoolType, AlignedLengthRequired, 'aSeS');
|
||
|
||
if ( *CapturedArray == NULL ) {
|
||
if (RequestorMode != KernelMode) {
|
||
ExFreePool( TempArray );
|
||
}
|
||
return( STATUS_INSUFFICIENT_RESOURCES );
|
||
}
|
||
}
|
||
|
||
|
||
//
|
||
// Now copy everything.
|
||
// This is done by copying all the SID_AND_ATTRIBUTES and then
|
||
// copying each individual SID.
|
||
//
|
||
// All SIDs have already been probed for READ access. We just
|
||
// need to copy them.
|
||
//
|
||
//
|
||
|
||
if (RequestorMode != KernelMode) {
|
||
try {
|
||
|
||
//
|
||
// Copy the SID_AND_ATTRIBUTES array elements
|
||
// This really only sets the attributes, since we
|
||
// over-write the SID pointer field later on.
|
||
//
|
||
|
||
NextBufferLocation = (*CapturedArray);
|
||
RtlCopyMemory( NextBufferLocation, InputArray, ArraySize );
|
||
NextBufferLocation = (PVOID)((ULONG_PTR)NextBufferLocation +
|
||
(ULONG)LongAlignSize(ArraySize) );
|
||
|
||
//
|
||
// Now go through and copy each referenced SID.
|
||
// Validate each SID as it is copied.
|
||
//
|
||
|
||
NextIndex = 0;
|
||
NextElement = (*CapturedArray);
|
||
while ( (NextIndex < ArrayCount) &&
|
||
(CompletionStatus == STATUS_SUCCESS) ) {
|
||
|
||
|
||
RtlCopyMemory( NextBufferLocation,
|
||
TempArray[NextIndex].Sid,
|
||
TempArray[NextIndex].SidLength );
|
||
|
||
|
||
NextElement[NextIndex].Sid = (PSID)NextBufferLocation;
|
||
NextBufferLocation =
|
||
(PVOID)((ULONG_PTR)NextBufferLocation +
|
||
(ULONG)LongAlignSize(TempArray[NextIndex].SidLength));
|
||
|
||
//
|
||
// Verify the sid is valid and its length didn't change
|
||
//
|
||
|
||
if (!RtlValidSid(NextElement[NextIndex].Sid) ) {
|
||
CompletionStatus = STATUS_INVALID_SID;
|
||
} else if (RtlLengthSid(NextElement[NextIndex].Sid) != TempArray[NextIndex].SidLength) {
|
||
CompletionStatus = STATUS_INVALID_SID;
|
||
}
|
||
|
||
|
||
NextIndex += 1;
|
||
|
||
} //end while
|
||
|
||
|
||
} except(EXCEPTION_EXECUTE_HANDLER) {
|
||
|
||
if (!ARGUMENT_PRESENT(CaptureBuffer)) {
|
||
ExFreePool( (*CapturedArray) );
|
||
}
|
||
|
||
ExFreePool( TempArray );
|
||
|
||
return GetExceptionCode();
|
||
}
|
||
} else {
|
||
|
||
//
|
||
// Requestor mode is kernel mode -
|
||
// don't need protection, probing, and validating
|
||
//
|
||
|
||
//
|
||
// Copy the SID_AND_ATTRIBUTES array elements
|
||
// This really only sets the attributes, since we
|
||
// over-write the SID pointer field later on.
|
||
//
|
||
|
||
NextBufferLocation = (*CapturedArray);
|
||
RtlCopyMemory( NextBufferLocation, InputArray, ArraySize );
|
||
NextBufferLocation = (PVOID)( (ULONG_PTR)NextBufferLocation +
|
||
(ULONG)LongAlignSize(ArraySize));
|
||
|
||
//
|
||
// Now go through and copy each referenced SID
|
||
//
|
||
|
||
NextIndex = 0;
|
||
NextElement = (*CapturedArray);
|
||
while (NextIndex < ArrayCount) {
|
||
|
||
GetSidSubAuthorityCount =
|
||
((PISID)(NextElement[NextIndex].Sid))->SubAuthorityCount;
|
||
|
||
RtlCopyMemory(
|
||
NextBufferLocation,
|
||
NextElement[NextIndex].Sid,
|
||
RtlLengthRequiredSid(GetSidSubAuthorityCount) );
|
||
SidSize = RtlLengthRequiredSid( GetSidSubAuthorityCount );
|
||
AlignedSidSize = (ULONG)LongAlignSize(SidSize);
|
||
|
||
NextElement[NextIndex].Sid = (PSID)NextBufferLocation;
|
||
|
||
NextIndex += 1;
|
||
NextBufferLocation = (PVOID)((ULONG_PTR)NextBufferLocation +
|
||
AlignedSidSize);
|
||
|
||
} //end while
|
||
|
||
}
|
||
|
||
if (RequestorMode != KernelMode) {
|
||
ExFreePool( TempArray );
|
||
}
|
||
|
||
if (!ARGUMENT_PRESENT(CaptureBuffer) && !NT_SUCCESS(CompletionStatus)) {
|
||
ExFreePool( (*CapturedArray) );
|
||
*CapturedArray = NULL ;
|
||
}
|
||
|
||
return CompletionStatus;
|
||
}
|
||
|
||
|
||
VOID
|
||
SeReleaseSidAndAttributesArray (
|
||
IN PSID_AND_ATTRIBUTES CapturedArray,
|
||
IN KPROCESSOR_MODE RequestorMode,
|
||
IN BOOLEAN ForceCapture
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine releases a previously captured array of SID_AND_ATTRIBUTES.
|
||
|
||
This routine should NOT be called if the array was captured into a
|
||
provided CaptureBuffer (see SeCaptureSidAndAttributesArray).
|
||
|
||
Arguments:
|
||
|
||
CapturedArray - Supplies the array to release.
|
||
|
||
RequestorMode - The processor mode specified when the array was captured.
|
||
|
||
ForceCapture - The ForceCapture value specified when the array was
|
||
captured.
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
--*/
|
||
|
||
{
|
||
//
|
||
// We only have something to deallocate if the requestor was user
|
||
// mode or kernel mode requesting ForceCapture.
|
||
//
|
||
|
||
PAGED_CODE();
|
||
|
||
if ( ((RequestorMode == KernelMode) && (ForceCapture == TRUE)) ||
|
||
(RequestorMode == UserMode ) ) {
|
||
|
||
ExFreePool(CapturedArray);
|
||
|
||
}
|
||
|
||
return;
|
||
|
||
}
|
||
|
||
|
||
NTSTATUS
|
||
SeCaptureAuditPolicy(
|
||
IN PTOKEN_AUDIT_POLICY Policy,
|
||
IN KPROCESSOR_MODE RequestorMode,
|
||
IN PVOID CaptureBuffer OPTIONAL,
|
||
IN ULONG CaptureBufferLength,
|
||
IN POOL_TYPE PoolType,
|
||
IN BOOLEAN ForceCapture,
|
||
OUT PTOKEN_AUDIT_POLICY *CapturedPolicy
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description
|
||
|
||
This routine probes and captures a copy of the specified
|
||
TOKEN_AUDIT_POLICY.
|
||
|
||
It is either captured into a provided buffer, or pool
|
||
allocated to receive the policy.
|
||
|
||
if the requestor mode is not kernel mode then
|
||
|
||
probe and capture the input
|
||
|
||
if the requstor mode is kernel mode then
|
||
|
||
if force capture is true then
|
||
|
||
do not probe the input, but do capture it
|
||
|
||
else
|
||
|
||
return address of original, but don't copy
|
||
|
||
Arguments:
|
||
|
||
RequestorMode - Specifies the caller's access mode.
|
||
|
||
CaptureBuffer - Specifies a buffer into which the policy is to be
|
||
captured. If this parameter is not provided, pool will be allocated
|
||
to hold the captured data.
|
||
|
||
CaptureBufferLength - Indicates the length, in bytes, of the capture
|
||
buffer.
|
||
|
||
PoolType - Specifies which pool type to allocate to capture the
|
||
input into. This parameter is ignored if CaptureBuffer is provided.
|
||
|
||
ForceCapture - Specifies whether the policy should be captured even if
|
||
requestor mode is kernel.
|
||
|
||
CapturedPolicy - Supplies the address of a pointer to a TOKEN_AUDIT_POLICY.
|
||
The pointer will be set to point to the captured (or uncaptured) policy.
|
||
|
||
Return Value:
|
||
|
||
STATUS_SUCCESS indicates the capture was successful.
|
||
|
||
STATUS_BUFFER_TOO_SMALL - indicates the buffer provided to capture the policy
|
||
into wasn't large enough.
|
||
|
||
Any access violations encountered will be returned.
|
||
|
||
--*/
|
||
|
||
{
|
||
ULONG PolicyCount;
|
||
ULONG PolicySize;
|
||
ULONG i;
|
||
|
||
PAGED_CODE();
|
||
|
||
//
|
||
// check if the requestors mode is kernel mode and we are not
|
||
// to force a capture.
|
||
//
|
||
|
||
if ((RequestorMode == KernelMode) && (ForceCapture == FALSE)) {
|
||
|
||
//
|
||
// We don't need to do any work and can simply
|
||
// return a pointer to the input policy
|
||
//
|
||
|
||
(*CapturedPolicy) = Policy;
|
||
|
||
return STATUS_SUCCESS;
|
||
}
|
||
|
||
//
|
||
// Get the length needed to hold the policy
|
||
//
|
||
|
||
if (RequestorMode != KernelMode) {
|
||
|
||
try {
|
||
|
||
PolicyCount = ProbeAndReadLong( &Policy->PolicyCount );
|
||
PolicySize = PER_USER_AUDITING_POLICY_SIZE_BY_COUNT(PolicyCount);
|
||
|
||
ProbeForRead(
|
||
Policy,
|
||
PolicySize,
|
||
sizeof(ULONG)
|
||
);
|
||
|
||
} except(EXCEPTION_EXECUTE_HANDLER) {
|
||
|
||
return GetExceptionCode();
|
||
}
|
||
|
||
} else {
|
||
|
||
PolicyCount = Policy->PolicyCount;
|
||
PolicySize = PER_USER_AUDITING_POLICY_SIZE_BY_COUNT(PolicyCount);
|
||
}
|
||
|
||
|
||
//
|
||
// If a buffer was provided, compare lengths.
|
||
// Otherwise, allocate a buffer.
|
||
//
|
||
|
||
if (ARGUMENT_PRESENT( CaptureBuffer )) {
|
||
|
||
if (PolicySize > CaptureBufferLength) {
|
||
|
||
return STATUS_BUFFER_TOO_SMALL;
|
||
|
||
} else {
|
||
|
||
(*CapturedPolicy) = CaptureBuffer;
|
||
}
|
||
|
||
} else {
|
||
|
||
(*CapturedPolicy) = (PTOKEN_AUDIT_POLICY)ExAllocatePoolWithTag(PoolType, PolicySize, 'aPeS');
|
||
|
||
if ( (*CapturedPolicy) == NULL ) {
|
||
|
||
return( STATUS_INSUFFICIENT_RESOURCES );
|
||
}
|
||
}
|
||
|
||
//
|
||
// Now copy the Policy and validate it
|
||
//
|
||
|
||
try {
|
||
|
||
RtlCopyMemory( (*CapturedPolicy), Policy, PolicySize );
|
||
|
||
} except(EXCEPTION_EXECUTE_HANDLER) {
|
||
|
||
if (!ARGUMENT_PRESENT(CaptureBuffer)) {
|
||
|
||
ExFreePool( (*CapturedPolicy) );
|
||
*CapturedPolicy = NULL;
|
||
}
|
||
|
||
return GetExceptionCode();
|
||
|
||
}
|
||
|
||
//
|
||
// Validate captured structure.
|
||
//
|
||
|
||
for (i = 0; i < PolicyCount; i++) {
|
||
|
||
if (!VALID_TOKEN_AUDIT_POLICY_ELEMENT( (*CapturedPolicy)->Policy[i] )) {
|
||
#if DBG
|
||
DbgPrint("SeCaptureAuditPolicy: element %d mask 0x%x category %d invalid.\n",
|
||
i,
|
||
(*CapturedPolicy)->Policy[i].PolicyMask,
|
||
(*CapturedPolicy)->Policy[i].Category
|
||
);
|
||
ASSERT(FALSE);
|
||
#endif
|
||
if (!ARGUMENT_PRESENT(CaptureBuffer)) {
|
||
|
||
ExFreePool( (*CapturedPolicy) );
|
||
*CapturedPolicy = NULL;
|
||
}
|
||
|
||
return STATUS_INVALID_PARAMETER;
|
||
}
|
||
}
|
||
|
||
return STATUS_SUCCESS;
|
||
}
|
||
|
||
|
||
VOID
|
||
SeReleaseAuditPolicy (
|
||
IN PTOKEN_AUDIT_POLICY CapturedPolicy,
|
||
IN KPROCESSOR_MODE RequestorMode,
|
||
IN BOOLEAN ForceCapture
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine releases a previously captured TOKEN_AUDIT_POLICY.
|
||
|
||
This routine should NOT be called if the policy was captured into a
|
||
provided CaptureBuffer (see SeCaptureAuditPolicy).
|
||
|
||
Arguments:
|
||
|
||
CapturedPolicy - Supplies the policy to release.
|
||
|
||
RequestorMode - The processor mode specified when the data was captured.
|
||
|
||
ForceCapture - The ForceCapture value specified when the data was
|
||
captured.
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
--*/
|
||
|
||
{
|
||
//
|
||
// We only have something to deallocate if the requestor was user
|
||
// mode or kernel mode requesting ForceCapture.
|
||
//
|
||
|
||
PAGED_CODE();
|
||
|
||
if ( CapturedPolicy &&
|
||
(((RequestorMode == KernelMode) && (ForceCapture == TRUE)) || (RequestorMode == UserMode)) ) {
|
||
|
||
ExFreePool(CapturedPolicy);
|
||
|
||
}
|
||
}
|
||
|
||
NTSTATUS
|
||
SeComputeQuotaInformationSize(
|
||
IN PSECURITY_DESCRIPTOR SecurityDescriptor,
|
||
OUT PULONG Size
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine computes the size of the Group and DACL for the
|
||
passed security descriptor.
|
||
|
||
This quantity will later be used in calculating the amount
|
||
of quota to charge for this object.
|
||
|
||
Arguments:
|
||
|
||
SecurityDescriptor - Supplies a pointer to the security descriptor
|
||
to be examined.
|
||
|
||
Size - Returns the size in bytes of the sum of the Group and Dacl
|
||
fields of the security descriptor.
|
||
|
||
Return Value:
|
||
|
||
STATUS_SUCCESS - The operation was successful.
|
||
|
||
STATUS_INVALID_REVISION - The passed security descriptor was of
|
||
an unknown revision.
|
||
|
||
--*/
|
||
|
||
{
|
||
PISECURITY_DESCRIPTOR ISecurityDescriptor;
|
||
|
||
PSID Group;
|
||
PACL Dacl;
|
||
|
||
PAGED_CODE();
|
||
|
||
ISecurityDescriptor = (PISECURITY_DESCRIPTOR)SecurityDescriptor;
|
||
*Size = 0;
|
||
|
||
if (ISecurityDescriptor->Revision != SECURITY_DESCRIPTOR_REVISION) {
|
||
return( STATUS_UNKNOWN_REVISION );
|
||
}
|
||
|
||
Group = RtlpGroupAddrSecurityDescriptor( ISecurityDescriptor );
|
||
|
||
Dacl = RtlpDaclAddrSecurityDescriptor( ISecurityDescriptor );
|
||
|
||
if (Group != NULL) {
|
||
*Size += (ULONG)LongAlignSize(SeLengthSid( Group ));
|
||
}
|
||
|
||
if (Dacl != NULL) {
|
||
*Size += (ULONG)LongAlignSize(Dacl->AclSize);
|
||
}
|
||
|
||
return( STATUS_SUCCESS );
|
||
}
|
||
|
||
|
||
BOOLEAN
|
||
SeValidSecurityDescriptor(
|
||
IN ULONG Length,
|
||
IN PSECURITY_DESCRIPTOR SecurityDescriptor
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Validates a security descriptor for structural correctness. The idea is to make
|
||
sure that the security descriptor may be passed to other kernel callers, without
|
||
fear that they're going to choke while manipulating it.
|
||
|
||
This routine does not enforce policy (e.g., ACL/ACE revision information). It is
|
||
entirely possible for a security descriptor to be approved by this routine, only
|
||
to be later found to be invalid by some later routine.
|
||
|
||
This routine is designed to be used by callers who have a security descriptor in
|
||
kernel memory. Callers wishing to validate a security descriptor passed from user
|
||
mode should call RtlValidSecurityDescriptor.
|
||
|
||
Arguments:
|
||
|
||
Length - Length in bytes of passed Security Descriptor.
|
||
|
||
SecurityDescriptor - Points to the Security Descriptor (in kernel memory) to be
|
||
validatated.
|
||
|
||
Return Value:
|
||
|
||
TRUE - The passed security descriptor is correctly structured
|
||
FALSE - The passed security descriptor is badly formed
|
||
|
||
--*/
|
||
|
||
{
|
||
PISECURITY_DESCRIPTOR_RELATIVE ISecurityDescriptor =
|
||
(PISECURITY_DESCRIPTOR_RELATIVE)SecurityDescriptor;
|
||
PISID OwnerSid;
|
||
PISID GroupSid;
|
||
PACE_HEADER Ace;
|
||
PISID Sid;
|
||
PISID Sid2;
|
||
PACL Dacl;
|
||
PACL Sacl;
|
||
ULONG i;
|
||
|
||
PAGED_CODE();
|
||
|
||
if (Length < sizeof(SECURITY_DESCRIPTOR_RELATIVE)) {
|
||
return(FALSE);
|
||
}
|
||
|
||
//
|
||
// Check the revision information.
|
||
//
|
||
|
||
if (ISecurityDescriptor->Revision != SECURITY_DESCRIPTOR_REVISION) {
|
||
return(FALSE);
|
||
}
|
||
|
||
//
|
||
// Make sure the passed SecurityDescriptor is in self-relative form
|
||
//
|
||
|
||
if (!(ISecurityDescriptor->Control & SE_SELF_RELATIVE)) {
|
||
return(FALSE);
|
||
}
|
||
|
||
//
|
||
// Check the owner. A valid SecurityDescriptor must have an owner.
|
||
// It must also be long aligned.
|
||
//
|
||
|
||
if ((ISecurityDescriptor->Owner == 0) ||
|
||
(!LongAligned((PVOID)(ULONG_PTR)(ULONG)ISecurityDescriptor->Owner)) ||
|
||
(ISecurityDescriptor->Owner > Length) ||
|
||
(Length - ISecurityDescriptor->Owner < sizeof(SID))) {
|
||
|
||
return(FALSE);
|
||
}
|
||
|
||
//
|
||
// It is safe to reference the owner's SubAuthorityCount, compute the
|
||
// expected length of the SID
|
||
//
|
||
|
||
OwnerSid = (PSID)RtlOffsetToPointer( ISecurityDescriptor, ISecurityDescriptor->Owner );
|
||
|
||
if (OwnerSid->Revision != SID_REVISION) {
|
||
return(FALSE);
|
||
}
|
||
|
||
if (OwnerSid->SubAuthorityCount > SID_MAX_SUB_AUTHORITIES) {
|
||
return(FALSE);
|
||
}
|
||
|
||
if (Length - ISecurityDescriptor->Owner < (ULONG) SeLengthSid(OwnerSid)) {
|
||
return(FALSE);
|
||
}
|
||
|
||
//
|
||
// The owner appears to be a structurally valid SID that lies within
|
||
// the bounds of the security descriptor. Do the same for the Group
|
||
// if there is one.
|
||
//
|
||
|
||
if (ISecurityDescriptor->Group != 0) {
|
||
|
||
//
|
||
// Check alignment
|
||
//
|
||
|
||
if (!LongAligned( (PVOID)(ULONG_PTR)(ULONG)ISecurityDescriptor->Group)) {
|
||
return(FALSE);
|
||
}
|
||
|
||
if (ISecurityDescriptor->Group > Length) {
|
||
return(FALSE);
|
||
}
|
||
|
||
if (Length - ISecurityDescriptor->Group < sizeof (SID)) {
|
||
return(FALSE);
|
||
}
|
||
|
||
//
|
||
// It is safe to reference the Group's SubAuthorityCount, compute the
|
||
// expected length of the SID
|
||
//
|
||
|
||
GroupSid = (PSID)RtlOffsetToPointer( ISecurityDescriptor, ISecurityDescriptor->Group );
|
||
|
||
if (GroupSid->Revision != SID_REVISION) {
|
||
return(FALSE);
|
||
}
|
||
|
||
if (GroupSid->SubAuthorityCount > SID_MAX_SUB_AUTHORITIES) {
|
||
return(FALSE);
|
||
}
|
||
|
||
if (Length - ISecurityDescriptor->Group < (ULONG) SeLengthSid(GroupSid)) {
|
||
return(FALSE);
|
||
}
|
||
}
|
||
|
||
//
|
||
// Validate the DACL. A structurally valid SecurityDescriptor may not necessarily
|
||
// have a DACL.
|
||
//
|
||
|
||
if (ISecurityDescriptor->Dacl != 0) {
|
||
|
||
//
|
||
// Check alignment
|
||
//
|
||
|
||
if (!LongAligned( (PVOID)(ULONG_PTR)(ULONG)ISecurityDescriptor->Dacl)) {
|
||
return(FALSE);
|
||
}
|
||
|
||
//
|
||
// Make sure the DACL structure is within the bounds of the security descriptor.
|
||
//
|
||
|
||
if ((ISecurityDescriptor->Dacl > Length) ||
|
||
(Length - ISecurityDescriptor->Dacl < sizeof(ACL))) {
|
||
return(FALSE);
|
||
}
|
||
|
||
Dacl = (PACL) RtlOffsetToPointer( ISecurityDescriptor, ISecurityDescriptor->Dacl );
|
||
|
||
|
||
//
|
||
// Make sure the DACL length fits within the bounds of the security descriptor.
|
||
//
|
||
|
||
if (Length - ISecurityDescriptor->Dacl < Dacl->AclSize) {
|
||
return(FALSE);
|
||
}
|
||
|
||
//
|
||
// Make sure the ACL is structurally valid.
|
||
//
|
||
|
||
if (!RtlValidAcl( Dacl )) {
|
||
return(FALSE);
|
||
}
|
||
}
|
||
|
||
//
|
||
// Validate the SACL. A structurally valid SecurityDescriptor may not
|
||
// have a SACL.
|
||
//
|
||
|
||
if (ISecurityDescriptor->Sacl != 0) {
|
||
|
||
//
|
||
// Check alignment
|
||
//
|
||
|
||
if (!LongAligned( (PVOID)(ULONG_PTR)(ULONG)ISecurityDescriptor->Sacl)) {
|
||
return(FALSE);
|
||
}
|
||
|
||
//
|
||
// Make sure the SACL structure is within the bounds of the security descriptor.
|
||
//
|
||
|
||
if ((ISecurityDescriptor->Sacl > Length) ||
|
||
(Length - ISecurityDescriptor->Sacl < sizeof(ACL))) {
|
||
return(FALSE);
|
||
}
|
||
|
||
//
|
||
// Make sure the Sacl structure is within the bounds of the security descriptor.
|
||
//
|
||
|
||
Sacl = (PACL)RtlOffsetToPointer( ISecurityDescriptor, ISecurityDescriptor->Sacl );
|
||
|
||
|
||
if (Length - ISecurityDescriptor->Sacl < Sacl->AclSize) {
|
||
return(FALSE);
|
||
}
|
||
|
||
//
|
||
// Make sure the ACL is structurally valid.
|
||
//
|
||
|
||
if (!RtlValidAcl( Sacl )) {
|
||
return(FALSE);
|
||
}
|
||
}
|
||
|
||
return(TRUE);
|
||
}
|
||
|