/*++

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:

    sertl.c

Abstract:

    This Module implements many security rtl routines defined in ntseapi.h

--*/


#include "ntrtlp.h"
#include <winerror.h>
#include <stdio.h>
#include "seopaque.h"
#include "sertlp.h"
#include <..\se\sep.h>

#undef RtlEqualLuid

NTSYSAPI
BOOLEAN
NTAPI
RtlEqualLuid (
    PLUID Luid1,
    PLUID Luid2
    );

NTSTATUS
RtlpConvertAclToAutoInherit (
    IN PACL ParentAcl OPTIONAL,
    IN PACL ChildAcl,
    IN GUID *ObjectType OPTIONAL,
    IN BOOLEAN IsDirectoryObject,
    IN PSID OwnerSid,
    IN PSID GroupSid,
    IN PGENERIC_MAPPING GenericMapping,
    OUT PACL *NewAcl,
    OUT PULONG NewGenericControl
    );

BOOLEAN
RtlpCopyEffectiveAce (
    IN PACE_HEADER OldAce,
    IN BOOLEAN AutoInherit,
    IN BOOLEAN WillGenerateInheritAce,
    IN PSID ClientOwnerSid,
    IN PSID ClientGroupSid,
    IN PSID ServerOwnerSid OPTIONAL,
    IN PSID ServerGroupSid OPTIONAL,
    IN PGENERIC_MAPPING GenericMapping,
    IN GUID **pNewObjectType OPTIONAL,
    IN ULONG GuidCount,
    IN OUT PVOID *AcePosition,
    OUT PULONG NewAceLength,
    OUT PACL NewAcl,
    OUT PBOOLEAN ObjectAceInherited OPTIONAL,
    OUT PBOOLEAN EffectiveAceMapped,
    OUT PBOOLEAN AclOverflowed
    );

typedef enum {
     CopyInheritedAces,
     CopyNonInheritedAces,
     CopyAllAces } ACE_TYPE_TO_COPY;

NTSTATUS
RtlpCopyAces(
    IN PACL Acl,
    IN PGENERIC_MAPPING GenericMapping,
    IN ACE_TYPE_TO_COPY AceTypeToCopy,
    IN UCHAR AceFlagsToReset,
    IN BOOLEAN MapSids,
    IN PSID ClientOwnerSid,
    IN PSID ClientGroupSid,
    IN PSID ServerOwnerSid OPTIONAL,
    IN PSID ServerGroupSid OPTIONAL,
    IN BOOLEAN IsDirectoryObject,
    IN BOOLEAN RetainInheritedAceBit,
    OUT PULONG NewAclSizeParam,
    OUT PACL NewAcl
    );

NTSTATUS
RtlpGenerateInheritedAce (
    IN PACE_HEADER OldAce,
    IN BOOLEAN IsDirectoryObject,
    IN BOOLEAN AutoInherit,
    IN PSID ClientOwnerSid,
    IN PSID ClientGroupSid,
    IN PSID ServerOwnerSid OPTIONAL,
    IN PSID ServerGroupSid OPTIONAL,
    IN PGENERIC_MAPPING GenericMapping,
    IN GUID **pNewObjectType OPTIONAL,
    IN ULONG GuidCount,
    OUT PULONG NewAceLength,
    OUT PACL NewAcl,
    OUT PULONG NewAceExtraLength,
    OUT PBOOLEAN ObjectAceInherited
    );

NTSTATUS
RtlpGenerateInheritAcl(
    IN PACL Acl,
    IN BOOLEAN IsDirectoryObject,
    IN BOOLEAN AutoInherit,
    IN PSID ClientOwnerSid,
    IN PSID ClientGroupSid,
    IN PSID ServerOwnerSid OPTIONAL,
    IN PSID ServerGroupSid OPTIONAL,
    IN PGENERIC_MAPPING GenericMapping,
    IN GUID **pNewObjectType OPTIONAL,
    IN ULONG GuidCount,
    OUT PULONG NewAclSizeParam,
    OUT PACL NewAcl,
    OUT PBOOLEAN ObjectAceInherited
    );

NTSTATUS
RtlpInheritAcl2 (
    IN PACL DirectoryAcl,
    IN PACL ChildAcl,
    IN ULONG ChildGenericControl,
    IN BOOLEAN IsDirectoryObject,
    IN BOOLEAN AutoInherit,
    IN BOOLEAN DefaultDescriptorForObject,
    IN PSID OwnerSid,
    IN PSID GroupSid,
    IN PSID ServerOwnerSid OPTIONAL,
    IN PSID ServerGroupSid OPTIONAL,
    IN PGENERIC_MAPPING GenericMapping,
    IN BOOLEAN IsSacl,
    IN GUID **pNewObjectType OPTIONAL,
    IN ULONG GuidCount,
    IN PULONG AclBufferSize,
    IN OUT PUCHAR AclBuffer,
    OUT PBOOLEAN NewAclExplicitlyAssigned,
    OUT PULONG NewGenericControl
    );

NTSTATUS
RtlpComputeMergedAcl (
    IN PACL CurrentAcl,
    IN ULONG CurrentGenericControl,
    IN PACL ModificationAcl,
    IN ULONG ModificationGenericControl,
    IN PSID ClientOwnerSid,
    IN PSID ClientGroupSid,
    IN PGENERIC_MAPPING GenericMapping,
    IN BOOLEAN IsSacl,
    OUT PACL *NewAcl,
    OUT PULONG NewGenericControl
    );

NTSTATUS
RtlpComputeMergedAcl2 (
    IN PACL CurrentAcl,
    IN ULONG CurrentGenericControl,
    IN PACL ModificationAcl,
    IN ULONG ModificationGenericControl,
    IN PSID ClientOwnerSid,
    IN PSID ClientGroupSid,
    IN PGENERIC_MAPPING GenericMapping,
    IN BOOLEAN IsSacl,
    IN PULONG AclBufferSize,
    IN OUT PUCHAR AclBuffer,
    OUT PULONG NewGenericControl
    );

BOOLEAN
RtlpCompareAces(
    IN PKNOWN_ACE InheritedAce,
    IN PKNOWN_ACE ChildAce,
    IN PSID OwnerSid,
    IN PSID GroupSid
    );

BOOLEAN
RtlpCompareKnownObjectAces(
    IN PKNOWN_OBJECT_ACE InheritedAce,
    IN PKNOWN_OBJECT_ACE ChildAce,
    IN PSID OwnerSid OPTIONAL,
    IN PSID GroupSid OPTIONAL
    );

BOOLEAN
RtlpCompareKnownAces(
    IN PKNOWN_ACE InheritedAce,
    IN PKNOWN_ACE ChildAce,
    IN PSID OwnerSid OPTIONAL,
    IN PSID GroupSid OPTIONAL
    );

BOOLEAN
RtlpIsDuplicateAce(
    IN PACL Acl,
    IN PKNOWN_ACE NewAce
    );

BOOLEAN
RtlpGuidPresentInGuidList(
    IN GUID *InheritedObjectType,
    IN GUID **pNewObjectType,
    IN ULONG GuidCount
    );

NTSTATUS
RtlpCreateServerAcl(
    IN PACL Acl,
    IN BOOLEAN AclUntrusted,
    IN PSID ServerSid,
    OUT PACL *ServerAcl,
    OUT BOOLEAN *ServerAclAllocated
    );

NTSTATUS
RtlpGetDefaultsSubjectContext(
    HANDLE ClientToken,
    OUT PTOKEN_OWNER *OwnerInfo,
    OUT PTOKEN_PRIMARY_GROUP *GroupInfo,
    OUT PTOKEN_DEFAULT_DACL *DefaultDaclInfo,
    OUT PTOKEN_OWNER *ServerOwner,
    OUT PTOKEN_PRIMARY_GROUP *ServerGroup
    );

BOOLEAN RtlpValidateSDOffsetAndSize (
    IN ULONG   Offset,
    IN ULONG   Length,
    IN ULONG   MinLength,
    OUT PULONG MaxLength
    );

BOOLEAN
RtlValidRelativeSecurityDescriptor (
    IN PSECURITY_DESCRIPTOR SecurityDescriptorInput,
    IN ULONG SecurityDescriptorLength,
    IN SECURITY_INFORMATION RequiredInformation
    );

NTSTATUS
RtlpImpersonateSelfEx (
    IN SECURITY_IMPERSONATION_LEVEL ImpersonationLevel,
    IN ACCESS_MASK AdditionalAccess,
    OUT PHANDLE ThreadToken OPTIONAL
    );

#if defined(ALLOC_PRAGMA)
#pragma alloc_text(PAGE,RtlRunEncodeUnicodeString)
#pragma alloc_text(PAGE,RtlRunDecodeUnicodeString)
#pragma alloc_text(PAGE,RtlEraseUnicodeString)
#pragma alloc_text(PAGE,RtlAdjustPrivilege)
#pragma alloc_text(PAGE,RtlValidSid)
#pragma alloc_text(PAGE,RtlEqualSid)
#pragma alloc_text(PAGE,RtlEqualPrefixSid)
#pragma alloc_text(PAGE,RtlLengthRequiredSid)
#pragma alloc_text(PAGE,RtlInitializeSid)
#pragma alloc_text(PAGE,RtlIdentifierAuthoritySid)
#pragma alloc_text(PAGE,RtlSubAuthoritySid)
#pragma alloc_text(PAGE,RtlSubAuthorityCountSid)
#pragma alloc_text(PAGE,RtlLengthSid)
#pragma alloc_text(PAGE,RtlCopySid)
#pragma alloc_text(PAGE,RtlCopySidAndAttributesArray)
#pragma alloc_text(PAGE,RtlLengthSidAsUnicodeString)
#pragma alloc_text(PAGE,RtlConvertSidToUnicodeString)
#pragma alloc_text(PAGE,RtlEqualLuid)
#pragma alloc_text(PAGE,RtlCopyLuid)
#pragma alloc_text(PAGE,RtlCopyLuidAndAttributesArray)
#pragma alloc_text(PAGE,RtlCreateSecurityDescriptor)
#pragma alloc_text(PAGE,RtlCreateSecurityDescriptorRelative)
#pragma alloc_text(PAGE,RtlValidSecurityDescriptor)
#pragma alloc_text(PAGE,RtlLengthSecurityDescriptor)
#pragma alloc_text(PAGE,RtlSetAttributesSecurityDescriptor)
#pragma alloc_text(PAGE,RtlGetControlSecurityDescriptor)
#pragma alloc_text(PAGE,RtlSetControlSecurityDescriptor)
#pragma alloc_text(PAGE,RtlSetDaclSecurityDescriptor)
#pragma alloc_text(PAGE,RtlGetDaclSecurityDescriptor)
#pragma alloc_text(PAGE,RtlSetSaclSecurityDescriptor)
#pragma alloc_text(PAGE,RtlGetSaclSecurityDescriptor)
#pragma alloc_text(PAGE,RtlSetOwnerSecurityDescriptor)
#pragma alloc_text(PAGE,RtlGetOwnerSecurityDescriptor)
#pragma alloc_text(PAGE,RtlSetGroupSecurityDescriptor)
#pragma alloc_text(PAGE,RtlGetGroupSecurityDescriptor)
#pragma alloc_text(PAGE,RtlAreAllAccessesGranted)
#pragma alloc_text(PAGE,RtlAreAnyAccessesGranted)
#pragma alloc_text(PAGE,RtlMapGenericMask)
#pragma alloc_text(PAGE,RtlpImpersonateSelfEx)
#pragma alloc_text(PAGE,RtlImpersonateSelf)
#pragma alloc_text(PAGE,RtlpApplyAclToObject)
#pragma alloc_text(PAGE,RtlpCopyEffectiveAce)
#pragma alloc_text(PAGE,RtlpCopyAces)
#pragma alloc_text(PAGE,RtlpGuidPresentInGuidList)
#pragma alloc_text(PAGE,RtlpInheritAcl2)
#pragma alloc_text(PAGE,RtlpInheritAcl)
#pragma alloc_text(PAGE,RtlpGenerateInheritedAce)
#pragma alloc_text(PAGE,RtlpGenerateInheritAcl)
#pragma alloc_text(PAGE,RtlpComputeMergedAcl2)
#pragma alloc_text(PAGE,RtlpComputeMergedAcl)
#pragma alloc_text(PAGE,RtlpConvertToAutoInheritSecurityObject)
#pragma alloc_text(PAGE,RtlpCompareAces)
#pragma alloc_text(PAGE,RtlpCompareKnownAces)
#pragma alloc_text(PAGE,RtlpCompareKnownObjectAces)
#pragma alloc_text(PAGE,RtlpConvertAclToAutoInherit)
#pragma alloc_text(PAGE,RtlpIsDuplicateAce)
#pragma alloc_text(PAGE,RtlpCreateServerAcl)
#pragma alloc_text(PAGE,RtlpNewSecurityObject)
#pragma alloc_text(PAGE,RtlpSetSecurityObject)
#pragma alloc_text(PAGE,RtlpValidateSDOffsetAndSize)
#pragma alloc_text(PAGE,RtlValidRelativeSecurityDescriptor)
#pragma alloc_text(PAGE,RtlGetSecurityDescriptorRMControl)
#pragma alloc_text(PAGE,RtlSetSecurityDescriptorRMControl)
#endif


///////////////////////////////////////////////////////////////////////////////
//                                                                           //
//    Local Macros and Symbols                                               //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////


#define CREATOR_SID_SIZE 12

#define max(a,b)            (((a) > (b)) ? (a) : (b))

//
// Define an array mapping all ACE types to their base type.
//
// For instance, all allowed ACE types are similar.  As are all denied ACE types.
//

#if defined(ALLOC_DATA_PRAGMA)
#pragma const_seg("PAGECONST")
#endif

const UCHAR RtlBaseAceType[] = {
    ACCESS_ALLOWED_ACE_TYPE,    // ACCESS_ALLOWED_ACE_TYPE (0x0)
    ACCESS_DENIED_ACE_TYPE,     // ACCESS_DENIED_ACE_TYPE (0x1)
    SYSTEM_AUDIT_ACE_TYPE,      // SYSTEM_AUDIT_ACE_TYPE (0x2)
    SYSTEM_ALARM_ACE_TYPE,      // SYSTEM_ALARM_ACE_TYPE (0x3)
    ACCESS_ALLOWED_ACE_TYPE,    // ACCESS_ALLOWED_COMPOUND_ACE_TYPE (0x4)
    ACCESS_ALLOWED_ACE_TYPE,    // ACCESS_ALLOWED_OBJECT_ACE_TYPE (0x5)
    ACCESS_DENIED_ACE_TYPE,     // ACCESS_DENIED_OBJECT_ACE_TYPE (0x6)
    SYSTEM_AUDIT_ACE_TYPE,      // SYSTEM_AUDIT_OBJECT_ACE_TYPE (0x7)
    SYSTEM_ALARM_ACE_TYPE       // SYSTEM_ALARM_OBJECT_ACE_TYPE (0x8)
};

//
// Define an array defining whether an ACE is a system ACE
//

const UCHAR RtlIsSystemAceType[] = {
    FALSE,    // ACCESS_ALLOWED_ACE_TYPE (0x0)
    FALSE,    // ACCESS_DENIED_ACE_TYPE (0x1)
    TRUE,     // SYSTEM_AUDIT_ACE_TYPE (0x2)
    TRUE,     // SYSTEM_ALARM_ACE_TYPE (0x3)
    FALSE,    // ACCESS_ALLOWED_COMPOUND_ACE_TYPE (0x4)
    FALSE,    // ACCESS_ALLOWED_OBJECT_ACE_TYPE (0x5)
    FALSE,    // ACCESS_DENIED_OBJECT_ACE_TYPE (0x6)
    TRUE,     // SYSTEM_AUDIT_OBJECT_ACE_TYPE (0x7)
    TRUE      // SYSTEM_ALARM_OBJECT_ACE_TYPE (0x8)
};

#if DBG
BOOLEAN RtlpVerboseConvert = FALSE;
#endif // DBG

#define SE_VALID_CONTROL_BITS ( SE_DACL_UNTRUSTED | \
                                SE_SERVER_SECURITY | \
                                SE_DACL_AUTO_INHERIT_REQ | \
                                SE_SACL_AUTO_INHERIT_REQ | \
                                SE_DACL_AUTO_INHERITED | \
                                SE_SACL_AUTO_INHERITED | \
                                SE_DACL_PROTECTED | \
                                SE_SACL_PROTECTED )


///////////////////////////////////////////////////////////////////////////////
//                                                                           //
//    Null DACL assertions                                                   //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

#if DBG
#define ASSERT_ON_NULL_DACL 1
#endif

#ifdef ASSERT_ON_NULL_DACL
ULONG RtlpAssertOnNullDacls;
#endif // ASSERT_ON_NULL_DACL



///////////////////////////////////////////////////////////////////////////////
//                                                                           //
//    Exported Procedures                                                    //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////


VOID
RtlRunEncodeUnicodeString(
    PUCHAR          Seed        OPTIONAL,
    PUNICODE_STRING String
    )

/*++

Routine Description:

    This function performs a trivial XOR run-encoding of a string.
    The purpose of this run-encoding is to change the character values
    to appear somewhat random and typically not printable.  This is
    useful for transforming passwords that you don't want to be easily
    distinguishable by visually scanning a paging file or memory dump.


Arguments:

    Seed - Points to a seed value to use in the encoding.  If the
        pointed to value is zero, then this routine will assign
        a value.

    String - The string to encode.  This string may be decode
        by passing it and the seed value to RtlRunDecodeUnicodeString().


Return Value:

    None - Nothing can really go wrong unless the caller passes bogus
        parameters.  In this case, the caller can catch the access
        violation.


--*/
{

    LARGE_INTEGER Time;
    PUCHAR        LocalSeed;
    NTSTATUS      Status;
    ULONG         i;
    PSTRING       S;


    RTL_PAGED_CODE();

    //
    // Typecast so we can work on bytes rather than WCHARs
    //

    S = (PSTRING)((PVOID)String);

    //
    // If a seed wasn't passed, use the 2nd byte of current time.
    // This byte seems to be sufficiently random (by observation).
    //

    if ((*Seed) == 0) {
        Status = NtQuerySystemTime ( &Time );
        ASSERT(NT_SUCCESS(Status));

        LocalSeed = (PUCHAR)((PVOID)&Time);

        i = 1;

        (*Seed) = LocalSeed[ i ];

        //
        // Occasionally, this byte could be zero.  That would cause the
        // string to become un-decodable, since 0 is the magic value that
        // causes us to re-gen the seed.  This loop makes sure that we
        // never end up with a zero byte (unless time is zero, as well).
        //

        while ( ((*Seed) == 0) && ( i < sizeof( Time ) ) )
        {
            (*Seed) |= LocalSeed[ i++ ] ;
        }

        if ( (*Seed) == 0 )
        {
            (*Seed) = 1;
        }
    }

    //
    // Transform the initial byte.
    // The funny constant just keeps the first byte from propagating
    // into the second byte in the next step.  Without a funny constant
    // this would happen for many languages (which typically have every
    // other byte zero.
    //
    //

    if (S->Length >= 1) {
        S->Buffer[0] ^= ((*Seed) | 0X43);
    }


    //
    // Now transform the rest of the string
    //

    for (i=1; i<S->Length; i++) {

        //
        //  There are export issues that cause us to want to
        //  keep this algorithm simple.
        //

        //
        // In order to be compatible with zero terminated unicode strings,
        //  this algorithm is designed to not produce a wide character of
        //  zero as long a the seed is not zero.
        //

        //
        // Simple running XOR with the previous byte and the
        // seed value.
        //

        S->Buffer[i] ^= (S->Buffer[i-1]^(*Seed));

    }


    return;

}


VOID
RtlRunDecodeUnicodeString(
    UCHAR           Seed,
    PUNICODE_STRING String
    )
/*++

Routine Description:

    This function performs the inverse of the function performed
    by RtlRunEncodeUnicodeString().  Please see RtlRunEncodeUnicodeString()
    for details.


Arguments:

    Seed - The seed value to use in RtlRunEncodeUnicodeString().

    String - The string to reveal.


Return Value:

    None - Nothing can really go wrong unless the caller passes bogus
        parameters.  In this case, the caller can catch the access
        violation.


--*/

{

    ULONG
        i;

    PSTRING
        S;

    RTL_PAGED_CODE();

    //
    // Typecast so we can work on bytes rather than WCHARs
    //

    S = (PSTRING)((PVOID)String);


    //
    // Transform the end of the string
    //

    for (i=S->Length; i>1; i--) {

        //
        // a simple running XOR with the previous byte and the
        // seed value.
        //

        S->Buffer[i-1] ^= (S->Buffer[i-2]^Seed);

    }

    //
    // Finally, transform the initial byte
    //

    if (S->Length >= 1) {
        S->Buffer[0] ^= (Seed | 0X43);
    }


    return;
}



VOID
RtlEraseUnicodeString(
    PUNICODE_STRING String
    )
/*++

Routine Description:

    This function scrubs the passed string by over-writing all
    characters in the string.  The entire string (i.e., MaximumLength)
    is erased, not just the current length.


Arguments:

    String - The string to be erased.


Return Value:

    None - Nothing can really go wrong unless the caller passes bogus
        parameters.  In this case, the caller can catch the access
        violation.


--*/

{
    RTL_PAGED_CODE();

    if ((String->Buffer == NULL) || (String->MaximumLength == 0)) {
        return;
    }

    RtlZeroMemory( (PVOID)String->Buffer, (ULONG)String->MaximumLength );

    String->Length = 0;

    return;
}



NTSTATUS
RtlAdjustPrivilege(
    ULONG Privilege,
    BOOLEAN Enable,
    BOOLEAN Client,
    PBOOLEAN WasEnabled
    )

/*++

Routine Description:

    This procedure enables or disables a privilege process-wide.

Arguments:

    Privilege - The lower 32-bits of the privilege ID to be enabled or
        disabled.  The upper 32-bits is assumed to be zero.

    Enable - A boolean indicating whether the privilege is to be enabled
        or disabled.  TRUE indicates the privilege is to be enabled.
        FALSE indicates the privilege is to be disabled.

    Client - A boolean indicating whether the privilege should be adjusted
        in a client token or the process's own token.   TRUE indicates
        the client's token should be used (and an error returned if there
        is no client token).  FALSE indicates the process's token should
        be used.

    WasEnabled - points to a boolean to receive an indication of whether
        the privilege was previously enabled or disabled.  TRUE indicates
        the privilege was previously enabled.  FALSE indicates the privilege
        was previously disabled.  This value is useful for returning the
        privilege to its original state after using it.


Return Value:

    STATUS_SUCCESS - The privilege has been successfully enabled or disabled.

    STATUS_PRIVILEGE_NOT_HELD - The privilege is not held by the specified context.

    Other status values as may be returned by:

            NtOpenProcessToken()
            NtAdjustPrivilegesToken()


--*/

{
    NTSTATUS
        Status,
        TmpStatus;

    HANDLE
        Token;

    LUID
        LuidPrivilege;

    PTOKEN_PRIVILEGES
        NewPrivileges,
        OldPrivileges;

    ULONG
        Length;

    UCHAR
        Buffer1[sizeof(TOKEN_PRIVILEGES)+
                ((1-ANYSIZE_ARRAY)*sizeof(LUID_AND_ATTRIBUTES))],
        Buffer2[sizeof(TOKEN_PRIVILEGES)+
                ((1-ANYSIZE_ARRAY)*sizeof(LUID_AND_ATTRIBUTES))];


    RTL_PAGED_CODE();

    NewPrivileges = (PTOKEN_PRIVILEGES)Buffer1;
    OldPrivileges = (PTOKEN_PRIVILEGES)Buffer2;

    //
    // Open the appropriate token...
    //

    if (Client == TRUE) {
        Status = NtOpenThreadToken(
                     NtCurrentThread(),
                     TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY,
                     FALSE,
                     &Token
                     );
    } else {

        Status = NtOpenProcessToken(
                     NtCurrentProcess(),
                     TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY,
                     &Token
                    );
    }

    if (!NT_SUCCESS(Status)) {
        return(Status);
    }



    //
    // Initialize the privilege adjustment structure
    //

    LuidPrivilege = RtlConvertUlongToLuid(Privilege);


    NewPrivileges->PrivilegeCount = 1;
    NewPrivileges->Privileges[0].Luid = LuidPrivilege;
    NewPrivileges->Privileges[0].Attributes = Enable ? SE_PRIVILEGE_ENABLED : 0;



    //
    // Adjust the privilege
    //

    Status = NtAdjustPrivilegesToken(
                 Token,                     // TokenHandle
                 FALSE,                     // DisableAllPrivileges
                 NewPrivileges,             // NewPrivileges
                 sizeof(Buffer1),           // BufferLength
                 OldPrivileges,             // PreviousState (OPTIONAL)
                 &Length                    // ReturnLength
                 );


    TmpStatus = NtClose(Token);
    ASSERT(NT_SUCCESS(TmpStatus));


    //
    // Map the success code NOT_ALL_ASSIGNED to an appropriate error
    // since we're only trying to adjust the one privilege.
    //

    if (Status == STATUS_NOT_ALL_ASSIGNED) {
        Status = STATUS_PRIVILEGE_NOT_HELD;
    }


    if (NT_SUCCESS(Status)) {

        //
        // If there are no privileges in the previous state, there were
        // no changes made. The previous state of the privilege
        // is whatever we tried to change it to.
        //

        if (OldPrivileges->PrivilegeCount == 0) {

            (*WasEnabled) = Enable;

        } else {

            (*WasEnabled) =
                (OldPrivileges->Privileges[0].Attributes & SE_PRIVILEGE_ENABLED)
                ? TRUE : FALSE;
        }
    }

    return(Status);
}


BOOLEAN
RtlValidSid (
    IN PSID Sid
    )

/*++

Routine Description:

    This procedure validates an SID's structure.

Arguments:

    Sid - Pointer to the SID structure to validate.

Return Value:

    BOOLEAN - TRUE if the structure of Sid is valid.

--*/

{
    PISID Isid = (PISID) Sid;
    RTL_PAGED_CODE();
    //
    // Make sure revision is SID_REVISION and sub authority count is not
    // greater than maximum number of allowed sub-authorities.
    //

    try {

        if ( Isid != NULL && (Isid->Revision & 0x0f) == SID_REVISION) {
            if (Isid->SubAuthorityCount <= SID_MAX_SUB_AUTHORITIES) {

                return TRUE;
          }
        }

    } except(EXCEPTION_EXECUTE_HANDLER) {
        return FALSE;
    }

    return FALSE;

}



BOOLEAN
RtlEqualSid (
    IN PSID Sid1,
    IN PSID Sid2
    )

/*++

Routine Description:

    This procedure tests two SID values for equality.

Arguments:

    Sid1, Sid2 - Supply pointers to the two SID values to compare.
        The SID structures are assumed to be valid.

Return Value:

    BOOLEAN - TRUE if the value of Sid1 is equal to Sid2, and FALSE
        otherwise.

--*/

{
   ULONG SidLength;

   C_ASSERT (FIELD_OFFSET (SID, Revision) + sizeof (((SID *)Sid1)->Revision) == FIELD_OFFSET (SID, SubAuthorityCount));
   C_ASSERT (sizeof (((SID *)Sid1)->Revision) + sizeof (((SID *)Sid1)->SubAuthorityCount) == sizeof (USHORT));

   RTL_PAGED_CODE();

   //
   // Make sure they are the same revision. To make this routine faster we make the assumption that
   // the revision and subauthority fields are adjacent.
   //

   if (*(USHORT *)&((SID *)Sid1)->Revision == *(USHORT *)&((SID *)Sid2)->Revision) {
       SidLength = SeLengthSid (Sid1);
       return( (BOOLEAN)RtlEqualMemory( Sid1, Sid2, SidLength) );
   }

   return( FALSE );

}



BOOLEAN
RtlEqualPrefixSid (
    IN PSID Sid1,
    IN PSID Sid2
    )

/*++

Routine Description:

    This procedure tests two SID prefix values for equality.

    An SID prefix is the entire SID except for the last sub-authority
    value.

Arguments:

    Sid1, Sid2 - Supply pointers to the two SID values to compare.
        The SID structures are assumed to be valid.

Return Value:

    BOOLEAN - TRUE if the prefix value of Sid1 is equal to Sid2, and FALSE
        otherwise.

--*/


{
    LONG Index;

    //
    // Typecast to the opaque SID structures.
    //

    SID *ISid1 = Sid1;
    SID *ISid2 = Sid2;

    RTL_PAGED_CODE();

    //
    // Make sure they are the same revision
    //

    if (ISid1->Revision == ISid2->Revision ) {

        //
        // Compare IdentifierAuthority values
        //

        if ( (ISid1->IdentifierAuthority.Value[0] ==
              ISid2->IdentifierAuthority.Value[0])  &&
             (ISid1->IdentifierAuthority.Value[1]==
              ISid2->IdentifierAuthority.Value[1])  &&
             (ISid1->IdentifierAuthority.Value[2] ==
              ISid2->IdentifierAuthority.Value[2])  &&
             (ISid1->IdentifierAuthority.Value[3] ==
              ISid2->IdentifierAuthority.Value[3])  &&
             (ISid1->IdentifierAuthority.Value[4] ==
              ISid2->IdentifierAuthority.Value[4])  &&
             (ISid1->IdentifierAuthority.Value[5] ==
              ISid2->IdentifierAuthority.Value[5])
            ) {

            //
            // Compare SubAuthorityCount values
            //

            if (ISid1->SubAuthorityCount == ISid2->SubAuthorityCount) {

                if (ISid1->SubAuthorityCount == 0) {
                    return TRUE;
                }

                Index = 0;
                while (Index < (ISid1->SubAuthorityCount - 1)) {
                    if ((ISid1->SubAuthority[Index]) != (ISid2->SubAuthority[Index])) {

                        //
                        // Found some SubAuthority values that weren't equal.
                        //

                        return FALSE;
                    }
                    Index += 1;
                }

                //
                // All SubAuthority values are equal.
                //

                return TRUE;
            }
        }
    }

    //
    // Either the Revision, SubAuthorityCount, or IdentifierAuthority values
    // weren't equal.
    //

    return FALSE;
}



ULONG
RtlLengthRequiredSid (
    IN ULONG SubAuthorityCount
    )

/*++

Routine Description:

    This routine returns the length, in bytes, required to store an SID
    with the specified number of Sub-Authorities.

Arguments:

    SubAuthorityCount - The number of sub-authorities to be stored in the SID.

Return Value:

    ULONG - The length, in bytes, required to store the SID.


--*/

{
    RTL_PAGED_CODE();

    return (8L + (4 * SubAuthorityCount));

}


NTSTATUS
RtlInitializeSid(
    IN PSID Sid,
    IN PSID_IDENTIFIER_AUTHORITY IdentifierAuthority,
    IN UCHAR SubAuthorityCount
    )
/*++

Routine Description:

    This function initializes an SID data structure.  It does not, however,
    set the sub-authority values.  This must be done separately.

Arguments:

    Sid - Pointer to the SID data structure to initialize.

    IdentifierAuthority - Pointer to the Identifier Authority value to
        set in the SID.

    SubAuthorityCount - The number of sub-authorities that will be placed in
        the SID (a separate action).

Return Value:


--*/
{
    PISID ISid;

    RTL_PAGED_CODE();

    //
    //  Typecast to the opaque SID
    //

    ISid = (PISID)Sid;

    if ( SubAuthorityCount > SID_MAX_SUB_AUTHORITIES ) {
        return( STATUS_INVALID_PARAMETER );
    }

    ISid->SubAuthorityCount = (UCHAR)SubAuthorityCount;
    ISid->Revision = 1;
    ISid->IdentifierAuthority = *IdentifierAuthority;

    return( STATUS_SUCCESS );

}


PSID_IDENTIFIER_AUTHORITY
RtlIdentifierAuthoritySid(
    IN PSID Sid
    )
/*++

Routine Description:

    This function returns the address of an SID's IdentifierAuthority field.

Arguments:

    Sid - Pointer to the SID data structure.

Return Value:


--*/
{
    PISID ISid;

    RTL_PAGED_CODE();

    //
    //  Typecast to the opaque SID
    //

    ISid = (PISID)Sid;

    return &(ISid->IdentifierAuthority);

}

PULONG
RtlSubAuthoritySid(
    IN PSID Sid,
    IN ULONG SubAuthority
    )
/*++

Routine Description:

    This function returns the address of a sub-authority array element of
    an SID.

Arguments:

    Sid - Pointer to the SID data structure.

    SubAuthority - An index indicating which sub-authority is being specified.
        This value is not compared against the number of sub-authorities in the
        SID for validity.

Return Value:


--*/
{
    RTL_PAGED_CODE();

    return RtlpSubAuthoritySid( Sid, SubAuthority );
}

PUCHAR
RtlSubAuthorityCountSid(
    IN PSID Sid
    )
/*++

Routine Description:

    This function returns the address of the sub-authority count field of
    an SID.

Arguments:

    Sid - Pointer to the SID data structure.

Return Value:


--*/
{
    PISID ISid;

    RTL_PAGED_CODE();

    //
    //  Typecast to the opaque SID
    //

    ISid = (PISID)Sid;

    return &(ISid->SubAuthorityCount);

}

ULONG
RtlLengthSid (
    IN PSID Sid
    )

/*++

Routine Description:

    This routine returns the length, in bytes, of a structurally valid SID.

Arguments:

    Sid - Points to the SID whose length is to be returned.  The
        SID's structure is assumed to be valid.

Return Value:

    ULONG - The length, in bytes, of the SID.


--*/

{
    RTL_PAGED_CODE();

    return SeLengthSid(Sid);
}


NTSTATUS
RtlCopySid (
    IN ULONG DestinationSidLength,
    OUT PSID DestinationSid,
    IN PSID SourceSid
    )

/*++

Routine Description:

    This routine copies the value of the source SID to the destination
    SID.

Arguments:

    DestinationSidLength - Indicates the length, in bytes, of the
        destination SID buffer.

    DestinationSid - Pointer to a buffer to receive a copy of the
        source Sid value.

    SourceSid - Supplies the Sid value to be copied.

Return Value:

    STATUS_SUCCESS - Indicates the SID was successfully copied.

    STATUS_BUFFER_TOO_SMALL - Indicates the target buffer wasn't
        large enough to receive a copy of the SID.


--*/

{
    ULONG SidLength = SeLengthSid(SourceSid);

    RTL_PAGED_CODE();

    if (SidLength > DestinationSidLength) {

        return STATUS_BUFFER_TOO_SMALL;

    }

    //
    // Buffer is large enough
    //

    RtlMoveMemory( DestinationSid, SourceSid, SidLength );

    return STATUS_SUCCESS;

}


NTSTATUS
RtlCopySidAndAttributesArray (
    IN ULONG ArrayLength,
    IN PSID_AND_ATTRIBUTES Source,
    IN ULONG TargetSidBufferSize,
    OUT PSID_AND_ATTRIBUTES TargetArrayElement,
    OUT PSID TargetSid,
    OUT PSID *NextTargetSid,
    OUT PULONG RemainingTargetSidBufferSize
    )

/*++

Routine Description:

    This routine copies the value of the source SID_AND_ATTRIBUTES array
    to the target.  The actual SID values are placed according to a separate
    parameter.  This allows multiple arrays to be merged using this service
    to copy each.

Arguments:

    ArrayLength - Number of elements in the source array to copy.

    Source - Pointer to the source array.

    TargetSidBufferSize - Indicates the length, in bytes, of the buffer
        to receive the actual SID values.  If this value is less than
        the actual amount needed, then STATUS_BUFFER_TOO_SMALL is returned.

    TargetArrayElement - Indicates where the array elements are to be
        copied to (but not the SID values themselves).

    TargetSid - Indicates where the target SID values s are to be copied.  This
        is assumed to be ULONG aligned.  Each SID value will be copied
        into this buffer.  Each SID will be ULONG aligned.

    NextTargetSid - On completion, will be set to point to the ULONG
        aligned address following the last SID copied.

    RemainingTargetSidBufferSize - On completion, receives an indicatation
        of how much of the SID buffer is still unused.


Return Value:

    STATUS_SUCCESS - The call completed successfully.

    STATUS_BUFFER_TOO_SMALL - Indicates the buffer to receive the SID
        values wasn't large enough.



--*/

{

    ULONG Index = 0;
    PSID NextSid = TargetSid;
    ULONG NextSidLength;
    ULONG AlignedSidLength;
    ULONG RemainingLength = TargetSidBufferSize;

    RTL_PAGED_CODE();

    while (Index < ArrayLength) {

        NextSidLength = SeLengthSid( Source[Index].Sid );
        AlignedSidLength = PtrToUlong(LongAlign(NextSidLength));

        if (NextSidLength > RemainingLength) {
            return STATUS_BUFFER_TOO_SMALL;
        }

        RemainingLength -= AlignedSidLength;

        TargetArrayElement[Index].Sid = NextSid;
        TargetArrayElement[Index].Attributes = Source[Index].Attributes;

        RtlCopySid( NextSidLength, NextSid, Source[Index].Sid );

        NextSid = (PSID)((PCHAR)NextSid + AlignedSidLength);

        Index += 1;

    } //end_while

    (*NextTargetSid) = NextSid;
    (*RemainingTargetSidBufferSize) = RemainingLength;

    return STATUS_SUCCESS;

}



NTSTATUS
RtlLengthSidAsUnicodeString(
    PSID Sid,
    PULONG StringLength
    )

/*++

Routine Description:


    This function returns the maximum length of the string needed
    to represent the SID supplied.  The actual string may be shorter,
    but this is intended to be a quick calculation.

Arguments:


    Sid - Supplies the SID that is to be converted to unicode.

    StringLength - Receives the max length required in bytes.

Return Value:

    SUCCESS - The conversion was successful

    STATUS_INVALID_SID - The sid provided does not have a valid structure,
        or has too many sub-authorities (more than SID_MAX_SUB_AUTHORITIES).

--*/

{
    ULONG   i ;

    PISID   iSid = (PISID)Sid;  // pointer to opaque structure


    RTL_PAGED_CODE();

    if ( RtlValidSid( Sid ) != TRUE)
    {
        return(STATUS_INVALID_SID);
    }

    //
    // if the SID's IA value has 5 or 6 significant bytes, the
    // representation will be in hex, with a 0x preceding.  Otherwise
    // it will be in decimal, with at most 10 characters.
    //

    if (  (iSid->IdentifierAuthority.Value[0] != 0)  ||
          (iSid->IdentifierAuthority.Value[1] != 0)  )
    {
        i = 14 ;    // 0x665544332211

    }
    else
    {
        i = 10 ;    // 4294967295 is the max ulong, at 10 chars
    }

    i += 4 ;        // room for the S-1-

    //
    // for each sub authority, it is a max of 10 chars (for a ulong),
    // plus the - separator
    //

    i += 11 * iSid->SubAuthorityCount ;

    *StringLength = i * sizeof( WCHAR );

    return STATUS_SUCCESS ;

}




NTSTATUS
RtlConvertSidToUnicodeString(
    PUNICODE_STRING UnicodeString,
    PSID Sid,
    BOOLEAN AllocateDestinationString
    )

/*++

Routine Description:


    This function generates a printable unicode string representation
    of a SID.

    The resulting string will take one of two forms.  If the
    IdentifierAuthority value is not greater than 2^32, then
    the SID will be in the form:


        S-1-281736-12-72-9-110
              ^    ^^ ^^ ^ ^^^
              |     |  | |  |
              +-----+--+-+--+---- Decimal



    Otherwise it will take the form:


        S-1-0x173495281736-12-72-9-110
            ^^^^^^^^^^^^^^ ^^ ^^ ^ ^^^
             Hexadecimal    |  | |  |
                            +--+-+--+---- Decimal






Arguments:



    UnicodeString - Returns a unicode string that is equivalent to
        the SID. The maximum length field is only set if
        AllocateDestinationString is TRUE.

    Sid - Supplies the SID that is to be converted to unicode.

    AllocateDestinationString - Supplies a flag that controls whether or
        not this API allocates the buffer space for the destination
        string.  If it does, then the buffer must be deallocated using
        RtlFreeUnicodeString (note that only storage for
        DestinationString->Buffer is allocated by this API).

Return Value:

    SUCCESS - The conversion was successful

    STATUS_INVALID_SID - The sid provided does not have a valid structure,
        or has too many sub-authorities (more than SID_MAX_SUB_AUTHORITIES).

    STATUS_NO_MEMORY - There was not sufficient memory to allocate the
        target string.  This is returned only if AllocateDestinationString
        is specified as TRUE.

    STATUS_BUFFER_OVERFLOW - This is returned only if
        AllocateDestinationString is specified as FALSE.


--*/

{
    NTSTATUS Status;
    WCHAR UniBuffer[ 256 ];
    PWSTR Offset ;
    UNICODE_STRING LocalString ;

    UCHAR   i;
    ULONG   Tmp;
    LARGE_INTEGER Auth ;

    PISID   iSid = (PISID)Sid;  // pointer to opaque structure


    RTL_PAGED_CODE();

    if (RtlValidSid( Sid ) != TRUE) {
        return(STATUS_INVALID_SID);
    }

    if ( iSid->Revision != SID_REVISION )
    {
        return STATUS_INVALID_SID ;
    }

    wcscpy( UniBuffer, L"S-1-" );

    Offset = &UniBuffer[ 4 ];

    if (  (iSid->IdentifierAuthority.Value[0] != 0)  ||
          (iSid->IdentifierAuthority.Value[1] != 0)     ){

        //
        // Ugly hex dump.
        //
	
        wcscat( UniBuffer, L"0x" );
	
        Offset = &UniBuffer[ 6 ];

        Auth.HighPart = (LONG) (iSid->IdentifierAuthority.Value[ 0 ] << 8) +
                        (LONG) iSid->IdentifierAuthority.Value[ 1 ] ;

        Auth.LowPart = (ULONG)iSid->IdentifierAuthority.Value[5]          +
                       (ULONG)(iSid->IdentifierAuthority.Value[4] <<  8)  +
                       (ULONG)(iSid->IdentifierAuthority.Value[3] << 16)  +
                       (ULONG)(iSid->IdentifierAuthority.Value[2] << 24);

        Status = RtlLargeIntegerToUnicode(
                        &Auth,
                        16,
                        256 - (LONG) (Offset - UniBuffer),
                        Offset );


    } else {

        Tmp = (ULONG)iSid->IdentifierAuthority.Value[5]          +
              (ULONG)(iSid->IdentifierAuthority.Value[4] <<  8)  +
              (ULONG)(iSid->IdentifierAuthority.Value[3] << 16)  +
              (ULONG)(iSid->IdentifierAuthority.Value[2] << 24);

        Status = RtlIntegerToUnicode(
                        Tmp,
                        10,
                        256 - (LONG) (Offset - UniBuffer),
                        Offset );

    }

    if ( !NT_SUCCESS( Status ) )
    {
        return Status ;
    }


    for (i=0;i<iSid->SubAuthorityCount ;i++ ) {

        while ( *Offset && ( Offset < &UniBuffer[ 255 ] ) )
        {
            Offset++ ;
        }

        *Offset++ = L'-' ;

        Status = RtlIntegerToUnicode(
                        iSid->SubAuthority[ i ],
                        10,
                        256 - (LONG) (Offset - UniBuffer),
                        Offset );

        if ( !NT_SUCCESS( Status ) )
        {
            return Status ;
        }
    }

    if ( AllocateDestinationString )
    {
        if ( RtlCreateUnicodeString( UnicodeString,
                                         UniBuffer ) )
        {
            Status = STATUS_SUCCESS ;
        }
        else
        {
            Status = STATUS_NO_MEMORY ;
        }

    }
    else
    {

        while ( *Offset && ( Offset < &UniBuffer[ 255 ] ) )
        {
            Offset++ ;
        }

        Tmp = (ULONG) (Offset - UniBuffer) * sizeof( WCHAR );

        if ( Tmp < UnicodeString->MaximumLength )
        {
            LocalString.Length = (USHORT) Tmp ;
            LocalString.MaximumLength = LocalString.Length + sizeof( WCHAR );
            LocalString.Buffer = UniBuffer ;

            RtlCopyUnicodeString(
                        UnicodeString,
                        &LocalString );

            Status = STATUS_SUCCESS ;
        }
        else
        {
            Status = STATUS_BUFFER_OVERFLOW ;
        }

    }

    return(Status);
}




BOOLEAN
RtlEqualLuid (
    IN PLUID Luid1,
    IN PLUID Luid2
    )

/*++

Routine Description:

    This procedure test two LUID values for equality.

    This routine is here for backwards compatibility only. New code
    should use the macro.

Arguments:

    Luid1, Luid2 - Supply pointers to the two LUID values to compare.

Return Value:

    BOOLEAN - TRUE if the value of Luid1 is equal to Luid2, and FALSE
        otherwise.


--*/

{
    LUID UNALIGNED * TempLuid1;
    LUID UNALIGNED * TempLuid2;

    RTL_PAGED_CODE();

    return((Luid1->HighPart == Luid2->HighPart) &&
           (Luid1->LowPart  == Luid2->LowPart));

}


VOID
RtlCopyLuid (
    OUT PLUID DestinationLuid,
    IN PLUID SourceLuid
    )

/*++

Routine Description:

    This routine copies the value of the source LUID to the
    destination LUID.

Arguments:

    DestinationLuid - Receives a copy of the source Luid value.

    SourceLuid - Supplies the Luid value to be copied.  This LUID is
                 assumed to be structurally valid.

Return Value:

    None.

--*/

{
    RTL_PAGED_CODE();

    (*DestinationLuid) = (*SourceLuid);
    return;
}

VOID
RtlCopyLuidAndAttributesArray (
    IN ULONG ArrayLength,
    IN PLUID_AND_ATTRIBUTES Source,
    OUT PLUID_AND_ATTRIBUTES Target
    )

/*++

Routine Description:

    This routine copies the value of the source LUID_AND_ATTRIBUTES array
    to the target.

Arguments:

    ArrayLength - Number of elements in the source array to copy.

    Source - The source array.

    Target - Indicates where the array elements are to be copied to.


Return Value:

    None.


--*/

{

    ULONG Index = 0;

    RTL_PAGED_CODE();

    while (Index < ArrayLength) {

        Target[Index] = Source[Index];

        Index += 1;

    } //end_while


    return;

}

NTSTATUS
RtlCreateSecurityDescriptor (
    IN PSECURITY_DESCRIPTOR SecurityDescriptor,
    IN ULONG Revision
    )

/*++

Routine Description:

    This procedure initializes a new "absolute format" security descriptor.
    After the procedure call the security descriptor is initialized with no
    system ACL, no discretionary ACL, no owner, no primary group and
    all control flags set to false (null).

Arguments:


    SecurityDescriptor - Supplies the security descriptor to
        initialize.

    Revision - Provides the revision level to assign to the security
        descriptor.  This should be one (1) for this release.

Return Value:

    STATUS_SUCCESS - Indicates the call completed successfully.

    STATUS_UNKNOWN_REVISION - Indicates the revision level provided
        is not supported by this routine.

--*/

{
    RTL_PAGED_CODE();

    //
    // Check the requested revision
    //

    if (Revision == SECURITY_DESCRIPTOR_REVISION) {

        //
        // Typecast to the opaque SECURITY_DESCRIPTOR structure.
        //

        SECURITY_DESCRIPTOR *ISecurityDescriptor = SecurityDescriptor;

        RtlZeroMemory( ISecurityDescriptor, sizeof(SECURITY_DESCRIPTOR));

        ISecurityDescriptor->Revision = SECURITY_DESCRIPTOR_REVISION;

        return STATUS_SUCCESS;
    }

    return STATUS_UNKNOWN_REVISION;
}


NTSTATUS
RtlCreateSecurityDescriptorRelative (
    IN PISECURITY_DESCRIPTOR_RELATIVE SecurityDescriptor,
    IN ULONG Revision
    )

/*++

Routine Description:

    This procedure initializes a new "relative format" security descriptor.
    After the procedure call the security descriptor is initialized with no
    system ACL, no discretionary ACL, no owner, no primary group and
    all control flags set to false (null).

Arguments:


    SecurityDescriptor - Supplies the security descriptor to
        initialize.

    Revision - Provides the revision level to assign to the security
        descriptor.  This should be one (1) for this release.

Return Value:

    STATUS_SUCCESS - Indicates the call completed successfully.

    STATUS_UNKNOWN_REVISION - Indicates the revision level provided
        is not supported by this routine.

Note:
    Warning, this code assume the caller allocated a relative security
    descriptor rather than a relative one.  Absolute is larger on systems
    with 64-bit pointers.

--*/

{
    RTL_PAGED_CODE();

    //
    // Check the requested revision
    //

    if (Revision == SECURITY_DESCRIPTOR_REVISION) {

        //
        // Typecast to the opaque SECURITY_DESCRIPTOR structure.
        //

        RtlZeroMemory( SecurityDescriptor, sizeof(SECURITY_DESCRIPTOR_RELATIVE));

        SecurityDescriptor->Revision = SECURITY_DESCRIPTOR_REVISION;

        return STATUS_SUCCESS;
    }

    return STATUS_UNKNOWN_REVISION;
}


BOOLEAN
RtlValidSecurityDescriptor (
    IN PSECURITY_DESCRIPTOR SecurityDescriptor
    )

/*++

Routine Description:

    This procedure validates a SecurityDescriptor's structure.  This
    involves validating the revision levels of each component of the
    security descriptor.

Arguments:

    SecurityDescriptor - Pointer to the SECURITY_DESCRIPTOR structure
        to validate.

Return Value:

    BOOLEAN - TRUE if the structure of SecurityDescriptor is valid.


--*/

{
    PSID Owner;
    PSID Group;
    PACL Dacl;
    PACL Sacl;

    //
    // Typecast to the opaque SECURITY_DESCRIPTOR structure.
    //

    SECURITY_DESCRIPTOR *ISecurityDescriptor = SecurityDescriptor;

    RTL_PAGED_CODE();

    try {

        //
        // known revision ?
        //

        if (ISecurityDescriptor->Revision != SECURITY_DESCRIPTOR_REVISION) {
            return FALSE;
        }


        //
        // Validate each element contained in the security descriptor
        //

        Owner = RtlpOwnerAddrSecurityDescriptor( ISecurityDescriptor );

        if (Owner != NULL) {
            if (!RtlValidSid( Owner )) {
                return FALSE;
            }
        }

        Group = RtlpGroupAddrSecurityDescriptor( ISecurityDescriptor );

        if (Group != NULL) {
            if (!RtlValidSid( Group )) {
                return FALSE;
            }
        }

        Dacl = RtlpDaclAddrSecurityDescriptor( ISecurityDescriptor );
        if (Dacl != NULL ) {

            if (!RtlValidAcl( Dacl )) {
                return FALSE;
            }
        }

        Sacl = RtlpSaclAddrSecurityDescriptor( ISecurityDescriptor );
        if ( Sacl != NULL ) {
            if (!RtlValidAcl( Sacl )) {
                return FALSE;
            }
        }

    } except(EXCEPTION_EXECUTE_HANDLER) {
        return FALSE;
    }

    //
    // All components are valid
    //

    return TRUE;


}


ULONG
RtlLengthSecurityDescriptor (
    IN PSECURITY_DESCRIPTOR SecurityDescriptor
    )

/*++

Routine Description:

    This routine returns the length, in bytes, necessary to capture a
    structurally valid SECURITY_DESCRIPTOR.  The length includes the length
    of all associated data structures (like SIDs and ACLs).  The length also
    takes into account the alignment requirements of each component.

    The minimum length of a security descriptor (one which has no associated
    SIDs or ACLs) is SECURITY_DESCRIPTOR_MIN_LENGTH.


Arguments:

    SecurityDescriptor - Points to the SECURITY_DESCRIPTOR whose
        length is to be returned.  The SECURITY_DESCRIPTOR's
        structure is assumed to be valid.

Return Value:

    ULONG - The length, in bytes, of the SECURITY_DESCRIPTOR.


--*/

{
    ULONG sum;
    PVOID Temp;


    //
    // Typecast to the opaque SECURITY_DESCRIPTOR structure.
    //

    SECURITY_DESCRIPTOR *ISecurityDescriptor = (SECURITY_DESCRIPTOR *)SecurityDescriptor;

    RTL_PAGED_CODE();

    //
    // The length is the sum of the following:
    //
    //       SECURITY_DESCRIPTOR_MIN_LENGTH (or sizeof(SECURITY_DESCRIPTOR))
    //       length of Owner SID (if present)
    //       length of Group SID (if present)
    //       length of Discretionary ACL (if present and non-null)
    //       length of System ACL (if present and non-null)
    //

    sum = ISecurityDescriptor->Control & SE_SELF_RELATIVE ?
                            sizeof(SECURITY_DESCRIPTOR_RELATIVE) :
                            sizeof(SECURITY_DESCRIPTOR);

    //
    // Add in length of Owner SID
    //

    Temp = RtlpOwnerAddrSecurityDescriptor(ISecurityDescriptor);
    if (Temp != NULL) {
        sum += LongAlignSize(SeLengthSid(Temp));
    }

    //
    // Add in length of Group SID
    //

    Temp = RtlpGroupAddrSecurityDescriptor(ISecurityDescriptor);
    if (Temp != NULL) {
        sum += LongAlignSize(SeLengthSid(Temp));
    }

    //
    // Add in used length of Discretionary ACL
    //

    Temp = RtlpDaclAddrSecurityDescriptor(ISecurityDescriptor);
    if ( Temp != NULL ) {

        sum += LongAlignSize(((PACL) Temp)->AclSize );
    }

    //
    // Add in used length of System Acl
    //

    Temp = RtlpSaclAddrSecurityDescriptor(ISecurityDescriptor);
    if ( Temp != NULL ) {

        sum += LongAlignSize(((PACL) Temp)->AclSize );
    }

    return sum;
}


NTSTATUS
RtlSetAttributesSecurityDescriptor(
    IN PSECURITY_DESCRIPTOR SecurityDescriptor,
    IN SECURITY_DESCRIPTOR_CONTROL Control,
    OUT PULONG Revision
    )
{
    RTL_PAGED_CODE();

    //
    // Always return the revision value - even if this isn't a valid
    // security descriptor
    //

    *Revision = ((SECURITY_DESCRIPTOR *)SecurityDescriptor)->Revision;

    if ( ((SECURITY_DESCRIPTOR *)SecurityDescriptor)->Revision
         != SECURITY_DESCRIPTOR_REVISION ) {
        return STATUS_UNKNOWN_REVISION;
    }

    // This is a worthless API.  There is no way to turn any of the bits off.
    // Use the newer RtlSetControlSecurityDescriptor.
    Control &= SE_VALID_CONTROL_BITS;
    return RtlSetControlSecurityDescriptor ( SecurityDescriptor, Control, Control );
}



NTSTATUS
RtlGetControlSecurityDescriptor (
    IN PSECURITY_DESCRIPTOR SecurityDescriptor,
    OUT PSECURITY_DESCRIPTOR_CONTROL Control,
    OUT PULONG Revision
    )

/*++

Routine Description:

    This procedure retrieves the control information from a security descriptor.

Arguments:

    SecurityDescriptor - Supplies the security descriptor.

    Control - Receives the control information.

    Revision - Receives the revision of the security descriptor.
               This value will always be returned, even if an error
               is returned by this routine.

Return Value:

    STATUS_SUCCESS - Indicates the call completed successfully.

    STATUS_UNKNOWN_REVISION - Indicates the revision of the security
        descriptor is not known to the routine.  It may be a newer
        revision than the routine knows about.


--*/

{
    RTL_PAGED_CODE();

    //
    // Always return the revision value - even if this isn't a valid
    // security descriptor
    //

    *Revision = ((SECURITY_DESCRIPTOR *)SecurityDescriptor)->Revision;


    if ( ((SECURITY_DESCRIPTOR *)SecurityDescriptor)->Revision
         != SECURITY_DESCRIPTOR_REVISION ) {
        return STATUS_UNKNOWN_REVISION;
    }


    *Control = ((SECURITY_DESCRIPTOR *)SecurityDescriptor)->Control;

    return STATUS_SUCCESS;

}

NTSTATUS
RtlSetControlSecurityDescriptor (
    IN PSECURITY_DESCRIPTOR pSecurityDescriptor,
    IN SECURITY_DESCRIPTOR_CONTROL ControlBitsOfInterest,
    IN SECURITY_DESCRIPTOR_CONTROL ControlBitsToSet
    )
/*++

Routine Description:

    This procedure sets the control information in a security descriptor.


    For instance,

        SetSecurityDescriptorControl( &SecDesc,
                                      SE_DACL_PROTECTED,
                                      SE_DACL_PROTECTED );

    marks the DACL on the security descriptor as protected. And

        SetSecurityDescriptorControl( &SecDesc,
                                      SE_DACL_PROTECTED,
                                      0 );


    marks the DACL as not protected.

Arguments:

    pSecurityDescriptor - Supplies the security descriptor.

    ControlBitsOfInterest - A mask of the control bits being changed, set,
        or reset by this call.  The mask is the logical OR of one or more of
        the following flags:

            SE_DACL_UNTRUSTED
            SE_SERVER_SECURITY
            SE_DACL_AUTO_INHERIT_REQ
            SE_SACL_AUTO_INHERIT_REQ
            SE_DACL_AUTO_INHERITED
            SE_SACL_AUTO_INHERITED
            SE_DACL_PROTECTED
            SE_SACL_PROTECTED

    ControlBitsToSet - A mask indicating what the bits specified by ControlBitsOfInterest
        should be set to.

Return Value:

    Returns TRUE for success, FALSE for failure.  Extended error status
    is available using GetLastError.

--*/
{
    //
    // Ensure the caller passed valid bits.
    //

    if ( (ControlBitsOfInterest & ~SE_VALID_CONTROL_BITS) != 0 ||
         (ControlBitsToSet & ~ControlBitsOfInterest) != 0 ) {
        return STATUS_INVALID_PARAMETER;
    }

    ((SECURITY_DESCRIPTOR *)pSecurityDescriptor)->Control &= ~ControlBitsOfInterest;
    ((SECURITY_DESCRIPTOR *)pSecurityDescriptor)->Control |= ControlBitsToSet;

    return STATUS_SUCCESS;
}


NTSTATUS
RtlSetDaclSecurityDescriptor (
    IN PSECURITY_DESCRIPTOR SecurityDescriptor,
    IN BOOLEAN DaclPresent,
    IN PACL Dacl OPTIONAL,
    IN BOOLEAN DaclDefaulted OPTIONAL
    )

/*++

Routine Description:

    This procedure sets the discretionary ACL information of an absolute
    format security descriptor.  If there is already a discretionary ACL
    present in the security descriptor, it is superseded.

Arguments:

    SecurityDescriptor - Supplies the security descriptor to be which
        the discretionary ACL is to be added.

    DaclPresent - If FALSE, indicates the DaclPresent flag in the
        security descriptor should be set to FALSE.  In this case,
        the remaining optional parameters are ignored.  Otherwise,
        the DaclPresent control flag in the security descriptor is
        set to TRUE and the remaining optional parameters are not
        ignored.

    Dacl - Supplies the discretionary ACL for the security
        descriptor.  If this optional parameter is not passed, then a
        null ACL is assigned to the security descriptor.  A null
        discretionary ACL unconditionally grants access.  The ACL is
        referenced by, not copied into, by the security descriptor.

    DaclDefaulted - When set, indicates the discretionary ACL was
        picked up from some default mechanism (rather than explicitly
        specified by a user).  This value is set in the DaclDefaulted
        control flag in the security descriptor.  If this optional
        parameter is not passed, then the DaclDefaulted flag will be
        cleared.

Return Value:

    STATUS_SUCCESS - Indicates the call completed successfully.

    STATUS_UNKNOWN_REVISION - Indicates the revision of the security
        descriptor is not known to the routine.  It may be a newer
        revision than the routine knows about.

    STATUS_INVALID_SECURITY_DESCR - Indicates the security descriptor
        is not an absolute format security descriptor.


--*/

{

    //
    // Typecast to the opaque SECURITY_DESCRIPTOR structure.
    //

    SECURITY_DESCRIPTOR *ISecurityDescriptor = SecurityDescriptor;

    RTL_PAGED_CODE();

    //
    // Check the revision
    //

    if (ISecurityDescriptor->Revision != SECURITY_DESCRIPTOR_REVISION) {
       return STATUS_UNKNOWN_REVISION;
    }

    //
    // Make sure the descriptor is absolute format
    //

    if (ISecurityDescriptor->Control & SE_SELF_RELATIVE) {
        return STATUS_INVALID_SECURITY_DESCR;
    }

    //
    // Assign the DaclPresent flag value passed
    //


    if (DaclPresent) {

        ISecurityDescriptor->Control |= SE_DACL_PRESENT;

        //
        // Assign the ACL address if passed, otherwise set to null.
        //

        ISecurityDescriptor->Dacl = NULL;
        if (ARGUMENT_PRESENT(Dacl)) {
            ISecurityDescriptor->Dacl = Dacl;
        }




        //
        // Assign DaclDefaulted flag if passed, otherwise clear it.
        //

        ISecurityDescriptor->Control &= ~SE_DACL_DEFAULTED;
        if (DaclDefaulted == TRUE) {
            ISecurityDescriptor->Control |= SE_DACL_DEFAULTED;
        }
    } else {

        ISecurityDescriptor->Control &= ~SE_DACL_PRESENT;

    }


    return STATUS_SUCCESS;

}


NTSTATUS
RtlGetDaclSecurityDescriptor (
    IN PSECURITY_DESCRIPTOR SecurityDescriptor,
    OUT PBOOLEAN DaclPresent,
    OUT PACL *Dacl,
    OUT PBOOLEAN DaclDefaulted
    )

/*++

Routine Description:

    This procedure retrieves the discretionary ACL information of a
    security descriptor.

Arguments:

    SecurityDescriptor - Supplies the security descriptor.

    DaclPresent - If TRUE, indicates that the security descriptor
        does contain a discretionary ACL.  In this case, the
        remaining OUT parameters will receive valid values.
        Otherwise, the security descriptor does not contain a
        discretionary ACL and the remaining OUT parameters will not
        receive valid values.

    Dacl - This value is returned only if the value returned for the
        DaclPresent flag is TRUE.  In this case, the Dacl parameter
        receives the address of the security descriptor's
        discretionary ACL.  If this value is returned as null, then
        the security descriptor has a null discretionary ACL.

    DaclDefaulted - This value is returned only if the value returned
        for the DaclPresent flag is TRUE.  In this case, the
        DaclDefaulted parameter receives the value of the security
        descriptor's DaclDefaulted control flag.

Return Value:

    STATUS_SUCCESS - Indicates the call completed successfully.

    STATUS_UNKNOWN_REVISION - Indicates the revision of the security
        descriptor is not known to the routine.  It may be a newer
        revision than the routine knows about.


--*/

{
    //
    // Typecast to the opaque SECURITY_DESCRIPTOR structure.
    //

    SECURITY_DESCRIPTOR *ISecurityDescriptor = SecurityDescriptor;

    RTL_PAGED_CODE();

    //
    // Check the revision
    //

    if (ISecurityDescriptor->Revision != SECURITY_DESCRIPTOR_REVISION) {
        return STATUS_UNKNOWN_REVISION;
    }

    //
    // Assign the DaclPresent flag value
    //

    *DaclPresent = RtlpAreControlBitsSet( ISecurityDescriptor, SE_DACL_PRESENT );

    if (*DaclPresent) {

        //
        // Assign the ACL address.
        //

        *Dacl = RtlpDaclAddrSecurityDescriptor(ISecurityDescriptor);

        //
        // Assign DaclDefaulted flag.
        //

        *DaclDefaulted = RtlpAreControlBitsSet( ISecurityDescriptor, SE_DACL_DEFAULTED );
    }

    return STATUS_SUCCESS;

}


NTSTATUS
RtlSetSaclSecurityDescriptor (
    IN PSECURITY_DESCRIPTOR SecurityDescriptor,
    IN BOOLEAN SaclPresent,
    IN PACL Sacl OPTIONAL,
    IN BOOLEAN SaclDefaulted OPTIONAL
    )

/*++

Routine Description:

    This procedure sets the system ACL information of an absolute security
    descriptor.  If there is already a system ACL present in the
    security descriptor, it is superseded.

Arguments:

    SecurityDescriptor - Supplies the security descriptor to be which
        the system ACL is to be added.

    SaclPresent - If FALSE, indicates the SaclPresent flag in the
        security descriptor should be set to FALSE.  In this case,
        the remaining optional parameters are ignored.  Otherwise,
        the SaclPresent control flag in the security descriptor is
        set to TRUE and the remaining optional parameters are not
        ignored.

    Sacl - Supplies the system ACL for the security descriptor.  If
        this optional parameter is not passed, then a null ACL is
        assigned to the security descriptor.  The ACL is referenced
        by, not copied into, by the security descriptor.

    SaclDefaulted - When set, indicates the system ACL was picked up
        from some default mechanism (rather than explicitly specified
        by a user).  This value is set in the SaclDefaulted control
        flag in the security descriptor.  If this optional parameter
        is not passed, then the SaclDefaulted flag will be cleared.

Return Value:

    STATUS_SUCCESS - Indicates the call completed successfully.

    STATUS_UNKNOWN_REVISION - Indicates the revision of the security
        descriptor is not known to the routine.  It may be a newer
        revision than the routine knows about.

    STATUS_INVALID_SECURITY_DESCR - Indicates the security descriptor
        is not an absolute format security descriptor.


--*/

{

    //
    // Typecast to the opaque SECURITY_DESCRIPTOR structure.
    //

    SECURITY_DESCRIPTOR *ISecurityDescriptor = SecurityDescriptor;

    RTL_PAGED_CODE();

    //
    // Check the revision
    //

    if (ISecurityDescriptor->Revision != SECURITY_DESCRIPTOR_REVISION) {
        return STATUS_UNKNOWN_REVISION;
    }

    //
    // Make sure the descriptor is absolute format
    //

    if (ISecurityDescriptor->Control & SE_SELF_RELATIVE) {
        return STATUS_INVALID_SECURITY_DESCR;
    }

    //
    // Assign the SaclPresent flag value passed
    //


    if (SaclPresent) {

        ISecurityDescriptor->Control |= SE_SACL_PRESENT;

        //
        // Assign the ACL address if passed, otherwise set to null.
        //

        ISecurityDescriptor->Sacl = NULL;
        if (ARGUMENT_PRESENT(Sacl)) {
           ISecurityDescriptor->Sacl = Sacl;
        }

        //
        // Assign SaclDefaulted flag if passed, otherwise clear it.
        //

        ISecurityDescriptor->Control &= ~ SE_SACL_DEFAULTED;
        if (ARGUMENT_PRESENT(SaclDefaulted)) {
            ISecurityDescriptor->Control |= SE_SACL_DEFAULTED;
        }
    } else {

        ISecurityDescriptor->Control &= ~SE_SACL_PRESENT;
    }

    return STATUS_SUCCESS;

}


NTSTATUS
RtlGetSaclSecurityDescriptor (
    IN PSECURITY_DESCRIPTOR SecurityDescriptor,
    OUT PBOOLEAN SaclPresent,
    OUT PACL *Sacl,
    OUT PBOOLEAN SaclDefaulted
    )

/*++

Routine Description:

    This procedure retrieves the system ACL information of a security
    descriptor.

Arguments:

    SecurityDescriptor - Supplies the security descriptor.

    SaclPresent - If TRUE, indicates that the security descriptor
        does contain a system ACL.  In this case, the remaining OUT
        parameters will receive valid values.  Otherwise, the
        security descriptor does not contain a system ACL and the
        remaining OUT parameters will not receive valid values.

    Sacl - This value is returned only if the value returned for the
        SaclPresent flag is TRUE.  In this case, the Sacl parameter
        receives the address of the security descriptor's system ACL.
        If this value is returned as null, then the security
        descriptor has a null system ACL.

    SaclDefaulted - This value is returned only if the value returned
        for the SaclPresent flag is TRUE.  In this case, the
        SaclDefaulted parameter receives the value of the security
        descriptor's SaclDefaulted control flag.

Return Value:

    STATUS_SUCCESS - Indicates the call completed successfully.

    STATUS_UNKNOWN_REVISION - Indicates the revision of the security
        descriptor is not known to the routine.  It may be a newer
        revision than the routine knows about.


--*/

{

    //
    // Typecast to the opaque SECURITY_DESCRIPTOR structure.
    //

    SECURITY_DESCRIPTOR *ISecurityDescriptor = SecurityDescriptor;

    RTL_PAGED_CODE();

    //
    // Check the revision
    //

    if (ISecurityDescriptor->Revision != SECURITY_DESCRIPTOR_REVISION) {
        return STATUS_UNKNOWN_REVISION;
    }

    //
    // Assign the SaclPresent flag value
    //

    *SaclPresent = RtlpAreControlBitsSet( ISecurityDescriptor, SE_SACL_PRESENT );

    if (*SaclPresent) {

        //
        // Assign the ACL address.
        //

        *Sacl = RtlpSaclAddrSecurityDescriptor(ISecurityDescriptor);

        //
        // Assign SaclDefaulted flag.
        //

        *SaclDefaulted = RtlpAreControlBitsSet( ISecurityDescriptor, SE_SACL_DEFAULTED );

    }

    return STATUS_SUCCESS;

}


NTSTATUS
RtlSetOwnerSecurityDescriptor (
    IN PSECURITY_DESCRIPTOR SecurityDescriptor,
    IN PSID Owner OPTIONAL,
    IN BOOLEAN OwnerDefaulted OPTIONAL
    )

/*++

Routine Description:

    This procedure sets the owner information of an absolute security
    descriptor.  If there is already an owner present in the security
    descriptor, it is superseded.

Arguments:

    SecurityDescriptor - Supplies the security descriptor in which
        the owner is to be set.  If the security descriptor already
        includes an owner, it will be superseded by the new owner.

    Owner - Supplies the owner SID for the security descriptor.  If
        this optional parameter is not passed, then the owner is
        cleared (indicating the security descriptor has no owner).
        The SID is referenced by, not copied into, the security
        descriptor.

    OwnerDefaulted - When set, indicates the owner was picked up from
        some default mechanism (rather than explicitly specified by a
        user).  This value is set in the OwnerDefaulted control flag
        in the security descriptor.  If this optional parameter is
        not passed, then the SaclDefaulted flag will be cleared.

Return Value:

    STATUS_SUCCESS - Indicates the call completed successfully.

    STATUS_UNKNOWN_REVISION - Indicates the revision of the security
        descriptor is not known to the routine.  It may be a newer
        revision than the routine knows about.

    STATUS_INVALID_SECURITY_DESCR - Indicates the security descriptor
        is not an absolute format security descriptor.


--*/

{

    //
    // Typecast to the opaque SECURITY_DESCRIPTOR structure.
    //

    SECURITY_DESCRIPTOR *ISecurityDescriptor = SecurityDescriptor;

    RTL_PAGED_CODE();

    //
    // Check the revision
    //

    if (ISecurityDescriptor->Revision != SECURITY_DESCRIPTOR_REVISION) {
        return STATUS_UNKNOWN_REVISION;
    }

    //
    // Make sure the descriptor is absolute format
    //

    if (ISecurityDescriptor->Control & SE_SELF_RELATIVE) {
        return STATUS_INVALID_SECURITY_DESCR;
    }

    //
    // Assign the Owner field if passed, otherwise clear it.
    //

    ISecurityDescriptor->Owner = NULL;
    if (ARGUMENT_PRESENT(Owner)) {
        ISecurityDescriptor->Owner = Owner;
    }

    //
    // Assign the OwnerDefaulted flag if passed, otherwise clear it.
    //

    ISecurityDescriptor->Control &= ~SE_OWNER_DEFAULTED;
    if (OwnerDefaulted == TRUE) {
        ISecurityDescriptor->Control |= SE_OWNER_DEFAULTED;
    }

    return STATUS_SUCCESS;

}


NTSTATUS
RtlGetOwnerSecurityDescriptor (
    IN PSECURITY_DESCRIPTOR SecurityDescriptor,
    OUT PSID *Owner,
    OUT PBOOLEAN OwnerDefaulted
    )

/*++

Routine Description:

    This procedure retrieves the owner information of a security
    descriptor.

Arguments:

    SecurityDescriptor - Supplies the security descriptor.

    Owner - Receives a pointer to the owner SID.  If the security
        descriptor does not currently contain an owner, then this
        value will be returned as null.  In this case, the remaining
        OUT parameters are not given valid return values.  Otherwise,
        this parameter points to an SID and the remaining OUT
        parameters are provided valid return values.

    OwnerDefaulted - This value is returned only if the value
        returned for the Owner parameter is not null.  In this case,
        the OwnerDefaulted parameter receives the value of the
        security descriptor's OwnerDefaulted control flag.

Return Value:

    STATUS_SUCCESS - Indicates the call completed successfully.

    STATUS_UNKNOWN_REVISION - Indicates the revision of the security
        descriptor is not known to the routine.  It may be a newer
        revision than the routine knows about.


--*/

{

    //
    // Typecast to the opaque SECURITY_DESCRIPTOR structure.
    //

    SECURITY_DESCRIPTOR *ISecurityDescriptor = SecurityDescriptor;

    RTL_PAGED_CODE();

    //
    // Check the revision
    //

    if (ISecurityDescriptor->Revision != SECURITY_DESCRIPTOR_REVISION) {
        return STATUS_UNKNOWN_REVISION;
    }

    //
    // Return the Owner field value.
    //

    *Owner = RtlpOwnerAddrSecurityDescriptor(ISecurityDescriptor);

    //
    // Return the OwnerDefaulted flag value.
    //

    *OwnerDefaulted = RtlpAreControlBitsSet( ISecurityDescriptor, SE_OWNER_DEFAULTED );

    return STATUS_SUCCESS;

}


NTSTATUS
RtlSetGroupSecurityDescriptor (
    IN PSECURITY_DESCRIPTOR SecurityDescriptor,
    IN PSID Group OPTIONAL,
    IN BOOLEAN GroupDefaulted OPTIONAL
    )

/*++

Routine Description:

    This procedure sets the primary group information of an absolute security
    descriptor.  If there is already an primary group present in the
    security descriptor, it is superseded.

Arguments:

    SecurityDescriptor - Supplies the security descriptor in which
        the primary group is to be set.  If the security descriptor
        already includes a primary group, it will be superseded by
        the new group.

    Group - Supplies the primary group SID for the security
        descriptor.  If this optional parameter is not passed, then
        the primary group is cleared (indicating the security
        descriptor has no primary group).  The SID is referenced by,
        not copied into, the security descriptor.

    GroupDefaulted - When set, indicates the owner was picked up from
        some default mechanism (rather than explicitly specified by a
        user).  This value is set in the OwnerDefaulted control flag
        in the security descriptor.  If this optional parameter is
        not passed, then the SaclDefaulted flag will be cleared.

Return Value:

    STATUS_SUCCESS - Indicates the call completed successfully.

    STATUS_UNKNOWN_REVISION - Indicates the revision of the security
        descriptor is not known to the routine.  It may be a newer
        revision than the routine knows about.

    STATUS_INVALID_SECURITY_DESCR - Indicates the security descriptor
        is not an absolute format security descriptor.


--*/

{

    //
    // Typecast to the opaque SECURITY_DESCRIPTOR structure.
    //

    SECURITY_DESCRIPTOR *ISecurityDescriptor = SecurityDescriptor;

    RTL_PAGED_CODE();

    //
    // Check the revision
    //

    if (ISecurityDescriptor->Revision != SECURITY_DESCRIPTOR_REVISION) {
        return STATUS_UNKNOWN_REVISION;
    }

    //
    // Make sure the descriptor is absolute format
    //

    if (ISecurityDescriptor->Control & SE_SELF_RELATIVE) {
        return STATUS_INVALID_SECURITY_DESCR;
    }

    //
    // Assign the Group field if passed, otherwise clear it.
    //

    ISecurityDescriptor->Group = NULL;
    if (ARGUMENT_PRESENT(Group)) {
        ISecurityDescriptor->Group = Group;
    }

    //
    // Assign the GroupDefaulted flag if passed, otherwise clear it.
    //

    ISecurityDescriptor->Control &= ~SE_GROUP_DEFAULTED;
    if (ARGUMENT_PRESENT(GroupDefaulted)) {
        ISecurityDescriptor->Control |= SE_GROUP_DEFAULTED;
    }

    return STATUS_SUCCESS;

}


NTSTATUS
RtlGetGroupSecurityDescriptor (
    IN PSECURITY_DESCRIPTOR SecurityDescriptor,
    OUT PSID *Group,
    OUT PBOOLEAN GroupDefaulted
    )

/*++

Routine Description:

    This procedure retrieves the primary group information of a
    security descriptor.

Arguments:

    SecurityDescriptor - Supplies the security descriptor.

    Group - Receives a pointer to the primary group SID.  If the
        security descriptor does not currently contain a primary
        group, then this value will be returned as null.  In this
        case, the remaining OUT parameters are not given valid return
        values.  Otherwise, this parameter points to an SID and the
        remaining OUT parameters are provided valid return values.

    GroupDefaulted - This value is returned only if the value
        returned for the Group parameter is not null.  In this case,
        the GroupDefaulted parameter receives the value of the
        security descriptor's GroupDefaulted control flag.

Return Value:

    STATUS_SUCCESS - Indicates the call completed successfully.

    STATUS_UNKNOWN_REVISION - Indicates the revision of the security
        descriptor is not known to the routine.  It may be a newer
        revision than the routine knows about.


--*/

{

    //
    // Typecast to the opaque SECURITY_DESCRIPTOR structure.
    //

    SECURITY_DESCRIPTOR *ISecurityDescriptor =
        (SECURITY_DESCRIPTOR *)SecurityDescriptor;

    RTL_PAGED_CODE();

    //
    // Check the revision
    //

    if (ISecurityDescriptor->Revision != SECURITY_DESCRIPTOR_REVISION) {
        return STATUS_UNKNOWN_REVISION;
    }

    //
    // Return the Group field value.
    //

    *Group = RtlpGroupAddrSecurityDescriptor(ISecurityDescriptor);

    //
    // Return the GroupDefaulted flag value.
    //

    *GroupDefaulted = RtlpAreControlBitsSet( ISecurityDescriptor, SE_GROUP_DEFAULTED );

    return STATUS_SUCCESS;

}


BOOLEAN
RtlAreAllAccessesGranted(
    IN ACCESS_MASK GrantedAccess,
    IN ACCESS_MASK DesiredAccess
    )

/*++

Routine Description:

    This routine is used to check a desired access mask against a
    granted access mask.  It is used by the Object Management
    component when dereferencing a handle.

Arguments:

        GrantedAccess - Specifies the granted access mask.

        DesiredAccess - Specifies the desired access mask.

Return Value:

    BOOLEAN - TRUE if the GrantedAccess mask has all the bits set
        that the DesiredAccess mask has set.  That is, TRUE is
        returned if all of the desired accesses have been granted.

--*/

{
    RTL_PAGED_CODE();

    return ((BOOLEAN)((~(GrantedAccess) & (DesiredAccess)) == 0));
}


BOOLEAN
RtlAreAnyAccessesGranted(
    IN ACCESS_MASK GrantedAccess,
    IN ACCESS_MASK DesiredAccess
    )

/*++

Routine Description:

    This routine is used to test whether any of a set of desired
    accesses are granted by a granted access mask.  It is used by
    components other than the the Object Management component for
    checking access mask subsets.

Arguments:

        GrantedAccess - Specifies the granted access mask.

        DesiredAccess - Specifies the desired access mask.

Return Value:

    BOOLEAN - TRUE if the GrantedAccess mask contains any of the bits
        specified in the DesiredAccess mask.  That is, if any of the
        desired accesses have been granted, TRUE is returned.


--*/

{
    RTL_PAGED_CODE();

    return ((BOOLEAN)(((GrantedAccess) & (DesiredAccess)) != 0));
}


VOID
RtlMapGenericMask(
    IN OUT PACCESS_MASK AccessMask,
    IN PGENERIC_MAPPING GenericMapping
    )

/*++

Routine Description:

    This routine maps all generic accesses in the provided access mask
    to specific and standard accesses according to the provided
    GenericMapping.

Arguments:

        AccessMask - Points to the access mask to be mapped.

        GenericMapping - The mapping of generic to specific and standard
                         access types.

Return Value:

    None.

--*/

{
    RTL_PAGED_CODE();

//    //
//    // Make sure the pointer is properly aligned
//    //
//

    if (*AccessMask & GENERIC_READ) {

        *AccessMask |= GenericMapping->GenericRead;
    }

    if (*AccessMask & GENERIC_WRITE) {

        *AccessMask |= GenericMapping->GenericWrite;
    }

    if (*AccessMask & GENERIC_EXECUTE) {

        *AccessMask |= GenericMapping->GenericExecute;
    }

    if (*AccessMask & GENERIC_ALL) {

        *AccessMask |= GenericMapping->GenericAll;
    }

    //
    // Now clear the generic flags
    //

    *AccessMask &= ~(GENERIC_READ | GENERIC_WRITE | GENERIC_EXECUTE | GENERIC_ALL);

    return;
}

NTSTATUS
RtlpImpersonateSelfEx (
    IN SECURITY_IMPERSONATION_LEVEL ImpersonationLevel,
    IN ACCESS_MASK AdditionalAccess,
    OUT PHANDLE ThreadToken OPTIONAL
    )

/*++

Routine Description:

    This routine may be used to obtain an Impersonation token representing
    your own process's context.  This may be useful for enabling a privilege
    for a single thread rather than for the entire process; or changing
    the default DACL for a single thread.

    The token is assigned to the callers thread.

    The caller may optionally request to receive a handle to the token assigned
    to the thread, obviating the need for a subsequent call solely to open the
    thread token.

Arguments:

    ImpersonationLevel - The level to make the impersonation token.

    AdditionalAccess - Additional requested access rights to the thread token 
        handle beyond TOKEN_IMPERSONATE. If an output handle is not specified, 
        this value must be zero.

    ThreadToken - Optional output pointer which receives a handle to the current 
        thread token upon successful completion of this routine.

Return Value:

    NTSTATUS.

--*/

{
    NTSTATUS Status, Status1;
    HANDLE Token1, Token2;
    OBJECT_ATTRIBUTES ObjectAttributes;
    SECURITY_QUALITY_OF_SERVICE Qos;

    RTL_PAGED_CODE ();

    if (!ARGUMENT_PRESENT (ThreadToken) && (AdditionalAccess != 0)) {
        return STATUS_INVALID_PARAMETER_2;
    }
    
    InitializeObjectAttributes (&ObjectAttributes, NULL, 0, 0, NULL);

    Qos.Length = sizeof (SECURITY_QUALITY_OF_SERVICE);
    Qos.ImpersonationLevel = ImpersonationLevel;
    Qos.ContextTrackingMode = SECURITY_DYNAMIC_TRACKING;
    Qos.EffectiveOnly = FALSE;
    ObjectAttributes.SecurityQualityOfService = &Qos;

    Status = NtOpenProcessToken (NtCurrentProcess(), TOKEN_DUPLICATE, &Token1);

    if (NT_SUCCESS(Status)) {
        Status = NtDuplicateToken (
                     Token1,
                     TOKEN_IMPERSONATE | AdditionalAccess,
                     &ObjectAttributes,
                     FALSE,                 //EffectiveOnly
                     TokenImpersonation,
                     &Token2
                     );
        
        if (NT_SUCCESS(Status)) {
            
            Status = NtSetInformationThread (
                         NtCurrentThread (),
                         ThreadImpersonationToken,
                         &Token2,
                         sizeof(HANDLE)
                         );

            if (NT_SUCCESS (Status) && ARGUMENT_PRESENT (ThreadToken)) {
                *ThreadToken = Token2;
            } else {
                Status1 = NtClose (Token2);
                NT_ASSERT (NT_SUCCESS (Status1));
            }
        }

        Status1 = NtClose (Token1);
        NT_ASSERT (NT_SUCCESS (Status1));
    }

    return Status;
}

NTSTATUS
RtlImpersonateSelf (
    IN SECURITY_IMPERSONATION_LEVEL ImpersonationLevel
    )

/*++

Routine Description:

    This routine may be used to obtain an Impersonation token representing
    your own process's context.  This may be useful for enabling a privilege
    for a single thread rather than for the entire process; or changing
    the default DACL for a single thread.

    N.B. This routine now simply invokes RtlpImpersonateSelfEx.
    
Arguments:

    ImpersonationLevel - The level to make the impersonation token.

Return Value:

    NSTATUS.

--*/

{
    RTL_PAGED_CODE ();

    return RtlpImpersonateSelfEx (ImpersonationLevel, 0, NULL);
}


VOID
RtlpApplyAclToObject (
    IN PACL Acl,
    IN PGENERIC_MAPPING GenericMapping
    )

/*++

Routine Description:

    This is a private routine that maps Access Masks of an ACL so that
    they are applicable to the object type the ACL is being applied to.

    Only known DSA ACEs are mapped.  Unknown ACE types are ignored.

    Only access types in the GenericAll mapping for the target object
    type will be non-zero upon return.

Arguments:

    Acl - Supplies the acl being applied.

    GenericMapping - Specifies the generic mapping to use.


Return Value:

    None.

--*/

{
    ULONG i;

    PACE_HEADER Ace;

    RTL_PAGED_CODE();

    //
    //  First check if the acl is null
    //

    if (Acl == NULL) {

        return;

    }


    //
    // Now walk the ACL, mapping each ACE as we go.
    //

    for (i = 0, Ace = FirstAce(Acl);
         i < Acl->AceCount;
         i += 1, Ace = NextAce(Ace)) {

        if (IsMSAceType( Ace )) {

            RtlApplyAceToObject( Ace, GenericMapping );
        }

    }

    return;
}


BOOLEAN
RtlpCopyEffectiveAce (
    IN PACE_HEADER OldAce,
    IN BOOLEAN AutoInherit,
    IN BOOLEAN WillGenerateInheritAce,
    IN PSID ClientOwnerSid,
    IN PSID ClientGroupSid,
    IN PSID ServerOwnerSid OPTIONAL,
    IN PSID ServerGroupSid OPTIONAL,
    IN PGENERIC_MAPPING GenericMapping,
    IN GUID **pNewObjectType OPTIONAL,
    IN ULONG GuidCount,
    IN OUT PVOID *AcePosition,
    OUT PULONG NewAceLength,
    OUT PACL NewAcl,
    OUT PBOOLEAN ObjectAceInherited OPTIONAL,
    OUT PBOOLEAN EffectiveAceMapped,
    OUT PBOOLEAN AclOverflowed
    )

/*++

Routine Description:

    This routine copy a specified ACE into an ACL as an effective ACE.
    The resultant ACE has all the inheritance bits turned of.
    The resultant ACE has the SID mapped from a generic SID to a specific SID
    (e.g., From "creator owner" to the passed in owner sid).

Arguments:

    OldAce - Supplies the ace being inherited

    AutoInherit - Specifies if the inheritance is an "automatic inheritance".
        As such, the inherited ACEs will be marked as such.

    WillGenerateInheritAce - Specifies if the caller intends to generate an
        inheritable ACE the corresponds to OldAce.  If TRUE, this routine will
        try to not map the effective ACE (increasing the likelyhood that
        EffectiveAceMapped will return FALSE),

    ClientOwnerSid - Specifies the owner Sid to use

    ClientGroupSid - Specifies the new Group Sid to use

    ServerSid - Optionally specifies the Server Sid to use in compound ACEs.

    ClientSid - Optionally specifies the Client Sid to use in compound ACEs.

    GenericMapping - Specifies the generic mapping to use

    pNewObjectType - List of types of object being inherited to.  If not 
        specified, the object has no object type.

    GuidCount - Number of object types in the list.

    AcePosition - On entry and exit, specifies location of the next available ACE
        position in NewAcl.
        A NULL ACE position means there is no room at all in NewAcl.

    NewAceLength - Returns the length (in bytes) needed in NewAcl to
        copy the specified ACE. This might be zero to indicate that the ACE
        need not be copied at all.

    NewAcl - Provides a pointer to the ACL into which the ACE is to be
        inherited.

    ObjectAceInherited - Returns true if one or more object ACEs were inherited
        based on NewObjectType
        If NULL, NewObjectType is ignored and the object ACE is always inherited

    EffectiveAceMapped - Return TRUE if the SID, guid, or access mask of Old Ace
        was modifed when copying the ACE.

    AclOverflowed - Returns TRUE if NewAcl wasn't long enough to contain NewAceLength.

Return Value:

    TRUE - No problem was detected.
    FALSE - Indicates something went wrong preventing
        the ACE from being copied.  This generally represents a bugcheck
        situation when returned from this call.

--*/
{
    ULONG LengthRequired;
    ACCESS_MASK LocalMask;
    BOOLEAN GuidOptimizationPossible = FALSE;

    PSID LocalServerOwner;
    PSID LocalServerGroup;
    NTSTATUS Status;

    ULONG CreatorSid[CREATOR_SID_SIZE];

    SID_IDENTIFIER_AUTHORITY  CreatorSidAuthority = SECURITY_CREATOR_SID_AUTHORITY;


    RTL_PAGED_CODE();

    //
    // Allocate and initialize the universal SIDs we're going to need
    // to look for inheritable ACEs.
    //

    ASSERT(RtlLengthRequiredSid( 1 ) == CREATOR_SID_SIZE);
    Status = RtlInitializeSid( (PSID)CreatorSid, &CreatorSidAuthority, 1 );

    if ( !NT_SUCCESS(Status) ) {
        return FALSE;
    }

    *(RtlpSubAuthoritySid( (PSID)CreatorSid, 0 )) = SECURITY_CREATOR_OWNER_RID;

    LocalServerOwner = ARGUMENT_PRESENT(ServerOwnerSid) ? ServerOwnerSid : ClientOwnerSid;
    LocalServerGroup = ARGUMENT_PRESENT(ServerGroupSid) ? ServerGroupSid : ClientGroupSid;


    //
    // Initialization
    //
    *EffectiveAceMapped = FALSE;
    if ( ARGUMENT_PRESENT(ObjectAceInherited)) {
        *ObjectAceInherited = FALSE;
    }
    *AclOverflowed = FALSE;
    LengthRequired = (ULONG)OldAce->AceSize;

    //
    // Process all MS ACE types specially
    //

    if ( IsMSAceType(OldAce) ) {
        ULONG Rid;
        PSID SidToCopy = NULL;
        ULONG AceHeaderToCopyLength;
        PACE_HEADER AceHeaderToCopy = OldAce;
        PSID ServerSidToCopy = NULL;

        UCHAR DummyAce[sizeof(KNOWN_OBJECT_ACE)+sizeof(GUID)];

        //
        // Grab the Sid pointer and access mask as a function of the ACE type
        //
        if (IsKnownAceType( OldAce ) ) {
            SidToCopy = &((PKNOWN_ACE)OldAce)->SidStart;
            AceHeaderToCopyLength = FIELD_OFFSET(KNOWN_ACE, SidStart);

        } else if (IsCompoundAceType(OldAce)) {

            SidToCopy = RtlCompoundAceClientSid( OldAce );
            AceHeaderToCopyLength = FIELD_OFFSET(KNOWN_COMPOUND_ACE, SidStart);
            ASSERT( FIELD_OFFSET(KNOWN_COMPOUND_ACE, Mask) ==
                    FIELD_OFFSET(KNOWN_ACE, Mask) );

            //
            // Compound ACEs have two SIDs (Map one now).
            //
            ServerSidToCopy = RtlCompoundAceServerSid( OldAce );

            if (RtlEqualPrefixSid ( ServerSidToCopy, CreatorSid )) {

                Rid = *RtlpSubAuthoritySid( ServerSidToCopy, 0 );
                switch (Rid) {
                case SECURITY_CREATOR_OWNER_RID:
                    ServerSidToCopy = ClientOwnerSid;
                    LengthRequired = LengthRequired - CREATOR_SID_SIZE + SeLengthSid(ClientOwnerSid);
                    *EffectiveAceMapped = TRUE;
                    break;
                case SECURITY_CREATOR_GROUP_RID:
                    if ( ClientGroupSid != NULL ) {
                        ServerSidToCopy = ClientGroupSid;
                        LengthRequired = LengthRequired - CREATOR_SID_SIZE + SeLengthSid(ClientGroupSid);
                        *EffectiveAceMapped = TRUE;
                    }
                    break;
                case SECURITY_CREATOR_OWNER_SERVER_RID:
                    ServerSidToCopy = LocalServerOwner;
                    LengthRequired = LengthRequired - CREATOR_SID_SIZE + SeLengthSid(LocalServerOwner);
                    *EffectiveAceMapped = TRUE;
                    break;
                case SECURITY_CREATOR_GROUP_SERVER_RID:
                    ServerSidToCopy = LocalServerGroup;
                    LengthRequired = LengthRequired - CREATOR_SID_SIZE + SeLengthSid(LocalServerGroup);
                    *EffectiveAceMapped = TRUE;
                    break;
                }

                //
                // If we don't know what this SID is, just copy the original.
                //
                if ( !*EffectiveAceMapped ) {
                    AceHeaderToCopyLength += SeLengthSid( ServerSidToCopy );
                    ServerSidToCopy = NULL;
                }

            } else {
                //
                // We don't know what this SID is, just copy the original.
                //
                AceHeaderToCopyLength += SeLengthSid( ServerSidToCopy );
                ServerSidToCopy = NULL;
            }

        //
        // Handle Object ACEs
        //
        } else {
            GUID *InheritedObjectType;

            SidToCopy = RtlObjectAceSid( OldAce );
            AceHeaderToCopyLength = (ULONG) ((PUCHAR)SidToCopy - (PUCHAR)OldAce);
            ASSERT( FIELD_OFFSET(KNOWN_OBJECT_ACE, Mask) ==
                    FIELD_OFFSET(KNOWN_ACE, Mask) );

            //
            // Handle ACEs that are only inherited for a specific object type,
            //
            InheritedObjectType = RtlObjectAceInheritedObjectType( OldAce );
            if ( ARGUMENT_PRESENT(ObjectAceInherited) && InheritedObjectType != NULL ) {

                //
                // If the object type doesn't match the inherited object type,
                //  don't inherit the ACE.
                //

                if ( pNewObjectType == NULL ||
                     !RtlpGuidPresentInGuidList( InheritedObjectType,
                                      pNewObjectType,
                                      GuidCount ) ) {

                    LengthRequired = 0;

                //
                // If the object type matches the inherited object type,
                //  Inherit an ACE with no inherited object type.
                //

                } else {

                    //
                    // Tell the caller we inherited an object type specific ACE.
                    //

                    *ObjectAceInherited = TRUE;

                    //
                    // If the caller is not going to generate an inheritable ACE,
                    //  deleting the inherited object type GUID for the effective ACE.
                    //
                    // Otherwise, leave it so the caller can merge the two ACEs.
                    //

                    if ( !WillGenerateInheritAce ) {
                        *EffectiveAceMapped = TRUE;

                        //
                        // If an object type GUID is present,
                        //  simply delete the inherited object type GUID.
                        //
                        if ( RtlObjectAceObjectTypePresent( OldAce )) {
                            LengthRequired -= sizeof(GUID);
                            AceHeaderToCopyLength -= sizeof(GUID);
                            RtlCopyMemory( DummyAce, OldAce, AceHeaderToCopyLength );

                            AceHeaderToCopy = (PACE_HEADER)DummyAce;
                            ((PKNOWN_OBJECT_ACE)AceHeaderToCopy)->Flags &= ~ACE_INHERITED_OBJECT_TYPE_PRESENT;


                        //
                        // If an object type GUID is not present,
                        //  convert the ACE to non-object type specific.
                        //
                        } else {
                            AceHeaderToCopyLength = AceHeaderToCopyLength -
                                             sizeof(GUID) +
                                             sizeof(KNOWN_ACE) -
                                             sizeof(KNOWN_OBJECT_ACE);
                            LengthRequired = LengthRequired -
                                             sizeof(GUID) +
                                             sizeof(KNOWN_ACE) -
                                             sizeof(KNOWN_OBJECT_ACE);

                            RtlCopyMemory( DummyAce, OldAce, AceHeaderToCopyLength );
                            AceHeaderToCopy = (PACE_HEADER)DummyAce;

                            AceHeaderToCopy->AceType = RtlBaseAceType[ OldAce->AceType ];

                        }
                    } else {
                        GuidOptimizationPossible = TRUE;
                    }
                }

            }
        }

        //
        // Only proceed if we've not already determined to drop the ACE.
        //

        if ( LengthRequired != 0 ) {

            //
            // If after mapping the access mask, the access mask
            // is empty, then drop the ACE.
            //
            // This is incompatible with NT 4.0 which simply mapped and left
            //  undefined access bits set.

            LocalMask = ((PKNOWN_ACE)(OldAce))->Mask;
            RtlApplyGenericMask( OldAce, &LocalMask, GenericMapping);

            if ( LocalMask != ((PKNOWN_ACE)(OldAce))->Mask ) {
                *EffectiveAceMapped = TRUE;
            }

            //
            // Mask off any bits that aren't meaningful
            //

            LocalMask &= ( STANDARD_RIGHTS_ALL | SPECIFIC_RIGHTS_ALL | ACCESS_SYSTEM_SECURITY );

            if (LocalMask == 0) {

                LengthRequired = 0;

            } else {

                //
                // See if the SID in the ACE is one of the various CREATOR_* SIDs by
                // comparing identifier authorities.
                //

                if (RtlEqualPrefixSid ( SidToCopy, CreatorSid )) {

                    Rid = *RtlpSubAuthoritySid( SidToCopy, 0 );

                    switch (Rid) {
                    case SECURITY_CREATOR_OWNER_RID:
                        SidToCopy = ClientOwnerSid;
                        LengthRequired = LengthRequired - CREATOR_SID_SIZE + SeLengthSid(ClientOwnerSid);
                        *EffectiveAceMapped = TRUE;
                        break;
                    case SECURITY_CREATOR_GROUP_RID:
                        if ( ClientGroupSid != NULL ) {
                            SidToCopy = ClientGroupSid;
                            LengthRequired = LengthRequired - CREATOR_SID_SIZE + SeLengthSid(ClientGroupSid);
                            *EffectiveAceMapped = TRUE;
                        }
                        break;
                    case SECURITY_CREATOR_OWNER_SERVER_RID:
                        SidToCopy = LocalServerOwner;
                        LengthRequired = LengthRequired - CREATOR_SID_SIZE + SeLengthSid(LocalServerOwner);
                        *EffectiveAceMapped = TRUE;
                        break;
                    case SECURITY_CREATOR_GROUP_SERVER_RID:
                        SidToCopy = LocalServerGroup;
                        LengthRequired = LengthRequired - CREATOR_SID_SIZE + SeLengthSid(LocalServerGroup);
                        *EffectiveAceMapped = TRUE;
                        break;
                    default :
                        //
                        // We don't know what this SID is, just copy the original.
                        //
                        break;
                    }
                }

                //
                // In cases where effective ace has been mapped because of
                //     a. CreatorOwner/Group OR
                //     b. Generic flags
                // AND
                //     this is an object type ace which will generate an IO ace
                // we can save space for a guid.
                //

                if (GuidOptimizationPossible && *EffectiveAceMapped) {

                    //
                    // If an object type GUID is present,
                    //  simply delete the inherited object type GUID.
                    //

                    if ( RtlObjectAceObjectTypePresent( OldAce )) {
                        LengthRequired -= sizeof(GUID);
                        AceHeaderToCopyLength -= sizeof(GUID);
                        RtlCopyMemory( DummyAce, OldAce, AceHeaderToCopyLength );

                        AceHeaderToCopy = (PACE_HEADER)DummyAce;
                        ((PKNOWN_OBJECT_ACE)AceHeaderToCopy)->Flags &= ~ACE_INHERITED_OBJECT_TYPE_PRESENT;


                    //
                    // If an object type GUID is not present,
                    //  convert the ACE to non-object type specific.
                    //
                    } else {
                        AceHeaderToCopyLength = AceHeaderToCopyLength -
                                         sizeof(GUID) +
                                         sizeof(KNOWN_ACE) -
                                         sizeof(KNOWN_OBJECT_ACE);
                        LengthRequired = LengthRequired -
                                         sizeof(GUID) +
                                         sizeof(KNOWN_ACE) -
                                         sizeof(KNOWN_OBJECT_ACE);

                        RtlCopyMemory( DummyAce, OldAce, AceHeaderToCopyLength );
                        AceHeaderToCopy = (PACE_HEADER)DummyAce;

                        AceHeaderToCopy->AceType = RtlBaseAceType[ OldAce->AceType ];

                    }
                }

                //
                // If the ACE doesn't fit,
                //  just note the fact and don't copy the ACE.
                //

                if ( *AcePosition == NULL ||
                     LengthRequired > (ULONG)NewAcl->AclSize - ((PUCHAR)(*AcePosition) - (PUCHAR)NewAcl) ) {
                    *AclOverflowed = TRUE;
                } else {

                    PUCHAR Target;

                    //
                    // Copy individual parts of the ACE separately.
                    //

                    Target = (PUCHAR)*AcePosition;

                    RtlCopyMemory(
                        Target,
                        AceHeaderToCopy,
                        AceHeaderToCopyLength );

                    Target += AceHeaderToCopyLength;

                    //
                    // Now copy the correct server SID
                    //

                    if ( ServerSidToCopy != NULL ) {
                        RtlCopyMemory(
                            Target,
                            ServerSidToCopy,
                            SeLengthSid(ServerSidToCopy)
                            );
                        Target += SeLengthSid(ServerSidToCopy);
                    }

                    //
                    // Now copy the correct SID
                    //

                    RtlCopyMemory(
                        Target,
                        SidToCopy,
                        SeLengthSid(SidToCopy)
                        );
                    Target += SeLengthSid(SidToCopy);

                    //
                    // Set the size of the ACE accordingly
                    //

                    if ( LengthRequired < (ULONG)(Target - (PUCHAR)*AcePosition) ) {
                        return FALSE;
                    }
                    LengthRequired = (ULONG)(Target - (PUCHAR)*AcePosition);
                    ((PKNOWN_ACE)*AcePosition)->Header.AceSize =
                        (USHORT)LengthRequired;


                    //
                    // Put the mapped access mask in the new ACE
                    //

                    ((PKNOWN_ACE)*AcePosition)->Mask = LocalMask;

                }
            }
        }

    } else {

        //
        // If the ACE doesn't fit,
        //  just note the fact and don't copy the ACE.
        //

        if ( LengthRequired > (ULONG)NewAcl->AclSize - ((PUCHAR)*AcePosition - (PUCHAR)NewAcl) ) {
            *AclOverflowed = TRUE;
        } else {

            //
            // Not a known ACE type, copy ACE as is
            //

            RtlCopyMemory(
                *AcePosition,
                OldAce,
                LengthRequired );
         }
    }

    //
    // If the ACE was actually kept, clear all the inherit flags
    // and update the ACE count of the ACL.
    //

    if ( !*AclOverflowed && LengthRequired != 0 ) {
        ((PACE_HEADER)*AcePosition)->AceFlags &= ~VALID_INHERIT_FLAGS;
        if ( AutoInherit ) {
            ((PACE_HEADER)*AcePosition)->AceFlags |= INHERITED_ACE;
        }
        NewAcl->AceCount += 1;
    }

    //
    // We have the length of the new ACE, but we've calculated
    // it with a ULONG.  It must fit into a USHORT.  See if it
    // does.
    //

    if (LengthRequired > 0xFFFF) {
        return FALSE;
    }

    //
    // Move the Ace Position to where the next ACE goes.
    //
    if ( !*AclOverflowed ) {
        *AcePosition = ((PUCHAR)*AcePosition) + LengthRequired;
    }

    //
    //  Now return to our caller
    //

    (*NewAceLength) = LengthRequired;

    return TRUE;
}

#ifndef WIN16

NTSTATUS
RtlpCopyAces(
    IN PACL Acl,
    IN PGENERIC_MAPPING GenericMapping,
    IN ACE_TYPE_TO_COPY AceTypeToCopy,
    IN UCHAR AceFlagsToReset,
    IN BOOLEAN MapSids,
    IN PSID ClientOwnerSid,
    IN PSID ClientGroupSid,
    IN PSID ServerOwnerSid OPTIONAL,
    IN PSID ServerGroupSid OPTIONAL,
    IN BOOLEAN IsDirectoryObject,
    IN BOOLEAN RetainInheritedAceBit,
    OUT PULONG NewAclSizeParam,
    OUT PACL NewAcl
    )

/*++

Routine Description:

    Copy ACEs from of an ACL and perform generic mapping.  Only ACEs specified
    by 'AceFilter' are copied.

Arguments:

    Acl - Supplies the ACL to copy from.

    GenericMapping - Specifies the generic mapping to use.

    AceTypeToCopy - Describes which aces to copy.

    AceFlagsToReset - Bit mask of ACE flags to reset (if set) on each ACE.

    MapSids - TRUE if the SID in the ACE is to be mapped to the corresponding
        actual SID.

    ClientOwnerSid - Specifies the owner Sid to use

    ClientGroupSid - Specifies the new Group Sid to use

    ServerOwnerSid - Optionally specifies the Server Sid to use in compound ACEs.

    ServerGroupSid - Optionally specifies the Server group Sid to use in compound ACEs.

    IsDirectoryObject - Whether the object is a container or a non-container
    
    RetainInheritedAceBit - Whether to retain INHERITED_ACE bit for effective aces.

    NewAclSizeParam - Receives the cumulative length of the copies ACEs

    NewAcl - Provides a pointer to the ACL to copy to.
        This ACL must already be initialized.


Return Value:

    STATUS_SUCCESS - An inheritable ACL has been generated.

    STATUS_BUFFER_TOO_SMALL - The ACL specified by NewAcl is too small for the
        copied ACEs.  The required size is returned in NewAceLength.


--*/

{

    NTSTATUS Status;
    ULONG i;

    PACE_HEADER OldAce;
    ULONG NewAclSize, NewAceSize;
    BOOLEAN AclOverflowed = FALSE;
    BOOLEAN CopyAce;
    PVOID AcePosition;
    BOOLEAN LocalAutoInherit = FALSE;


    RTL_PAGED_CODE();

    //
    // Validate the ACL.
    //

    if ( !ValidAclRevision(NewAcl) ) {
        return STATUS_UNKNOWN_REVISION;
    }

    //
    // Find where the first ACE goes.
    //

    if (!RtlFirstFreeAce( NewAcl, &AcePosition )) {
        return STATUS_BAD_INHERITANCE_ACL;
    }

    //
    // Walk through the original ACL copying ACEs.
    //

    NewAclSize = 0;
    for (i = 0, OldAce = FirstAce(Acl);
         i < Acl->AceCount;
         i += 1, OldAce = NextAce(OldAce)) {

        //
        // If the ACE wasn't inherited,
        //  copy it.
        //

        switch (AceTypeToCopy) {
        case CopyInheritedAces:
            CopyAce = AceInherited(OldAce);
            break;
        case CopyNonInheritedAces:
            CopyAce = !AceInherited(OldAce);
            break;
        case CopyAllAces:
            CopyAce = TRUE;
            break;
        default:
            CopyAce = FALSE;
            break;
        }

        if ( CopyAce ) {


            //
            // If SIDs are to be mapped,
            //  do so (and potentially create up to two ACEs).
            //

            if ( MapSids ) {
                PVOID TempAcePosition;
                ULONG EffectiveAceSize = 0;

                BOOLEAN EffectiveAceMapped;
                BOOLEAN GenerateInheritAce;

                //
                // Remember where the next ACE will be copied.
                //

                TempAcePosition = AcePosition;
                NewAceSize = 0;
                GenerateInheritAce = IsDirectoryObject &&
                    ((((PACE_HEADER)OldAce)->AceFlags & (OBJECT_INHERIT_ACE|CONTAINER_INHERIT_ACE)) != 0);


                //
                // If the original ACE is an effective ACE,
                //  create an effective ACE.
                //

                if ( !(((PACE_HEADER)OldAce)->AceFlags & INHERIT_ONLY_ACE)) {
                    BOOLEAN LocalAclOverflowed;

                    //
                    // If the ace has INHERITED_ACE bit and the caller has requested
                    // preservation of the bit, copy the effective ace as an
                    // INHERITED_ACE.
                    //

                    LocalAutoInherit = FALSE;

                    if ( RetainInheritedAceBit ) {
                        if ( OldAce->AceFlags & INHERITED_ACE) {
                            LocalAutoInherit = TRUE;
                        }
                    }

                    //
                    // Copy the effective ACE into the ACL.
                    //
                    if ( !RtlpCopyEffectiveAce (
                                    OldAce,
                                    LocalAutoInherit,  
                                    GenerateInheritAce,
                                    ClientOwnerSid,
                                    ClientGroupSid,
                                    ServerOwnerSid,
                                    ServerGroupSid,
                                    GenericMapping,
                                    NULL,   // Always copy object ACES
                                    0,
                                    &TempAcePosition,
                                    &EffectiveAceSize,
                                    NewAcl,
                                    NULL,   // Always copy object ACES
                                    &EffectiveAceMapped,
                                    &LocalAclOverflowed ) ) {

                        return STATUS_BAD_INHERITANCE_ACL;
                    }

                    if (LocalAclOverflowed) {
                        AclOverflowed = TRUE;
                    }
                    NewAceSize += EffectiveAceSize;

                    //
                    // Reset any undesirable AceFlags.
                    //
                    
                    if ( !AclOverflowed && EffectiveAceSize != 0 ) {
                        ((PACE_HEADER)AcePosition)->AceFlags &= ~AceFlagsToReset;
                    }

                }

                //
                // If the original ACE is inheritable,
                //  create an inheritable ACE.
                //
                // ASSERT: AcePosition points to where the effective ACE was copied
                // ASSERT: TempAcePosition points to where the inheritable ACE should be copied
                //

                if ( GenerateInheritAce ) {

                    //
                    // If a effective ACE was created,
                    //  and it wasn't mapped,
                    //  avoid generating another ACE and simply merge the inheritance bits into
                    //      the effective ACE.
                    //

                    if ( EffectiveAceSize != 0 && !EffectiveAceMapped ) {

                       //
                       // Copy the inherit bits from the original ACE.
                       //
                       if ( !AclOverflowed ) {
                            ((PACE_HEADER)AcePosition)->AceFlags |=
                                ((PACE_HEADER)OldAce)->AceFlags & (VALID_INHERIT_FLAGS);
                            ((PACE_HEADER)AcePosition)->AceFlags &= ~AceFlagsToReset;
                       }


                    //
                    // Otherwise, generate an explicit inheritance ACE.
                    //
                    // But only if the access mask isn't zero.
                    //

                    } else if ( !IsMSAceType(OldAce) || ((PKNOWN_ACE)(OldAce))->Mask != 0 ) {

                        //
                        // Account for the new ACE being added to the ACL.
                        //
                        NewAceSize += (ULONG)(((PACE_HEADER)OldAce)->AceSize);

                        if (NewAceSize > 0xFFFF) {
                            return STATUS_BAD_INHERITANCE_ACL;
                        }

                        //
                        // If the ACE doesn't fit,
                        //  just note the fact and don't copy the ACE.
                        //

                        if ( ((PACE_HEADER)OldAce)->AceSize > NewAcl->AclSize - ((PUCHAR)TempAcePosition - (PUCHAR)NewAcl) ) {
                            AclOverflowed = TRUE;
                        } else {

                            //
                            // copy it as is, but make sure the InheritOnly bit is set.
                            //

                            if ( !AclOverflowed ) {
                                RtlCopyMemory(
                                    TempAcePosition,
                                    OldAce,
                                    ((PACE_HEADER)OldAce)->AceSize
                                    );

                                ((PACE_HEADER)TempAcePosition)->AceFlags |= INHERIT_ONLY_ACE;
                                ((PACE_HEADER)TempAcePosition)->AceFlags &= ~AceFlagsToReset;
                                NewAcl->AceCount += 1;
                            }
                        }
                    }

                }

            } else {
                NewAceSize = (ULONG)OldAce->AceSize;

                //
                // If the ACE doesn't fit,
                //  just note the fact and don't copy the ACE.
                //

                if ( AcePosition == NULL ||
                     NewAceSize > (ULONG)NewAcl->AclSize - ((PUCHAR)AcePosition - (PUCHAR)NewAcl) ) {
                    AclOverflowed = TRUE;
                } else if ( !AclOverflowed ) {


                    //
                    // Copy the ACE.
                    //

                    RtlCopyMemory(
                        AcePosition,
                        OldAce,
                        NewAceSize );

                    //
                    // Map the generic bits.
                    //
                    // Is it really right to map the generic bits on an ACE
                    // that's both effective and inheritable.  Shouldn't this
                    // be split into two ACEs in that case?  Or just skip the mapping?
                    //
                    if (IsMSAceType( AcePosition )) {
                        RtlApplyAceToObject( (PACE_HEADER)AcePosition, GenericMapping );
                    }

                    //
                    // Reset any undesirable AceFlags.
                    //

                    ((PACE_HEADER)AcePosition)->AceFlags &= ~AceFlagsToReset;

                    //
                    // Account for the new ACE.
                    //

                    NewAcl->AceCount += 1;
                }
            }


            //
            // Move the Ace Position to where the next ACE goes.
            //
            if ( !AclOverflowed ) {
                AcePosition = ((PUCHAR)AcePosition) + NewAceSize;
            } else {
                // On overflow, ensure no other ACEs are actually output to the buffer
                AcePosition = ((PUCHAR)NewAcl) + NewAcl->AclSize;
            }
            NewAclSize += NewAceSize;

        }
    }


    //
    // We have the length of the new ACE, but we've calculated
    // it with a ULONG.  It must fit into a USHORT.  See if it
    // does.
    //

    if (NewAclSize > 0xFFFF) {
        return STATUS_BAD_INHERITANCE_ACL;
    }

    (*NewAclSizeParam) = NewAclSize;

    return AclOverflowed ? STATUS_BUFFER_TOO_SMALL : STATUS_SUCCESS;

}


NTSTATUS
RtlpInheritAcl2 (
    IN PACL DirectoryAcl,
    IN PACL ChildAcl,
    IN ULONG ChildGenericControl,
    IN BOOLEAN IsDirectoryObject,
    IN BOOLEAN AutoInherit,
    IN BOOLEAN DefaultDescriptorForObject,
    IN PSID OwnerSid,
    IN PSID GroupSid,
    IN PSID ServerOwnerSid OPTIONAL,
    IN PSID ServerGroupSid OPTIONAL,
    IN PGENERIC_MAPPING GenericMapping,
    IN BOOLEAN IsSacl,
    IN GUID **pNewObjectType OPTIONAL,
    IN ULONG GuidCount,
    IN PULONG AclBufferSize,
    IN OUT PUCHAR AclBuffer,
    OUT PBOOLEAN NewAclExplicitlyAssigned,
    OUT PULONG NewGenericControl
    )

/*++

Routine Description:

    This is a private routine that produces an inherited acl from
    a parent acl according to the rules of inheritance

Arguments:

    DirectoryAcl - Supplies the acl being inherited.

    ChildAcl - Supplies the acl associated with the object.  This
        is either the current acl on the object or the acl being assigned
        to the object.

    ChildGenericControl - Specifies the control flags from the SecurityDescriptor
        describing the ChildAcl:

        SEP_ACL_PRESENT: Specifies that the child ACL is explictly supplied by
            the caller.

        SEP_ACL_DEFAULTED: Specifies that the child ACL was supplied by some
            defaulting mechanism.

        SEP_ACL_PROTECTED: Specifies that the child ACL is protected and
            should not inherit any ACE from the DirectoryACL

    IsDirectoryObject - Specifies if the new acl is for a directory.

    AutoInherit - Specifies if the inheritance is an "automatic inheritance".
        As such, the non-inherited ACEs from the ChildAcl will be preserved and
        the inherited ACEs from the DirectoryAcl will be marked as such.

    DefaultDescriptorForObject - If set, the CreatorDescriptor
        is the default descriptor for ObjectType.  As such, the
        CreatorDescriptor will be ignored if any ObjectType specific
        ACEs are inherited from the parent.  If not such ACEs are inherited,
        the CreatorDescriptor is handled as though this flag were not
        specified.

    OwnerSid - Specifies the owner Sid to use.

    GroupSid - Specifies the group SID to use.

    GenericMapping - Specifies the generic mapping to use.

    IsSacl - True if this is the SACL.  False if this is the DACL.

    pNewObjectType - List of types of object being inherited to.  If not 
        specified, the object has no object type.

    GuidCount - Number of object types in the list.

    AclBufferSize - On input, specifies the size of AclBuffer.
        On output, on success, returns the used size of AclBuffer.
        On output, if the buffer is too small, returns the required size of AclBuffer.

    AclBuffer - Receives a pointer to the new (inherited) acl.

    NewAclExplicitlyAssigned - Returns true to indicate that some portion of
        "NewAcl" was derived from an the explicit ChildAcl

    NewGenericControl - Specifies the control flags for the newly
        generated ACL.

        SEP_ACL_AUTO_INHERITED: Set if the ACL was generated using the
            Automatic Inheritance algorithm.

        SEP_ACL_PROTECTED: Specifies that the ACL is protected and
            was not inherited from the parent ACL.

Return Value:

    STATUS_SUCCESS - An inheritable ACL was successfully generated.

    STATUS_NO_INHERITANCE - An inheritable ACL was not successfully generated.
        This is a warning completion status.  The caller should use the default
        ACL.

    STATUS_BAD_INHERITANCE_ACL - Indicates the acl built was not a valid ACL.
        This can be caused by a number of things.  One of the more probable
        causes is the replacement of a CreatorId with an SID that didn't fit
        into the ACE or ACL.

    STATUS_UNKNOWN_REVISION - Indicates the source ACL is a revision that
        is unknown to this routine.

    STATUS_BUFFER_TOO_SMALL - The ACL specified by NewAcl is too small for the
        inheritance ACEs.  The required size is returned in AclBufferSize.

--*/

{
    NTSTATUS Status;
    ULONG ChildNewAclSize = 0;
    ULONG UsedChildNewAclSize = 0;
    ULONG DirectoryNewAclSize = 0;
    ULONG AclRevision;
    USHORT ChildAceCount;
    PVOID ChildAcePosition;
    PVOID DirectoryAcePosition;
    BOOLEAN AclOverflowed = FALSE;
    BOOLEAN AclProtected = FALSE;
    BOOLEAN NullAclOk = TRUE;
    BOOLEAN ObjectAceInherited;

    RTL_PAGED_CODE();


    //
    // Assume the ACL revision.
    //

    AclRevision = ACL_REVISION;
    RtlCreateAcl( (PACL)AclBuffer, *AclBufferSize, AclRevision );
    *NewAclExplicitlyAssigned = FALSE;
    *NewGenericControl = AutoInherit ? SEP_ACL_AUTO_INHERITED : 0;

    //
    // If the a current child ACL is not defaulted,
    //  the non-inherited ACEs from the current child ACL are to be preserved.
    //

    if ( (ChildGenericControl & SEP_ACL_DEFAULTED) == 0 ) {

        //
        // The resultant ACL should be protected if the input ACL is
        //  protected.
        //

        if ( ChildGenericControl & SEP_ACL_PROTECTED ) {
            AclProtected = TRUE;
            *NewGenericControl |= SEP_ACL_PROTECTED;
        }

        //
        // Only copy ACEs if the child ACL is actually present.
        //
        if ( (ChildGenericControl & (SEP_ACL_PRESENT|SEP_ACL_PROTECTED)) != 0 ) {


            if ( ChildAcl != NULL ) {
                ACE_TYPE_TO_COPY AceTypeToCopy;
                UCHAR AceFlagsToReset;
                BOOLEAN MapSids;


                AclRevision = max( AclRevision, ChildAcl->AclRevision );

                //
                // Since we're explicitly using the ACL specified by the caller,
                //  we never want to return a NULL ACL.
                //  Rather, if we have an ACL with no ACEs,
                //  we'll return exactly that.  For a DACL, that results
                //  in a DACL that grants no access rather than a DACL
                //  that grants all access.
                //

                NullAclOk = FALSE;

                //
                // If the caller doesn't understand auto inheritance,
                //  simply preserve the specified ACL 100% intact.
                //
                if ( !AutoInherit ) {

                    AceTypeToCopy = CopyAllAces;
                    AceFlagsToReset = 0;      // Don't turn off any ACE Flags
                    MapSids = FALSE;          // For backward compatibility

                //
                // If the child is protected,
                //  keep all of the ACEs turning off the INHERITED ACE flags.
                //
                } else if ( ChildGenericControl & SEP_ACL_PROTECTED ) {

                    AceTypeToCopy = CopyAllAces;
                    AceFlagsToReset = INHERITED_ACE; // Turn off all INHERITED_ACE flags
                    MapSids = TRUE;

                //
                // If the child is not protected,
                //  just copy the non-inherited ACEs.
                //
                // (The inherited ACEs will be recomputed from the parent.)
                //
                } else {

                    AceTypeToCopy = CopyNonInheritedAces;
                    AceFlagsToReset = 0;      // Don't turn off any ACE Flags
                    MapSids = TRUE;

                }

                //
                // Copy the requested ACEs.
                //

                Status = RtlpCopyAces(
                            ChildAcl,
                            GenericMapping,
                            AceTypeToCopy,
                            AceFlagsToReset,
                            MapSids,
                            OwnerSid,
                            GroupSid,
                            ServerOwnerSid,
                            ServerGroupSid,
                            IsDirectoryObject,
                            FALSE, // Do not retain INHERITED_ACE bit for effective aces
                            &ChildNewAclSize,
                            (PACL)AclBuffer );

                UsedChildNewAclSize = ChildNewAclSize;
                if ( Status == STATUS_BUFFER_TOO_SMALL ) {
                    AclOverflowed = TRUE;
                    Status = STATUS_SUCCESS;
                }

                if ( !NT_SUCCESS(Status) ) {
                    return Status;
                }

                //
                // If this ACL might be ignored later,
                //  remember the current state of the ACL.
                //

                if ( DefaultDescriptorForObject && ChildNewAclSize != 0 ) {
                    ChildAceCount = ((PACL)AclBuffer)->AceCount;

                    if (!RtlFirstFreeAce( (PACL)AclBuffer, &ChildAcePosition ) ) {
                        return STATUS_BAD_INHERITANCE_ACL;
                    }
                }

            //
            // If the ACL isn't protected,
            //  don't allow NULL ACL semantics.
            //  (those semantics are ambiguous for auto inheritance)
            //
            } else if ( AutoInherit &&
                        !IsSacl &&
                        (ChildGenericControl & (SEP_ACL_PRESENT|SEP_ACL_PROTECTED)) == SEP_ACL_PRESENT ) {
                return STATUS_INVALID_ACL;

            }

            *NewAclExplicitlyAssigned = TRUE;

        }

    }

    //
    // Inherit ACEs from the Directory ACL in any of the following cases:
    //  If !AutoInheriting,
    //      Inherit if there is no explicit child ACL (ignoring a defaulted child).
    //  If AutoInheriting,
    //      observe the protected flag.
    //

    if ( (!AutoInherit &&
            (ChildGenericControl & SEP_ACL_PRESENT) == 0 ||
                (ChildGenericControl & SEP_ACL_DEFAULTED) != 0) ||
         (AutoInherit && !AclProtected) ) {

        //
        //  If there is no directory ACL,
        //      don't inherit from it.
        //

        if ( DirectoryAcl != NULL ) {

            //
            // If the DirectoryAcl is used,
            //  the revision of the Directory ACL is picked up.
            //

            if ( !ValidAclRevision(DirectoryAcl) ) {
                return STATUS_UNKNOWN_REVISION;
            }

            AclRevision = max( AclRevision, DirectoryAcl->AclRevision );

            //
            // Inherit the Parent's ACL.
            //

            Status = RtlpGenerateInheritAcl(
                         DirectoryAcl,
                         IsDirectoryObject,
                         AutoInherit,
                         OwnerSid,
                         GroupSid,
                         ServerOwnerSid,
                         ServerGroupSid,
                         GenericMapping,
                         pNewObjectType,
                         GuidCount,
                         &DirectoryNewAclSize,
                         (PACL)AclBuffer,
                         &ObjectAceInherited );

            if ( Status == STATUS_BUFFER_TOO_SMALL ) {
                AclOverflowed = TRUE;
                Status = STATUS_SUCCESS;
            }

            if ( !NT_SUCCESS(Status) ) {
                return Status;
            }

            //
            // If the default descriptor for the object should be ditched,
            //  because object specific ACEs were inherited from the directory,
            //  ditch them now.
            //

            if ( DefaultDescriptorForObject &&
                 ChildNewAclSize != 0 &&
                 ObjectAceInherited &&
                 !AclOverflowed ) {

                //
                // Compute the last used byte of the combined ACL
                //
                if (!RtlFirstFreeAce( (PACL)AclBuffer, &DirectoryAcePosition ) ) {
                    return STATUS_BAD_INHERITANCE_ACL;
                }
                if ( DirectoryAcePosition == NULL ) {
                    DirectoryAcePosition = AclBuffer + ((PACL)AclBuffer)->AclSize;
                }



                //
                // Move all the inherited ACEs to the front of the ACL.
                //

                RtlMoveMemory( FirstAce( AclBuffer ),
                               ChildAcePosition,
                               (ULONG)(((PUCHAR)DirectoryAcePosition) -
                                (PUCHAR)ChildAcePosition) );

                //
                // Adjust the ACE count to remove the deleted ACEs
                //

                ((PACL)AclBuffer)->AceCount -= ChildAceCount;

                //
                // Save the number of bytes of the Child ACL that were
                //  actually used.
                //

                UsedChildNewAclSize = 0;

            }
        }

    }

    //
    // If this routine didn't build the ACL,
    //  tell the caller.
    //

    if ( DirectoryNewAclSize + UsedChildNewAclSize == 0) {

        //
        // If the ACL was not explicitly assigned,
        //  tell the caller to default the ACL.
        //
        if ( !(*NewAclExplicitlyAssigned) ) {
            *AclBufferSize = 0;
            return STATUS_NO_INHERITANCE;

        //
        // If the Acl was explictly assigned,
        //  generate a NULL ACL based on the path taken above.
        //

        } else if ( NullAclOk ) {
            *AclBufferSize = 0;
            return STATUS_SUCCESS;
        }

        // DbgBreakPoint();
    }


    //
    // And make sure we don't exceed the length limitations of an ACL (WORD)
    //

    if ( DirectoryNewAclSize + UsedChildNewAclSize + sizeof(ACL) > 0xFFFF) {
        return(STATUS_BAD_INHERITANCE_ACL);
    }

    // The caller has to allocate a buffer large enough for
    // ChildNewAclSize rather than UsedChildNewAclSize.  Due to the nature of
    // my algorithm above.
    (*AclBufferSize) = DirectoryNewAclSize + ChildNewAclSize + sizeof(ACL);

    if ( AclOverflowed ) {
        return STATUS_BUFFER_TOO_SMALL;
    }

    //
    // Patch the real ACL size and revision into the ACL
    //

    ((PACL)AclBuffer)->AclSize = (USHORT)
        (DirectoryNewAclSize + UsedChildNewAclSize + sizeof(ACL));
    ((PACL)AclBuffer)->AclRevision = (UCHAR) AclRevision;

    return STATUS_SUCCESS;
}



NTSTATUS
RtlpInheritAcl (
    IN PACL DirectoryAcl,
    IN PACL ChildAcl,
    IN ULONG ChildGenericControl,
    IN BOOLEAN IsDirectoryObject,
    IN BOOLEAN AutoInherit,
    IN BOOLEAN DefaultDescriptorForObject,
    IN PSID OwnerSid,
    IN PSID GroupSid,
    IN PSID ServerOwnerSid OPTIONAL,
    IN PSID ServerGroupSid OPTIONAL,
    IN PGENERIC_MAPPING GenericMapping,
    IN BOOLEAN IsSacl,
    IN GUID **pNewObjectType OPTIONAL,
    IN ULONG GuidCount,
    OUT PACL *NewAcl,
    OUT PBOOLEAN NewAclExplicitlyAssigned,
    OUT PULONG NewGenericControl
    )

/*++

Routine Description:

    This is a private routine that produces an inherited acl from
    a parent acl according to the rules of inheritance

Arguments:

    DirectoryAcl - Supplies the acl being inherited.

    ChildAcl - Supplies the acl associated with the object.  This
        is either the current acl on the object or the acl being assigned
        to the object.

    ChildGenericControl - Specifies the control flags from the SecurityDescriptor
        describing the ChildAcl:

        SEP_ACL_PRESENT: Specifies that the child ACL is explictly supplied by
            the caller.

        SEP_ACL_DEFAULTED: Specifies that the child ACL was supplied by some
            defaulting mechanism.

        SEP_ACL_PROTECTED: Specifies that the child ACL is protected and
            should not inherit any ACE from the DirectoryACL

    IsDirectoryObject - Specifies if the new acl is for a directory.

    AutoInherit - Specifies if the inheritance is an "automatic inheritance".
        As such, the non-inherited ACEs from the ChildAcl will be preserved and
        the inherited ACEs from the DirectoryAcl will be marked as such.

    DefaultDescriptorForObject - If set, the CreatorDescriptor
        is the default descriptor for ObjectType.  As such, the
        CreatorDescriptor will be ignored if any ObjectType specific
        ACEs are inherited from the parent.  If not such ACEs are inherited,
        the CreatorDescriptor is handled as though this flag were not
        specified.

    OwnerSid - Specifies the owner Sid to use.

    GroupSid - Specifies the group SID to use.

    GenericMapping - Specifies the generic mapping to use.

    IsSacl - True if this is the SACL.  False if this is the DACL.

    pNewObjectType - List of types of object being inherited to.  If not 
        specified, the object has no object type.

    GuidCount - Number of object types in the list.

    NewAcl - Receives a pointer to the new (inherited) acl.

    NewAclExplicitlyAssigned - Returns true to indicate that some portion of
        "NewAcl" was derived from an the explicit ChildAcl

    NewGenericControl - Specifies the control flags for the newly
        generated ACL.

        SEP_ACL_AUTO_INHERITED: Set if the ACL was generated using the
            Automatic Inheritance algorithm.

        SEP_ACL_PROTECTED: Specifies that the ACL is protected and
            was not inherited from the parent ACL.

Return Value:

    STATUS_SUCCESS - An inheritable ACL was successfully generated.

    STATUS_NO_INHERITANCE - An inheritable ACL was not successfully generated.
        This is a warning completion status.

    STATUS_BAD_INHERITANCE_ACL - Indicates the acl built was not a valid ACL.
        This can be caused by a number of things.  One of the more probable
        causes is the replacement of a CreatorId with an SID that didn't fit
        into the ACE or ACL.

    STATUS_UNKNOWN_REVISION - Indicates the source ACL is a revision that
        is unknown to this routine.

--*/

{
//////////////////////////////////////////////////////////////////////////////
//                                                                          //
//   The logic in the ACL inheritance code must mirror the code for         //
//   inheritance in the executive (in seassign.c).  Do not make changes     //
//   here without also making changes in that module.                       //
//                                                                          //
//////////////////////////////////////////////////////////////////////////////



    NTSTATUS Status;
    ULONG AclBufferSize;
    ULONG i;

    RTL_PAGED_CODE();

    //
    // Implement a two pass strategy.
    //
    // First try to create the ACL in a fixed length buffer.
    // If that is too small,
    //  then use the buffer size determined on the first pass
    //

    AclBufferSize = 200;
    for ( i=0; i<2 ; i++ ) {

        //
        // Allocate heap for the new ACL.
        //

        (*NewAcl) = ExAllocatePoolWithTag(
                        PagedPool,
                        AclBufferSize,
                        'cAeS' );

        if ((*NewAcl) == NULL ) {
            return( STATUS_NO_MEMORY );
        }

        //
        // Actually build the inherited ACL.
        //

        Status = RtlpInheritAcl2 (
                    DirectoryAcl,
                    ChildAcl,
                    ChildGenericControl,
                    IsDirectoryObject,
                    AutoInherit,
                    DefaultDescriptorForObject,
                    OwnerSid,
                    GroupSid,
                    ServerOwnerSid,
                    ServerGroupSid,
                    GenericMapping,
                    IsSacl,
                    pNewObjectType,
                    GuidCount,
                    &AclBufferSize,
                    (PUCHAR) *NewAcl,
                    NewAclExplicitlyAssigned,
                    NewGenericControl );


        if ( NT_SUCCESS(Status) ) {

            //
            // If a NULL ACL should be used,
            //  tell the caller.
            //

            if ( AclBufferSize == 0 ) {

                ExFreePool( *NewAcl );

                *NewAcl = NULL;
            }

            break;

        } else {
            ExFreePool( *NewAcl );

            *NewAcl = NULL;

            if ( Status != STATUS_BUFFER_TOO_SMALL ) {
                break;
            }
        }
    }


    return Status;
}


NTSTATUS
RtlpGenerateInheritedAce (
    IN PACE_HEADER OldAce,
    IN BOOLEAN IsDirectoryObject,
    IN BOOLEAN AutoInherit,
    IN PSID ClientOwnerSid,
    IN PSID ClientGroupSid,
    IN PSID ServerOwnerSid OPTIONAL,
    IN PSID ServerGroupSid OPTIONAL,
    IN PGENERIC_MAPPING GenericMapping,
    IN GUID **pNewObjectType OPTIONAL,
    IN ULONG GuidCount,
    OUT PULONG NewAceLength,
    OUT PACL NewAcl,
    OUT PULONG NewAceExtraLength,
    OUT PBOOLEAN ObjectAceInherited
    )

/*++

Routine Description:

    This is a private routine that checks if the input ace is inheritable
    and produces 0, 1, or 2 inherited aces in the given buffer.

Arguments:

    OldAce - Supplies the ace being inherited

    IsDirectoryObject - Specifies if the new ACE is for a directory

    AutoInherit - Specifies if the inheritance is an "automatic inheritance".
        As such, the inherited ACEs will be marked as such.

    ClientOwnerSid - Specifies the owner Sid to use

    ClientGroupSid - Specifies the new Group Sid to use

    ServerSid - Optionally specifies the Server Sid to use in compound ACEs.

    ClientSid - Optionally specifies the Client Sid to use in compound ACEs.

    GenericMapping - Specifies the generic mapping to use

    pNewObjectType - List of types of object being inherited to.  If not 
        specified, the object has no object type.

    GuidCount - Number of object types in the list.

    NewAceLength - Receives the length (number of bytes) needed to allow for
        the inheritance of the specified ACE.  This might be zero.

    NewAcl - Provides a pointer to the ACL into which the ACE is to be
        inherited.

    NewAceExtraLength - Receives a length (number of bytes) temporarily used
        in the ACL for the inheritance ACE.  This might be zero

    ObjectAceInherited - Returns true if one or more object ACEs were inherited
        based on NewObjectType

Return Value:

    STATUS_SUCCESS - The ACE was inherited successfully.

    STATUS_BAD_INHERITANCE_ACL - Indicates something went wrong preventing
        the ACE from being inherited.  This generally represents a bugcheck
        situation when returned from this call.

    STATUS_BUFFER_TOO_SMALL - The ACL specified by NewAcl is too small for the
        inheritance ACEs.  The required size is returned in NewAceLength.


--*/

{
    ///////////////////////////////////////////////////////////////////////////
    //                                                                       //
    // The inheritance flags AND the sid of the ACE determine whether        //
    // we need 0, 1, or 2 ACEs.                                              //
    //                                                                       //
    // BE CAREFUL WHEN CHANGING THIS CODE.  READ THE DSA ACL ARCHITECTURE    //
    // SECTION COVERING INHERITANCE BEFORE ASSUMING YOU KNOW WHAT YOU ARE    //
    // DOING!!!!                                                             //
    //                                                                       //
    // The general gist of the algorithm is:                                 //
    //                                                                       //
    //       if ( (container  && ContainerInherit) ||                        //
    //            (!container && ObjectInherit)      ) {                     //
    //               GenerateEffectiveAce;                                   //
    //       }                                                               //
    //                                                                       //
    //                                                                       //
    //       if (Container && Propagate) {                                   //
    //           Propagate copy of ACE and set InheritOnly;                  //
    //       }                                                               //
    //                                                                       //
    //                                                                       //
    // A slightly more accurate description of this algorithm is:            //
    //                                                                       //
    //   IO  === InheritOnly flag                                            //
    //   CI  === ContainerInherit flag                                       //
    //   OI  === ObjectInherit flag                                          //
    //   NPI === NoPropagateInherit flag                                     //
    //                                                                       //
    //   if ( (container  && CI) ||                                          //
    //        (!container && OI)   ) {                                       //
    //       Copy Header of ACE;                                             //
    //       Clear IO, NPI, CI, OI;                                          //
    //                                                                       //
    //       if (KnownAceType) {                                             //
    //           if (SID is a creator ID) {                                  //
    //               Copy appropriate creator SID;                           //
    //           } else {                                                    //
    //               Copy SID of original;                                   //
    //           }                                                           //
    //                                                                       //
    //           Copy AccessMask of original;                                //
    //           MapGenericAccesses;                                         //
    //           if (AccessMask == 0) {                                      //
    //               discard new ACE;                                        //
    //           }                                                           //
    //                                                                       //
    //       } else {                                                        //
    //           Copy body of ACE;                                           //
    //       }                                                               //
    //                                                                       //
    //   }                                                                   //
    //                                                                       //
    //   if (!NPI) {                                                         //
    //       Copy ACE as is;                                                 //
    //       Set IO;                                                         //
    //   }                                                                   //
    //                                                                       //
    //                                                                       //
    //                                                                       //
    ///////////////////////////////////////////////////////////////////////////



    ULONG LengthRequired = 0;
    ULONG ExtraLengthRequired = 0;
    PVOID AcePosition;
    PVOID EffectiveAcePosition;
    ULONG EffectiveAceSize = 0;

    BOOLEAN EffectiveAceMapped;
    BOOLEAN AclOverflowed = FALSE;
    BOOLEAN GenerateInheritAce;

    RTL_PAGED_CODE();

    //
    //  This is gross and ugly, but it's better than allocating
    //  virtual memory to hold the ClientSid, because that can
    //  fail, and propagating the error back is a tremendous pain
    //

    ASSERT(RtlLengthRequiredSid( 1 ) == CREATOR_SID_SIZE);
    *ObjectAceInherited = FALSE;
    GenerateInheritAce = IsDirectoryObject && Propagate(OldAce);

    //
    // Allocate and initialize the universal SIDs we're going to need
    // to look for inheritable ACEs.
    //

    if (!RtlFirstFreeAce(NewAcl, &AcePosition) || (AcePosition == NULL)) {
        return STATUS_BAD_INHERITANCE_ACL;
    }

    //
    //  check to see if we will have a effective ACE (one mapped to
    //  the target object type).
    //

    if ( (IsDirectoryObject  && ContainerInherit(OldAce)) ||
         (!IsDirectoryObject && ObjectInherit(OldAce))      ) {


        //
        // Remember where the effective ACE will be copied to.
        //
        EffectiveAcePosition = AcePosition;

        //
        // Copy the effective ACE into the ACL.
        //
        if ( !RtlpCopyEffectiveAce (
                        OldAce,
                        AutoInherit,
                        GenerateInheritAce,
                        ClientOwnerSid,
                        ClientGroupSid,
                        ServerOwnerSid,
                        ServerGroupSid,
                        GenericMapping,
                        pNewObjectType,
                        GuidCount,
                        &AcePosition,
                        &EffectiveAceSize,
                        NewAcl,
                        ObjectAceInherited,
                        &EffectiveAceMapped,
                        &AclOverflowed ) ) {

            return STATUS_BAD_INHERITANCE_ACL;
        }

        //
        // If the effective ACE is a duplicate of existing inherited ACEs,
        //  Don't really generate it.
        //

        if ( !AclOverflowed &&
             EffectiveAceSize > 0 &&
             EffectiveAcePosition != NULL &&
                RtlpIsDuplicateAce(
                    NewAcl,
                    EffectiveAcePosition ) ) {


            //
            // Truncate the ACE we just added.
            //

            NewAcl->AceCount--;
            AcePosition = EffectiveAcePosition;
            ExtraLengthRequired = max( ExtraLengthRequired, EffectiveAceSize );
            EffectiveAceSize = 0;
        }

        LengthRequired += EffectiveAceSize;

    }

    //
    // If we are inheriting onto a container, then we may need to
    // propagate the inheritance as well.
    //

    if ( GenerateInheritAce ) {

        //
        // If a effective ACE was created,
        //  and it wasn't mapped,
        //  avoid generating another ACE and simply merge the inheritance bits into
        //      the effective ACE.
        //

        if ( EffectiveAceSize != 0 && !EffectiveAceMapped ) {

           //
           // Copy the inherit bits from the original ACE.
           //
           if ( !AclOverflowed ) {
               ((PACE_HEADER)EffectiveAcePosition)->AceFlags |=
                    ((PACE_HEADER)OldAce)->AceFlags & (CONTAINER_INHERIT_ACE | OBJECT_INHERIT_ACE);
               if ( AutoInherit ) {
                   ((PACE_HEADER)EffectiveAcePosition)->AceFlags |= INHERITED_ACE;
               }
           }


        //
        // Otherwise, generate an explicit inheritance ACE.
        //
        // But only if the access mask isn't zero.
        //

        } else if ( !IsMSAceType(OldAce) || ((PKNOWN_ACE)(OldAce))->Mask != 0 ) {

            //
            // Account for the new ACE being added to the ACL.
            //
            LengthRequired += (ULONG)(((PACE_HEADER)OldAce)->AceSize);

            if (LengthRequired > 0xFFFF) {
                return STATUS_BAD_INHERITANCE_ACL;
            }

            //
            // If the ACE doesn't fit,
            //  just note the fact and don't copy the ACE.
            //

            if ( ((PACE_HEADER)OldAce)->AceSize > NewAcl->AclSize - ((PUCHAR)AcePosition - (PUCHAR)NewAcl) ) {
                AclOverflowed = TRUE;
            } else if (!AclOverflowed){

                //
                // copy it as is, but make sure the InheritOnly bit is set.
                //

                RtlCopyMemory(
                    AcePosition,
                    OldAce,
                    ((PACE_HEADER)OldAce)->AceSize
                    );

                ((PACE_HEADER)AcePosition)->AceFlags |= INHERIT_ONLY_ACE;
                NewAcl->AceCount += 1;
                if ( AutoInherit ) {
                    ((PACE_HEADER)AcePosition)->AceFlags |= INHERITED_ACE;

                    //
                    // If the inheritance ACE is a duplicate of existing inherited ACEs,
                    //  Don't really generate it.
                    //

                    if ( RtlpIsDuplicateAce(
                                NewAcl,
                                AcePosition ) ) {


                        //
                        // Truncate the ACE we just added.
                        //

                        NewAcl->AceCount--;
                        ExtraLengthRequired = max( ExtraLengthRequired,
                                                   ((PACE_HEADER)OldAce)->AceSize );
                        LengthRequired -= (ULONG)(((PACE_HEADER)OldAce)->AceSize);
                    }
                }

            }
        }
    }

    //
    //  Now return to our caller
    //

    (*NewAceLength) = LengthRequired;
    (*NewAceExtraLength) = ExtraLengthRequired;

    return AclOverflowed ? STATUS_BUFFER_TOO_SMALL : STATUS_SUCCESS;
}


NTSTATUS
RtlpGenerateInheritAcl(
    IN PACL Acl,
    IN BOOLEAN IsDirectoryObject,
    IN BOOLEAN AutoInherit,
    IN PSID ClientOwnerSid,
    IN PSID ClientGroupSid,
    IN PSID ServerOwnerSid OPTIONAL,
    IN PSID ServerGroupSid OPTIONAL,
    IN PGENERIC_MAPPING GenericMapping,
    IN GUID **pNewObjectType OPTIONAL,
    IN ULONG GuidCount,
    OUT PULONG NewAclSizeParam,
    OUT PACL NewAcl,
    OUT PBOOLEAN ObjectAceInherited
    )

/*++

Routine Description:

    This is a private routine that produces an inheritable ACL.

    The buffer to contain the inherted ACL is passed in.  If the buffer is
    too small, the corect size is computed and STATUS_BUFFER_TOO_SMALL is
    returned.

Arguments:

    Acl - Supplies the acl being inherited.

    IsDirectoryObject - Specifies if the new acl is for a directory.

    AutoInherit - Specifies if the inheritance is an "automatic inheritance".
        As such, the inherited ACEs will be marked as such.

    OwnerSid - Specifies the owner Sid to use.

    GroupSid - Specifies the group SID to use.

    GenericMapping - Specifies the generic mapping to use.

    pNewObjectType - List of types of object being inherited to.  If not 
        specified, the object has no object type.

    GuidCount - Number of object types in the list.

    NewAclSizeParam - Receives the length of the inherited ACL.

    NewAcl - Provides a pointer to the buffer to receive the new
        (inherited) acl.  This ACL must already be initialized.

    ObjectAceInherited - Returns true if one or more object ACEs were inherited
        based on NewObjectType


Return Value:

    STATUS_SUCCESS - An inheritable ACL has been generated.

    STATUS_BAD_INHERITANCE_ACL - Indicates the acl built was not a valid ACL.
        This can be caused by a number of things.  One of the more probable
        causes is the replacement of a CreatorId with an SID that didn't fit
        into the ACE or ACL.

    STATUS_BUFFER_TOO_SMALL - The ACL specified by NewAcl is too small for the
        inheritance ACEs.  The required size is returned in NewAceLength.


--*/

{

    NTSTATUS Status;
    ULONG i;

    PACE_HEADER OldAce;
    ULONG NewAclSize, NewAceSize;
    ULONG NewAclExtraSize, NewAceExtraSize;
    BOOLEAN AclOverflowed = FALSE;
    BOOLEAN LocalObjectAceInherited;


    RTL_PAGED_CODE();

    //
    // Walk through the original ACL generating any necessary
    // inheritable ACEs.
    //

    NewAclSize = 0;
    NewAclExtraSize = 0;
    *ObjectAceInherited = FALSE;
    for (i = 0, OldAce = FirstAce(Acl);
         i < Acl->AceCount;
         i += 1, OldAce = NextAce(OldAce)) {

        //
        //  RtlpGenerateInheritedAce() will generate the ACE(s) necessary
        //  to inherit a single ACE.  This may be 0, 1, or more ACEs.
        //

        Status = RtlpGenerateInheritedAce(
                     OldAce,
                     IsDirectoryObject,
                     AutoInherit,
                     ClientOwnerSid,
                     ClientGroupSid,
                     ServerOwnerSid,
                     ServerGroupSid,
                     GenericMapping,
                     pNewObjectType,
                     GuidCount,
                     &NewAceSize,
                     NewAcl,
                     &NewAceExtraSize,
                     &LocalObjectAceInherited
                     );

        if ( Status == STATUS_BUFFER_TOO_SMALL ) {
            AclOverflowed = TRUE;
            Status = STATUS_SUCCESS;
        }

        if ( !NT_SUCCESS(Status) ) {
            return Status;
        }

        if ( LocalObjectAceInherited ) {
            *ObjectAceInherited = TRUE;
        }

        //
        // Make room in the ACL for the new ACE
        //
        NewAclSize += NewAceSize;

        //
        // If a previous ACE needed 'extra' space,
        //  reduce that requirement by the size of this ACE.
        //
        // The previous ACE can use this ACE's space temporarily
        //
        if ( NewAceSize > NewAclExtraSize ) {
            NewAclExtraSize = 0 ;
        } else {
            NewAclExtraSize -= NewAceSize;
        }

        //
        // The 'extra' space needed is the larger of that needed by any
        //  previous ACE and that need by this ACE
        //
        NewAclExtraSize = max( NewAclExtraSize, NewAceExtraSize );

    }

    //
    // We only need to include the "ExtraSize" if we've overflowed.
    //  In those cases, the caller will allocate the size we requested and
    //  try again.  Otherwise, the caller won't call back so we don't care
    //  if it knows about the extra size.
    //

    if ( AclOverflowed ) {
        (*NewAclSizeParam) = NewAclSize + NewAclExtraSize;
        return STATUS_BUFFER_TOO_SMALL;
    } else {
        (*NewAclSizeParam) = NewAclSize;
        return STATUS_SUCCESS;
    }

}


NTSTATUS
RtlpComputeMergedAcl2 (
    IN PACL CurrentAcl,
    IN ULONG CurrentGenericControl,
    IN PACL ModificationAcl,
    IN ULONG ModificationGenericControl,
    IN PSID ClientOwnerSid,
    IN PSID ClientGroupSid,
    IN PGENERIC_MAPPING GenericMapping,
    IN BOOLEAN IsSacl,
    IN PULONG AclBufferSize,
    IN OUT PUCHAR AclBuffer,
    OUT PULONG NewGenericControl
    )

/*++

Routine Description:

    This routine implements the 'set' semantics for auto inheritance.

    This routine builds the actual ACL that should be set on an object.
    The built ACL is a composite of the previous ACL on an object and
    the newly set ACL on the object.  The New ACL is built as follows:

    If SEP_ACL_PROTECTED is set in neither CurrentAcl nor ModificationAcl,
    the NewAcl is constructed from the inherited ACEs from the
    CurrentAcl and the non-inherited ACEs from the ModificationAcl.
    (That is, it is impossible to edit an inherited ACE by changing the
    ACL on an object.)

    If SEP_ACL_PROTECTED is set on ModificationAcl, CurrentAcl is ignored.
    NewAcl is built as a copy of ModificationAcl with any INHERITED_ACE
    bits turned off.

    If SEP_ACL_PROTECTED is set on CurrentAcl and not ModificationAcl, the
    CurrentAcl is ignored.  NewAcl is built as a copy of
    ModificationDescriptor.  It is the callers responsibility to ensure
    that the correct ACEs have the INHERITED_ACE bit turned on.

Arguments:

    CurrentAcl - The current ACL on the object.

    CurrentGenericControl - Specifies the control flags from the SecurityDescriptor
        describing the CurrentAcl.

    ModificationAcl - The ACL being applied to the object.

    ModificationGenericControl - Specifies the control flags from the SecurityDescriptor
        describing the CurrentAcl.

    ClientOwnerSid - Specifies the owner Sid to use

    ClientGroupSid - Specifies the new Group Sid to use

    GenericMapping - The mapping of generic to specific and standard
                     access types.

    IsSacl - True if this is the SACL.  False if this is the DACL.

    AclBufferSize - On input, specifies the size of AclBuffer.
        On output, on success, returns the used size of AclBuffer.
        On output, if the buffer is too small, returns the required size of AclBuffer.

    AclBuffer - Receives a pointer to the new (inherited) acl.

    NewGenericControl - Specifies the control flags for the newly
        generated ACL.

        Only the Protected and AutoInherited bits are returned.

Return Value:

    STATUS_SUCCESS - An ACL was successfully generated.

    STATUS_UNKNOWN_REVISION - Indicates the source ACL is a revision that
        is unknown to this routine.

--*/

{
    NTSTATUS Status;
    ULONG ModificationNewAclSize = 0;
    ULONG CurrentNewAclSize = 0;
    ULONG AclRevision;
    BOOLEAN AclOverflowed = FALSE;
    BOOLEAN NullAclOk = TRUE;

    RTL_PAGED_CODE();


    //
    // Assume the ACL revision.
    //

    AclRevision = ACL_REVISION;
    RtlCreateAcl( (PACL)AclBuffer, *AclBufferSize, AclRevision );

    //
    // This routine is only called for the AutoInheritance case.
    //

    *NewGenericControl = SEP_ACL_AUTO_INHERITED;

    //
    // If the new ACL is protected,
    //  simply use the new ACL with the INHERITED_ACE bits turned off.
    //

    if ( (ModificationGenericControl & SEP_ACL_PROTECTED) != 0 ) {

        //
        // Set the Control bits for the resultant descriptor.
        //

        *NewGenericControl |= SEP_ACL_PROTECTED;

        //
        // Only copy the ACL if it is actually present
        //

        if ( ModificationAcl != NULL ) {

            AclRevision = max( AclRevision, ModificationAcl->AclRevision );

            //
            // Copy all ACES, turn off the inherited bit, and generic map them.
            //

            Status = RtlpCopyAces(
                        ModificationAcl,
                        GenericMapping,
                        CopyAllAces,
                        INHERITED_ACE,  // Turn off all INHERITED_ACE flags
                        TRUE,           // Map sids as needed
                        ClientOwnerSid,
                        ClientGroupSid,
                        ClientOwnerSid, // Not technically correct. s.b. server sid
                        ClientGroupSid, // Not technically correct. s.b. server sid
                        TRUE,           // Assume container and skip optimization
                        FALSE,          // Do not retain INHERITED_ACE bit for effective aces
                        &ModificationNewAclSize,
                        (PACL)AclBuffer );

            if ( Status == STATUS_BUFFER_TOO_SMALL ) {
                AclOverflowed = TRUE;
                Status = STATUS_SUCCESS;
            }

            if ( !NT_SUCCESS(Status) ) {
                return Status;
            }

            //
            // If the caller specified an ACL with no ACES,
            //  make sure we generate an ACL with no ACES.
            //

            NullAclOk = FALSE;
        }

    //
    // If the old ACL is protected but the new one isn't,
    //  simply use the new ACL as is.
    //
    // Rely on the caller to get the INHERITED_ACE bits right.
    //

    } else if ( (CurrentGenericControl & SEP_ACL_PROTECTED) != 0 ) {

        //
        // Only do the copy if the new ACL is specified.
        //

        if ( ModificationAcl != NULL ) {
            AclRevision = max( AclRevision, ModificationAcl->AclRevision );

            //
            // Copy all ACES, and generic map them.
            //

            Status = RtlpCopyAces(
                        ModificationAcl,
                        GenericMapping,
                        CopyAllAces,
                        0,
                        TRUE,           // Map sids as needed
                        ClientOwnerSid,
                        ClientGroupSid,
                        ClientOwnerSid, // Not technically correct. s.b. server sid
                        ClientGroupSid, // Not technically correct. s.b. server sid
                        TRUE,           // Assume container and skip optimization
                        TRUE,           // Retain INHERITED_ACE bit for effective aces
                        &ModificationNewAclSize,
                        (PACL)AclBuffer );

            if ( Status == STATUS_BUFFER_TOO_SMALL ) {
                AclOverflowed = TRUE;
                Status = STATUS_SUCCESS;
            }

            if ( !NT_SUCCESS(Status) ) {
                return Status;
            }

            //
            // If the caller specified an ACL with no ACES,
            //  make sure we generate an ACL with no ACES.
            //

            NullAclOk = FALSE;

        //
        // Since the ACL isn't protected,
        //  don't allow NULL ACL semantics.
        //  (those semantics are ambiguous for auto inheritance)
        //
        } else if ( !IsSacl ) {
            return STATUS_INVALID_ACL;
        }


    //
    // If neither are protected,
    //  use the non-inherited ACEs from the new ACL, and
    //  preserve the inherited ACEs from the old ACL.
    //

    } else {

        //
        // NULL ACLs are always OK for a SACL.
        // NULL ACLs are never OK for a non-protected DACL.
        //

        NullAclOk = IsSacl;


        //
        // Only do the copy if the new ACL is specified.
        //

        if ( ModificationAcl != NULL ) {
            AclRevision = max( AclRevision, ModificationAcl->AclRevision );

            //
            // Copy the non-inherited ACES, and generic map them.
            //

            Status = RtlpCopyAces(
                        ModificationAcl,
                        GenericMapping,
                        CopyNonInheritedAces,
                        0,
                        TRUE,           // Map sids as needed
                        ClientOwnerSid,
                        ClientGroupSid,
                        ClientOwnerSid, // Not technically correct. s.b. server sid
                        ClientGroupSid, // Not technically correct. s.b. server sid
                        TRUE,           // Assume container and skip optimization
                        FALSE,          // Do not retain INHERITED_ACE bit for effective aces
                        &ModificationNewAclSize,
                        (PACL)AclBuffer );

            if ( Status == STATUS_BUFFER_TOO_SMALL ) {
                AclOverflowed = TRUE;
                Status = STATUS_SUCCESS;
            }

            if ( !NT_SUCCESS(Status) ) {
                return Status;
            }

            //
            // If the caller specified an ACL with no ACES,
            //  make sure we generate an ACL with no ACES.
            //
            // If inherited aces were deleted, leave the flag alone allowing
            //  a NULL SACL to be generated.
            //

            if ( ModificationAcl->AceCount == 0 ) {
                NullAclOk = FALSE;
            }

        //
        // Since the ACL isn't protected,
        //  don't allow NULL ACL semantics.
        //  (those semantics are ambiguous for auto inheritance)
        //
        } else if ( !IsSacl ) {
            return STATUS_INVALID_ACL;
        }


        //
        // Only do the copy if the old ACL is specified.
        //

        if ( CurrentAcl != NULL ) {

            AclRevision = max( AclRevision, CurrentAcl->AclRevision );


            //
            // Copy the inherited ACES, and generic map them.
            //
            // Don't bother mapping the sids in these ACEs.  They got mapped
            // during inheritance.
            //

            Status = RtlpCopyAces(
                        CurrentAcl,
                        GenericMapping,
                        CopyInheritedAces,
                        0,
                        FALSE,          // Don't map the sids,
                        NULL,
                        NULL,
                        NULL,
                        NULL,
                        TRUE,           // Assume container and skip optimization
                        FALSE,          // Do not retain INHERITED_ACE bit for effective aces
                        &CurrentNewAclSize,
                        (PACL)AclBuffer );

            if ( Status == STATUS_BUFFER_TOO_SMALL ) {
                AclOverflowed = TRUE;
                Status = STATUS_SUCCESS;
            }

            if ( !NT_SUCCESS(Status) ) {
                return Status;
            }
        }
    }

    //
    // If this routine didn't build the ACL,
    //  tell the caller to use an explict NULL ACL
    //

    if ( ModificationNewAclSize + CurrentNewAclSize == 0) {
        //
        // If the Acl was explictly assigned,
        //  generate a NULL ACL based on the path taken above.
        //

        if ( NullAclOk ) {
            *AclBufferSize = 0;
            return STATUS_SUCCESS;
        }
    }


    //
    // And make sure we don't exceed the length limitations of an ACL (WORD)
    //

    if ( ModificationNewAclSize + CurrentNewAclSize + sizeof(ACL) > 0xFFFF) {
        return(STATUS_BAD_INHERITANCE_ACL);
    }

    (*AclBufferSize) = ModificationNewAclSize + CurrentNewAclSize + sizeof(ACL);

    if ( AclOverflowed ) {
        return STATUS_BUFFER_TOO_SMALL;
    }

    //
    // Patch the real ACL size and revision into the ACL
    //

    ((PACL)AclBuffer)->AclSize = (USHORT) *AclBufferSize;
    ((PACL)AclBuffer)->AclRevision = (UCHAR) AclRevision;

    return STATUS_SUCCESS;
}


NTSTATUS
RtlpComputeMergedAcl (
    IN PACL CurrentAcl,
    IN ULONG CurrentGenericControl,
    IN PACL ModificationAcl,
    IN ULONG ModificationGenericControl,
    IN PSID ClientOwnerSid,
    IN PSID ClientGroupSid,
    IN PGENERIC_MAPPING GenericMapping,
    IN BOOLEAN IsSacl,
    OUT PACL *NewAcl,
    OUT PULONG NewGenericControl
    )

/*++

Routine Description:

    This routine builds the actual ACL that should be set on an object.
    The built ACL is a composite of the previous ACL on an object and
    the newly set ACL on the object.  The New ACL is built as follows:

    If SEP_ACL_PROTECTED is set in neither CurrentAcl nor ModificationAcl,
    the NewAcl is constructed from the inherited ACEs from the
    CurrentAcl and the non-inherited ACEs from the ModificationAcl.
    (That is, it is impossible to edit an inherited ACE by changing the
    ACL on an object.)

    If SEP_ACL_PROTECTED is set on ModificationAcl, CurrentAcl is ignored.
    NewAcl is built as a copy of ModificationAcl with any INHERITED_ACE
    bits turned off.

    If SEP_ACL_PROTECTED is set on CurrentAcl and not ModificationAcl, the
    CurrentAcl is ignored.  NewAcl is built as a copy of
    ModificationDescriptor.  It is the callers responsibility to ensure
    that the correct ACEs have the INHERITED_ACE bit turned on.

Arguments:

    CurrentAcl - The current ACL on the object.

    CurrentGenericControl - Specifies the control flags from the SecurityDescriptor
        describing the CurrentAcl.

    ModificationAcl - The ACL being applied to the object.

    ModificationGenericControl - Specifies the control flags from the SecurityDescriptor
        describing the CurrentAcl.

    ClientOwnerSid - Specifies the owner Sid to use

    ClientGroupSid - Specifies the new Group Sid to use

    GenericMapping - The mapping of generic to specific and standard
                     access types.

    IsSacl - True if this is the SACL.  False if this is the DACL.

    NewAcl - Receives a pointer to the new resultant acl.

    NewGenericControl - Specifies the control flags for the newly
        generated ACL.

        Only the Protected and AutoInherited bits are returned.

Return Value:

    STATUS_SUCCESS - An ACL was successfully generated.

    STATUS_UNKNOWN_REVISION - Indicates the source ACL is a revision that
        is unknown to this routine.

--*/

{
    NTSTATUS Status;
    ULONG AclBufferSize;
    ULONG i;

    RTL_PAGED_CODE();

    //
    // Implement a two pass strategy.
    //
    // First try to create the ACL in a fixed length buffer.
    // If that is too small,
    //  then use the buffer size determined on the first pass
    //

    AclBufferSize = 1024;
    for ( i=0; i<2 ; i++ ) {

        //
        // Allocate heap for the new ACL.
        //

        (*NewAcl) = ExAllocatePoolWithTag(
                        PagedPool,
                        AclBufferSize,
                        'cAeS' );

        if ((*NewAcl) == NULL ) {
            return( STATUS_NO_MEMORY );
        }

        //
        // Merge the ACLs
        //

        Status = RtlpComputeMergedAcl2 (
                    CurrentAcl,
                    CurrentGenericControl,
                    ModificationAcl,
                    ModificationGenericControl,
                    ClientOwnerSid,
                    ClientGroupSid,
                    GenericMapping,
                    IsSacl,
                    &AclBufferSize,
                    (PUCHAR) *NewAcl,
                    NewGenericControl );


        if ( NT_SUCCESS(Status) ) {

            //
            // If a NULL ACL should be used,
            //  tell the caller.
            //

            if ( AclBufferSize == 0 ) {
                ExFreePool( *NewAcl );
                *NewAcl = NULL;
            }

            break;

        } else {
            ExFreePool( *NewAcl );
            *NewAcl = NULL;

            if ( Status != STATUS_BUFFER_TOO_SMALL ) {
                break;
            }
        }
    }


    return Status;
}

#endif // WIN16

#if DBG
NTSTATUS
RtlDumpUserSid(
    VOID
    )
{
    NTSTATUS Status;
    HANDLE   TokenHandle;
    CHAR     Buffer[200];
    ULONG    ReturnLength;
    PSID     pSid;
    UNICODE_STRING SidString;
    PTOKEN_USER  User;

    //
    // Attempt to open the impersonation token first
    //

    Status = NtOpenThreadToken(
                 NtCurrentThread(),
                 GENERIC_READ,
                 FALSE,
                 &TokenHandle
                 );

    if (!NT_SUCCESS( Status )) {

        DbgPrint("Not impersonating, status = %X, trying process token\n",Status);

        Status = NtOpenProcessToken(
                     NtCurrentProcess(),
                     GENERIC_READ,
                     &TokenHandle
                     );

        if (!NT_SUCCESS( Status )) {
            DbgPrint("Unable to open process token, status = %X\n",Status);
            return( Status );
        }
    }

    Status = NtQueryInformationToken (
                 TokenHandle,
                 TokenUser,
                 Buffer,
                 200,
                 &ReturnLength
                 );

    if (!NT_SUCCESS( Status )) {

        DbgPrint("Unable to query user sid, status = %X \n",Status);
        NtClose(TokenHandle);
        return( Status );
    }

    User = (PTOKEN_USER)Buffer;

    pSid = User->User.Sid;

    Status = RtlConvertSidToUnicodeString( &SidString, pSid, TRUE );

    if (!NT_SUCCESS( Status )) {
        DbgPrint("Unable to format sid string, status = %X \n",Status);
        NtClose(TokenHandle);
        return( Status );
    }

    DbgPrint("Current Sid = %wZ \n",&SidString);

    RtlFreeUnicodeString( &SidString );

    return( STATUS_SUCCESS );
}

#endif


NTSTATUS
RtlpConvertToAutoInheritSecurityObject(
    IN PSECURITY_DESCRIPTOR ParentDescriptor OPTIONAL,
    IN PSECURITY_DESCRIPTOR CurrentSecurityDescriptor,
    OUT PSECURITY_DESCRIPTOR *NewSecurityDescriptor,
    IN GUID *ObjectType OPTIONAL,
    IN BOOLEAN IsDirectoryObject,
    IN PGENERIC_MAPPING GenericMapping
    )
/*++

Routine Description:

    This routine a converts a security descriptor whose ACLs are not marked
    as AutoInherit to a security descriptor whose ACLs are marked as
    AutoInherit.

    See comments for RtlConvertToAutoInheritSecurityObject.

Arguments:

    ParentDescriptor - Supplies the Security Descriptor for the parent
        directory under which a object exists.  If there is
        no parent directory, then this argument is specified as NULL.

    CurrentSecurityDescriptor - Supplies a pointer to the objects security descriptor
        that is going to be altered by this procedure.

    NewSecurityDescriptor Points to a pointer that is to be made to point to the
        newly allocated self-relative security descriptor. When no
        longer needed, this descriptor must be freed using
        DestroyPrivateObjectSecurity().

    ObjectType - GUID of the object type being created.  If the object being
        created has no GUID associated with it, then this argument is
        specified as NULL.

    IsDirectoryObject - Specifies if the object is a
        directory object.  A value of TRUE indicates the object is a
        container of other objects.

    GenericMapping - Supplies a pointer to a generic mapping array denoting
        the mapping between each generic right to specific rights.

Return Value:

    STATUS_SUCCESS - The operation was successful.

    See comments for RtlConvertToAutoInheritSecurityObject.


--*/
{
    NTSTATUS Status;
    PISECURITY_DESCRIPTOR CurrentDescriptor;
    PACL CurrentSacl;
    PACL CurrentDacl;

    PSID NewOwner;
    PSID NewGroup;

    PACL NewSacl = NULL;
    ULONG NewSaclControl = 0;
    BOOLEAN NewSaclAllocated = FALSE;

    PACL NewDacl = NULL;
    ULONG NewDaclControl = 0;
    BOOLEAN NewDaclAllocated = FALSE;
    PACL TemplateInheritedDacl = NULL;
    ULONG GenericControl;

    ULONG AllocationSize;
    ULONG NewOwnerSize;
    ULONG NewGroupSize;
    ULONG NewSaclSize;
    ULONG NewDaclSize;

    PCHAR Field;
    PCHAR Base;

    PISECURITY_DESCRIPTOR_RELATIVE INewDescriptor = NULL;
    ULONG ReturnLength;
    NTSTATUS PassedStatus;
    HANDLE PrimaryToken;

    RTL_PAGED_CODE();

    CurrentDescriptor = CurrentSecurityDescriptor;

    //
    // Validate the incoming security descriptor.
    //

    if (!RtlValidSecurityDescriptor ( CurrentDescriptor )) {
        Status = STATUS_INVALID_SECURITY_DESCR;
        goto Cleanup;
    }


    NewOwner = RtlpOwnerAddrSecurityDescriptor( CurrentDescriptor );
    if ( NewOwner == NULL ) {
        Status = STATUS_INVALID_SECURITY_DESCR;
        goto Cleanup;
    }

    NewGroup = RtlpGroupAddrSecurityDescriptor( CurrentDescriptor );




    //
    // Handle the SACL.
    //
    //
    // If the SACL isn't present,
    //  special case it.
    //

    CurrentSacl = RtlpSaclAddrSecurityDescriptor( CurrentDescriptor );

    if ( CurrentSacl == NULL ) {
        PACL ParentSacl;

        // Preserve the Acl Present bit and protected bit from the existing descriptor.
        NewSaclControl |= CurrentDescriptor->Control & (SE_SACL_PROTECTED|SE_SACL_PRESENT);

        // Always set the autoinherited bit.
        NewSaclControl |= SE_SACL_AUTO_INHERITED;


        //
        // If the Parent also has a NULL SACL,
        //  just consider this SACL as inherited.
        //  otherwise, this SACL is protected.
        //

        ParentSacl = ARGUMENT_PRESENT(ParentDescriptor) ?
                        RtlpSaclAddrSecurityDescriptor( ((SECURITY_DESCRIPTOR *)ParentDescriptor)) :
                        NULL;
        if ( ParentSacl != NULL) {
            NewSaclControl |= SE_SACL_PROTECTED;
        }


    //
    // If the SACL is already converted,
    //  or if this object is at the root of the tree,
    //  simply leave it alone.
    //
    // Don't force the Protect bit on at the root of the tree since it is semantically
    //  a no-op and gets in the way if the object is ever moved.
    //

    } else if ( RtlpAreControlBitsSet( CurrentDescriptor, SE_SACL_AUTO_INHERITED) ||
                RtlpAreControlBitsSet( CurrentDescriptor, SE_SACL_PROTECTED ) ||
                !ARGUMENT_PRESENT(ParentDescriptor) ) {

        // Preserve the Acl Present bit and protected bit from the existing descriptor.
        NewSaclControl |= CurrentDescriptor->Control & (SE_SACL_PROTECTED|SE_SACL_PRESENT);

        // Always set the autoinherited bit.
        NewSaclControl |= SE_SACL_AUTO_INHERITED;

        NewSacl = CurrentSacl;


    //
    // If the SACL is present,
    //  compute a new SACL with appropriate ACEs marked as inherited.
    //

    } else {


        Status = RtlpConvertAclToAutoInherit (
                    ARGUMENT_PRESENT(ParentDescriptor) ?
                        RtlpSaclAddrSecurityDescriptor(
                            ((SECURITY_DESCRIPTOR *)ParentDescriptor)) :
                        NULL,
                    RtlpSaclAddrSecurityDescriptor(CurrentDescriptor),
                    ObjectType,
                    IsDirectoryObject,
                    RtlpOwnerAddrSecurityDescriptor(CurrentDescriptor),
                    RtlpGroupAddrSecurityDescriptor(CurrentDescriptor),
                    GenericMapping,
                    &NewSacl,
                    &GenericControl );

        if ( !NT_SUCCESS(Status) ) {
            goto Cleanup;
        }

        NewSaclAllocated = TRUE;
        NewSaclControl |= SE_SACL_PRESENT | SeControlGenericToSacl( GenericControl );
    }


    //
    // Handle the DACL.
    //
    //
    // If the DACL isn't present,
    //  special case it.
    //

    CurrentDacl = RtlpDaclAddrSecurityDescriptor( CurrentDescriptor );

    if ( CurrentDacl == NULL ) {
        // Preserve the Dacl Present bit from the existing descriptor.
        NewDaclControl |= CurrentDescriptor->Control & SE_DACL_PRESENT;

        // Always set the autoinherited bit.
        // Force it protected.
        NewDaclControl |= SE_DACL_AUTO_INHERITED | SE_DACL_PROTECTED;



    //
    // If the DACL is already converted,
    //  or if this object is at the root of the tree,
    //  simply leave it alone.
    //
    // Don't force the Protect bit on at the root of the tree since it is semantically
    //  a no-op and gets in the way if the object is ever moved.
    //

    } else if ( RtlpAreControlBitsSet( CurrentDescriptor, SE_DACL_AUTO_INHERITED) ||
                RtlpAreControlBitsSet( CurrentDescriptor, SE_DACL_PROTECTED ) ||
                !ARGUMENT_PRESENT(ParentDescriptor) ) {

        // Preserve the Acl Present bit and protected bit from the existing descriptor.
        NewDaclControl |= CurrentDescriptor->Control & (SE_DACL_PROTECTED|SE_DACL_PRESENT);

        // Always set the autoinherited bit.
        NewDaclControl |= SE_DACL_AUTO_INHERITED;

        NewDacl = CurrentDacl;



    //
    // If the DACL is present,
    //  compute a new DACL with appropriate ACEs marked as inherited.
    //

    } else {


        Status = RtlpConvertAclToAutoInherit (
                    ARGUMENT_PRESENT(ParentDescriptor) ?
                        RtlpDaclAddrSecurityDescriptor(
                            ((SECURITY_DESCRIPTOR *)ParentDescriptor)) :
                        NULL,
                    RtlpDaclAddrSecurityDescriptor(CurrentDescriptor),
                    ObjectType,
                    IsDirectoryObject,
                    RtlpOwnerAddrSecurityDescriptor(CurrentDescriptor),
                    RtlpGroupAddrSecurityDescriptor(CurrentDescriptor),
                    GenericMapping,
                    &NewDacl,
                    &GenericControl );

        if ( !NT_SUCCESS(Status) ) {
            goto Cleanup;
        }

        NewDaclAllocated = TRUE;
        NewDaclControl |= SE_DACL_PRESENT | SeControlGenericToDacl( GenericControl );
    }



    //
    // Build the resultant security descriptor
    //
    // Also map the ACEs for application to the target object
    // type, if they haven't already been mapped.
    //
    NewOwnerSize = LongAlignSize(SeLengthSid(NewOwner));

    if ( NewGroup != NULL ) {
        NewGroupSize = LongAlignSize(SeLengthSid(NewGroup));
    } else {
        NewGroupSize = 0;
    }

    if (NewSacl != NULL) {
        NewSaclSize = LongAlignSize(NewSacl->AclSize);
    } else {
        NewSaclSize = 0;
    }

    if (NewDacl != NULL) {
        NewDaclSize = LongAlignSize(NewDacl->AclSize);
    } else {
        NewDaclSize = 0;
    }

    AllocationSize = LongAlignSize(sizeof(SECURITY_DESCRIPTOR_RELATIVE)) +
                     NewOwnerSize +
                     NewGroupSize +
                     NewSaclSize  +
                     NewDaclSize;

    //
    // Allocate and initialize the security descriptor as
    // self-relative form.
    //


    INewDescriptor = ExAllocatePoolWithTag(
                        PagedPool,
                        AllocationSize,
                        'dSeS' );

    if ( INewDescriptor == NULL ) {
        Status = STATUS_NO_MEMORY;
        goto Cleanup;
    }


    //
    // Initialize the security descriptor as self-relative form.
    //

    RtlCreateSecurityDescriptorRelative(
        INewDescriptor,
        SECURITY_DESCRIPTOR_REVISION
        );

    RtlpSetControlBits( INewDescriptor, SE_SELF_RELATIVE );

    Base = (PCHAR)(INewDescriptor);
    Field =  Base + sizeof(SECURITY_DESCRIPTOR_RELATIVE);

    //
    // Copy the Sacl
    //

    RtlpSetControlBits( INewDescriptor, NewSaclControl );
    if (NewSacl != NULL ) {

        RtlCopyMemory( Field, NewSacl, NewSacl->AclSize );
        INewDescriptor->Sacl = RtlPointerToOffset(Base,Field);
        Field += NewSaclSize;

    } else {

        INewDescriptor->Sacl = 0;
    }

    //
    // Copy the Dacl
    //

    RtlpSetControlBits( INewDescriptor, NewDaclControl );
    if (NewDacl != NULL ) {

        RtlCopyMemory( Field, NewDacl, NewDacl->AclSize );
        INewDescriptor->Dacl = RtlPointerToOffset(Base,Field);
        Field += NewDaclSize;

    } else {

        INewDescriptor->Dacl = 0;
    }

    //
    // Assign the owner
    //

    RtlCopyMemory( Field, NewOwner, SeLengthSid(NewOwner) );
    INewDescriptor->Owner = RtlPointerToOffset(Base,Field);
    Field += NewOwnerSize;

    if ( NewGroup != NULL ) {
        RtlCopyMemory( Field, NewGroup, SeLengthSid(NewGroup) );
        INewDescriptor->Group = RtlPointerToOffset(Base,Field);
    }

    Status = STATUS_SUCCESS;



    //
    // Cleanup any locally used resources.
    //
Cleanup:
    if (NewDaclAllocated) {
            ExFreePool( NewDacl );
    }
    if (NewSaclAllocated) {
            ExFreePool( NewSacl );
    }

    *NewSecurityDescriptor = (PSECURITY_DESCRIPTOR) INewDescriptor;

    return Status;


}

//
// Local macro to classify the ACE flags in an ACE.
//
// Returns one or more of the following ACE flags:
//
//  CONTAINER_INHERIT_ACE - ACE is inherited to child containers
//  OBJECT_INHERIT_ACE - ACE is inherited to child leaf objects
//  EFFECTIVE_ACE - ACE is used during access validation
//

#define MAX_CHILD_SID_GROUP_SIZE 3 // Number of bits in above list
#define EFFECTIVE_ACE INHERIT_ONLY_ACE
#define AceFlagsInAce( _Ace) \
            (((PACE_HEADER)(_Ace))->AceFlags & (OBJECT_INHERIT_ACE|CONTAINER_INHERIT_ACE) | \
             (((PACE_HEADER)(_Ace))->AceFlags & INHERIT_ONLY_ACE) ^ INHERIT_ONLY_ACE )


BOOLEAN
RtlpCompareAces(
    IN PKNOWN_ACE InheritedAce,
    IN PKNOWN_ACE ChildAce,
    IN PSID OwnerSid,
    IN PSID GroupSid
    )
/*++

Routine Description:

    Compare two aces to see if they are "substantially" the same.

Arguments:

    InheritedAce - Computed ACE as inherited from DirectoryAcl.

    ChildAce - The current acl on the object.  This ACL must be a revision 2 ACL.

    ObjectType - GUID of the object type being created.  If the object being
        created has no GUID associated with it, then this argument is
        specified as NULL.

    OwnerSid - Specifies the owner Sid to use.
        If not specified, the owner sid is not treated as special.

    GroupSid - Specifies the group SID to use.
        If not specified, the group sid is not treated as special.

Return Value:

    TRUE - The ACEs are substantially the same.
    FALSE - The ACEs are not substantially the same.

--*/
{
    BOOLEAN AcesCompare = FALSE;

    if (IsObjectAceType(InheritedAce) && IsObjectAceType(ChildAce)) {

        AcesCompare = RtlpCompareKnownObjectAces( (PKNOWN_OBJECT_ACE)InheritedAce,
                                                  (PKNOWN_OBJECT_ACE)ChildAce,
                                                  OwnerSid,
                                                  GroupSid
                                                  );
    } else {

        if (!IsObjectAceType(InheritedAce) && !IsObjectAceType(ChildAce)) {

            AcesCompare = RtlpCompareKnownAces( InheritedAce,
                                                ChildAce,
                                                OwnerSid,
                                                GroupSid
                                                );
        }
    }

    return( AcesCompare );
}


BOOLEAN
RtlpCompareKnownAces(
    IN PKNOWN_ACE InheritedAce,
    IN PKNOWN_ACE ChildAce,
    IN PSID OwnerSid OPTIONAL,
    IN PSID GroupSid OPTIONAL
    )

/*++

Routine Description:

    Compare two aces to see if they are "substantially" the same.

Arguments:

    InheritedAce - Computed ACE as inherited from DirectoryAcl.

    ChildAce - The current acl on the object.  This ACL must be a revision 2 ACL.

    OwnerSid - Specifies the owner Sid to use.
        If not specified, the owner sid is not treated as special.

    GroupSid - Specifies the group SID to use.
        If not specified, the group sid is not treated as special.

Return Value:

    TRUE - The ACEs are substantially the same.
    FALSE - The ACEs are not substantially the same.

--*/

{
    NTSTATUS Status;
    ACE_HEADER volatile *InheritedAceHdr = &InheritedAce->Header;

    RTL_PAGED_CODE();

    ASSERT(!IsObjectAceType(InheritedAce));
    ASSERT(!IsObjectAceType(ChildAce));

    //
    // If the Ace types are different,
    //  we don't match.
    //
    if ( RtlBaseAceType[ChildAce->Header.AceType] != RtlBaseAceType[InheritedAceHdr->AceType] ) {
#if DBG
        if ( RtlpVerboseConvert ) {
            KdPrint(("AceType mismatch"));
        }
#endif // DBG
        return FALSE;
    }

    //
    // If this is a system ACE,
    //  ensure the SUCCESS/FAILURE flags match.
    //

    if ( RtlIsSystemAceType[ChildAce->Header.AceType] ) {
        if ( (ChildAce->Header.AceFlags & (SUCCESSFUL_ACCESS_ACE_FLAG|FAILED_ACCESS_ACE_FLAG)) !=
             (InheritedAceHdr->AceFlags & (SUCCESSFUL_ACCESS_ACE_FLAG|FAILED_ACCESS_ACE_FLAG)) ) {
#if DBG
            if ( RtlpVerboseConvert ) {
                KdPrint(("System ace success/fail mismatch"));
            }
#endif // DBG
            return FALSE;
        }
    }

    //
    // If the SID of the inherited ACE doesn't match,
    //  we don't match.
    //

    if ( !RtlEqualSid( (PSID)&ChildAce->SidStart, (PSID)&InheritedAce->SidStart )) {

        //
        // The inheritance algorithm only does SID mapping when building the effective
        //  ace.  So, we only check for a mapped SID if the child ACE is an effective ACE.
        //

        if ( AceFlagsInAce(ChildAce) != EFFECTIVE_ACE ) {
#if DBG
            if ( RtlpVerboseConvert ) {
                KdPrint(("SID mismatch"));
            }
#endif // DBG
            return FALSE;
        }

        //
        // In the case of CreatorOwner and CreatorGroup, the SIDs don't have to
        //  exactly match.  When the InheritedAce was generated, care was taken
        //  to NOT map these sids.  The SID may (or may not) have been mapped in
        //  the ChildAce.  We want to compare equal in both cases.
        //
        // If the InheritedAce contains a CreatorOwner/Group SID,
        //  do the another comparison of the SID in the child ACE with the
        //  real owner/group from the child security descriptor.
        //

        if ( OwnerSid != NULL || GroupSid != NULL ) {
            SID_IDENTIFIER_AUTHORITY  CreatorSidAuthority = SECURITY_CREATOR_SID_AUTHORITY;
            ULONG CreatorSid[CREATOR_SID_SIZE];

            //
            // Allocate and initialize the universal SIDs we're going to need
            // to look for inheritable ACEs.
            //

            ASSERT(RtlLengthRequiredSid( 1 ) == CREATOR_SID_SIZE);
            Status = RtlInitializeSid( (PSID)CreatorSid, &CreatorSidAuthority, 1 );
            if ( !NT_SUCCESS(Status) ) {
                return FALSE;
            }

            *(RtlpSubAuthoritySid( (PSID)CreatorSid, 0 )) = SECURITY_CREATOR_OWNER_RID;

            if (RtlEqualPrefixSid ( (PSID)&InheritedAce->SidStart, CreatorSid )) {
                ULONG Rid;

                Rid = *RtlpSubAuthoritySid( (PSID)&InheritedAce->SidStart, 0 );
                switch (Rid) {
                case SECURITY_CREATOR_OWNER_RID:
                    if ( OwnerSid == NULL ||
                         !RtlEqualSid( (PSID)&ChildAce->SidStart, OwnerSid )) {
#if DBG
                        if ( RtlpVerboseConvert ) {
                            KdPrint(("SID mismatch (Creator Owner)"));
                        }
#endif // DBG
                        return FALSE;
                    }
                    break;
                case SECURITY_CREATOR_GROUP_RID:
                    if ( GroupSid == NULL ||
                         !RtlEqualSid( (PSID)&ChildAce->SidStart, GroupSid )) {
#if DBG
                        if ( RtlpVerboseConvert ) {
                            KdPrint(("SID mismatch (Creator Group)"));
                        }
#endif // DBG
                        return FALSE;
                    }
                    break;
                default:
#if DBG
                    if ( RtlpVerboseConvert ) {
                        KdPrint(("SID mismatch (Creator)"));
                    }
#endif // DBG
                    return FALSE;
                }

            } else {
#if DBG
                if ( RtlpVerboseConvert ) {
                    KdPrint(("SID mismatch"));
                }
#endif // DBG
                return FALSE;
            }
        } else {
#if DBG
            if ( RtlpVerboseConvert ) {
                KdPrint(("SID mismatch"));
            }
#endif // DBG
            return FALSE;
        }
    }

    return TRUE;

}


BOOLEAN
RtlpCompareKnownObjectAces(
    IN PKNOWN_OBJECT_ACE InheritedAce,
    IN PKNOWN_OBJECT_ACE ChildAce,
    IN PSID OwnerSid OPTIONAL,
    IN PSID GroupSid OPTIONAL
    )

/*++

Routine Description:

    Compare two aces to see if they are "substantially" the same.

Arguments:

    InheritedAce - Computed ACE as inherited from DirectoryAcl.

    ChildAce - The current acl on the object.  This ACL must be a revision 2 ACL.

    ObjectType - GUID of the object type being created.  If the object being
        created has no GUID associated with it, then this argument is
        specified as NULL.

    OwnerSid - Specifies the owner Sid to use.
        If not specified, the owner sid is not treated as special.

    GroupSid - Specifies the group SID to use.
        If not specified, the group sid is not treated as special.

Return Value:

    TRUE - The ACEs are substantially the same.
    FALSE - The ACEs are not substantially the same.

--*/

{
    NTSTATUS Status;
    BOOLEAN DoingObjectAces;
    GUID *ChildObjectGuid;
    GUID *InhObjectGuid;
    GUID *ChildInheritedObjectGuid;
    GUID *InhInheritedObjectGuid;
    ACE_HEADER volatile *InheritedAceHdr = &InheritedAce->Header;

    RTL_PAGED_CODE();

    ASSERT(IsObjectAceType(InheritedAce));
    ASSERT(IsObjectAceType(ChildAce));
    //
    // If the Ace types are different,
    //  we don't match.
    //
    if ( RtlBaseAceType[ChildAce->Header.AceType] != RtlBaseAceType[InheritedAceHdr->AceType] ) {
#if DBG
        if ( RtlpVerboseConvert ) {
            KdPrint(("AceType mismatch"));
        }
#endif // DBG
        return FALSE;
    }

    //
    // If this is a system ACE,
    //  ensure the SUCCESS/FAILURE flags match.
    //

    if ( RtlIsSystemAceType[ChildAce->Header.AceType] ) {
        if ( (ChildAce->Header.AceFlags & (SUCCESSFUL_ACCESS_ACE_FLAG|FAILED_ACCESS_ACE_FLAG)) !=
             (InheritedAceHdr->AceFlags & (SUCCESSFUL_ACCESS_ACE_FLAG|FAILED_ACCESS_ACE_FLAG)) ) {
#if DBG
            if ( RtlpVerboseConvert ) {
                KdPrint(("System ace success/fail mismatch"));
            }
#endif // DBG
            return FALSE;
        }
    }

    //
    // Get the GUIDs from the Object Aces
    //

    ChildObjectGuid = RtlObjectAceObjectType(ChildAce);
    ChildInheritedObjectGuid = RtlObjectAceInheritedObjectType(ChildAce);

    InhObjectGuid = RtlObjectAceObjectType(InheritedAce);
    InhInheritedObjectGuid = RtlObjectAceInheritedObjectType(InheritedAce);

    //
    // If the InheritedObjectGuid is present in either ACE,
    //  they must be equal.
    //

    if ( ChildInheritedObjectGuid != NULL || InhInheritedObjectGuid != NULL ) {

        if ( ChildInheritedObjectGuid == NULL ||
             InhInheritedObjectGuid == NULL ||
             !RtlpIsEqualGuid( ChildInheritedObjectGuid, InhInheritedObjectGuid )) {
#if DBG
            if ( RtlpVerboseConvert ) {
                KdPrint(("InheritedObject GUID mismatch"));
            }
#endif // DBG
            return FALSE;
        }
    }

    //
    // If the ObjectGUID is present in either ACE,
    //  they must be equal.
    //
    // Any missing object GUID defaults to the passed in object GUID.
    //

    if ( (ChildObjectGuid != NULL) && (InhObjectGuid != NULL) ) {

        if (!RtlpIsEqualGuid( ChildObjectGuid, InhObjectGuid )) {
#if DBG
            if ( RtlpVerboseConvert ) {
                KdPrint(("Object GUID mismatch"));
            }
#endif // DBG

            return( FALSE );
        }
    } else {

        //
        // One or both is NULL, if it's only one, they don't match.
        //

        if ( !((ChildObjectGuid == NULL) && (InhObjectGuid == NULL)) ) {
#if DBG
            if ( RtlpVerboseConvert ) {
                KdPrint(("Object GUID mismatch"));
            }
#endif // DBG

            return( FALSE );
        }
    }

    //
    // If the SID of the inherited ACE doesn't match,
    //  we don't match.
    //

    if ( !RtlEqualSid( RtlObjectAceSid(ChildAce), RtlObjectAceSid(InheritedAce))) {

        //
        // The inheritance algorithm only does SID mapping when building the effective
        //  ace.  So, we only check for a mapped SID if the child ACE is an effective ACE.
        //

        if ( AceFlagsInAce(ChildAce) != EFFECTIVE_ACE ) {
#if DBG
            if ( RtlpVerboseConvert ) {
                KdPrint(("SID mismatch"));
            }
#endif // DBG
            return FALSE;
        }



        //
        // In the case of CreatorOwner and CreatorGroup, the SIDs don't have to
        //  exactly match.  When the InheritedAce was generated, care was taken
        //  to NOT map these sids.  The SID may (or may not) have been mapped in
        //  the ChildAce.  We want to compare equal in both cases.
        //
        // If the InheritedAce contains a CreatorOwner/Group SID,
        //  do the another comparison of the SID in the child ACE with the
        //  real owner/group from the child security descriptor.
        //

        if ( OwnerSid != NULL || GroupSid != NULL ) {
            SID_IDENTIFIER_AUTHORITY  CreatorSidAuthority = SECURITY_CREATOR_SID_AUTHORITY;
            ULONG CreatorSid[CREATOR_SID_SIZE];

            //
            // Allocate and initialize the universal SIDs we're going to need
            // to look for inheritable ACEs.
            //

            ASSERT(RtlLengthRequiredSid( 1 ) == CREATOR_SID_SIZE);
            Status = RtlInitializeSid( (PSID)CreatorSid, &CreatorSidAuthority, 1 );
            if ( !NT_SUCCESS(Status) ) {
                return FALSE;
            }

            *(RtlpSubAuthoritySid( (PSID)CreatorSid, 0 )) = SECURITY_CREATOR_OWNER_RID;

            if (RtlEqualPrefixSid ( RtlObjectAceSid(InheritedAce), CreatorSid )) {
                ULONG Rid;

                Rid = *RtlpSubAuthoritySid( RtlObjectAceSid(InheritedAce), 0 );
                switch (Rid) {
                case SECURITY_CREATOR_OWNER_RID:
                    if ( OwnerSid == NULL ||
                         !RtlEqualSid( RtlObjectAceSid(ChildAce), OwnerSid )) {
#if DBG
                        if ( RtlpVerboseConvert ) {
                            KdPrint(("SID mismatch (Creator Owner)"));
                        }
#endif // DBG
                        return FALSE;
                    }
                    break;
                case SECURITY_CREATOR_GROUP_RID:
                    if ( GroupSid == NULL ||
                         !RtlEqualSid( RtlObjectAceSid(ChildAce), GroupSid )) {
#if DBG
                        if ( RtlpVerboseConvert ) {
                            KdPrint(("SID mismatch (Creator Group)"));
                        }
#endif // DBG
                        return FALSE;
                    }
                    break;
                default:
#if DBG
                    if ( RtlpVerboseConvert ) {
                        KdPrint(("SID mismatch (Creator)"));
                    }
#endif // DBG
                    return FALSE;
                }

            } else {
#if DBG
                if ( RtlpVerboseConvert ) {
                    KdPrint(("SID mismatch"));
                }
#endif // DBG
                return FALSE;
            }
        } else {
#if DBG
            if ( RtlpVerboseConvert ) {
                KdPrint(("SID mismatch"));
            }
#endif // DBG
            return FALSE;
        }
    }

    return TRUE;

}




NTSTATUS
RtlpConvertAclToAutoInherit (
    IN PACL ParentAcl OPTIONAL,
    IN PACL ChildAcl,
    IN GUID *ObjectType OPTIONAL,
    IN BOOLEAN IsDirectoryObject,
    IN PSID OwnerSid,
    IN PSID GroupSid,
    IN PGENERIC_MAPPING GenericMapping,
    OUT PACL *NewAcl,
    OUT PULONG NewGenericControl
    )

/*++

Routine Description:

    This is a private routine that produces an auto inherited acl from
    a ChildAcl that is not marked as auto inherited.  The passed in InheritedAcl
    is computed as the pure inherited ACL of Parent ACL of the object.

    See comments for RtlConvertToAutoInheritSecurityObject.

Arguments:

    ParentAcl - Supplies the ACL of the parent object.

    ChildAcl - Supplies the acl associated with the object.  This
        is the current acl on the object.

    ObjectType - GUID of the object type being created.  If the object being
        created has no GUID associated with it, then this argument is
        specified as NULL.

    IsDirectoryObject - Specifies if the object is a
        directory object.  A value of TRUE indicates the object is a
        container of other objects.

    OwnerSid - Specifies the owner Sid to use.

    GroupSid - Specifies the group SID to use.

    GenericMapping - Specifies the generic mapping to use.

    NewAcl - Receives a pointer to the new (auto inherited) acl.
        The ACL must be deallocated using the pool (kernel mode) or
            heap (user mode) deallocator.

    NewGenericControl - Specifies the control flags for the newly
        generated ACL.

        SEP_ACL_PRESENT: Specifies that the ACL is explictly supplied by
            the caller. ?? Ever set?

        SEP_ACL_DEFAULTED: Specifies that the ACL was supplied by some
            defaulting mechanism. ?? Ever set

        SEP_ACL_AUTO_INHERITED: Set if the ACL was generated using the
            Automatic Inheritance algorithm.

        SEP_ACL_PROTECTED: Specifies that the ACL is protected and
            was not inherited from the parent ACL.

Return Value:

    STATUS_SUCCESS - An inheritable ACL was successfully generated.

    STATUS_UNKNOWN_REVISION - Indicates the source ACL is a revision that
        is unknown to this routine.

    STATUS_INVALID_ACL - The structure of one of the ACLs in invalid.

--*/

{
    NTSTATUS Status;

    PACL InheritedAcl = NULL;
    PACL RealInheritedAcl = NULL;
    BOOLEAN AclExplicitlyAssigned;
    ULONG GenericControl;

    PKNOWN_ACE ChildAce = NULL;
    PKNOWN_ACE InheritedAce;

    LONG ChildAceIndex;
    LONG InheritedAceIndex;

    BOOLEAN InheritedAllowFound;
    BOOLEAN InheritedDenyFound;

    BOOLEAN AcesCompare;

    ACCESS_MASK InheritedContainerInheritMask;
    ACCESS_MASK InheritedObjectInheritMask;
    ACCESS_MASK InheritedEffectiveMask;
    ACCESS_MASK OriginalInheritedContainerInheritMask;
    ACCESS_MASK OriginalInheritedObjectInheritMask;
    ACCESS_MASK OriginalInheritedEffectiveMask;

    ULONG InheritedAceFlags;
    ULONG MatchedFlags;
    ULONG NonInheritedAclSize;


    PACE_HEADER AceHeader;
    PUCHAR Where;

    // ULONG i;

    //
    // This routine maintains an array of the structure below (one element per ACE in
    // the ChildAcl).
    //
    // The ACE is broken down into its component parts.  The access mask is 'triplicated'.
    // That is, if the ACE is a ContainerInherit ACE, the access mask is remembered as
    // being a "ContainerInheritMask".  The same is true if the ACE is an ObjectInherit ACE
    // on an effective ACE.  This is done since each of the resultant 96 bits are
    // individually matched against corresponding bits in the computed inherited ACE.
    //
    // Each of the above mentioned masks are maintained in two forms.  The first is never
    // changed and represents the bits as the originally appeared in the child ACL.
    // This second is modified as the corresponding bits are matched in the inherited ACL.
    // When the algorithm is completed, bits that haven't been matched represent ACEs
    // that weren't inherited from the parent.
    //

    typedef struct {
        ACCESS_MASK OriginalContainerInheritMask;
        ACCESS_MASK OriginalObjectInheritMask;
        ACCESS_MASK OriginalEffectiveMask;

        ACCESS_MASK ContainerInheritMask;
        ACCESS_MASK ObjectInheritMask;
        ACCESS_MASK EffectiveMask;
    } ACE_INFO, *PACE_INFO;

    PACE_INFO ChildAceInfo = NULL;

    ULONG CreatorOwnerSid[CREATOR_SID_SIZE];
    ULONG CreatorGroupSid[CREATOR_SID_SIZE];

    SID_IDENTIFIER_AUTHORITY  CreatorSidAuthority = SECURITY_CREATOR_SID_AUTHORITY;

    RTL_PAGED_CODE();

    //
    // Allocate and initialize the universal SIDs we're going to need
    // to look for inheritable ACEs.
    //

    ASSERT(RtlLengthRequiredSid( 1 ) == CREATOR_SID_SIZE);
    Status = RtlInitializeSid( (PSID)CreatorOwnerSid, &CreatorSidAuthority, 1 );
    if ( !NT_SUCCESS(Status) ) {
        goto Cleanup;
    }

    *(RtlpSubAuthoritySid( (PSID)CreatorOwnerSid, 0 )) = SECURITY_CREATOR_OWNER_RID;

    Status = RtlInitializeSid( (PSID)CreatorGroupSid, &CreatorSidAuthority, 1 );
    if ( !NT_SUCCESS(Status) ) {
        goto Cleanup;
    }

    *(RtlpSubAuthoritySid( (PSID)CreatorGroupSid, 0 )) = SECURITY_CREATOR_GROUP_RID;

    //
    // Ensure the passed in ACLs are valid.
    //

    *NewGenericControl = SEP_ACL_AUTO_INHERITED;
    *NewAcl = NULL;

    if ( ParentAcl != NULL && !RtlValidAcl( ParentAcl ) ) {
        Status = STATUS_INVALID_ACL;
        goto Cleanup;
    }

    if (!RtlValidAcl( ChildAcl ) ) {
        Status = STATUS_INVALID_ACL;
        goto Cleanup;
    }


    //
    // Compute what the inherited ACL "should" look like.
    //
    // The inherited ACL is computed to NOT SID-map Creator Owner and Creator Group.
    // This allows use to later recognize the constant SIDs and special case them
    // rather than mistakenly confuse them with the mapped SID.
    //

    Status = RtlpInheritAcl (
                ParentAcl,
                NULL,   // No explicit child ACL
                0,      // No Child Generic Control
                IsDirectoryObject,
                TRUE,   // AutoInherit the DACL
                FALSE,  // Not default descriptor for object
                CreatorOwnerSid,   // Subsitute a constant SID
                CreatorGroupSid,   // Subsitute a constant SID
                CreatorOwnerSid,   // Server Owner (Technically incorrect, but OK since we don't support compound ACEs)
                CreatorGroupSid,   // Server Group
                GenericMapping,
                TRUE,   // Is a SACL
                ObjectType ? &ObjectType : NULL, 
                ObjectType ? 1 : 0,
                &InheritedAcl,
                &AclExplicitlyAssigned,
                &GenericControl );

    if ( Status == STATUS_NO_INHERITANCE ) {
        *NewGenericControl |= SEP_ACL_PROTECTED;
#if DBG
        if ( RtlpVerboseConvert ) {
            KdPrint(("NO_INHERITANCE of the parent ACL\n" ));
        }
#endif // DBG
        Status = STATUS_SUCCESS;
        goto Cleanup;
    }

    if ( !NT_SUCCESS(Status) ) {
#if DBG
        if ( RtlpVerboseConvert ) {
            KdPrint(("Can't build inherited ACL %lX\n", Status ));
        }
#endif // DBG
        goto Cleanup;
    }

    //
    // Allocate a work buffer describing the ChildAcl
    //

    ChildAceInfo = ExAllocatePoolWithTag(
                        PagedPool,
                        ChildAcl->AceCount * sizeof(ACE_INFO),
                        'cAeS' );

    if (ChildAceInfo == NULL ) {
        Status = STATUS_NO_MEMORY;
        goto Cleanup;
    }

    for (ChildAceIndex = 0, ChildAce = FirstAce(ChildAcl);
         ChildAceIndex < ChildAcl->AceCount;
         ChildAceIndex += 1, ChildAce = NextAce(ChildAce)) {
        ACCESS_MASK LocalMask;
        ULONG ChildAceFlags;

        if ( !IsV4AceType(ChildAce) || IsCompoundAceType(ChildAce)) {
             *NewGenericControl |= SEP_ACL_PROTECTED;
#if DBG
             if ( RtlpVerboseConvert ) {
                 KdPrint(("Inherited Ace type (%ld) not known\n", ChildAce->Header.AceType ));
             }
#endif // DBG
             Status = STATUS_SUCCESS;
             goto Cleanup;
        }

        //
        // Compute the generic mapped mask for use in all comparisons.  The
        //  generic mapping will be undone if needed later.
        //
        // All V4 aces have an access mask in the same location.
        //
        LocalMask = ((PKNOWN_ACE)(ChildAce))->Mask;
        RtlApplyGenericMask( ChildAce, &LocalMask, GenericMapping);


        //
        // Break the ACE into its component parts.
        //
        ChildAceFlags = AceFlagsInAce( ChildAce );
        if ( ChildAceFlags & CONTAINER_INHERIT_ACE ) {
            ChildAceInfo[ChildAceIndex].OriginalContainerInheritMask = LocalMask;
            ChildAceInfo[ChildAceIndex].ContainerInheritMask = LocalMask;
        } else {
            ChildAceInfo[ChildAceIndex].OriginalContainerInheritMask = 0;
            ChildAceInfo[ChildAceIndex].ContainerInheritMask = 0;
        }

        if ( ChildAceFlags & OBJECT_INHERIT_ACE ) {
            ChildAceInfo[ChildAceIndex].OriginalObjectInheritMask = LocalMask;
            ChildAceInfo[ChildAceIndex].ObjectInheritMask = LocalMask;
        } else {
            ChildAceInfo[ChildAceIndex].OriginalObjectInheritMask = 0;
            ChildAceInfo[ChildAceIndex].ObjectInheritMask = 0;
        }

        if ( ChildAceFlags & EFFECTIVE_ACE ) {
            ChildAceInfo[ChildAceIndex].OriginalEffectiveMask = LocalMask;
            ChildAceInfo[ChildAceIndex].EffectiveMask = LocalMask;
        } else {
            ChildAceInfo[ChildAceIndex].OriginalEffectiveMask = 0;
            ChildAceInfo[ChildAceIndex].EffectiveMask = 0;
        }

    }


    //
    // Walk through the computed inherited ACL one ACE at a time.
    //

    for (InheritedAceIndex = 0, InheritedAce = FirstAce(InheritedAcl);
         InheritedAceIndex < InheritedAcl->AceCount;
         InheritedAceIndex += 1, InheritedAce = NextAce(InheritedAce)) {

        ACCESS_MASK LocalMask;

        //
        // If the ACE isn't a valid version 4 ACE,
        //  this isn't an ACL we're interested in handling.
        //

        if ( !IsV4AceType(InheritedAce) || IsCompoundAceType(InheritedAce)) {
             *NewGenericControl |= SEP_ACL_PROTECTED;
#if DBG
             if ( RtlpVerboseConvert ) {
                 KdPrint(("Inherited Ace type (%ld) not known\n", InheritedAce->Header.AceType ));
             }
#endif // DBG
             Status = STATUS_SUCCESS;
             goto Cleanup;
        }

        //
        // Compute the generic mapped mask for use in all comparisons.  The
        //  generic mapping will be undone if needed later.
        //
        // All V4 aces have an access mask in the same location.
        //
        LocalMask = ((PKNOWN_ACE)(InheritedAce))->Mask;
        RtlApplyGenericMask( InheritedAce, &LocalMask, GenericMapping);

        if ( LocalMask == 0 ) {
#if DBG
            if ( RtlpVerboseConvert ) {
                KdPrint(("Worthless INH ACE: %ld 0x%8.8lx\n", InheritedAceIndex, LocalMask ));
            }
#endif // DBG
            continue;
        }

        //
        // This ACE is some combination of an effective ACE, a container
        //  inherit ACE and an object inherit ACE.  Process each of those
        //  attributes separately since they might be represented separately
        //  in the ChildAcl.
        //

        InheritedAceFlags = AceFlagsInAce( InheritedAce );

        if  ( InheritedAceFlags == 0 ) {
#if DBG
            if ( RtlpVerboseConvert ) {
                KdPrint(("Worthless INH ACE: %ld 0x%lx\n", InheritedAceIndex, InheritedAceFlags ));
            }
#endif // DBG
            continue;
        }

        if ( InheritedAceFlags & CONTAINER_INHERIT_ACE ) {
            OriginalInheritedContainerInheritMask = InheritedContainerInheritMask = LocalMask;
        } else {
            OriginalInheritedContainerInheritMask = InheritedContainerInheritMask = 0;
        }

        if ( InheritedAceFlags & OBJECT_INHERIT_ACE ) {
            OriginalInheritedObjectInheritMask = InheritedObjectInheritMask = LocalMask;
        } else {
            OriginalInheritedObjectInheritMask = InheritedObjectInheritMask = 0;
        }

        if ( InheritedAceFlags & EFFECTIVE_ACE ) {
            OriginalInheritedEffectiveMask = InheritedEffectiveMask = LocalMask;
        } else {
            OriginalInheritedEffectiveMask = InheritedEffectiveMask = 0;
        }

#if DBG
        if ( RtlpVerboseConvert ) {
            KdPrint(("Doing INH ACE:  %ld %8.8lX %8.8lX %8.8lX\n", InheritedAceIndex, InheritedEffectiveMask, InheritedContainerInheritMask, InheritedObjectInheritMask ));
        }
#endif // DBG


        //
        // Loop through the entire child ACL comparing each inherited ACE with
        //  each child ACE.  Don't stop simply because we've matched once.
        //  Multiple ACEs in the one ACL may have been condensed into a single ACE
        //  in the other ACL in any combination (by any of our friendly ACL editors).
        //  In all cases, it is better to compute a resultant auto inherited ACL
        //  than it is to compute a protected ACL.
        //

        for (ChildAceIndex = 0, ChildAce = FirstAce(ChildAcl);
             ChildAceIndex < ChildAcl->AceCount;
             ChildAceIndex += 1, ChildAce = NextAce(ChildAce)) {


            //
            // Ensure the ACE represents the same principal and object,
            //

#if DBG
            if ( RtlpVerboseConvert ) {
                KdPrint(("Compare Child Ace: %ld ", ChildAceIndex ));
            }
#endif // DBG

            if ( !RtlpCompareAces( InheritedAce,
                                   ChildAce,
                                   OwnerSid,
                                   GroupSid ) ) {
#if DBG
                if ( RtlpVerboseConvert ) {
                    KdPrint(("\n" ));
                }
#endif // DBG
                continue;
            }
#if DBG
            if ( RtlpVerboseConvert ) {
                KdPrint(("\n" ));
            }
#endif // DBG


            //
            // Match as many access bits in the INH ACE as possible.
            //
            // Don't pay any attention to whether the bits have been previously matched
            // in the CHILD ACE.  To do so, would imply that there is a one-to-one
            // correspondance between bits in the INH ACL and Child ACL.  Unfortunately,
            // ACL editors feel free to compress out duplicate bits in both
            // the CHILD ACL and PARENT ACL as they see fit.
            //

            InheritedEffectiveMask &= ~ChildAceInfo[ChildAceIndex].OriginalEffectiveMask;
            InheritedContainerInheritMask &= ~ChildAceInfo[ChildAceIndex].OriginalContainerInheritMask;
            InheritedObjectInheritMask &= ~ChildAceInfo[ChildAceIndex].OriginalObjectInheritMask;

#if DBG
            if ( RtlpVerboseConvert ) {
                KdPrint(("New   INH MASKs %ld %8.8lX %8.8lX %8.8lX\n", InheritedAceIndex, InheritedEffectiveMask, InheritedContainerInheritMask, InheritedObjectInheritMask ));
            }
#endif // DBG


            //
            // Match as many access bits in the child ACE as possible.
            //
            // Same reasoning as above.
            //

            ChildAceInfo[ChildAceIndex].EffectiveMask &= ~OriginalInheritedEffectiveMask;
            ChildAceInfo[ChildAceIndex].ContainerInheritMask &= ~OriginalInheritedContainerInheritMask;
            ChildAceInfo[ChildAceIndex].ObjectInheritMask &= ~OriginalInheritedObjectInheritMask;

#if DBG
            if ( RtlpVerboseConvert ) {
                KdPrint(("New Child MASKs %ld %8.8lX %8.8lX %8.8lX\n", ChildAceIndex, ChildAceInfo[ChildAceIndex].EffectiveMask, ChildAceInfo[ChildAceIndex].ContainerInheritMask, ChildAceInfo[ChildAceIndex].ObjectInheritMask ));
            }
#endif // DBG

        }


        //
        // If we couldn't process this inherited ACE,
        //  then the child ACL wasn't inherited.
        //

        if ( (InheritedEffectiveMask | InheritedContainerInheritMask | InheritedObjectInheritMask) != 0 ) {
            *NewGenericControl |= SEP_ACL_PROTECTED;
#if DBG
            if ( RtlpVerboseConvert ) {
                KdPrint(("INH ACE not completely matched: %ld %8.8lX %8.8lX %8.8lX\n", InheritedAceIndex, InheritedEffectiveMask, InheritedContainerInheritMask, InheritedObjectInheritMask ));
            }
#endif // DBG
            Status = STATUS_SUCCESS;
            goto Cleanup;
        }


    }

    //
    // ASSERT: All of the inherited ACEs have been processed.
    //

    //
    // Loop through the Child ACL ensuring we can build a valid auto inherited ACL
    //

    InheritedAllowFound = FALSE;
    InheritedDenyFound = FALSE;
    NonInheritedAclSize = 0;
    for (ChildAceIndex = 0, ChildAce = FirstAce(ChildAcl);
         ChildAceIndex < ChildAcl->AceCount;
         ChildAceIndex += 1, ChildAce = NextAce(ChildAce)) {

        ACCESS_MASK ResultantMask;

        //
        // Any Child ACE access bits not eliminated above required than an
        //  explicit non-inherited ACE by built.  That ACE will have an
        //  access mask that is the combined access mask of the unmatched bit
        //  in the effective, container inherit, and object inherit categories.
        //  Even though, the combined mask may include access bits not absolutely
        //  required (since they were already inherited), this strategy prevents
        //  us from having to build multiple ACEs (one for each category) for this
        //  single ACE.
        //

        ResultantMask =
            ChildAceInfo[ChildAceIndex].EffectiveMask |
            ChildAceInfo[ChildAceIndex].ContainerInheritMask |
            ChildAceInfo[ChildAceIndex].ObjectInheritMask;


        //
        // Handle an inherited ACE
        //

        if ( ResultantMask == 0 ) {

            //
            // Keep track of whether inherited "allow" and "deny" ACEs are found.
            //

            if ( RtlBaseAceType[ChildAce->Header.AceType] == ACCESS_ALLOWED_ACE_TYPE ) {
                InheritedAllowFound = TRUE;
            }

            if ( RtlBaseAceType[ChildAce->Header.AceType] == ACCESS_DENIED_ACE_TYPE ) {
                InheritedDenyFound = TRUE;
            }

        //
        // Handle a non-inherited ACE
        //

        } else {

            //
            // Keep a running tab of the size of the non-inherited ACEs.
            //

            NonInheritedAclSize += ChildAce->Header.AceSize;

            //
            // Since non-inherited ACEs will be moved to the front of the ACL,
            //  we have to be careful that we don't move a deny ACE in front of a
            //  previously found inherited allow ACE (and vice-versa).  To do so would
            //  change the semantics of the ACL.
            //

            if ( RtlBaseAceType[ChildAce->Header.AceType] == ACCESS_ALLOWED_ACE_TYPE && InheritedDenyFound ) {
                *NewGenericControl |= SEP_ACL_PROTECTED;
#if DBG
                if ( RtlpVerboseConvert ) {
                    KdPrint(("Previous deny found Child ACE: %ld\n", ChildAceIndex ));
                }
#endif // DBG
                Status = STATUS_SUCCESS;
                goto Cleanup;
            }

            if ( RtlBaseAceType[ChildAce->Header.AceType] == ACCESS_DENIED_ACE_TYPE && InheritedAllowFound ) {
                *NewGenericControl |= SEP_ACL_PROTECTED;
#if DBG
                if ( RtlpVerboseConvert ) {
                    KdPrint(("Previous allow found Child ACE: %ld\n", ChildAceIndex ));
                }
#endif // DBG
                Status = STATUS_SUCCESS;
                goto Cleanup;
            }

        }

    }

    //
    // The resultant ACL is composed of the non-inherited ACEs followed by
    // the inherited ACE. The inherited ACEs are built by running the
    // inheritance algorithm over the Parent ACL.
    //
    // The Inherited ACL computed below is almost identical to InheritedAcl.
    // However, InheritedAcl didn't properly substitute the correct owner and
    // group SID.
    //

    Status = RtlpInheritAcl (
                ParentAcl,
                NULL,   // No explicit child ACL
                0,      // No Child Generic Control
                IsDirectoryObject,
                TRUE,   // AutoInherit the DACL
                FALSE,  // Not default descriptor for object
                OwnerSid,   // Subsitute a constant SID
                GroupSid,   // Subsitute a constant SID
                OwnerSid,   // Server Owner (Technically incorrect, but OK since we don't support compound ACEs)
                GroupSid,   // Server Group
                GenericMapping,
                TRUE,   // Is a SACL
                ObjectType ? &ObjectType : NULL, 
                ObjectType ? 1 : 0,
                &RealInheritedAcl,
                &AclExplicitlyAssigned,
                &GenericControl );

    if ( !NT_SUCCESS(Status) ) {
#if DBG
        if ( RtlpVerboseConvert ) {
            KdPrint(("Can't build real inherited ACL %lX\n", Status ));
        }
#endif // DBG
        goto Cleanup;
    }



    //
    // Allocate a buffer for the inherited ACL
    //

    *NewAcl = ExAllocatePoolWithTag(
                        PagedPool,
                        RealInheritedAcl->AclSize + NonInheritedAclSize,
                        'cAeS' );

    if ( *NewAcl == NULL ) {
        Status = STATUS_NO_MEMORY;
        goto Cleanup;
    }

    //
    // All non-inherited ACEs are copied first.
    // The inherited ACES are grabbed from real inherited ACL.
    //
    // Build an ACL Header.
    //

    Status = RtlCreateAcl( *NewAcl,
                           RealInheritedAcl->AclSize + NonInheritedAclSize,
                           max( RealInheritedAcl->AclRevision, ChildAcl->AclRevision ) );

    if ( !NT_SUCCESS(Status) ) {
#if DBG
        if ( RtlpVerboseConvert ) {
            KdPrint(("Can't create final ACL %lX\n", Status ));
        }
#endif // DBG
        //
        // The only reason for failure would be if the combined ACL is too large.
        // So just create a protected ACL (better than a failure).
        //
        *NewGenericControl |= SEP_ACL_PROTECTED;
        Status = STATUS_SUCCESS;
        goto Cleanup;
    }

    //
    // Copy the non-inherited ACES.
    //

    Where = ((PUCHAR)(*NewAcl)) + sizeof(ACL);
    for (ChildAceIndex = 0, ChildAce = FirstAce(ChildAcl);
         ChildAceIndex < ChildAcl->AceCount;
         ChildAceIndex += 1, ChildAce = NextAce(ChildAce)) {

        ACCESS_MASK ResultantMask;

        //
        // Copy the non-inherited ACE from the Child only if there's a non-zero access mask.
        //

        ResultantMask =
            ChildAceInfo[ChildAceIndex].EffectiveMask |
            ChildAceInfo[ChildAceIndex].ContainerInheritMask |
            ChildAceInfo[ChildAceIndex].ObjectInheritMask;

        if ( ResultantMask != 0 ) {
            PKNOWN_ACE NewAce;
            ULONG GenericBitToTry;

            //
            // Use the original ChildAce as the template.
            //

            RtlCopyMemory( Where, ChildAce, ChildAce->Header.AceSize );
            NewAce = (PKNOWN_ACE)Where;
            NewAce->Header.AceFlags &= ~INHERITED_ACE;  // Clear stray bits
            Where += ChildAce->Header.AceSize;

            (*NewAcl)->AceCount ++;

            //
            // The AccessMask on the ACE are those access bits that didn't get matched
            //  by inherited ACEs.
            //

            NewAce->Mask = ChildAce->Mask & ResultantMask;
            ResultantMask &= ~ChildAce->Mask;
#if DBG
            if ( RtlpVerboseConvert ) {
                KdPrint(("Original non-inherited: %ld %8.8lX %8.8lX\n", ChildAceIndex, NewAce->Mask, ResultantMask ));
            }
#endif // DBG

            //
            // Map any remaining bits back to generic access bits.
            // Doing so might expand the ResultantMask to beyond what was computed above.
            // Doing so will never expand the computed ACE to beyond what the original
            //  ChildAce granted.
            //

            ASSERT( GENERIC_WRITE == (GENERIC_READ >> 1));
            ASSERT( GENERIC_EXECUTE == (GENERIC_WRITE >> 1));
            ASSERT( GENERIC_ALL == (GENERIC_EXECUTE >> 1));

            GenericBitToTry = GENERIC_READ;

            while ( ResultantMask && GenericBitToTry >= GENERIC_ALL ) {

                //
                // Only map generic bits that are in the ChildAce.
                //

                if ( GenericBitToTry & ChildAce->Mask ) {
                    ACCESS_MASK GenericMask;

                    //
                    // Compute the real access mask corresponding to the Generic bit.
                    //

                    GenericMask = GenericBitToTry;
                    RtlMapGenericMask( &GenericMask, GenericMapping );

                    //
                    // If the current generic bit matches any of the bits remaining,
                    //  set the generic bit in the current ACE.
                    //

                    if ( (ResultantMask & GenericMask) != 0 ) {
                        NewAce->Mask |= GenericBitToTry;
                        ResultantMask &= ~GenericMask;
                    }
#if DBG
                    if ( RtlpVerboseConvert ) {
                        KdPrint(("Generic  non-inherited: %ld %8.8lX %8.8lX\n", ChildAceIndex, NewAce->Mask, ResultantMask ));
                    }
#endif // DBG
                }

                //
                // Try the next Generic bit.
                //

                GenericBitToTry >>= 1;
            }


            //
            // This is really an internal error, but press on regardless.
            //

            ASSERT(ResultantMask == 0 );
            NewAce->Mask |= ResultantMask;
#if DBG
            if ( RtlpVerboseConvert ) {
                KdPrint(("Final    non-inherited: %ld %8.8lX %8.8lX\n", ChildAceIndex, NewAce->Mask, ResultantMask ));
            }
#endif // DBG

        }
    }

    //
    // Copy the inherited ACES.
    //  Simply copy computed Inherited ACL.
    //

    RtlCopyMemory( Where,
                   FirstAce(RealInheritedAcl),
                   RealInheritedAcl->AclSize - (ULONG)(((PUCHAR)FirstAce(RealInheritedAcl)) - (PUCHAR)RealInheritedAcl));
    Where += RealInheritedAcl->AclSize - (ULONG)(((PUCHAR)FirstAce(RealInheritedAcl)) - (PUCHAR)RealInheritedAcl);

    (*NewAcl)->AceCount += RealInheritedAcl->AceCount;
    ASSERT( (*NewAcl)->AclSize == Where - (PUCHAR)(*NewAcl) );


    Status = STATUS_SUCCESS;
Cleanup:

    //
    // If successful,
    //  build the resultant autoinherited ACL.
    //

    if ( NT_SUCCESS(Status) ) {

        //
        // If the Child ACL is protected,
        //  just build it as a copy of the original ACL
        //

        if ( *NewGenericControl & SEP_ACL_PROTECTED ) {

            //
            // If we've already allocated a new ACL (and couldn't finish it for some reason),
            //  free it.

            if ( *NewAcl != NULL) {
                ExFreePool( *NewAcl );
                *NewAcl = NULL;
            }

            //
            // Allocate a buffer for the protected ACL.
            //

            *NewAcl = ExAllocatePoolWithTag(
                                PagedPool,
                                ChildAcl->AclSize,
                                'cAeS' );

            if ( *NewAcl == NULL ) {
                Status = STATUS_NO_MEMORY;
            } else {
                RtlCopyMemory( *NewAcl, ChildAcl, ChildAcl->AclSize );
            }
        }

    }

    if ( ChildAceInfo != NULL) {
        ExFreePool( ChildAceInfo );
    }

    if ( InheritedAcl != NULL) {
        ExFreePool( InheritedAcl );
    }

    if ( RealInheritedAcl != NULL) {
        ExFreePool( RealInheritedAcl );
    }

    return Status;
}


BOOLEAN
RtlpIsDuplicateAce(
    IN PACL Acl,
    IN PKNOWN_ACE NewAce
    )

/*++

Routine Description:

    This routine determine if an ACE is a duplicate of an ACE already in an
    ACL.  If so, the NewAce can be removed from the end of the ACL.

    This routine currently only detects duplicate version 4 ACEs.  If the
    ACE isn't version 4, the ACE will be declared to be a non-duplicate.

    This routine only detects duplicate INHERTED ACEs.

Arguments:

    Acl - Existing ACL

    NewAce - Ace to determine if it is already in Acl.
        NewAce is expected to be the last ACE in "Acl".

Return Value:

    TRUE - NewAce is a duplicate of another ACE on the Acl
    FALSE - NewAce is NOT a duplicate of another ACE on the Acl

--*/

{
    NTSTATUS Status;
    BOOLEAN RetVal = FALSE;

    LONG AceIndex;

    ACCESS_MASK NewAceContainerInheritMask;
    ACCESS_MASK NewAceObjectInheritMask;
    ACCESS_MASK NewAceEffectiveMask;

    ACCESS_MASK LocalMask;

    PKNOWN_ACE AceFromAcl;

    RTL_PAGED_CODE();


    //
    // Ensure the passed in ACE is one this routine understands
    //

    if ( !IsV4AceType(NewAce) || IsCompoundAceType(NewAce)) {
#if DBG
        if ( RtlpVerboseConvert ) {
            KdPrint(("New Ace type (%ld) not known\n", NewAce->Header.AceType ));
        }
#endif // DBG
        RetVal = FALSE;
        goto Cleanup;
    }

    //
    // This routine only works for ACEs marked as INHERITED.
    //

    if ( (NewAce->Header.AceFlags & INHERITED_ACE ) == 0 ) {
#if DBG
        if ( RtlpVerboseConvert ) {
            KdPrint(("New Ace type isn't inherited\n" ));
        }
#endif // DBG
        RetVal = FALSE;
        goto Cleanup;
    }


    //
    // Break the new ACE into its component parts.
    //
    // All V4 aces have an access mask in the same location.
    //
    LocalMask = ((PKNOWN_ACE)(NewAce))->Mask;

    if ( NewAce->Header.AceFlags & CONTAINER_INHERIT_ACE ) {
        NewAceContainerInheritMask = LocalMask;
    } else {
        NewAceContainerInheritMask = 0;
    }

    if ( NewAce->Header.AceFlags & OBJECT_INHERIT_ACE ) {
        NewAceObjectInheritMask = LocalMask;
    } else {
        NewAceObjectInheritMask = 0;
    }

    if ( (NewAce->Header.AceFlags & INHERIT_ONLY_ACE) == 0 ) {
        NewAceEffectiveMask = LocalMask;
    } else {
        NewAceEffectiveMask = 0;
    }
#if DBG
    if ( RtlpVerboseConvert ) {
        KdPrint(("Starting MASKs:  %8.8lX %8.8lX %8.8lX", NewAceEffectiveMask, NewAceContainerInheritMask, NewAceObjectInheritMask ));
    }
#endif // DBG




    //
    // Walk through the ACL one ACE at a time.
    //

    for (AceIndex = 0, AceFromAcl = FirstAce(Acl);
         AceIndex < Acl->AceCount-1;    // NewAce is the last ACE
         AceIndex += 1, AceFromAcl = NextAce(AceFromAcl)) {


        //
        // If the ACE isn't a valid version 4 ACE,
        //  this isn't an ACE we're interested in handling.
        //

        if ( !IsV4AceType(AceFromAcl) || IsCompoundAceType(AceFromAcl)) {
            continue;
        }

        //
        // This routine only works for ACEs marked as INHERITED.
        //

        if ( (AceFromAcl->Header.AceFlags & INHERITED_ACE ) == 0 ) {
            continue;
        }


        //
        // Compare the Ace from the ACL with the New ACE
        //
        //  Don't stop simply because we've matched once.
        //  Multiple ACEs in the one ACL may have been condensed into a single ACE
        //  in the other ACL in any combination (by any of our friendly ACL editors).
        //
#if DBG
        if ( RtlpVerboseConvert ) {
            KdPrint(("Compare Ace: %ld ", AceIndex ));
        }
#endif // DBG

        if ( RtlpCompareAces( AceFromAcl,
                              NewAce,
                              NULL,
                              NULL ) ) {


            //
            // Match the bits from the current ACE with bits from the New ACE.
            //
            // All V4 aces have an access mask in the same location.
            //

            LocalMask = ((PKNOWN_ACE)(AceFromAcl))->Mask;

            if ( AceFromAcl->Header.AceFlags & CONTAINER_INHERIT_ACE ) {
                NewAceContainerInheritMask &= ~LocalMask;
            }

            if ( AceFromAcl->Header.AceFlags & OBJECT_INHERIT_ACE ) {
                NewAceObjectInheritMask &= ~LocalMask;
            }

            if ( (AceFromAcl->Header.AceFlags & INHERIT_ONLY_ACE) == 0 ) {
                NewAceEffectiveMask &= ~LocalMask;
            }

#if DBG
            if ( RtlpVerboseConvert ) {
                KdPrint(("Remaining MASKs:  %8.8lX %8.8lX %8.8lX", NewAceEffectiveMask, NewAceContainerInheritMask, NewAceObjectInheritMask ));
            }
#endif // DBG

            //
            // If all bits have been matched in the New Ace,
            //  then this is a duplicate ACE.
            //

            if ( (NewAceEffectiveMask | NewAceContainerInheritMask | NewAceObjectInheritMask) == 0 ) {
#if DBG
                if ( RtlpVerboseConvert ) {
                    KdPrint(("\n"));
                }
#endif // DBG
                RetVal = TRUE;
                goto Cleanup;
            }
        }
#if DBG
        if ( RtlpVerboseConvert ) {
              KdPrint(("\n"));
        }
#endif // DBG


    }

    //
    // All of the ACEs of the ACL have been processed.
    //
    // We haven't matched all of the bits in the New Ace so this is not a duplicate ACE.
    //

    RetVal = FALSE;
Cleanup:

    return RetVal;

}


NTSTATUS
RtlpCreateServerAcl(
    IN PACL Acl,
    IN BOOLEAN AclUntrusted,
    IN PSID ServerSid,
    OUT PACL *ServerAcl,
    OUT BOOLEAN *ServerAclAllocated
    )

/*++

Routine Description:

    This routine takes an ACL and converts it into a server ACL.
    Currently, that means converting all of the GRANT ACEs into
    Compount Grants, and if necessary sanitizing any Compound
    Grants that are encountered.

Arguments:



Return Value:


--*/

{
    USHORT RequiredSize = sizeof(ACL);
    USHORT AceSizeAdjustment;
    USHORT ServerSidSize;
    PACE_HEADER Ace;
    ULONG i;
    PVOID Target;
    PVOID AcePosition;
    PSID UntrustedSid;
    PSID ClientSid;
    NTSTATUS Status;

    RTL_PAGED_CODE();

    if (Acl == NULL) {
        *ServerAclAllocated = FALSE;
        *ServerAcl = NULL;
        return( STATUS_SUCCESS );
    }

    AceSizeAdjustment = sizeof( KNOWN_COMPOUND_ACE ) - sizeof( KNOWN_ACE );
    ASSERT( sizeof( KNOWN_COMPOUND_ACE ) >= sizeof( KNOWN_ACE ) );

    ServerSidSize = (USHORT)SeLengthSid( ServerSid );

    //
    // Do this in two passes.  First, determine how big the final
    // result is going to be, and then allocate the space and make
    // the changes.
    //

    for (i = 0, Ace = FirstAce(Acl);
         i < Acl->AceCount;
         i += 1, Ace = NextAce(Ace)) {

        //
        // If it's an ACCESS_ALLOWED_ACE_TYPE, we'll need to add in the
        // size of the Server SID.
        //

        if (Ace->AceType == ACCESS_ALLOWED_ACE_TYPE) {

            //
            // Simply add the size of the new Server SID plus whatever
            // adjustment needs to be made to increase the size of the ACE.
            //

            RequiredSize += ( ServerSidSize + AceSizeAdjustment );

        } else {

            if (AclUntrusted && Ace->AceType == ACCESS_ALLOWED_COMPOUND_ACE_TYPE ) {

                //
                // Since the Acl is untrusted, we don't care what is in the
                // server SID, we're going to replace it.
                //

                UntrustedSid = RtlCompoundAceServerSid( Ace );
                if ((USHORT)SeLengthSid(UntrustedSid) > ServerSidSize) {
                    RequiredSize += ((USHORT)SeLengthSid(UntrustedSid) - ServerSidSize);
                } else {
                    RequiredSize += (ServerSidSize - (USHORT)SeLengthSid(UntrustedSid));

                }
            }
        }

        RequiredSize += Ace->AceSize;
    }

    (*ServerAcl) = (PACL)ExAllocatePoolWithTag( PagedPool, RequiredSize, 'cAeS' );

    if ((*ServerAcl) == NULL) {
        return STATUS_INSUFFICIENT_RESOURCES;
    }

    //
    // Mark as allocated so caller knows to free it.
    //

    *ServerAclAllocated = TRUE;

    Status = RtlCreateAcl( (*ServerAcl), RequiredSize, ACL_REVISION3 );
    ASSERT( NT_SUCCESS( Status ));

    for (i = 0, Ace = FirstAce(Acl), Target=FirstAce( *ServerAcl );
         i < Acl->AceCount;
         i += 1, Ace = NextAce(Ace)) {

        //
        // If it's an ACCESS_ALLOWED_ACE_TYPE, convert to a Server ACE.
        //

        if (Ace->AceType == ACCESS_ALLOWED_ACE_TYPE ||
           (AclUntrusted && Ace->AceType == ACCESS_ALLOWED_COMPOUND_ACE_TYPE )) {

            AcePosition = Target;

            if (Ace->AceType == ACCESS_ALLOWED_ACE_TYPE) {
                ClientSid =  &((PKNOWN_ACE)Ace)->SidStart;
            } else {
                ClientSid = RtlCompoundAceClientSid( Ace );
            }

            //
            // Copy up to the access mask.
            //

            RtlCopyMemory(
                Target,
                Ace,
                FIELD_OFFSET(KNOWN_ACE, SidStart)
                );

            //
            // Now copy the correct Server SID
            //

            Target = ((PCHAR)Target + (UCHAR)(FIELD_OFFSET(KNOWN_COMPOUND_ACE, SidStart)));

            RtlCopyMemory(
                Target,
                ServerSid,
                SeLengthSid(ServerSid)
                );

            Target = ((PCHAR)Target + (UCHAR)SeLengthSid(ServerSid));

            //
            // Now copy in the correct client SID.  We can copy this right out of
            // the original ACE.
            //

            RtlCopyMemory(
                Target,
                ClientSid,
                SeLengthSid(ClientSid)
                );

            Target = ((PCHAR)Target + SeLengthSid(ClientSid));

            //
            // Set the size of the ACE accordingly
            //

            ((PKNOWN_COMPOUND_ACE)AcePosition)->Header.AceSize =
                (USHORT)FIELD_OFFSET(KNOWN_COMPOUND_ACE, SidStart) +
                (USHORT)SeLengthSid(ServerSid) +
                (USHORT)SeLengthSid(ClientSid);

            //
            // Set the type
            //

            ((PKNOWN_COMPOUND_ACE)AcePosition)->Header.AceType = ACCESS_ALLOWED_COMPOUND_ACE_TYPE;
            ((PKNOWN_COMPOUND_ACE)AcePosition)->CompoundAceType = COMPOUND_ACE_IMPERSONATION;

        } else {

            //
            // Just copy the ACE as is.
            //

            RtlCopyMemory( Target, Ace, Ace->AceSize );

            Target = ((PCHAR)Target + Ace->AceSize);
        }
    }

    (*ServerAcl)->AceCount = Acl->AceCount;

    return( STATUS_SUCCESS );
}


NTSTATUS
RtlpNewSecurityObject (
    IN PSECURITY_DESCRIPTOR ParentDescriptor OPTIONAL,
    IN PSECURITY_DESCRIPTOR CreatorDescriptor OPTIONAL,
    OUT PSECURITY_DESCRIPTOR * NewDescriptor,
    IN GUID **pObjectType OPTIONAL,
    IN ULONG GuidCount,
    IN BOOLEAN IsDirectoryObject,
    IN ULONG AutoInheritFlags,
    IN HANDLE Token OPTIONAL,
    IN PGENERIC_MAPPING GenericMapping
    )
/*++

Routine Description:

    The procedure is used to allocate and initialize a self-relative
    Security Descriptor for a new protected server's object.  It is called
    when a new protected server object is being created.  The generated
    security descriptor will be in self-relative form.

    This procedure, called only from user mode, is used to establish a
    security descriptor for a new protected server's object.  Memory is
    allocated to hold each of the security descriptor's components (using
    NtAllocateVirtualMemory()).  The final security descriptor generated by
    this procedure is produced according to the rules stated in ???

    System and Discretionary ACL Assignment
    ---------------------------------------

    The assignment of system and discretionary ACLs is governed by the
    logic illustrated in the following table:

                 |  Explicit      |  Explicit     |
                 | (non-default)  |  Default      |   No
                 |  Acl           |  Acl          |   Acl
                 |  Specified     |  Specified    |   Specified
    -------------+----------------+---------------+--------------
                 |                |               |
    Inheritable  | Assign         |  Assign       | Assign
    Acl From     | Specified      |  Inherited    | Inherited
    Parent       | Acl(1)(2)      |  Acl          | Acl
                 |                |               |
    -------------+----------------+---------------+--------------
    No           |                |               |
    Inheritable  | Assign         |  Assign       | Assign
    Acl From     | Specified      |  Default      | No Acl
    Parent       | Acl(1)         |  Acl          |
                 |                |               |
    -------------+----------------+---------------+--------------

    (1) Any ACEs with the INHERITED_ACE bit set are NOT copied to the assigned
    security descriptor.

    (2) If the AutoInheritFlags is flagged to automatically inherit ACEs from
    parent (SEF_DACL_AUTO_INHERIT or SEF_SACL_AUTO_INHERIT), inherited
    ACEs from the parent will be appended after explicit ACEs from the
    CreatorDescriptor.


    Note that an explicitly specified ACL, whether a default ACL or
    not, may be empty or null.

    If the caller is explicitly assigning a system acl, default or
    non-default, the caller must either be a kernel mode client or
    must be appropriately privileged.


    Owner and Group Assignment
    --------------------------

    The assignment of the new object's owner and group is governed
    by the following logic:

       1)   If the passed security descriptor includes an owner, it
            is assigned as the new object's owner.  Otherwise, the
            caller's token is looked in for the owner.  Within the
            token, if there is a default owner, it is assigned.
            Otherwise, the caller's user ID is assigned.

       2)   If the passed security descriptor includes a group, it
            is assigned as the new object's group.  Otherwise, the
            caller's token is looked in for the group.  Within the
            token, if there is a default group, it is assigned.
            Otherwise, the caller's primary group ID is assigned.


Arguments:

    ParentDescriptor - Supplies the Security Descriptor for the parent
        directory under which a new object is being created.  If there is
        no parent directory, then this argument is specified as NULL.

    CreatorDescriptor - (Optionally) Points to a security descriptor
        presented by the creator of the object.  If the creator of the
        object did not explicitly pass security information for the new
        object, then a null pointer should be passed.

    NewDescriptor - Points to a pointer that is to be made to point to the
        newly allocated self-relative security descriptor.

    ObjectType - GUID of the object type being created.  If the object being
        created has no GUID associated with it, then this argument is
        specified as NULL.

    IsDirectoryObject - Specifies if the new object is going to be a
        directory object.  A value of TRUE indicates the object is a
        container of other objects.

    AutoInheritFlags - Controls automatic inheritance of ACES from the Parent
        Descriptor.  Valid values are a bits mask of the logical OR of
        one or more of the following bits:

        SEF_DACL_AUTO_INHERIT - If set, inherit ACEs from the
            DACL ParentDescriptor are inherited to NewDescriptor in addition
            to any explicit ACEs specified by the CreatorDescriptor.

        SEF_SACL_AUTO_INHERIT - If set, inherit ACEs from the
            SACL ParentDescriptor are inherited to NewDescriptor in addition
            to any explicit ACEs specified by the CreatorDescriptor.

        SEF_DEFAULT_DESCRIPTOR_FOR_OBJECT - If set, the CreatorDescriptor
            is the default descriptor for ObjectType.  As such, the
            CreatorDescriptor will be ignored if any ObjectType specific
            ACEs are inherited from the parent.  If no such ACEs are inherited,
            the CreatorDescriptor is handled as though this flag were not
            specified.

        SEF_AVOID_PRIVILEGE_CHECK - If set, no privilege checking is done by this
            routine.  This flag is useful while implementing automatic inheritance
            to avoid checking privileges on each child updated.

        SEF_AVOID_OWNER_CHECK - If set, no owner checking is done by this routine.

        SEF_DEFAULT_OWNER_FROM_PARENT - If set, the owner of NewDescriptor will
            default to the owner from ParentDescriptor.  If not set, the owner
            of NewDescriptor will default to the user specified in Token.

            In either case, the owner of NewDescriptor is set to the owner from
            the CreatorDescriptor if that field is specified.

        SEF_DEFAULT_GROUP_FROM_PARENT - If set, the group of NewDescriptor will
            default to the group from ParentDescriptor.  If not set, the group
            of NewDescriptor will default to the group specified in Token.

            In either case, the group of NewDescriptor is set to the group from
            the CreatorDescriptor if that field is specified.

    Token - Supplies the token for the client on whose behalf the
        object is being created.  If it is an impersonation token,
        then it must be at SecurityIdentification level or higher.  If
        it is not an impersonation token, the operation proceeds
        normally.

        A client token is used to retrieve default security
        information for the new object, such as default owner, primary
        group, and discretionary access control.  The token must be
        open for TOKEN_QUERY access.

        For calls from the kernel, Supplies the security context of the subject creating the
        object. This is used to retrieve default security information for the
        new object, such as default owner, primary group, and discretionary
        access control.

        If not specified, the Owner and Primary group must be specified in the
        CreatorDescriptor.

    GenericMapping - Supplies a pointer to a generic mapping array denoting
        the mapping between each generic right to specific rights.

Return Value:

    STATUS_SUCCESS - The operation was successful.

    STATUS_INVALID_OWNER - The owner SID provided as the owner of the
        target security descriptor is not one the subject is authorized to
        assign as the owner of an object.

    STATUS_NO_CLIENT_TOKEN - Indicates a client token was not explicitly
        provided and the caller is not currently impersonating a client.

    STATUS_PRIVILEGE_NOT_HELD - The caller does not have the privilege
        necessary to explicitly assign the specified system ACL.
        SeSecurityPrivilege privilege is needed to explicitly assign
        system ACLs to objects.


--*/
{


    SECURITY_DESCRIPTOR *CapturedDescriptor;
    SECURITY_DESCRIPTOR InCaseOneNotPassed;
    BOOLEAN SecurityDescriptorPassed;

    NTSTATUS Status;

    PACL NewSacl = NULL;
    BOOLEAN NewSaclInherited = FALSE;

    PACL NewDacl = NULL;
    PACL ServerDacl = NULL;
    BOOLEAN NewDaclInherited = FALSE;

    PSID NewOwner = NULL;
    PSID NewGroup = NULL;

    BOOLEAN SaclExplicitlyAssigned = FALSE;
    BOOLEAN OwnerExplicitlyAssigned = FALSE;
    BOOLEAN DaclExplicitlyAssigned = FALSE;

    BOOLEAN ServerDaclAllocated = FALSE;

    BOOLEAN ServerObject;
    BOOLEAN DaclUntrusted;

    BOOLEAN HasPrivilege;
    PRIVILEGE_SET PrivilegeSet;

    PSID SubjectContextOwner = NULL;
    PSID SubjectContextGroup = NULL;
    PSID ServerOwner = NULL;
    PSID ServerGroup = NULL;

    PACL SubjectContextDacl = NULL;

    ULONG AllocationSize;
    ULONG NewOwnerSize, OwnerSize;
    ULONG NewGroupSize, GroupSize;
    ULONG NewSaclSize;
    ULONG NewDaclSize;

    PCHAR Field;
    PCHAR Base;



    PISECURITY_DESCRIPTOR_RELATIVE INewDescriptor = NULL;
    NTSTATUS PassedStatus;
    KPROCESSOR_MODE RequestorMode;

    ULONG GenericControl;
    ULONG NewControlBits = SE_SELF_RELATIVE;

    //
    // For kernel mode callers, the Token parameter is really
    // a pointer to a subject context structure.
    //

    PSECURITY_SUBJECT_CONTEXT SubjectSecurityContext;
    PVOID SubjectContextInfo = NULL;

    SubjectSecurityContext = (PSECURITY_SUBJECT_CONTEXT)Token;

    RTL_PAGED_CODE();

    //
    //  Get the previous mode of the caller
    //

    RequestorMode = KeGetPreviousMode();

    //
    //  The desired end result is to build a self-relative security descriptor.
    //  This means that a single block of memory will be allocated and all
    //  security information copied into it.  To minimize work along the way,
    //  it is desirable to reference (rather than copy) each field as we
    //  determine its source.  This can not be done with inherited ACLs, however,
    //  since they must be built from another ACL.  So, explicitly assigned
    //  and defaulted SIDs and ACLs are just referenced until they are copied
    //  into the self-relative descriptor.  Inherited ACLs are built in a
    //  temporary buffer which must be deallocated after being copied to the
    //  self-relative descriptor.
    //



    //
    //  If a security descriptor has been passed, capture it, otherwise
    //  cobble up a fake one to simplify the code that follows.
    //

    if (ARGUMENT_PRESENT(CreatorDescriptor)) {

        CapturedDescriptor = CreatorDescriptor;
        SecurityDescriptorPassed = TRUE;

    } else {

        //
        //  No descriptor passed, make a fake one
        //

        SecurityDescriptorPassed = FALSE;

        RtlCreateSecurityDescriptor(&InCaseOneNotPassed,
                                    SECURITY_DESCRIPTOR_REVISION);
        CapturedDescriptor = &InCaseOneNotPassed;

    }


    if ( CapturedDescriptor->Control & SE_SERVER_SECURITY ) {
        ServerObject = TRUE;
    } else {
        ServerObject = FALSE;
    }

    if ( CapturedDescriptor->Control & SE_DACL_UNTRUSTED ) {
        DaclUntrusted = TRUE;
    } else {
        DaclUntrusted = FALSE;
    }



    //
    // Get the required information from the token.
    //
    //
    // Grab pointers to the default owner, primary group, and
    // discretionary ACL.
    //
    if ( Token != NULL || ServerObject ) {

        PSID TmpSubjectContextOwner = NULL;
        PSID TmpSubjectContextGroup = NULL;
        PSID TmpServerOwner = NULL;
        PSID TmpServerGroup = NULL;

        PACL TmpSubjectContextDacl = NULL;

        SIZE_T SubjectContextInfoSize = 0;

        //
        // Lock the subject context for read access so that the pointers
        // we copy out of it don't disappear on us at random
        //

        SeLockSubjectContext( SubjectSecurityContext );

        SepGetDefaultsSubjectContext(
            SubjectSecurityContext,
            &TmpSubjectContextOwner,
            &TmpSubjectContextGroup,
            &TmpServerOwner,
            &TmpServerGroup,
            &TmpSubjectContextDacl
            );

        //
        // We can't keep the subject context locked, because
        // we may have to do a privilege check later, which calls
        // PsLockProcessSecurityFields, which can cause a deadlock
        // with PsImpersonateClient, which takes them in the reverse
        // order.
        //
        // Since we're giving up our read lock on the token, we
        // need to copy all the stuff that we just got back.  Since
        // it's not going to change, we can save some cycles and copy
        // it all into a single chunck of memory.
        //

        SubjectContextInfoSize = SeLengthSid( TmpSubjectContextOwner ) +
                                 SeLengthSid( TmpServerOwner )         +
                                 (TmpSubjectContextGroup != NULL ? SeLengthSid( TmpSubjectContextGroup ) : 0) +
                                 (TmpServerGroup         != NULL ? SeLengthSid( TmpServerGroup )         : 0) +
                                 (TmpSubjectContextDacl  != NULL ? TmpSubjectContextDacl->AclSize        : 0);

        SubjectContextInfo = ExAllocatePoolWithTag( PagedPool, SubjectContextInfoSize, 'dSeS');

        if (SubjectContextInfo) {

            //
            // Copy in the data
            //

            Base = SubjectContextInfo;

            //
            // There will always be an owner.
            //

            SubjectContextOwner = (PSID)Base;
            RtlCopySid( SeLengthSid( TmpSubjectContextOwner), Base, TmpSubjectContextOwner );
            Base += SeLengthSid( TmpSubjectContextOwner);

            //
            // Groups may be NULL
            //

            if (TmpSubjectContextGroup != NULL) {
                SubjectContextGroup = (PSID)Base;
                RtlCopySid( SeLengthSid( TmpSubjectContextGroup), Base, TmpSubjectContextGroup );
                Base += SeLengthSid( TmpSubjectContextGroup );
            } else {
                SubjectContextGroup = NULL;
            }

            ServerOwner = (PSID)Base;
            RtlCopySid( SeLengthSid( TmpServerOwner ), Base, TmpServerOwner );
            Base += SeLengthSid( TmpServerOwner );

            //
            // Groups may be NULL
            //

            if (TmpServerGroup != NULL) {
                ServerGroup = (PSID)Base;
                RtlCopySid( SeLengthSid( TmpServerGroup ), Base, TmpServerGroup );
                Base += SeLengthSid( TmpServerGroup );
            } else {
                ServerGroup = NULL;
            }

            if (TmpSubjectContextDacl != NULL) {
                SubjectContextDacl = (PACL)Base;
                RtlCopyMemory( Base, TmpSubjectContextDacl, TmpSubjectContextDacl->AclSize );
                // Base += TmpSubjectContextDacl->AclSize;
            } else {
                SubjectContextDacl = NULL;
            }

        } else {

            SeUnlockSubjectContext( SubjectSecurityContext );

            return( STATUS_INSUFFICIENT_RESOURCES );
        }

        SeUnlockSubjectContext( SubjectSecurityContext );
    }



    //
    // Establish an owner SID
    //

    NewOwner = RtlpOwnerAddrSecurityDescriptor(CapturedDescriptor);

    if ((NewOwner) != NULL) {

        //
        // Use the specified owner
        //

        OwnerExplicitlyAssigned = TRUE;

    } else {

        //
        // If the caller said to default the owner from the parent descriptor,
        //  grab it now.
        //

        if ( AutoInheritFlags & SEF_DEFAULT_OWNER_FROM_PARENT) {
            if ( !ARGUMENT_PRESENT(ParentDescriptor) ) {
                Status = STATUS_INVALID_OWNER;
                goto Cleanup;
            }
            NewOwner = RtlpOwnerAddrSecurityDescriptor((SECURITY_DESCRIPTOR *)ParentDescriptor);
            OwnerExplicitlyAssigned = TRUE;

            if ( NewOwner == NULL ) {
                Status = STATUS_INVALID_OWNER;
                goto Cleanup;
            }
        } else {

            //
            // Pick up the default from the subject's security context.
            //
            // This does NOT constitute explicit assignment of owner
            // and does not have to be checked as an ID that can be
            // assigned as owner.  This is because a default can not
            // be established in a token unless the user of the token
            // can assign it as an owner.
            //

            //
            // If we've been asked to create a ServerObject, we need to
            // make sure to pick up the new owner from the Primary token,
            // not the client token.  If we're not impersonating, they will
            // end up being the same.
            //

            NewOwner = ServerObject ? ServerOwner : SubjectContextOwner;

            //
            // Ensure an owner is now defined.
            //

            if ( NewOwner == NULL ) {
                Status = STATUS_NO_TOKEN;
                goto Cleanup;
            }
        }
    }


    //
    // Establish a Group SID
    //

    NewGroup = RtlpGroupAddrSecurityDescriptor(CapturedDescriptor);

    if (NewGroup == NULL) {

        //
        // If the caller said to default the group from the parent descriptor,
        //  grab it now.
        //

        if ( AutoInheritFlags & SEF_DEFAULT_GROUP_FROM_PARENT) {
            if ( !ARGUMENT_PRESENT(ParentDescriptor) ) {
                Status = STATUS_INVALID_PRIMARY_GROUP;
                goto Cleanup;
            }
            NewGroup = RtlpGroupAddrSecurityDescriptor((SECURITY_DESCRIPTOR *)ParentDescriptor);
        } else {
            //
            // Pick up the primary group from the subject's security context
            //
            // If we're creating a Server object, use the group from the server
            // context.
            //

            NewGroup = ServerObject ? ServerGroup : SubjectContextGroup;

        }

    }

    if (NewGroup == NULL) {
        Status = STATUS_INVALID_PRIMARY_GROUP;
        goto Cleanup;
    }



    //
    // Establish System Acl
    //

    Status = RtlpInheritAcl (
                ARGUMENT_PRESENT(ParentDescriptor) ?
                    RtlpSaclAddrSecurityDescriptor(
                        ((SECURITY_DESCRIPTOR *)ParentDescriptor)) :
                    NULL,
                RtlpSaclAddrSecurityDescriptor(CapturedDescriptor),
                SeControlSaclToGeneric( CapturedDescriptor->Control ),
                IsDirectoryObject,
                (BOOLEAN)((AutoInheritFlags & SEF_SACL_AUTO_INHERIT) != 0),
                (BOOLEAN)((AutoInheritFlags & SEF_DEFAULT_DESCRIPTOR_FOR_OBJECT) != 0),
                NewOwner,
                NewGroup,
                ServerOwner,
                ServerGroup,
                GenericMapping,
                TRUE,   // Is a SACL
                pObjectType,
                GuidCount,
                &NewSacl,
                &SaclExplicitlyAssigned,
                &GenericControl );

    if ( NT_SUCCESS(Status) ) {
        NewSaclInherited = TRUE;
        NewControlBits |= SE_SACL_PRESENT | SeControlGenericToSacl( GenericControl );

    } else if ( Status == STATUS_NO_INHERITANCE ) {

        //
        // Always set the auto inherit bit if the caller requested it.
        //

        if ( AutoInheritFlags & SEF_SACL_AUTO_INHERIT) {
            NewControlBits |= SE_SACL_AUTO_INHERITED;
        }

        //
        // No inheritable ACL - check for a defaulted one.
        //
        if ( RtlpAreControlBitsSet( CapturedDescriptor,
                                SE_SACL_PRESENT | SE_SACL_DEFAULTED ) ) {

            //
            // Reference the default ACL
            //

            NewSacl = RtlpSaclAddrSecurityDescriptor(CapturedDescriptor);
            NewControlBits |= SE_SACL_PRESENT;
            NewControlBits |= (CapturedDescriptor->Control & SE_SACL_PROTECTED);

            //
            // This counts as an explicit assignment.
            //
            SaclExplicitlyAssigned = TRUE;
        }

    } else {

        //
        // Some unusual error occured
        //

        goto Cleanup;
    }




    //
    // Establish Discretionary Acl
    //

    Status = RtlpInheritAcl (
                ARGUMENT_PRESENT(ParentDescriptor) ?
                    RtlpDaclAddrSecurityDescriptor(
                        ((SECURITY_DESCRIPTOR *)ParentDescriptor)) :
                    NULL,
                RtlpDaclAddrSecurityDescriptor(CapturedDescriptor),
                SeControlDaclToGeneric( CapturedDescriptor->Control ),
                IsDirectoryObject,
                (BOOLEAN)((AutoInheritFlags & SEF_DACL_AUTO_INHERIT) != 0),
                (BOOLEAN)((AutoInheritFlags & SEF_DEFAULT_DESCRIPTOR_FOR_OBJECT) != 0),
                NewOwner,
                NewGroup,
                ServerOwner,
                ServerGroup,
                GenericMapping,
                FALSE,   // Is a DACL
                pObjectType,
                GuidCount,
                &NewDacl,
                &DaclExplicitlyAssigned,
                &GenericControl );

    if ( NT_SUCCESS(Status) ) {
        NewDaclInherited = TRUE;
        NewControlBits |= SE_DACL_PRESENT | SeControlGenericToDacl( GenericControl );

    } else if ( Status == STATUS_NO_INHERITANCE ) {

        //
        // Always set the auto inherit bit if the caller requested it.
        //

        if ( AutoInheritFlags & SEF_DACL_AUTO_INHERIT) {
            NewControlBits |= SE_DACL_AUTO_INHERITED;
        }

        //
        // No inheritable ACL - check for a defaulted one.
        //
        if ( RtlpAreControlBitsSet( CapturedDescriptor,
                                SE_DACL_PRESENT | SE_DACL_DEFAULTED ) ) {

            //
            // Reference the default ACL
            //

            NewDacl = RtlpDaclAddrSecurityDescriptor(CapturedDescriptor);
            NewControlBits |= SE_DACL_PRESENT;
            NewControlBits |= (CapturedDescriptor->Control & SE_DACL_PROTECTED);

            //
            // This counts as an explicit assignment.
            //
            DaclExplicitlyAssigned = TRUE;

        //
        // Default to the DACL on the token.
        //
        } else if (ARGUMENT_PRESENT(SubjectContextDacl)) {

            NewDacl = SubjectContextDacl;
            NewControlBits |= SE_DACL_PRESENT;

        }


    } else {

        //
        // Some unusual error occured
        //

        goto Cleanup;
    }

#ifdef  ASSERT_ON_NULL_DACL

    //
    // Culprit will probably be the caller NtCreate*, or
    // RtlNewSecurityObject.  Note that although this will not always occur
    // because of explicit user action it still must be corrected.
    //
    
    if (RtlpAssertOnNullDacls) {

        ASSERT(("NULL DACLs are NOT allowed!", NewDacl != NULL));
    }

#endif // ASSERT_ON_NULL_DACL

    //
    // If auto inheriting and the computed child DACL is NULL,
    //  mark it as protected.
    //
    // NULL DACLs are problematic when ACEs are actually inherited from the
    // parent DACL.  It is better to mark them as protected NOW (even if we don't
    // end up inheriting any ACEs) to avoid confusion later.
    //

    if ( (AutoInheritFlags & SEF_DACL_AUTO_INHERIT) != 0 &&
         NewDacl == NULL ) {
        NewControlBits |= SE_DACL_PROTECTED;
    }



    //
    // Now make sure that the caller has the right to assign
    // everything in the descriptor.  The requestor is subjected
    // to privilege and restriction tests for some assignments.
    //
    if (RequestorMode == UserMode) {


        //
        // Anybody can assign any Discretionary ACL or group that they want to.
        //

        //
        //  See if the system ACL was explicitly specified
        //

        if ( SaclExplicitlyAssigned &&
             (AutoInheritFlags & SEF_AVOID_PRIVILEGE_CHECK) == 0 ) {

            //
            // Require a Token if we're to do the privilege check.
            //

            if ( Token == NULL ) {
                Status = STATUS_NO_TOKEN;
                goto Cleanup;
            }

            //
            // Check for appropriate Privileges
            // Audit/Alarm messages need to be generated due to the attempt
            // to perform a privileged operation.
            //

            //
            // Note: be sure to do the privilege check against
            // the passed subject context!
            //

            PrivilegeSet.PrivilegeCount = 1;
            PrivilegeSet.Control = PRIVILEGE_SET_ALL_NECESSARY;
            PrivilegeSet.Privilege[0].Luid = SeSecurityPrivilege;
            PrivilegeSet.Privilege[0].Attributes = 0;

            HasPrivilege = SePrivilegeCheck(
                               &PrivilegeSet,
                               SubjectSecurityContext,
                               RequestorMode
                               );

            if ( RequestorMode != KernelMode ) {

                SePrivilegedServiceAuditAlarm (
                    NULL,
                    SubjectSecurityContext,
                    &PrivilegeSet,
                    HasPrivilege
                    );
            }

            if ( !HasPrivilege ) {
                Status = STATUS_PRIVILEGE_NOT_HELD;
                goto Cleanup;
            }

        }

        //
        // See if the owner field is one the requestor can assign
        //

        if (OwnerExplicitlyAssigned &&
            (AutoInheritFlags & SEF_AVOID_OWNER_CHECK) == 0 ) {



            if (!SepValidOwnerSubjectContext(
                    SubjectSecurityContext,
                    NewOwner,
                    ServerObject)
                    ) {

                Status = STATUS_INVALID_OWNER;
                goto Cleanup;
            }
        }


        //
        // If the DACL was explictly assigned and this is a server object,
        //  convert the DACL to be a server DACL
        //

        if (DaclExplicitlyAssigned && ServerObject) {

            Status = RtlpCreateServerAcl(
                         NewDacl,
                         DaclUntrusted,
                         ServerOwner,
                         &ServerDacl,
                         &ServerDaclAllocated
                         );

            if (!NT_SUCCESS( Status )) {
                goto Cleanup;
            }
            
            if (NewDaclInherited && NewDacl) {
                ExFreePool( NewDacl );
            }

            NewDacl = ServerDacl;
            ServerDacl = NULL;
        }
    }


    //
    // Everything is assignable by the requestor.
    // Calculate the memory needed to house all the information in
    // a self-relative security descriptor.
    //
    // Also map the ACEs for application to the target object
    // type, if they haven't already been mapped.
    //
    OwnerSize = SeLengthSid(NewOwner);
    NewOwnerSize = LongAlignSize(OwnerSize);
    if (NewGroup != NULL) {
        GroupSize = SeLengthSid(NewGroup);
        NewGroupSize = LongAlignSize(GroupSize);
    }

    if ((NewControlBits & SE_SACL_PRESENT) && (NewSacl != NULL)) {
        NewSaclSize = LongAlignSize(NewSacl->AclSize);
    } else {
        NewSaclSize = 0;
    }

    if ( (NewControlBits & SE_DACL_PRESENT) && (NewDacl != NULL)) {
        NewDaclSize = LongAlignSize(NewDacl->AclSize);
    } else {
        NewDaclSize = 0;
    }

    AllocationSize = LongAlignSize(sizeof(SECURITY_DESCRIPTOR_RELATIVE)) +
                     NewOwnerSize +
                     NewGroupSize +
                     NewSaclSize  +
                     NewDaclSize;

    //
    // Allocate and initialize the security descriptor as
    // self-relative form.
    //

    INewDescriptor = (PSECURITY_DESCRIPTOR)ExAllocatePoolWithTag( PagedPool, AllocationSize, 'dSeS');

    if ( INewDescriptor == NULL ) {
        Status = STATUS_INSUFFICIENT_RESOURCES;
        goto Cleanup;
    }

    RtlCreateSecurityDescriptorRelative(
        INewDescriptor,
        SECURITY_DESCRIPTOR_REVISION
        );

    RtlpSetControlBits( INewDescriptor, NewControlBits );

    Base = (PCHAR)(INewDescriptor);
    Field =  Base + sizeof(SECURITY_DESCRIPTOR_RELATIVE);

    //
    // Map and Copy in the Sacl
    //

    if (NewControlBits & SE_SACL_PRESENT) {

        if (NewSacl != NULL) {

            RtlCopyMemory( Field, NewSacl, NewSacl->AclSize );

            if (!NewSaclInherited) {
                RtlpApplyAclToObject( (PACL)Field, GenericMapping );
            }

            INewDescriptor->Sacl = RtlPointerToOffset(Base,Field);
            if (NewSaclSize > NewSacl->AclSize) {
                RtlZeroMemory (Field + NewSacl->AclSize, NewSaclSize - NewSacl->AclSize);
            }
            Field += NewSaclSize;

        } else {

            INewDescriptor->Sacl = 0;
        }

    }

    //
    // Map and Copy in the Dacl
    //

    if (NewControlBits & SE_DACL_PRESENT) {

        if (NewDacl != NULL) {

            RtlCopyMemory( Field, NewDacl, NewDacl->AclSize );

            if (!NewDaclInherited) {
                RtlpApplyAclToObject( (PACL)Field, GenericMapping );
            }

            INewDescriptor->Dacl = RtlPointerToOffset(Base,Field);
            if (NewDaclSize > NewDacl->AclSize) {
                RtlZeroMemory (Field + NewDacl->AclSize, NewDaclSize - NewDacl->AclSize);
            }
            Field += NewDaclSize;

        } else {

            INewDescriptor->Dacl = 0;
        }

    }

    //
    // Assign the owner
    //

    RtlCopyMemory( Field, NewOwner, OwnerSize );
    if (NewOwnerSize > OwnerSize) {
        RtlZeroMemory (Field + OwnerSize, NewOwnerSize - OwnerSize);
    }
    INewDescriptor->Owner = RtlPointerToOffset(Base,Field);
    Field += NewOwnerSize;

    if (NewGroup != NULL) {
        RtlCopyMemory( Field, NewGroup, GroupSize );
        if (NewGroupSize > GroupSize) {
            RtlZeroMemory (Field + GroupSize, NewGroupSize - GroupSize);
        }
        INewDescriptor->Group = RtlPointerToOffset(Base,Field);
    }

    Status = STATUS_SUCCESS;



Cleanup:
    //
    // If we allocated memory for a Server DACL, free it now.
    //

    if (ServerDaclAllocated && ServerDacl != NULL) {
        ExFreePool( ServerDacl );
    }

    //
    // Either an error was encountered or the assignment has completed
    // successfully.  In either case, we have to clean up any memory.
    //

    if (SubjectContextInfo != NULL) {
        ExFreePool( SubjectContextInfo );
    }

    if (NewSaclInherited && NewSacl != NULL ) {
        ExFreePool( NewSacl );
    }

    if (NewDaclInherited && NewDacl != NULL ) {
        ExFreePool( NewDacl );
    }

    *NewDescriptor = (PSECURITY_DESCRIPTOR) INewDescriptor;


    return Status;
}


NTSTATUS
RtlpSetSecurityObject (
    IN PVOID Object OPTIONAL,
    IN SECURITY_INFORMATION SecurityInformation,
    IN PSECURITY_DESCRIPTOR ModificationDescriptor,
    IN OUT PSECURITY_DESCRIPTOR *ObjectsSecurityDescriptor,
    IN ULONG AutoInheritFlags,
    IN ULONG PoolType,
    IN PGENERIC_MAPPING GenericMapping,
    IN HANDLE Token OPTIONAL
    )


/*++

Routine Description:

    Modify an object's existing self-relative form security descriptor.

    This procedure, called only from user mode, is used to update a
    security descriptor on an existing protected server's object.  It
    applies changes requested by a new security descriptor to the existing
    security descriptor.  If necessary, this routine will allocate
    additional memory to produce a larger security descriptor.  All access
    checking is expected to be done before calling this routine.  This
    includes checking for WRITE_OWNER, WRITE_DAC, and privilege to assign a
    system ACL as appropriate.

    The caller of this routine must not be impersonating a client.

                                  - - WARNING - -

    This service is for use by protected subsystems that project their own
    type of object.  This service is explicitly not for use by the
    executive for executive objects and must not be called from kernel
    mode.

Arguments:

    Object - Optionally supplies the object whose security is
        being adjusted.  This is used to update security quota
        information.

    SecurityInformation - Indicates which security information is
        to be applied to the object.  The value(s) to be assigned are
        passed in the ModificationDescriptor parameter.

    ModificationDescriptor - Supplies the input security descriptor to be
        applied to the object.  The caller of this routine is expected
        to probe and capture the passed security descriptor before calling
        and release it after calling.

    ObjectsSecurityDescriptor - Supplies the address of a pointer to
        the objects security descriptor that is going to be altered by
        this procedure.  This security descriptor must be in self-
        relative form or an error will be returned.

    AutoInheritFlags - Controls automatic inheritance of ACES.
        Valid values are a bits mask of the logical OR of
        one or more of the following bits:

        SEF_DACL_AUTO_INHERIT - If set, inherited ACEs from the
            DACL in the ObjectsSecurityDescriptor are preserved and inherited ACEs from
            the ModificationDescriptor are ignored. Inherited ACEs are not supposed
            to be modified; so preserving them across this call is appropriate.
            If a protected server does not itself implement auto inheritance, it should
            not set this bit.  The caller of the protected server may implement
            auto inheritance and my indeed be modifying inherited ACEs.

        SEF_SACL_AUTO_INHERIT - If set, inherited ACEs from the
            SACL in the ObjectsSecurityDescriptor are preserved and inherited ACEs from
            the ModificationDescriptor are ignored. Inherited ACEs are not supposed
            to be modified; so preserving them across this call is appropriate.
            If a protected server does not itself implement auto inheritance, it should
            not set this bit.  The caller of the protected server may implement
            auto inheritance and my indeed be modifying inherited ACEs.

         SEF_AVOID_PRIVILEGE_CHECK - If set, the Token in not used to ensure the
            Owner passed in ModificationDescriptor is valid.

    PoolType - Specifies the type of pool to allocate for the objects
        security descriptor.

    GenericMapping - This argument provides the mapping of generic to
        specific/standard access types for the object being accessed.
        This mapping structure is expected to be safe to access
        (i.e., captured if necessary) prior to be passed to this routine.

    Token - (optionally) Supplies the token for the client on whose
        behalf the security is being modified.  This parameter is only
        required to ensure that the client has provided a legitimate
        value for a new owner SID.  The token must be open for
        TOKEN_QUERY access.

Return Value:

    STATUS_SUCCESS - The operation was successful.

    STATUS_INVALID_OWNER - The owner SID provided as the new owner of the
        target security descriptor is not one the caller is authorized to
        assign as the owner of an object, or the client did not pass
        a token at all.

    STATUS_NO_CLIENT_TOKEN - Indicates a client token was not explicitly
        provided and the caller is not currently impersonating a client.

    STATUS_BAD_DESCRIPTOR_FORMAT - Indicates the provided object's security
        descriptor was not in self-relative format.

--*/

{
    BOOLEAN NewGroupPresent = FALSE;
    BOOLEAN NewOwnerPresent = FALSE;

    BOOLEAN ServerAclAllocated = FALSE;
    BOOLEAN LocalDaclAllocated = FALSE;
    BOOLEAN LocalSaclAllocated = FALSE;
    BOOLEAN ServerObject;
    BOOLEAN DaclUntrusted;

    PCHAR Field;
    PCHAR Base;

    PISECURITY_DESCRIPTOR_RELATIVE NewDescriptor = NULL;

    NTSTATUS Status;

    TOKEN_STATISTICS ThreadTokenStatistics;

    ULONG ReturnLength;

    PSID NewGroup;
    PSID NewOwner;

    PACL NewDacl;
    PACL LocalDacl;
    PACL NewSacl;
    PACL LocalSacl;

    ULONG NewDaclSize;
    ULONG NewSaclSize;
    ULONG NewOwnerSize, OwnerSize;
    ULONG NewGroupSize, GroupSize;
    ULONG AllocationSize;
    ULONG ServerOwnerInfoSize;

    HANDLE PrimaryToken;
    ULONG GenericControl;
    ULONG NewControlBits = SE_SELF_RELATIVE;

    PACL ServerDacl;

    SECURITY_SUBJECT_CONTEXT SubjectContext;


    //
    // Typecast to internal representation of security descriptor.
    // Note that the internal one is not a pointer to a pointer.
    // It is just a pointer to a security descriptor.
    //
    PISECURITY_DESCRIPTOR IModificationDescriptor =
       (PISECURITY_DESCRIPTOR)ModificationDescriptor;

    PISECURITY_DESCRIPTOR *IObjectsSecurityDescriptor =
       (PISECURITY_DESCRIPTOR *)(ObjectsSecurityDescriptor);

    RTL_PAGED_CODE();

    //
    //  Validate that the provided SD is in self-relative form
    //

    if ( !RtlpAreControlBitsSet(*IObjectsSecurityDescriptor, SE_SELF_RELATIVE) ) {
        Status = STATUS_BAD_DESCRIPTOR_FORMAT;
        goto Cleanup;
    }

    //
    // Check to see if we need to edit the passed acl
    // either because we're creating a server object, or because
    // we were passed an untrusted ACL.
    //

    if (ARGUMENT_PRESENT(ModificationDescriptor)) {

        if ( RtlpAreControlBitsSet(IModificationDescriptor, SE_SERVER_SECURITY)) {
            ServerObject = TRUE;
        } else {
            ServerObject = FALSE;
        }

        if ( RtlpAreControlBitsSet(IModificationDescriptor, SE_DACL_UNTRUSTED)) {
            DaclUntrusted = TRUE;
        } else {
            DaclUntrusted = FALSE;
        }

    } else {

        ServerObject = FALSE;
        DaclUntrusted = FALSE;

    }


    //
    // For each item specified in the SecurityInformation, extract it
    // and get it to the point where it can be copied into a new
    // descriptor.
    //

    //
    // if he's setting the owner field, make sure he's
    // allowed to set that value as an owner.
    //

    if (SecurityInformation & OWNER_SECURITY_INFORMATION) {

        NewOwner = RtlpOwnerAddrSecurityDescriptor( IModificationDescriptor );
        NewOwnerPresent = TRUE;

        if ((AutoInheritFlags & SEF_AVOID_PRIVILEGE_CHECK) == 0 ) {


            SeCaptureSubjectContext( &SubjectContext );

            if (!SepValidOwnerSubjectContext( &SubjectContext, NewOwner, ServerObject ) ) {

                SeReleaseSubjectContext( &SubjectContext );
                return( STATUS_INVALID_OWNER );

            } else {

                SeReleaseSubjectContext( &SubjectContext );
            }
        }

    } else {

        NewOwner = RtlpOwnerAddrSecurityDescriptor ( *IObjectsSecurityDescriptor );
        if (NewOwner == NULL) {
            Status = STATUS_INVALID_OWNER;
            goto Cleanup;
        }

    }
    ASSERT( NewOwner != NULL );
    if (!RtlValidSid( NewOwner )) {
        Status = STATUS_INVALID_OWNER;
        goto Cleanup;
    }


    if (SecurityInformation & GROUP_SECURITY_INFORMATION) {

        NewGroup = RtlpGroupAddrSecurityDescriptor(IModificationDescriptor);
        NewGroupPresent = TRUE;

    } else {

        NewGroup = RtlpGroupAddrSecurityDescriptor( *IObjectsSecurityDescriptor );
    }

    if (NewGroup != NULL) {
        if (!RtlValidSid( NewGroup )) {
            Status = STATUS_INVALID_PRIMARY_GROUP;
            goto Cleanup;
        }
    } else {
        Status = STATUS_INVALID_PRIMARY_GROUP;
        goto Cleanup;
    }


    if (SecurityInformation & DACL_SECURITY_INFORMATION) {

#ifdef  ASSERT_ON_NULL_DACL

        //
        // Culprit will probably be the caller NtSetSecurityObject, or
        // RtlSetSecurityObject. 
        // 

        if (RtlpAssertOnNullDacls) {

            ASSERT(("NULL DACLs are NOT allowed!",
                    RtlpDaclAddrSecurityDescriptor(IModificationDescriptor) != NULL));
        }
#endif // ASSERT_ON_NULL_DACL

        //
        // If AutoInherit is requested,
        //  build a merged ACL.
        //

        if ( AutoInheritFlags & SEF_DACL_AUTO_INHERIT ) {
            Status = RtlpComputeMergedAcl(
                        RtlpDaclAddrSecurityDescriptor( *IObjectsSecurityDescriptor ),
                        SeControlDaclToGeneric( (*IObjectsSecurityDescriptor)->Control ),
                        RtlpDaclAddrSecurityDescriptor( IModificationDescriptor ),
                        SeControlDaclToGeneric( IModificationDescriptor->Control ),
                        NewOwner,
                        NewGroup,
                        GenericMapping,
                        FALSE,      // Not a SACL
                        &LocalDacl,
                        &GenericControl );

            if ( !NT_SUCCESS(Status)) {
                goto Cleanup;
            }

            LocalDaclAllocated = TRUE;
            NewDacl = LocalDacl;
            NewControlBits |= SE_DACL_PRESENT;
            NewControlBits |= SeControlGenericToDacl( GenericControl );

        //
        // If AutoInherit isn't requested,
        //  just grab a copy of the input DACL.
        //

        } else {
            NewDacl = RtlpDaclAddrSecurityDescriptor( IModificationDescriptor );
            NewControlBits |= SE_DACL_PRESENT;
            NewControlBits |= IModificationDescriptor->Control & SE_DACL_PROTECTED;

            //
            // If the original caller claims he understands auto inheritance,
            //  preserve the AutoInherited flag.
            //

            if ( RtlpAreControlBitsSet(IModificationDescriptor, SE_DACL_AUTO_INHERIT_REQ|SE_DACL_AUTO_INHERITED) ) {
                NewControlBits |= SE_DACL_AUTO_INHERITED;
            }
        }

        if (ServerObject) {

            PSID SubjectContextOwner;
            PSID SubjectContextGroup;
            PSID SubjectContextServerOwner;
            PSID SubjectContextServerGroup;
            PACL SubjectContextDacl;

            SeCaptureSubjectContext( &SubjectContext );

            SepGetDefaultsSubjectContext(
                &SubjectContext,
                &SubjectContextOwner,
                &SubjectContextGroup,
                &SubjectContextServerOwner,
                &SubjectContextServerGroup,
                &SubjectContextDacl
                );

            Status = RtlpCreateServerAcl(
                         NewDacl,
                         DaclUntrusted,
                         SubjectContextServerOwner,
                         &ServerDacl,
                         &ServerAclAllocated
                         );

            SeReleaseSubjectContext( &SubjectContext );

            if (!NT_SUCCESS( Status )) {
                goto Cleanup;
            }

            NewDacl = ServerDacl;

        }

    } else {

        NewDacl = RtlpDaclAddrSecurityDescriptor( *IObjectsSecurityDescriptor );
    }



    if (SecurityInformation & SACL_SECURITY_INFORMATION) {


        //
        // If AutoInherit is requested,
        //  build a merged ACL.
        //

        if ( AutoInheritFlags & SEF_SACL_AUTO_INHERIT ) {
            Status = RtlpComputeMergedAcl(
                        RtlpSaclAddrSecurityDescriptor( *IObjectsSecurityDescriptor ),
                        SeControlSaclToGeneric( (*IObjectsSecurityDescriptor)->Control ),
                        RtlpSaclAddrSecurityDescriptor( IModificationDescriptor ),
                        SeControlSaclToGeneric( IModificationDescriptor->Control ),
                        NewOwner,
                        NewGroup,
                        GenericMapping,
                        TRUE,      // Is a SACL
                        &LocalSacl,
                        &GenericControl );

            if ( !NT_SUCCESS(Status)) {
                goto Cleanup;
            }
            LocalSaclAllocated = TRUE;
            NewSacl = LocalSacl;
            NewControlBits |= SE_SACL_PRESENT;
            NewControlBits |= SeControlGenericToSacl( GenericControl );
        } else {
            NewSacl = RtlpSaclAddrSecurityDescriptor( IModificationDescriptor );
            NewControlBits |= SE_SACL_PRESENT;
            NewControlBits |= IModificationDescriptor->Control & SE_SACL_PROTECTED;

            //
            // If the original caller claims he understands auto inheritance,
            //  preserve the AutoInherited flag.
            //

            if ( RtlpAreControlBitsSet(IModificationDescriptor, SE_SACL_AUTO_INHERIT_REQ|SE_SACL_AUTO_INHERITED) ) {
                NewControlBits |= SE_SACL_AUTO_INHERITED;
            }
        }

    } else {

        NewSacl = RtlpSaclAddrSecurityDescriptor( *IObjectsSecurityDescriptor );
    }


    //
    // Everything is assignable by the requestor.
    // Calculate the memory needed to house all the information in
    // a self-relative security descriptor.
    //
    // Also map the ACEs for application to the target object
    // type, if they haven't already been mapped.
    //
    OwnerSize = SeLengthSid(NewOwner);
    NewOwnerSize = LongAlignSize(OwnerSize);

    if (NewGroup != NULL) {
        GroupSize = SeLengthSid(NewGroup);
    } else {
        GroupSize = 0;
    }
    NewGroupSize = LongAlignSize(GroupSize);

    if (NewSacl != NULL) {
        NewSaclSize = LongAlignSize(NewSacl->AclSize);
    } else {
        NewSaclSize = 0;
    }

    if (NewDacl !=NULL) {
        NewDaclSize = LongAlignSize(NewDacl->AclSize);
    } else {
        NewDaclSize = 0;
    }

    AllocationSize = LongAlignSize(sizeof(SECURITY_DESCRIPTOR_RELATIVE)) +
                     NewOwnerSize +
                     NewGroupSize +
                     NewSaclSize  +
                     NewDaclSize;

    //
    // Allocate and initialize the security descriptor as
    // self-relative form.
    //

    NewDescriptor = ExAllocatePoolWithTag(PoolType, AllocationSize, 'dSeS');

    if ( NewDescriptor == NULL ) {
        Status = STATUS_NO_MEMORY;
        goto Cleanup;
    }

    Status = RtlCreateSecurityDescriptorRelative(
                 NewDescriptor,
                 SECURITY_DESCRIPTOR_REVISION
                 );

    ASSERT( NT_SUCCESS( Status ) );

    //
    // We must check to make sure that the Group and Dacl size
    // do not exceed the quota preallocated for this object's
    // security when it was created.
    //
    // Update SeComputeSecurityQuota if this changes.
    //


    if (ARGUMENT_PRESENT( Object )) {

        Status = ObValidateSecurityQuota(
                     Object,
                     NewGroupSize + NewDaclSize
                     );

        if (!NT_SUCCESS( Status )) {

            //
            // The new information is too big.
            //

            ExFreePool( NewDescriptor );
            goto Cleanup;
        }

    }

    Base = (PCHAR)NewDescriptor;
    Field =  Base + sizeof(SECURITY_DESCRIPTOR_RELATIVE);

    //
    // Map and Copy in the Sacl
    //


    //         if new item {
    //             PRESENT=TRUE
    //             DEFAULTED=FALSE
    //             if (NULL) {
    //                 set new pointer to NULL
    //             } else {
    //                 copy into new SD
    //             }
    //         } else {
    //             copy PRESENT bit
    //             copy DEFAULTED bit
    //             if (NULL) {
    //                 set new pointer to NULL
    //             } else {
    //                 copy old one into new SD
    //             }
    //         }

    RtlpSetControlBits( NewDescriptor, NewControlBits );


    if (IModificationDescriptor->Control & SE_RM_CONTROL_VALID) {
        NewDescriptor->Sbz1 = IModificationDescriptor->Sbz1;
        NewDescriptor->Control |= SE_RM_CONTROL_VALID;
    }

    if (NewSacl == NULL) {
        NewDescriptor->Sacl = 0;

    } else {
        RtlCopyMemory( Field, NewSacl, NewSacl->AclSize );
        RtlpApplyAclToObject( (PACL)Field, GenericMapping );
        NewDescriptor->Sacl = RtlPointerToOffset(Base,Field);
        if (NewSaclSize > NewSacl->AclSize) {
            RtlZeroMemory( Field + NewSacl->AclSize, NewSaclSize - NewSacl->AclSize);
        }
        Field += NewSaclSize;
    }




    if ( (NewControlBits & SE_SACL_PRESENT) == 0 ) {

        //
        // Propagate the SE_SACL_DEFAULTED and SE_SACL_PRESENT
        // bits from the old security descriptor into the new
        // one.
        //

        RtlpPropagateControlBits(
            NewDescriptor,
            *IObjectsSecurityDescriptor,
            SE_SACL_DEFAULTED | SE_SACL_PRESENT | SE_SACL_PROTECTED | SE_SACL_AUTO_INHERITED
            );

    }



    //
    // Fill in Dacl field in new SD
    //

    if (NewDacl == NULL) {
        NewDescriptor->Dacl = 0;

    } else {
        RtlCopyMemory( Field, NewDacl, NewDacl->AclSize );
        RtlpApplyAclToObject( (PACL)Field, GenericMapping );
        NewDescriptor->Dacl = RtlPointerToOffset(Base,Field);
        if (NewDaclSize > NewDacl->AclSize) {
            RtlZeroMemory( Field + NewDacl->AclSize, NewDaclSize - NewDacl->AclSize);
        }
        Field += NewDaclSize;
    }


    if ( (NewControlBits & SE_DACL_PRESENT) == 0 ) {

        //
        // Propagate the SE_DACL_DEFAULTED and SE_DACL_PRESENT
        // bits from the old security descriptor into the new
        // one.
        //

        RtlpPropagateControlBits(
            NewDescriptor,
            *IObjectsSecurityDescriptor,
            SE_DACL_DEFAULTED | SE_DACL_PRESENT | SE_DACL_PROTECTED | SE_DACL_AUTO_INHERITED
            );

    }

//         if new item {
//             PRESENT=TRUE
//             DEFAULTED=FALSE
//             if (NULL) {
//                 set new pointer to NULL
//             } else {
//                 copy into new SD
//             }
//         } else {
//             copy PRESENT bit
//             copy DEFAULTED bit
//             if (NULL) {
//                 set new pointer to NULL
//             } else {
//                 copy old one into new SD
//             }
//         }


    //
    // Fill in Owner field in new SD
    //

    RtlCopyMemory( Field, NewOwner, OwnerSize );
    if (OwnerSize < NewOwnerSize) {
        RtlZeroMemory( Field + OwnerSize, NewOwnerSize - OwnerSize );
    }
    NewDescriptor->Owner = RtlPointerToOffset(Base,Field);
    Field += NewOwnerSize;

    if (!NewOwnerPresent) {

        //
        // Propagate the SE_OWNER_DEFAULTED bit from the old SD.
        // If a new owner is being assigned, we want to leave
        // SE_OWNER_DEFAULTED off, which means leave it alone.
        //

        RtlpPropagateControlBits(
            NewDescriptor,
            *IObjectsSecurityDescriptor,
            SE_OWNER_DEFAULTED
            );

    } else {
        ASSERT( !RtlpAreControlBitsSet( NewDescriptor, SE_OWNER_DEFAULTED ) );
    }


    //
    // Fill in Group field in new SD
    //

    if ( NewGroup != NULL) {
        RtlCopyMemory( Field, NewGroup, GroupSize );
        if (GroupSize < NewGroupSize) {
            RtlZeroMemory( Field + GroupSize, NewGroupSize - GroupSize);
        }
        NewDescriptor->Group = RtlPointerToOffset(Base,Field);
    }

    if (!NewGroupPresent) {

        //
        // Propagate the SE_GROUP_DEFAULTED bit from the old SD
        // If a new owner is being assigned, we want to leave
        // SE_GROUP_DEFAULTED off, which means leave it alone.
        //

        RtlpPropagateControlBits(
            NewDescriptor,
            *IObjectsSecurityDescriptor,
            SE_GROUP_DEFAULTED
            );
    } else {
        ASSERT( !RtlpAreControlBitsSet( NewDescriptor, SE_GROUP_DEFAULTED ) );

    }

    //
    // Free old descriptor
    //

    // Kernel version doesn't free the old descriptor

    *ObjectsSecurityDescriptor = (PSECURITY_DESCRIPTOR)NewDescriptor;
    Status = STATUS_SUCCESS;

Cleanup:
    if ( LocalDaclAllocated ) {
        ExFreePool( LocalDacl );
    }
    if ( LocalSaclAllocated ) {
        ExFreePool( LocalSacl );
    }
    if (ServerAclAllocated) {
        ExFreePool( ServerDacl );
    }

    return( Status );
}

BOOLEAN RtlpValidateSDOffsetAndSize (
    IN ULONG   Offset,
    IN ULONG   Length,
    IN ULONG   MinLength,
    OUT PULONG MaxLength
    )
/*++

Routine Description:

    This procedure validates offsets within a SecurityDescriptor.
    It checks that the structure can have the minimum length,
    not overlap with the fixed header and returns the maximum size
    of the item and longword alignment.

Arguments:

    Offset - Offset from start of SD of structure to validate
    Length - Total size of SD
    MinLength - Minimum size this structure can be
    MaxLength - Returns the maximum length this item can be given by
                the enclosing structure.

Return Value:

    BOOLEAN - TRUE if the item is valid


--*/

{
    ULONG Left;

    *MaxLength = 0;
    //
    // Don't allow overlap with header just in case caller modifies control bits etc
    //
    if (Offset < sizeof (SECURITY_DESCRIPTOR_RELATIVE)) {
       return FALSE;
    }

    //
    // Don't allow offsets beyond the end of the buffer
    //
    if (Offset >= Length) {
       return FALSE;
    }

    //
    // Calculate maximum size of segment and check its limits
    //
    Left = Length - Offset;

    if (Left < MinLength) {
       return FALSE;
    }

    //
    // Reject unaligned offsets
    //
    if (Offset & (sizeof (ULONG) - 1)) {
       return FALSE;
    }
    *MaxLength = Left;
    return TRUE;
}


BOOLEAN
RtlValidRelativeSecurityDescriptor (
    IN PSECURITY_DESCRIPTOR SecurityDescriptorInput,
    IN ULONG SecurityDescriptorLength,
    IN SECURITY_INFORMATION RequiredInformation
    )

/*++

Routine Description:

    This procedure validates a SecurityDescriptor's structure
    contained within a flat buffer.  This involves validating
    the revision levels of each component of the security
    descriptor.

Arguments:

    SecurityDescriptor - Pointer to the SECURITY_DESCRIPTOR structure
        to validate.
    SecurityDescriptorLength - Size of flat buffer containing the security
        descriptor.
    RequiredInformation - Which SD components must be present to be valid.
        OWNER_SECURITY_INFORMATION etc as a bit mask.
        OWNER_SECURITY_INFORMATION - There must be a valid owner SID
        GROUP_SECURITY_INFORMATION - There must be a valid group SID
        DACL_SECURITY_INFORMATION - Ignored
        SACL_SECURITY_INFORMATION - Ignored

Return Value:

    BOOLEAN - TRUE if the structure of SecurityDescriptor is valid.


--*/

{
    PISECURITY_DESCRIPTOR_RELATIVE SecurityDescriptor;
    PISID OwnerSid;
    PISID GroupSid;
    PACE_HEADER Ace;
    PACL Dacl;
    PACL Sacl;
    ULONG MaxOwnerSidLength;
    ULONG MaxGroupSidLength;
    ULONG MaxDaclLength;
    ULONG MaxSaclLength;

    if (SecurityDescriptorLength < sizeof(SECURITY_DESCRIPTOR_RELATIVE)) {
        return FALSE;
    }

    //
    // Check the revision information.
    //

    if (((PISECURITY_DESCRIPTOR) SecurityDescriptorInput)->Revision !=
             SECURITY_DESCRIPTOR_REVISION) {
        return FALSE;
    }

    //
    // Make sure the passed SecurityDescriptor is in self-relative form
    //

    if (!(((PISECURITY_DESCRIPTOR) SecurityDescriptorInput)->Control & SE_SELF_RELATIVE)) {
        return FALSE;
    }

    SecurityDescriptor = (PISECURITY_DESCRIPTOR_RELATIVE) SecurityDescriptorInput;

    //
    // Validate the owner if it's there and see if its allowed to be missing
    //
    if (SecurityDescriptor->Owner == 0) {
        if (RequiredInformation & OWNER_SECURITY_INFORMATION) {
            return FALSE;
        }
    } else {
        if (!RtlpValidateSDOffsetAndSize (SecurityDescriptor->Owner,
                                          SecurityDescriptorLength,
                                          sizeof (SID),
                                          &MaxOwnerSidLength)) {
            return FALSE;
        }
        //
        // It is safe to reference the owner's SubAuthorityCount, compute the
        // expected length of the SID
        //

        OwnerSid = (PSID)RtlOffsetToPointer (SecurityDescriptor,
                                             SecurityDescriptor->Owner);

        if (OwnerSid->Revision != SID_REVISION) {
            return FALSE;
        }

        if (OwnerSid->SubAuthorityCount > SID_MAX_SUB_AUTHORITIES) {
            return FALSE;
        }

        if (MaxOwnerSidLength < (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.
    //
    //
    // Validate the group if it's there and see if its allowed to be missing
    //
    if (SecurityDescriptor->Group == 0) {
        if (RequiredInformation & GROUP_SECURITY_INFORMATION) {
            return FALSE;
        }
    } else {
        if (!RtlpValidateSDOffsetAndSize (SecurityDescriptor->Group,
                                          SecurityDescriptorLength,
                                          sizeof (SID),
                                          &MaxGroupSidLength)) {
            return FALSE;
        }
        //
        // It is safe to reference the group's SubAuthorityCount, compute the
        // expected length of the SID
        //

        GroupSid = (PSID)RtlOffsetToPointer (SecurityDescriptor,
                                             SecurityDescriptor->Group);

        if (GroupSid->Revision != SID_REVISION) {
            return FALSE;
        }

        if (GroupSid->SubAuthorityCount > SID_MAX_SUB_AUTHORITIES) {
            return FALSE;
        }

        if (MaxGroupSidLength < (ULONG) SeLengthSid (GroupSid)) {
             return FALSE;
        }

    }

    //
    // Validate the DACL if it's there and check if its allowed to be missing.
    //

    if (!RtlpAreControlBitsSet (SecurityDescriptor, SE_DACL_PRESENT)) {
//
// Some code does this kind of thing:
//
// InitializeSecurityDescriptor (&sd, SECURITY_DESCRIPTOR_REVISION);
// RegSetKeySecurity(hKey, DACL_SECURITY_INFORMATION, &sd) )
//
// With the current system this works the same as passing in a NULL DACL but it looks
// almost by accident
//
//        if (RequiredInformation & DACL_SECURITY_INFORMATION) {
//            return FALSE;
//        }
    } else if (SecurityDescriptor->Dacl) {
        if (!RtlpValidateSDOffsetAndSize (SecurityDescriptor->Dacl,
                                          SecurityDescriptorLength,
                                          sizeof (ACL),
                                          &MaxDaclLength)) {
            return FALSE;
        }

        Dacl = (PACL) RtlOffsetToPointer (SecurityDescriptor,
                                          SecurityDescriptor->Dacl);

        //
        // Make sure the DACL length fits within the bounds of the security descriptor.
        //
        if (MaxDaclLength < Dacl->AclSize) {
            return FALSE;
        }

        //
        // Make sure the ACL is structurally valid.
        //
        if (!RtlValidAcl (Dacl)) {
            return FALSE;
        }
    }

    //
    // Validate the SACL if it's there and check if its allowed to be missing.
    //

    if (!RtlpAreControlBitsSet (SecurityDescriptor, SE_SACL_PRESENT)) {
//        if (RequiredInformation & SACL_SECURITY_INFORMATION) {
//            return FALSE;
//        }
    } else if (SecurityDescriptor->Sacl) {
        if (!RtlpValidateSDOffsetAndSize (SecurityDescriptor->Sacl,
                                          SecurityDescriptorLength,
                                          sizeof (ACL),
                                          &MaxSaclLength)) {
            return FALSE;
        }

        Sacl = (PACL) RtlOffsetToPointer (SecurityDescriptor,
                                          SecurityDescriptor->Sacl);

        //
        // Make sure the SACL length fits within the bounds of the security descriptor.
        //

        if (MaxSaclLength < Sacl->AclSize) {
            return FALSE;
        }

        //
        // Make sure the ACL is structurally valid.
        //

        if (!RtlValidAcl (Sacl)) {
            return FALSE;
        }
    }

    return TRUE;
}





BOOLEAN
RtlGetSecurityDescriptorRMControl(
    IN PSECURITY_DESCRIPTOR SecurityDescriptor,
    OUT PUCHAR RMControl
    )

/*++

Routine Description:

    This procedure returns the RM Control flags from a SecurityDescriptor if
    SE_RM_CONTROL_VALID flags is present in the control field.

Arguments:

    SecurityDescriptor - Pointer to the SECURITY_DESCRIPTOR structure
    RMControl          - Returns the flags in the SecurityDescriptor if
                         SE_RM_CONTROL_VALID is set in the control bits of the
                         SecurityDescriptor.


Return Value:

    BOOLEAN - TRUE if SE_RM_CONTROL_VALID is set in the Control bits of the
              SecurityDescriptor.

Note:
    Parameter validation has already been done in Advapi.


--*/

{
    PISECURITY_DESCRIPTOR ISecurityDescriptor = (PISECURITY_DESCRIPTOR) SecurityDescriptor;

    if (!(ISecurityDescriptor->Control & SE_RM_CONTROL_VALID))
    {
        *RMControl = 0;
        return FALSE;
    }

    *RMControl = ISecurityDescriptor->Sbz1;

    return TRUE;
}


BOOLEAN
RtlpGuidPresentInGuidList(
    IN GUID *InheritedObjectType,
    IN GUID **pNewObjectType,
    IN ULONG GuidCount
    )

/*++

Routine Description:

    This routine returns whether a given guid is present in a list of guids.

Arguments:

    InheritedObjectType - Guid from the ace that will be compared against 
        the object types for the object.
        
    pNewObjectType - List of types of object being inherited to.

    GuidCount - Number of object types in the list.


Return Value:

    Returns TRUE if the given guid is present in the list of guids.
    FALSE otherwise.

--*/

{
    ULONG i;

    for (i = 0; i < GuidCount; i++) {

        if (RtlEqualMemory(
                InheritedObjectType,
                pNewObjectType[i],
                sizeof(GUID) ) ) {

            return TRUE;
        }

    }

    return FALSE;
}


VOID
RtlSetSecurityDescriptorRMControl(
    IN OUT PSECURITY_DESCRIPTOR SecurityDescriptor,
    IN PUCHAR RMControl OPTIONAL
    )

/*++

Routine Description:

    This procedure sets the RM Control flag in the control field of
    SecurityDescriptor and sets Sbz1 to the the byte to which RMContol points.
    If RMControl is NULL then the bits are cleared.

Arguments:

    SecurityDescriptor - Pointer to the SECURITY_DESCRIPTOR structure
    RMControl          - Pointer to the flags to set. If NULL then the bits
                         are cleared.

Note:
    Parameter validation has already been done in Advapi.


--*/

{
    PISECURITY_DESCRIPTOR ISecurityDescriptor = (PISECURITY_DESCRIPTOR) SecurityDescriptor;

    if (ARGUMENT_PRESENT(RMControl)) {
        ISecurityDescriptor->Control |= SE_RM_CONTROL_VALID;
        ISecurityDescriptor->Sbz1 = *RMControl;
    } else {
        ISecurityDescriptor->Control &= ~SE_RM_CONTROL_VALID;
        ISecurityDescriptor->Sbz1 = 0;
    }
}

NTSTATUS
RtlMapSecurityErrorToNtStatus(
    IN SECURITY_STATUS Error
    )
/*++

Routine Description:

    This procedure maps a security HRESULT to the proper NTSTATUS code.
    
Arguments:

    Error - a security HRESULT
    
Return Value:  The NTSTATUS code corresponding to the HRESULT. If no
               status code can be mapped, the original error is returned.

Note:
    


--*/
{
    NTSTATUS Status;

    switch(Error) {
    case SEC_E_INSUFFICIENT_MEMORY : Status = STATUS_INSUFFICIENT_RESOURCES; break;
    case SEC_E_INVALID_HANDLE : Status = STATUS_INVALID_HANDLE; break;
    case SEC_E_UNSUPPORTED_FUNCTION : Status = STATUS_NOT_SUPPORTED; break;
    case SEC_E_TARGET_UNKNOWN : Status = STATUS_BAD_NETWORK_PATH; break;
    case SEC_E_INTERNAL_ERROR : Status = STATUS_INTERNAL_ERROR; break;
    case SEC_E_SECPKG_NOT_FOUND : Status = STATUS_NO_SUCH_PACKAGE; break;
    case SEC_E_NOT_OWNER : Status = STATUS_PRIVILEGE_NOT_HELD; break;
    case SEC_E_CANNOT_INSTALL : Status = STATUS_NO_SUCH_PACKAGE; break;
    case SEC_E_INVALID_TOKEN : Status = STATUS_INVALID_PARAMETER; break;
    case SEC_E_CANNOT_PACK : Status = STATUS_INVALID_PARAMETER; break;
    case SEC_E_QOP_NOT_SUPPORTED : Status = STATUS_NOT_SUPPORTED; break;
    case SEC_E_NO_IMPERSONATION : Status = STATUS_CANNOT_IMPERSONATE; break;
    case SEC_E_LOGON_DENIED : Status = STATUS_LOGON_FAILURE; break;
    case SEC_E_UNKNOWN_CREDENTIALS : Status = STATUS_NO_SUCH_LOGON_SESSION; break;
    case SEC_E_NO_CREDENTIALS : Status = STATUS_NO_SUCH_LOGON_SESSION; break;
    case SEC_E_MESSAGE_ALTERED : Status = STATUS_ACCESS_DENIED; break;
    case SEC_E_OUT_OF_SEQUENCE : Status = STATUS_ACCESS_DENIED; break;
    case SEC_E_NO_AUTHENTICATING_AUTHORITY : Status = STATUS_NO_LOGON_SERVERS; break;
    case SEC_E_BAD_PKGID : Status = STATUS_NO_SUCH_PACKAGE; break;
    case SEC_E_TIME_SKEW : Status = STATUS_TIME_DIFFERENCE_AT_DC; break;

    default: Status = (NTSTATUS) Error;
    }

    return(Status);
}

#if defined(ALLOC_DATA_PRAGMA)
#pragma const_seg()
#endif

