You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
2901 lines
74 KiB
2901 lines
74 KiB
/*++
|
|
|
|
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);
|
|
}
|
|
|