/*++

Copyright (c) Microsoft Corporation. All rights reserved. 

You may only use this code if you agree to the terms of the Windows Research Kernel Source Code License agreement (see License.txt).
If you do not agree to the terms, do not use the code.


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.

--*/

#include "pch.h"
#pragma hdrstop

#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)

#define LongAligned( ptr )  (LongAlignPtr(ptr) == (ptr))

NTSTATUS
SeCaptureSecurityDescriptor (
    __in PSECURITY_DESCRIPTOR InputSecurityDescriptor,
    __in KPROCESSOR_MODE RequestorMode,
    __in POOL_TYPE PoolType,
    __in BOOLEAN ForceCapture,
    __deref_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 requestor 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 embedded 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_opt POBJECT_ATTRIBUTES ObjectAttributes,
    __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,
    __inout_bcount_opt(CaptureBufferLength) PVOID CaptureBuffer,
    __in ULONG CaptureBufferLength,
    __in POOL_TYPE PoolType,
    __in BOOLEAN ForceCapture,
    __deref_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 requestor 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,
    __inout_bcount_opt(CaptureBufferLength) PVOID CaptureBuffer,
    __in ULONG CaptureBufferLength,
    __in POOL_TYPE PoolType,
    __in BOOLEAN ForceCapture,
    __deref_out_bcount_full(*AlignedAclSize) 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 requestor 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_ecount(ArrayCount) PLUID_AND_ATTRIBUTES InputArray,
    __in ULONG ArrayCount,
    __in KPROCESSOR_MODE RequestorMode,
    __in_bcount_opt(CaptureBufferLength) PVOID CaptureBuffer,
    __in ULONG CaptureBufferLength,
    __in POOL_TYPE PoolType,
    __in BOOLEAN ForceCapture,
    __deref_out_bcount_full(*AlignedArraySize) 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 requestor 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_ecount(ArrayCount) PSID_AND_ATTRIBUTES InputArray,
    __in ULONG ArrayCount,
    __in KPROCESSOR_MODE RequestorMode,
    __in_bcount_opt(CaptureBufferLength) PVOID CaptureBuffer,
    __in ULONG CaptureBufferLength,
    __in POOL_TYPE PoolType,
    __in BOOLEAN ForceCapture,
    __deref_out_bcount_full(AlignedArraySize) 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 requestor 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_bcount_opt(CaptureBufferLength) PVOID CaptureBuffer,
    __in_opt ULONG CaptureBufferLength,
    __in POOL_TYPE PoolType,
    __in BOOLEAN ForceCapture,
    __deref_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 requestor 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_bcount(Length) 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);
}

