Windows-Server-2003/base/ntos/se/capture.c

2902 lines
71 KiB
C
Raw Permalink Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*++
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);
}