From e611b132f9b8abe35b362e5870b74bce94a1e58e Mon Sep 17 00:00:00 2001 From: Adam Date: Sat, 16 May 2020 20:51:50 -0700 Subject: initial commit --- private/ntos/se/capture.c | 2568 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 2568 insertions(+) create mode 100644 private/ntos/se/capture.c (limited to 'private/ntos/se/capture.c') diff --git a/private/ntos/se/capture.c b/private/ntos/se/capture.c new file mode 100644 index 000000000..30d6fc2f3 --- /dev/null +++ b/private/ntos/se/capture.c @@ -0,0 +1,2568 @@ +/*++ + +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 "sep.h" +#include "seopaque.h" + +#ifdef ALLOC_PRAGMA +#pragma alloc_text(PAGE,SeCaptureSecurityDescriptor) +#pragma alloc_text(PAGE,SeReleaseSecurityDescriptor) +#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,SeComputeQuotaInformationSize) +#pragma alloc_text(PAGE,SepCopyProxyData) +#pragma alloc_text(PAGE,SepProbeAndCaptureQosData) +#pragma alloc_text(PAGE,SepFreeProxyData) +#endif + +#define LongAligned( ptr ) (LongAlign(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. +--*/ + +{ + SECURITY_DESCRIPTOR Captured; + SECURITY_DESCRIPTOR *PIOutputSecurityDescriptor; + PCHAR DescriptorOffset; + + ULONG SaclSize; + ULONG NewSaclSize; + + ULONG DaclSize; + ULONG NewDaclSize; + + ULONG OwnerSubAuthorityCount; + ULONG OwnerSize; + ULONG NewOwnerSize; + + ULONG GroupSubAuthorityCount; + ULONG GroupSize; + 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 + // + + ProbeForRead( InputSecurityDescriptor, + sizeof(SECURITY_DESCRIPTOR), + sizeof(ULONG) ); + + // + // Capture the SecurityDescriptor main record. + // + + RtlMoveMemory( (&Captured), + InputSecurityDescriptor, + sizeof(SECURITY_DESCRIPTOR) ); + + } 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. + // + + RtlMoveMemory( (&Captured), + InputSecurityDescriptor, + sizeof(SECURITY_DESCRIPTOR) ); + + } + + // + // 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 = SepOwnerAddrSecurityDescriptor( + (SECURITY_DESCRIPTOR *)InputSecurityDescriptor + ); + Captured.Group = SepGroupAddrSecurityDescriptor( + (SECURITY_DESCRIPTOR *)InputSecurityDescriptor + ); + Captured.Sacl = SepSaclAddrSecurityDescriptor ( + (SECURITY_DESCRIPTOR *)InputSecurityDescriptor + ); + Captured.Dacl = SepDaclAddrSecurityDescriptor ( + (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)LongAlign( SaclSize ); + + } + + // + // 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)LongAlign( DaclSize ); + + } + + // + // 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)LongAlign( 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)LongAlign( GroupSize ); + + } + + + + // + // Now allocate enough pool to hold the descriptor + // + + Size = sizeof(SECURITY_DESCRIPTOR) + + NewSaclSize + + NewDaclSize + + NewOwnerSize + + NewGroupSize; + + (PIOutputSecurityDescriptor) = (SECURITY_DESCRIPTOR *)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 + // + + RtlMoveMemory( DescriptorOffset, + &Captured, + sizeof(SECURITY_DESCRIPTOR) ); + DescriptorOffset += sizeof(SECURITY_DESCRIPTOR); + + // + // 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 { + RtlMoveMemory( DescriptorOffset, + Captured.Sacl, + SaclSize ); + + PIOutputSecurityDescriptor->Sacl = (PACL)DescriptorOffset; + + DescriptorOffset += NewSaclSize; + } except(EXCEPTION_EXECUTE_HANDLER) { + ExFreePool( PIOutputSecurityDescriptor ); + return GetExceptionCode(); + } + + if ((RequestorMode != KernelMode) && + (!SepCheckAcl( PIOutputSecurityDescriptor->Sacl, SaclSize )) ) { + + ExFreePool( PIOutputSecurityDescriptor ); + return STATUS_INVALID_ACL; + } + + // + // Change pointer to offset + // + + PIOutputSecurityDescriptor->Sacl = + (PACL)( RtlPointerToOffset( + PIOutputSecurityDescriptor, + PIOutputSecurityDescriptor->Sacl + )); + + + } + + // + // 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 { + RtlMoveMemory( DescriptorOffset, + Captured.Dacl, + DaclSize ); + PIOutputSecurityDescriptor->Dacl = (PACL)DescriptorOffset; + DescriptorOffset += NewDaclSize; + } except(EXCEPTION_EXECUTE_HANDLER) { + ExFreePool( PIOutputSecurityDescriptor ); + return GetExceptionCode(); + } + + if ((RequestorMode != KernelMode) && + (!SepCheckAcl( PIOutputSecurityDescriptor->Dacl, DaclSize )) ) { + + ExFreePool( PIOutputSecurityDescriptor ); + return STATUS_INVALID_ACL; + } + + // + // Change pointer to offset + // + + PIOutputSecurityDescriptor->Dacl = + (PACL)( RtlPointerToOffset( + PIOutputSecurityDescriptor, + PIOutputSecurityDescriptor->Dacl + )); + + } + + // + // 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 { + RtlMoveMemory( DescriptorOffset, + Captured.Owner, + OwnerSize ); + PIOutputSecurityDescriptor->Owner = (PSID)DescriptorOffset; + DescriptorOffset += NewOwnerSize; + } except(EXCEPTION_EXECUTE_HANDLER) { + ExFreePool( PIOutputSecurityDescriptor ); + return GetExceptionCode(); + } + + if ((RequestorMode != KernelMode) && + (!RtlValidSid( PIOutputSecurityDescriptor->Owner )) ) { + + ExFreePool( PIOutputSecurityDescriptor ); + return STATUS_INVALID_SID; + } + + // + // Change pointer to offset + // + + PIOutputSecurityDescriptor->Owner = + (PSID)( RtlPointerToOffset( + PIOutputSecurityDescriptor, + PIOutputSecurityDescriptor->Owner + )); + + } + + // + // 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 { + RtlMoveMemory( DescriptorOffset, + Captured.Group, + GroupSize ); + PIOutputSecurityDescriptor->Group = (PSID)DescriptorOffset; + DescriptorOffset += NewGroupSize; + } except(EXCEPTION_EXECUTE_HANDLER) { + ExFreePool( PIOutputSecurityDescriptor ); + return GetExceptionCode(); + } + + if ((RequestorMode != KernelMode) && + (!RtlValidSid( PIOutputSecurityDescriptor->Group )) ) { + + ExFreePool( PIOutputSecurityDescriptor ); + return STATUS_INVALID_SID; + } + + // + // Change pointer to offset + // + + PIOutputSecurityDescriptor->Group = + (PSID)( RtlPointerToOffset( + PIOutputSecurityDescriptor, + PIOutputSecurityDescriptor->Group + )); + + } + + // + // 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, sizeof( SECURITY_TOKEN_PROXY_DATA ), 'dPoT' ); + + if (*DestProxyData == NULL) { + return( STATUS_INSUFFICIENT_RESOURCES ); + } + + + + (*DestProxyData)->PathInfo.Buffer = ExAllocatePoolWithTag( PagedPool, SourceProxyData->PathInfo.Length, 'dPoT' ); + + if ((*DestProxyData)->PathInfo.Buffer == NULL) { + ExFreePool( *DestProxyData ); + *DestProxyData = NULL; + return( STATUS_INSUFFICIENT_RESOURCES ); + } + + (*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) { + + if (ProxyData->PathInfo.Buffer != NULL) { + ExFreePool( ProxyData->PathInfo.Buffer ); + } + + 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; + PAGED_CODE(); + + CapturedProxyData = CapturedSecurityQos->ProxyData; + CapturedSecurityQos->ProxyData = NULL; + CapturedAuditData = CapturedSecurityQos->AuditData; + CapturedSecurityQos->AuditData = NULL; + + if (ARGUMENT_PRESENT( CapturedProxyData )) { + + PSECURITY_TOKEN_PROXY_DATA LocalProxyData = NULL; + UNICODE_STRING SavedPathInfo; + + // + // Make sure the body of the proxy data is ok to read. + // + + ProbeForRead( + CapturedProxyData, + sizeof(SECURITY_TOKEN_PROXY_DATA), + sizeof(ULONG) + ); + + if (CapturedProxyData->Length != sizeof( SECURITY_TOKEN_PROXY_DATA )) { + return( STATUS_INVALID_PARAMETER ); + } + + // + // Probe the passed pathinfo buffer + // + + ProbeForRead( + CapturedProxyData->PathInfo.Buffer, + CapturedProxyData->PathInfo.Length, + sizeof( UCHAR ) + ); + + Status = SepCopyProxyData( &CapturedSecurityQos->ProxyData, CapturedProxyData ); + + 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 + // + + ProbeForRead( + CapturedAuditData, + sizeof( SECURITY_TOKEN_AUDIT_DATA ), + sizeof( ULONG ) + ); + + if ( CapturedAuditData->Length != sizeof( SECURITY_TOKEN_AUDIT_DATA ) ) { + SepFreeProxyData( CapturedSecurityQos->ProxyData ); + CapturedSecurityQos->ProxyData = NULL; + return( STATUS_INVALID_PARAMETER ); + } + + 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; + + } + + 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; + 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) ) { + + ProbeForRead( ObjectAttributes, + sizeof(OBJECT_ATTRIBUTES), + sizeof(ULONG) + ); + + LocalSecurityQos = + (PSECURITY_QUALITY_OF_SERVICE)ObjectAttributes->SecurityQualityOfService; + + if ( ARGUMENT_PRESENT(LocalSecurityQos) ) { + + ProbeForRead( + LocalSecurityQos, + sizeof(SECURITY_QUALITY_OF_SERVICE), + sizeof(ULONG) + ); + + // + // Check the length and see if this is a QOS or Advanced QOS + // structure. + // + + if (LocalSecurityQos->Length == sizeof( SECURITY_QUALITY_OF_SERVICE )) { + + // + // It's a downlevel QOS, copy what's there and leave. + // + + (*SecurityQosPresent) = TRUE; + RtlMoveMemory( CapturedSecurityQos, LocalSecurityQos, sizeof( SECURITY_QUALITY_OF_SERVICE )); + CapturedSecurityQos->ProxyData = NULL; + CapturedSecurityQos->AuditData = NULL; + + } else { + + if (LocalSecurityQos->Length == sizeof( SECURITY_ADVANCED_QUALITY_OF_SERVICE )) { + + LocalAdvancedSecurityQos = + (PSECURITY_ADVANCED_QUALITY_OF_SERVICE)ObjectAttributes->SecurityQualityOfService; + + ProbeForRead( + LocalAdvancedSecurityQos, + sizeof(SECURITY_ADVANCED_QUALITY_OF_SERVICE), + sizeof(ULONG) + ); + + (*SecurityQosPresent) = TRUE; + *CapturedSecurityQos = *LocalAdvancedSecurityQos; + + // + // 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 )) { + + RtlMoveMemory( 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 { + + RtlMoveMemory( (*CapturedSid), InputSid, SidSize ); + + } except(EXCEPTION_EXECUTE_HANDLER) { + if (!ARGUMENT_PRESENT(CaptureBuffer)) { + ExFreePool( (*CapturedSid) ); + } + + return GetExceptionCode(); + } + + if ((!RtlValidSid( (*CapturedSid) )) ) { + + if (!ARGUMENT_PRESENT(CaptureBuffer)) { + ExFreePool( (*CapturedSid) ); + } + + 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)LongAlign( 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 { + + RtlMoveMemory( (*CapturedAcl), InputAcl, AclSize ); + + } except(EXCEPTION_EXECUTE_HANDLER) { + if (!ARGUMENT_PRESENT(CaptureBuffer)) { + ExFreePool( (*CapturedAcl) ); + } + + return GetExceptionCode(); + } + + if ( (!SepCheckAcl( (*CapturedAcl), AclSize )) ) { + + if (!ARGUMENT_PRESENT(CaptureBuffer)) { + ExFreePool( (*CapturedAcl) ); + } + + 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; + } + + // + // 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)LongAlign( 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 { + + RtlMoveMemory( (*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 ) ) { + + 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; + + 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 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(SID_AND_ATTRIBUTES); + AlignedLengthRequired = (ULONG)LongAlign( 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) { + + GetSidSubAuthorityCount = + ProbeAndReadUchar( &( ((PISID)(InputArray[NextIndex].Sid))->SubAuthorityCount) ); + + TempArray[NextIndex].Sid = ((PISID)(InputArray[NextIndex].Sid)); + TempArray[NextIndex].SidLength = + RtlLengthRequiredSid( GetSidSubAuthorityCount ); + + ProbeForRead( TempArray[NextIndex].Sid, + TempArray[NextIndex].SidLength, + sizeof(ULONG) ); + + AlignedLengthRequired += + (ULONG)LongAlign( TempArray[NextIndex].SidLength ); + + NextIndex += 1; + + } //end while + + } except(EXCEPTION_EXECUTE_HANDLER) { + + ExFreePool( TempArray ); + return GetExceptionCode(); + } + + + } 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)LongAlign(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); + RtlMoveMemory( NextBufferLocation, InputArray, ArraySize ); + NextBufferLocation = (PVOID)((ULONG)NextBufferLocation + + (ULONG)LongAlign(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) ) { + + + RtlMoveMemory( NextBufferLocation, + TempArray[NextIndex].Sid, + TempArray[NextIndex].SidLength ); + + if (!RtlValidSid(TempArray[NextIndex].Sid) ) { + CompletionStatus = STATUS_INVALID_SID; + } + + NextElement[NextIndex].Sid = (PSID)NextBufferLocation; + NextBufferLocation = + (PVOID)((ULONG)NextBufferLocation + + (ULONG)LongAlign(TempArray[NextIndex].SidLength)); + + 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); + RtlMoveMemory( NextBufferLocation, InputArray, ArraySize ); + NextBufferLocation = (PVOID)( (ULONG)NextBufferLocation + + (ULONG)LongAlign(ArraySize)); + + // + // Now go through and copy each referenced SID + // + + NextIndex = 0; + NextElement = (*CapturedArray); + while (NextIndex < ArrayCount) { + + GetSidSubAuthorityCount = + ((PISID)(NextElement[NextIndex].Sid))->SubAuthorityCount; + + RtlMoveMemory( + NextBufferLocation, + NextElement[NextIndex].Sid, + RtlLengthRequiredSid(GetSidSubAuthorityCount) ); + SidSize = RtlLengthRequiredSid( GetSidSubAuthorityCount ); + AlignedSidSize = (ULONG)LongAlign(SidSize); + + NextElement[NextIndex].Sid = (PSID)NextBufferLocation; + + NextIndex += 1; + NextBufferLocation = (PVOID)((ULONG)NextBufferLocation + + AlignedSidSize); + + } //end while + + } + + if (RequestorMode != KernelMode) { + ExFreePool( TempArray ); + } + + if (!ARGUMENT_PRESENT(CaptureBuffer) && !NT_SUCCESS(CompletionStatus)) { + ExFreePool( (*CapturedArray) ); + } + + 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 +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 = SepGroupAddrSecurityDescriptor( ISecurityDescriptor ); + + Dacl = SepDaclAddrSecurityDescriptor( ISecurityDescriptor ); + + if (Group != NULL) { + *Size += (ULONG)LongAlign(SeLengthSid( Group )); + } + + if (Dacl != NULL) { + *Size += (ULONG)LongAlign(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 ISecurityDescriptor = (PISECURITY_DESCRIPTOR)SecurityDescriptor; + PISID OwnerSid; + PISID GroupSid; + PACE_HEADER Ace; + PISID Sid; + PISID Sid2; + PACL Dacl; + PACL Sacl; + ULONG i; + + if (Length < sizeof(SECURITY_DESCRIPTOR)) { + 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 == NULL || !LongAligned(ISecurityDescriptor->Owner) || + (ULONG)((PCHAR)(ISecurityDescriptor->Owner)+sizeof(SID)) > Length) { + + 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 ((ULONG)((PCHAR)ISecurityDescriptor->Owner+SeLengthSid(OwnerSid)) > Length) { + 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 != NULL) { + + // + // Check alignment + // + + if (!LongAligned(ISecurityDescriptor->Group)) { + return(FALSE); + } + + if ((ULONG)((PCHAR)(ISecurityDescriptor->Group)+sizeof(SID)) > Length) { + 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 ((ULONG)((PCHAR)ISecurityDescriptor->Group+SeLengthSid(GroupSid)) > Length) { + return(FALSE); + } + } + + // + // Validate the DACL. A structurally valid SecurityDescriptor may not necessarily + // have a DACL. + // + + if (ISecurityDescriptor->Dacl != NULL) { + + // + // Check alignment + // + + if (!LongAligned(ISecurityDescriptor->Dacl)) { + return(FALSE); + } + + // + // Make sure the DACL structure is within the bounds of the security descriptor. + // + + if ((ULONG)((PCHAR)(ISecurityDescriptor->Dacl)+sizeof(ACL)) > Length) { + return(FALSE); + } + + Dacl = (PACL)RtlOffsetToPointer( ISecurityDescriptor, ISecurityDescriptor->Dacl ); + + // + // Make sure the DACL is at least as big as an ACL structure + // + + if (Dacl->AclSize < sizeof( ACL )) { + return( FALSE ); + } + + // + // Make sure the DACL length fits within the bounds of the security descriptor. + // + + if ((ULONG)((PUCHAR)ISecurityDescriptor->Dacl + Dacl->AclSize) > Length) { + return(FALSE); + } + + // + // Make sure the ACL is structurally valid. + // + + if (!SepCheckAcl( Dacl, Dacl->AclSize )) { + return(FALSE); + } + } + + // + // Validate the SACL. A structurally valid SecurityDescriptor may not + // have a SACL. + // + + if (ISecurityDescriptor->Sacl != NULL) { + + // + // Check alignment + // + + if (!LongAligned(ISecurityDescriptor->Sacl)) { + return(FALSE); + } + + // + // Make sure the Sacl structure is within the bounds of the security descriptor. + // + + if ((ULONG)((PCHAR)(ISecurityDescriptor->Sacl)+sizeof(ACL)) > Length) { + return(FALSE); + } + + Sacl = (PACL)RtlOffsetToPointer( ISecurityDescriptor, ISecurityDescriptor->Sacl ); + + // + // Make sure the SACL is at least as big as an ACL structure + // + + if (Sacl->AclSize < sizeof( ACL )) { + return( FALSE ); + } + + // + // Make sure the Sacl length fits within the bounds of the security descriptor. + // + + if ((ULONG)((PUCHAR)ISecurityDescriptor->Sacl + Sacl->AclSize) > Length) { + return(FALSE); + } + + // + // Make sure the ACL is structurally valid. + // + + if (!SepCheckAcl( Sacl, Sacl->AclSize )) { + return(FALSE); + } + } + + return(TRUE); +} -- cgit v1.2.3