/*
 * COPYRIGHT:       See COPYING in the top level directory
 * PROJECT:         Security Account Manager (SAM) Server
 * FILE:            reactos/dll/win32/samsrv/security.c
 * PURPOSE:         Security descriptor functions
 *
 * PROGRAMMERS:     Eric Kohl
 */

#include "samsrv.h"

/* GLOBALS *****************************************************************/

static SID_IDENTIFIER_AUTHORITY WorldAuthority = {SECURITY_WORLD_SID_AUTHORITY};
static SID_IDENTIFIER_AUTHORITY NtAuthority = {SECURITY_NT_AUTHORITY};


/* FUNCTIONS ***************************************************************/

NTSTATUS
SampCreateServerSD(OUT PSECURITY_DESCRIPTOR *ServerSd,
                   OUT PULONG Size)
{
    PSECURITY_DESCRIPTOR AbsSD = NULL;
    PSECURITY_DESCRIPTOR RelSD = NULL;
    PSID EveryoneSid = NULL;
    PSID AnonymousSid = NULL;
    PSID AdministratorsSid = NULL;
    PACL Dacl = NULL;
    PACL Sacl = NULL;
    ULONG DaclSize;
    ULONG SaclSize;
    ULONG RelSDSize = 0;
    NTSTATUS Status = STATUS_SUCCESS;


    /* Create the Everyone SID */
    Status = RtlAllocateAndInitializeSid(&WorldAuthority,
                                         1,
                                         SECURITY_WORLD_RID,
                                         0,
                                         0,
                                         0,
                                         0,
                                         0,
                                         0,
                                         0,
                                         &EveryoneSid);
    ASSERT(NT_SUCCESS(Status));
    if (!NT_SUCCESS(Status))
        goto done;

    /* Create the Anonymous SID */
    Status = RtlAllocateAndInitializeSid(&NtAuthority,
                                         1,
                                         SECURITY_ANONYMOUS_LOGON_RID,
                                         0,
                                         0,
                                         0,
                                         0,
                                         0,
                                         0,
                                         0,
                                         &AnonymousSid);
    ASSERT(NT_SUCCESS(Status));
    if (!NT_SUCCESS(Status))
        goto done;

    /* Create the Administrators SID */
    Status = RtlAllocateAndInitializeSid(&NtAuthority,
                                         2,
                                         SECURITY_BUILTIN_DOMAIN_RID,
                                         DOMAIN_ALIAS_RID_ADMINS,
                                         0,
                                         0,
                                         0,
                                         0,
                                         0,
                                         0,
                                         &AdministratorsSid);
    ASSERT(NT_SUCCESS(Status));
    if (!NT_SUCCESS(Status))
        goto done;


    /* Allocate a buffer for the absolute SD */
    AbsSD = RtlAllocateHeap(RtlGetProcessHeap(),
                            HEAP_ZERO_MEMORY,
                            sizeof(SECURITY_DESCRIPTOR));
    if (AbsSD == NULL)
    {
        Status = STATUS_INSUFFICIENT_RESOURCES;
    ASSERT(Status == STATUS_SUCCESS);
        goto done;
    }

    /* Create the absolute SD */
    Status = RtlCreateSecurityDescriptor(AbsSD,
                                         SECURITY_DESCRIPTOR_REVISION);
    ASSERT(NT_SUCCESS(Status));
    if (!NT_SUCCESS(Status))
        goto done;

    /* allocate and create the DACL */
    DaclSize = sizeof(ACL) + 
               2 * sizeof(ACE) +
               RtlLengthSid(EveryoneSid) +
               RtlLengthSid(AdministratorsSid);

    Dacl = RtlAllocateHeap(RtlGetProcessHeap(),
                           HEAP_ZERO_MEMORY,
                           DaclSize);
    if (Dacl == NULL)
    {
        Status = STATUS_INSUFFICIENT_RESOURCES;
    ASSERT(Status == STATUS_SUCCESS);
        goto done;
    }

    Status = RtlCreateAcl(Dacl,
                          DaclSize,
                          ACL_REVISION);
    ASSERT(NT_SUCCESS(Status));
    if (!NT_SUCCESS(Status))
        goto done;

    Status = RtlAddAccessAllowedAce(Dacl,
                                    ACL_REVISION,
                                    SAM_SERVER_READ | SAM_SERVER_EXECUTE,
                                    EveryoneSid);
    ASSERT(NT_SUCCESS(Status));
    if (!NT_SUCCESS(Status))
        goto done;

    Status = RtlAddAccessAllowedAce(Dacl,
                                    ACL_REVISION,
                                    SAM_SERVER_ALL_ACCESS,
                                    AdministratorsSid);
    ASSERT(Status == STATUS_SUCCESS);
    if (!NT_SUCCESS(Status))
        goto done;

    /* Set the DACL */
    Status = RtlSetDaclSecurityDescriptor(AbsSD,
                                          TRUE,
                                          Dacl,
                                          FALSE);
    ASSERT(Status == STATUS_SUCCESS);
    if (!NT_SUCCESS(Status))
        goto done;

    /* allocate and create the SACL */
    SaclSize = sizeof(ACL) + 
               2 * sizeof(ACE) +
               RtlLengthSid(EveryoneSid) +
               RtlLengthSid(AnonymousSid);

    Sacl = RtlAllocateHeap(RtlGetProcessHeap(),
                           HEAP_ZERO_MEMORY,
                           DaclSize);
    if (Sacl == NULL)
    {
        Status = STATUS_INSUFFICIENT_RESOURCES;
    ASSERT(Status == STATUS_SUCCESS);
        goto done;
    }

    Status = RtlCreateAcl(Sacl,
                          SaclSize,
                          ACL_REVISION);
    ASSERT(Status == STATUS_SUCCESS);
    if (!NT_SUCCESS(Status))
        goto done;

    Status = RtlAddAuditAccessAce(Sacl,
                                  ACL_REVISION,
                                  ACCESS_SYSTEM_SECURITY | WRITE_DAC | DELETE |
                                  SAM_SERVER_CREATE_DOMAIN | SAM_SERVER_INITIALIZE |
                                  SAM_SERVER_SHUTDOWN,
                                  EveryoneSid,
                                  TRUE,
                                  TRUE);
    ASSERT(Status == STATUS_SUCCESS);
    if (!NT_SUCCESS(Status))
        goto done;

    Status = RtlAddAuditAccessAce(Sacl,
                                  ACL_REVISION,
                                  STANDARD_RIGHTS_ALL | SPECIFIC_RIGHTS_ALL,
                                  AnonymousSid,
                                  TRUE,
                                  TRUE);
    ASSERT(Status == STATUS_SUCCESS);
    if (!NT_SUCCESS(Status))
        goto done;

    /* Set the SACL */
    Status = RtlSetSaclSecurityDescriptor(AbsSD,
                                          TRUE,
                                          Sacl,
                                          FALSE);
    ASSERT(Status == STATUS_SUCCESS);
    if (!NT_SUCCESS(Status))
        goto done;

    /* Set the owner SID */
    Status = RtlSetOwnerSecurityDescriptor(AbsSD,
                                           AdministratorsSid,
                                           FALSE);
    ASSERT(Status == STATUS_SUCCESS);
    if (!NT_SUCCESS(Status))
        goto done;

    /* Set the group SID */
    Status = RtlSetGroupSecurityDescriptor(AbsSD,
                                           AdministratorsSid,
                                           FALSE);
    ASSERT(Status == STATUS_SUCCESS);
    if (!NT_SUCCESS(Status))
        goto done;

    /* Get the reqired buffer size for the self-relative SD */
    Status = RtlAbsoluteToSelfRelativeSD(AbsSD,
                                         NULL,
                                         &RelSDSize);
    if (Status != STATUS_BUFFER_TOO_SMALL)
        goto done;

    /* Allocate a buffer for the self-relative SD */
    RelSD = RtlAllocateHeap(RtlGetProcessHeap(),
                            HEAP_ZERO_MEMORY,
                            RelSDSize);
    if (RelSD == NULL)
    {
        Status = STATUS_INSUFFICIENT_RESOURCES;
    ASSERT(Status == STATUS_SUCCESS);
        goto done;
    }

    /* Convert the absolute SD to self-relative format */
    Status = RtlAbsoluteToSelfRelativeSD(AbsSD,
                                         RelSD,
                                         &RelSDSize);
    if (Status == STATUS_BUFFER_TOO_SMALL)
    {
    ASSERT(Status == STATUS_SUCCESS);
        goto done;
    }

    *ServerSd = RelSD;
    *Size = RelSDSize;

done:
    if (!NT_SUCCESS(Status))
    {
        if (RelSD != NULL)
            RtlFreeHeap(RtlGetProcessHeap(), 0, RelSD);
    }

    if (EveryoneSid != NULL)
        RtlFreeSid(EveryoneSid);

    if (AnonymousSid != NULL)
        RtlFreeSid(AnonymousSid);

    if (AdministratorsSid != NULL)
        RtlFreeSid(AdministratorsSid);

    if (Dacl != NULL)
        RtlFreeHeap(RtlGetProcessHeap(), 0, Dacl);

    if (Sacl != NULL)
        RtlFreeHeap(RtlGetProcessHeap(), 0, Sacl);

    if (AbsSD != NULL)
        RtlFreeHeap(RtlGetProcessHeap(), 0, AbsSD);

    return Status;
}


NTSTATUS
SampCreateBuiltinDomainSD(OUT PSECURITY_DESCRIPTOR *ServerSd,
                          OUT PULONG Size)
{
    PSECURITY_DESCRIPTOR AbsSD = NULL;
    PSECURITY_DESCRIPTOR RelSD = NULL;
    PSID EveryoneSid = NULL;
    PSID AnonymousSid = NULL;
    PSID AdministratorsSid = NULL;
    PACL Dacl = NULL;
    PACL Sacl = NULL;
    ULONG DaclSize;
    ULONG SaclSize;
    ULONG RelSDSize = 0;
    NTSTATUS Status = STATUS_SUCCESS;


    /* Create the Everyone SID */
    Status = RtlAllocateAndInitializeSid(&WorldAuthority,
                                         1,
                                         SECURITY_WORLD_RID,
                                         0,
                                         0,
                                         0,
                                         0,
                                         0,
                                         0,
                                         0,
                                         &EveryoneSid);
    ASSERT(NT_SUCCESS(Status));
    if (!NT_SUCCESS(Status))
        goto done;

    /* Create the Anonymous SID */
    Status = RtlAllocateAndInitializeSid(&NtAuthority,
                                         1,
                                         SECURITY_ANONYMOUS_LOGON_RID,
                                         0,
                                         0,
                                         0,
                                         0,
                                         0,
                                         0,
                                         0,
                                         &AnonymousSid);
    ASSERT(NT_SUCCESS(Status));
    if (!NT_SUCCESS(Status))
        goto done;

    /* Create the Administrators SID */
    Status = RtlAllocateAndInitializeSid(&NtAuthority,
                                         2,
                                         SECURITY_BUILTIN_DOMAIN_RID,
                                         DOMAIN_ALIAS_RID_ADMINS,
                                         0,
                                         0,
                                         0,
                                         0,
                                         0,
                                         0,
                                         &AdministratorsSid);
    ASSERT(NT_SUCCESS(Status));
    if (!NT_SUCCESS(Status))
        goto done;


    /* Allocate a buffer for the absolute SD */
    AbsSD = RtlAllocateHeap(RtlGetProcessHeap(),
                            HEAP_ZERO_MEMORY,
                            sizeof(SECURITY_DESCRIPTOR));
    if (AbsSD == NULL)
    {
        Status = STATUS_INSUFFICIENT_RESOURCES;
    ASSERT(Status == STATUS_SUCCESS);
        goto done;
    }

    /* Create the absolute SD */
    Status = RtlCreateSecurityDescriptor(AbsSD,
                                         SECURITY_DESCRIPTOR_REVISION);
    ASSERT(NT_SUCCESS(Status));
    if (!NT_SUCCESS(Status))
        goto done;

    /* allocate and create the DACL */
    DaclSize = sizeof(ACL) + 
               2 * sizeof(ACE) +
               RtlLengthSid(EveryoneSid) +
               RtlLengthSid(AdministratorsSid);

    Dacl = RtlAllocateHeap(RtlGetProcessHeap(),
                           HEAP_ZERO_MEMORY,
                           DaclSize);
    if (Dacl == NULL)
    {
        Status = STATUS_INSUFFICIENT_RESOURCES;
    ASSERT(Status == STATUS_SUCCESS);
        goto done;
    }

    Status = RtlCreateAcl(Dacl,
                          DaclSize,
                          ACL_REVISION);
    ASSERT(NT_SUCCESS(Status));
    if (!NT_SUCCESS(Status))
        goto done;

    Status = RtlAddAccessAllowedAce(Dacl,
                                    ACL_REVISION,
                                    DOMAIN_READ | DOMAIN_EXECUTE,
                                    EveryoneSid);
    ASSERT(NT_SUCCESS(Status));
    if (!NT_SUCCESS(Status))
        goto done;

    Status = RtlAddAccessAllowedAce(Dacl,
                                    ACL_REVISION,
                                    SAM_SERVER_ALL_ACCESS,
                                    AdministratorsSid);
    ASSERT(Status == STATUS_SUCCESS);
    if (!NT_SUCCESS(Status))
        goto done;

    /* Set the DACL */
    Status = RtlSetDaclSecurityDescriptor(AbsSD,
                                          TRUE,
                                          Dacl,
                                          FALSE);
    ASSERT(Status == STATUS_SUCCESS);
    if (!NT_SUCCESS(Status))
        goto done;

    /* allocate and create the SACL */
    SaclSize = sizeof(ACL) + 
               2 * sizeof(ACE) +
               RtlLengthSid(EveryoneSid) +
               RtlLengthSid(AnonymousSid);

    Sacl = RtlAllocateHeap(RtlGetProcessHeap(),
                           HEAP_ZERO_MEMORY,
                           DaclSize);
    if (Sacl == NULL)
    {
        Status = STATUS_INSUFFICIENT_RESOURCES;
    ASSERT(Status == STATUS_SUCCESS);
        goto done;
    }

    Status = RtlCreateAcl(Sacl,
                          SaclSize,
                          ACL_REVISION);
    ASSERT(Status == STATUS_SUCCESS);
    if (!NT_SUCCESS(Status))
        goto done;

    Status = RtlAddAuditAccessAce(Sacl,
                                  ACL_REVISION,
                                  ACCESS_SYSTEM_SECURITY | WRITE_DAC | DELETE |
                                  SAM_SERVER_CREATE_DOMAIN | SAM_SERVER_INITIALIZE |
                                  SAM_SERVER_SHUTDOWN,
                                  EveryoneSid,
                                  TRUE,
                                  TRUE);
    ASSERT(Status == STATUS_SUCCESS);
    if (!NT_SUCCESS(Status))
        goto done;

    Status = RtlAddAuditAccessAce(Sacl,
                                  ACL_REVISION,
                                  STANDARD_RIGHTS_ALL | SPECIFIC_RIGHTS_ALL,
                                  AnonymousSid,
                                  TRUE,
                                  TRUE);
    ASSERT(Status == STATUS_SUCCESS);
    if (!NT_SUCCESS(Status))
        goto done;

    /* Set the SACL */
    Status = RtlSetSaclSecurityDescriptor(AbsSD,
                                          TRUE,
                                          Sacl,
                                          FALSE);
    ASSERT(Status == STATUS_SUCCESS);
    if (!NT_SUCCESS(Status))
        goto done;

    /* Set the owner SID */
    Status = RtlSetOwnerSecurityDescriptor(AbsSD,
                                           AdministratorsSid,
                                           FALSE);
    ASSERT(Status == STATUS_SUCCESS);
    if (!NT_SUCCESS(Status))
        goto done;

    /* Set the group SID */
    Status = RtlSetGroupSecurityDescriptor(AbsSD,
                                           AdministratorsSid,
                                           FALSE);
    ASSERT(Status == STATUS_SUCCESS);
    if (!NT_SUCCESS(Status))
        goto done;

    /* Get the reqired buffer size for the self-relative SD */
    Status = RtlAbsoluteToSelfRelativeSD(AbsSD,
                                         NULL,
                                         &RelSDSize);
    if (Status != STATUS_BUFFER_TOO_SMALL)
        goto done;

    /* Allocate a buffer for the self-relative SD */
    RelSD = RtlAllocateHeap(RtlGetProcessHeap(),
                            HEAP_ZERO_MEMORY,
                            RelSDSize);
    if (RelSD == NULL)
    {
        Status = STATUS_INSUFFICIENT_RESOURCES;
    ASSERT(Status == STATUS_SUCCESS);
        goto done;
    }

    /* Convert the absolute SD to self-relative format */
    Status = RtlAbsoluteToSelfRelativeSD(AbsSD,
                                         RelSD,
                                         &RelSDSize);
    if (Status == STATUS_BUFFER_TOO_SMALL)
    {
    ASSERT(Status == STATUS_SUCCESS);
        goto done;
    }

    *ServerSd = RelSD;
    *Size = RelSDSize;

done:
    if (!NT_SUCCESS(Status))
    {
        if (RelSD != NULL)
            RtlFreeHeap(RtlGetProcessHeap(), 0, RelSD);
    }

    if (EveryoneSid != NULL)
        RtlFreeSid(EveryoneSid);

    if (AnonymousSid != NULL)
        RtlFreeSid(AnonymousSid);

    if (AdministratorsSid != NULL)
        RtlFreeSid(AdministratorsSid);

    if (Dacl != NULL)
        RtlFreeHeap(RtlGetProcessHeap(), 0, Dacl);

    if (Sacl != NULL)
        RtlFreeHeap(RtlGetProcessHeap(), 0, Sacl);

    if (AbsSD != NULL)
        RtlFreeHeap(RtlGetProcessHeap(), 0, AbsSD);

    return Status;
}


NTSTATUS
SampCreateAccountDomainSD(OUT PSECURITY_DESCRIPTOR *ServerSd,
                          OUT PULONG Size)
{
    PSECURITY_DESCRIPTOR AbsSD = NULL;
    PSECURITY_DESCRIPTOR RelSD = NULL;
    PSID EveryoneSid = NULL;
    PSID AnonymousSid = NULL;
    PSID AdministratorsSid = NULL;
    PSID UsersSid = NULL;
    PSID GuestsSid = NULL;
    PACL Dacl = NULL;
    PACL Sacl = NULL;
    ULONG DaclSize;
    ULONG SaclSize;
    ULONG RelSDSize = 0;
    NTSTATUS Status = STATUS_SUCCESS;


    /* Create the Everyone SID */
    Status = RtlAllocateAndInitializeSid(&WorldAuthority,
                                         1,
                                         SECURITY_WORLD_RID,
                                         0,
                                         0,
                                         0,
                                         0,
                                         0,
                                         0,
                                         0,
                                         &EveryoneSid);
    ASSERT(NT_SUCCESS(Status));
    if (!NT_SUCCESS(Status))
        goto done;

    /* Create the Anonymous SID */
    Status = RtlAllocateAndInitializeSid(&NtAuthority,
                                         1,
                                         SECURITY_ANONYMOUS_LOGON_RID,
                                         0,
                                         0,
                                         0,
                                         0,
                                         0,
                                         0,
                                         0,
                                         &AnonymousSid);
    ASSERT(NT_SUCCESS(Status));
    if (!NT_SUCCESS(Status))
        goto done;

    /* Create the Administrators SID */
    Status = RtlAllocateAndInitializeSid(&NtAuthority,
                                         2,
                                         SECURITY_BUILTIN_DOMAIN_RID,
                                         DOMAIN_ALIAS_RID_ADMINS,
                                         0,
                                         0,
                                         0,
                                         0,
                                         0,
                                         0,
                                         &AdministratorsSid);
    ASSERT(NT_SUCCESS(Status));
    if (!NT_SUCCESS(Status))
        goto done;

    /* Create the Users SID */
    Status = RtlAllocateAndInitializeSid(&NtAuthority,
                                         2,
                                         SECURITY_BUILTIN_DOMAIN_RID,
                                         DOMAIN_ALIAS_RID_USERS,
                                         0,
                                         0,
                                         0,
                                         0,
                                         0,
                                         0,
                                         &UsersSid);
    ASSERT(NT_SUCCESS(Status));
    if (!NT_SUCCESS(Status))
        goto done;

    /* Create the Guests SID */
    Status = RtlAllocateAndInitializeSid(&NtAuthority,
                                         2,
                                         SECURITY_BUILTIN_DOMAIN_RID,
                                         DOMAIN_ALIAS_RID_GUESTS,
                                         0,
                                         0,
                                         0,
                                         0,
                                         0,
                                         0,
                                         &GuestsSid);
    ASSERT(NT_SUCCESS(Status));
    if (!NT_SUCCESS(Status))
        goto done;


    /* Allocate a buffer for the absolute SD */
    AbsSD = RtlAllocateHeap(RtlGetProcessHeap(),
                            HEAP_ZERO_MEMORY,
                            sizeof(SECURITY_DESCRIPTOR));
    if (AbsSD == NULL)
    {
        Status = STATUS_INSUFFICIENT_RESOURCES;
    ASSERT(Status == STATUS_SUCCESS);
        goto done;
    }

    /* Create the absolute SD */
    Status = RtlCreateSecurityDescriptor(AbsSD,
                                         SECURITY_DESCRIPTOR_REVISION);
    ASSERT(NT_SUCCESS(Status));
    if (!NT_SUCCESS(Status))
        goto done;

    /* allocate and create the DACL */
    DaclSize = sizeof(ACL) + 
               4 * sizeof(ACE) +
               RtlLengthSid(EveryoneSid) +
               RtlLengthSid(AdministratorsSid) +
               RtlLengthSid(UsersSid) +
               RtlLengthSid(GuestsSid);

    Dacl = RtlAllocateHeap(RtlGetProcessHeap(),
                           HEAP_ZERO_MEMORY,
                           DaclSize);
    if (Dacl == NULL)
    {
        Status = STATUS_INSUFFICIENT_RESOURCES;
    ASSERT(Status == STATUS_SUCCESS);
        goto done;
    }

    Status = RtlCreateAcl(Dacl,
                          DaclSize,
                          ACL_REVISION);
    ASSERT(NT_SUCCESS(Status));
    if (!NT_SUCCESS(Status))
        goto done;

    Status = RtlAddAccessAllowedAce(Dacl,
                                    ACL_REVISION,
                                    DOMAIN_READ | DOMAIN_EXECUTE,
                                    EveryoneSid);
    ASSERT(NT_SUCCESS(Status));
    if (!NT_SUCCESS(Status))
        goto done;

    Status = RtlAddAccessAllowedAce(Dacl,
                                    ACL_REVISION,
                                    DOMAIN_READ | DOMAIN_EXECUTE,
                                    UsersSid);
    ASSERT(NT_SUCCESS(Status));
    if (!NT_SUCCESS(Status))
        goto done;

    Status = RtlAddAccessAllowedAce(Dacl,
                                    ACL_REVISION,
                                    DOMAIN_ALL_ACCESS & ~DOMAIN_CREATE_GROUP,
                                    AdministratorsSid);
    ASSERT(Status == STATUS_SUCCESS);
    if (!NT_SUCCESS(Status))
        goto done;

    Status = RtlAddAccessAllowedAce(Dacl,
                                    ACL_REVISION,
                                    DOMAIN_READ | DOMAIN_EXECUTE | DOMAIN_CREATE_USER | DOMAIN_CREATE_ALIAS,
                                    GuestsSid);
    ASSERT(Status == STATUS_SUCCESS);
    if (!NT_SUCCESS(Status))
        goto done;

    /* Set the DACL */
    Status = RtlSetDaclSecurityDescriptor(AbsSD,
                                          TRUE,
                                          Dacl,
                                          FALSE);
    ASSERT(Status == STATUS_SUCCESS);
    if (!NT_SUCCESS(Status))
        goto done;

    /* allocate and create the SACL */
    SaclSize = sizeof(ACL) + 
               2 * sizeof(ACE) +
               RtlLengthSid(EveryoneSid) +
               RtlLengthSid(AnonymousSid);

    Sacl = RtlAllocateHeap(RtlGetProcessHeap(),
                           HEAP_ZERO_MEMORY,
                           DaclSize);
    if (Sacl == NULL)
    {
        Status = STATUS_INSUFFICIENT_RESOURCES;
    ASSERT(Status == STATUS_SUCCESS);
        goto done;
    }

    Status = RtlCreateAcl(Sacl,
                          SaclSize,
                          ACL_REVISION);
    ASSERT(Status == STATUS_SUCCESS);
    if (!NT_SUCCESS(Status))
        goto done;

    Status = RtlAddAuditAccessAce(Sacl,
                                  ACL_REVISION,
                                  ACCESS_SYSTEM_SECURITY | WRITE_DAC | DELETE |
                                  SAM_SERVER_CREATE_DOMAIN | SAM_SERVER_INITIALIZE |
                                  SAM_SERVER_SHUTDOWN,
                                  EveryoneSid,
                                  TRUE,
                                  TRUE);
    ASSERT(Status == STATUS_SUCCESS);
    if (!NT_SUCCESS(Status))
        goto done;

    Status = RtlAddAuditAccessAce(Sacl,
                                  ACL_REVISION,
                                  STANDARD_RIGHTS_ALL | SPECIFIC_RIGHTS_ALL,
                                  AnonymousSid,
                                  TRUE,
                                  TRUE);
    ASSERT(Status == STATUS_SUCCESS);
    if (!NT_SUCCESS(Status))
        goto done;

    /* Set the SACL */
    Status = RtlSetSaclSecurityDescriptor(AbsSD,
                                          TRUE,
                                          Sacl,
                                          FALSE);
    ASSERT(Status == STATUS_SUCCESS);
    if (!NT_SUCCESS(Status))
        goto done;

    /* Set the owner SID */
    Status = RtlSetOwnerSecurityDescriptor(AbsSD,
                                           AdministratorsSid,
                                           FALSE);
    ASSERT(Status == STATUS_SUCCESS);
    if (!NT_SUCCESS(Status))
        goto done;

    /* Set the group SID */
    Status = RtlSetGroupSecurityDescriptor(AbsSD,
                                           AdministratorsSid,
                                           FALSE);
    ASSERT(Status == STATUS_SUCCESS);
    if (!NT_SUCCESS(Status))
        goto done;

    /* Get the reqired buffer size for the self-relative SD */
    Status = RtlAbsoluteToSelfRelativeSD(AbsSD,
                                         NULL,
                                         &RelSDSize);
    if (Status != STATUS_BUFFER_TOO_SMALL)
        goto done;

    /* Allocate a buffer for the self-relative SD */
    RelSD = RtlAllocateHeap(RtlGetProcessHeap(),
                            HEAP_ZERO_MEMORY,
                            RelSDSize);
    if (RelSD == NULL)
    {
        Status = STATUS_INSUFFICIENT_RESOURCES;
    ASSERT(Status == STATUS_SUCCESS);
        goto done;
    }

    /* Convert the absolute SD to self-relative format */
    Status = RtlAbsoluteToSelfRelativeSD(AbsSD,
                                         RelSD,
                                         &RelSDSize);
    if (Status == STATUS_BUFFER_TOO_SMALL)
    {
    ASSERT(Status == STATUS_SUCCESS);
        goto done;
    }

    *ServerSd = RelSD;
    *Size = RelSDSize;

done:
    if (!NT_SUCCESS(Status))
    {
        if (RelSD != NULL)
            RtlFreeHeap(RtlGetProcessHeap(), 0, RelSD);
    }

    if (EveryoneSid != NULL)
        RtlFreeSid(EveryoneSid);

    if (AnonymousSid != NULL)
        RtlFreeSid(AnonymousSid);

    if (AdministratorsSid != NULL)
        RtlFreeSid(AdministratorsSid);

    if (Dacl != NULL)
        RtlFreeHeap(RtlGetProcessHeap(), 0, Dacl);

    if (Sacl != NULL)
        RtlFreeHeap(RtlGetProcessHeap(), 0, Sacl);

    if (AbsSD != NULL)
        RtlFreeHeap(RtlGetProcessHeap(), 0, AbsSD);

    return Status;
}


NTSTATUS
SampCreateAliasSD(OUT PSECURITY_DESCRIPTOR *AliasSd,
                  OUT PULONG Size)
{
    PSECURITY_DESCRIPTOR AbsSD = NULL;
    PSECURITY_DESCRIPTOR RelSD = NULL;
    PSID EveryoneSid = NULL;
    PSID AnonymousSid = NULL;
    PSID AdministratorsSid = NULL;
    PSID AccountOperatorsSid = NULL;
    PACL Dacl = NULL;
    PACL Sacl = NULL;
    ULONG DaclSize;
    ULONG SaclSize;
    ULONG RelSDSize = 0;
    NTSTATUS Status = STATUS_SUCCESS;


    /* Create the Everyone SID */
    Status = RtlAllocateAndInitializeSid(&WorldAuthority,
                                         1,
                                         SECURITY_WORLD_RID,
                                         0,
                                         0,
                                         0,
                                         0,
                                         0,
                                         0,
                                         0,
                                         &EveryoneSid);
    ASSERT(NT_SUCCESS(Status));
    if (!NT_SUCCESS(Status))
        goto done;

    /* Create the Anonymous SID */
    Status = RtlAllocateAndInitializeSid(&NtAuthority,
                                         1,
                                         SECURITY_ANONYMOUS_LOGON_RID,
                                         0,
                                         0,
                                         0,
                                         0,
                                         0,
                                         0,
                                         0,
                                         &AnonymousSid);
    ASSERT(NT_SUCCESS(Status));
    if (!NT_SUCCESS(Status))
        goto done;

    /* Create the Administrators SID */
    Status = RtlAllocateAndInitializeSid(&NtAuthority,
                                         2,
                                         SECURITY_BUILTIN_DOMAIN_RID,
                                         DOMAIN_ALIAS_RID_ADMINS,
                                         0,
                                         0,
                                         0,
                                         0,
                                         0,
                                         0,
                                         &AdministratorsSid);
    ASSERT(NT_SUCCESS(Status));
    if (!NT_SUCCESS(Status))
        goto done;

    /* Create the Account Operators SID */
    Status = RtlAllocateAndInitializeSid(&NtAuthority,
                                         2,
                                         SECURITY_BUILTIN_DOMAIN_RID,
                                         DOMAIN_ALIAS_RID_ACCOUNT_OPS,
                                         0,
                                         0,
                                         0,
                                         0,
                                         0,
                                         0,
                                         &AccountOperatorsSid);
    ASSERT(NT_SUCCESS(Status));
    if (!NT_SUCCESS(Status))
        goto done;

    /* Allocate a buffer for the absolute SD */
    AbsSD = RtlAllocateHeap(RtlGetProcessHeap(),
                            HEAP_ZERO_MEMORY,
                            sizeof(SECURITY_DESCRIPTOR));
    if (AbsSD == NULL)
    {
        Status = STATUS_INSUFFICIENT_RESOURCES;
    ASSERT(Status == STATUS_SUCCESS);
        goto done;
    }

    /* Create the absolute SD */
    Status = RtlCreateSecurityDescriptor(AbsSD,
                                         SECURITY_DESCRIPTOR_REVISION);
    ASSERT(NT_SUCCESS(Status));
    if (!NT_SUCCESS(Status))
        goto done;

    /* allocate and create the DACL */
    DaclSize = sizeof(ACL) + 
               3 * sizeof(ACE) +
               RtlLengthSid(EveryoneSid) +
               RtlLengthSid(AdministratorsSid) +
               RtlLengthSid(AccountOperatorsSid);

    Dacl = RtlAllocateHeap(RtlGetProcessHeap(),
                           HEAP_ZERO_MEMORY,
                           DaclSize);
    if (Dacl == NULL)
    {
        Status = STATUS_INSUFFICIENT_RESOURCES;
    ASSERT(Status == STATUS_SUCCESS);
        goto done;
    }

    Status = RtlCreateAcl(Dacl,
                          DaclSize,
                          ACL_REVISION);
    ASSERT(NT_SUCCESS(Status));
    if (!NT_SUCCESS(Status))
        goto done;

    Status = RtlAddAccessAllowedAce(Dacl,
                                    ACL_REVISION,
                                    READ_CONTROL | ALIAS_READ_INFORMATION | ALIAS_LIST_MEMBERS,
                                    EveryoneSid);
    ASSERT(NT_SUCCESS(Status));
    if (!NT_SUCCESS(Status))
        goto done;

    Status = RtlAddAccessAllowedAce(Dacl,
                                    ACL_REVISION,
                                    ALIAS_ALL_ACCESS,
                                    AdministratorsSid);
    ASSERT(Status == STATUS_SUCCESS);
    if (!NT_SUCCESS(Status))
        goto done;

    Status = RtlAddAccessAllowedAce(Dacl,
                                    ACL_REVISION,
                                    ALIAS_ALL_ACCESS,
                                    AccountOperatorsSid);
    ASSERT(Status == STATUS_SUCCESS);
    if (!NT_SUCCESS(Status))
        goto done;

    /* Set the DACL */
    Status = RtlSetDaclSecurityDescriptor(AbsSD,
                                          TRUE,
                                          Dacl,
                                          FALSE);
    ASSERT(Status == STATUS_SUCCESS);
    if (!NT_SUCCESS(Status))
        goto done;

    /* allocate and create the SACL */
    SaclSize = sizeof(ACL) + 
               2 * sizeof(ACE) +
               RtlLengthSid(EveryoneSid) +
               RtlLengthSid(AnonymousSid);

    Sacl = RtlAllocateHeap(RtlGetProcessHeap(),
                           HEAP_ZERO_MEMORY,
                           DaclSize);
    if (Sacl == NULL)
    {
        Status = STATUS_INSUFFICIENT_RESOURCES;
    ASSERT(Status == STATUS_SUCCESS);
        goto done;
    }

    Status = RtlCreateAcl(Sacl,
                          SaclSize,
                          ACL_REVISION);
    ASSERT(Status == STATUS_SUCCESS);
    if (!NT_SUCCESS(Status))
        goto done;

    Status = RtlAddAuditAccessAce(Sacl,
                                  ACL_REVISION,
                                  ACCESS_SYSTEM_SECURITY | WRITE_DAC | DELETE |
                                  ALIAS_WRITE_ACCOUNT | ALIAS_REMOVE_MEMBER |
                                  ALIAS_ADD_MEMBER,
                                  EveryoneSid,
                                  TRUE,
                                  TRUE);
    ASSERT(Status == STATUS_SUCCESS);
    if (!NT_SUCCESS(Status))
        goto done;

    Status = RtlAddAuditAccessAce(Sacl,
                                  ACL_REVISION,
                                  STANDARD_RIGHTS_ALL | SPECIFIC_RIGHTS_ALL,
                                  AnonymousSid,
                                  TRUE,
                                  TRUE);
    ASSERT(Status == STATUS_SUCCESS);
    if (!NT_SUCCESS(Status))
        goto done;

    /* Set the SACL */
    Status = RtlSetSaclSecurityDescriptor(AbsSD,
                                          TRUE,
                                          Sacl,
                                          FALSE);
    ASSERT(Status == STATUS_SUCCESS);
    if (!NT_SUCCESS(Status))
        goto done;

    /* Set the owner SID */
    Status = RtlSetOwnerSecurityDescriptor(AbsSD,
                                           AdministratorsSid,
                                           FALSE);
    ASSERT(Status == STATUS_SUCCESS);
    if (!NT_SUCCESS(Status))
        goto done;

    /* Set the group SID */
    Status = RtlSetGroupSecurityDescriptor(AbsSD,
                                           AdministratorsSid,
                                           FALSE);
    ASSERT(Status == STATUS_SUCCESS);
    if (!NT_SUCCESS(Status))
        goto done;

    /* Get the reqired buffer size for the self-relative SD */
    Status = RtlAbsoluteToSelfRelativeSD(AbsSD,
                                         NULL,
                                         &RelSDSize);
    if (Status != STATUS_BUFFER_TOO_SMALL)
        goto done;

    /* Allocate a buffer for the self-relative SD */
    RelSD = RtlAllocateHeap(RtlGetProcessHeap(),
                            HEAP_ZERO_MEMORY,
                            RelSDSize);
    if (RelSD == NULL)
    {
        Status = STATUS_INSUFFICIENT_RESOURCES;
    ASSERT(Status == STATUS_SUCCESS);
        goto done;
    }

    /* Convert the absolute SD to self-relative format */
    Status = RtlAbsoluteToSelfRelativeSD(AbsSD,
                                         RelSD,
                                         &RelSDSize);
    if (Status == STATUS_BUFFER_TOO_SMALL)
    {
    ASSERT(Status == STATUS_SUCCESS);
        goto done;
    }

    *AliasSd = RelSD;
    *Size = RelSDSize;

done:
    if (!NT_SUCCESS(Status))
    {
        if (RelSD != NULL)
            RtlFreeHeap(RtlGetProcessHeap(), 0, RelSD);
    }

    if (EveryoneSid != NULL)
        RtlFreeSid(EveryoneSid);

    if (AnonymousSid != NULL)
        RtlFreeSid(AnonymousSid);

    if (AdministratorsSid != NULL)
        RtlFreeSid(AdministratorsSid);

    if (Dacl != NULL)
        RtlFreeHeap(RtlGetProcessHeap(), 0, Dacl);

    if (Sacl != NULL)
        RtlFreeHeap(RtlGetProcessHeap(), 0, Sacl);

    if (AbsSD != NULL)
        RtlFreeHeap(RtlGetProcessHeap(), 0, AbsSD);

    return Status;
}


NTSTATUS
SampCreateGroupSD(OUT PSECURITY_DESCRIPTOR *GroupSd,
                  OUT PULONG Size)
{
    PSECURITY_DESCRIPTOR AbsSD = NULL;
    PSECURITY_DESCRIPTOR RelSD = NULL;
    PSID EveryoneSid = NULL;
    PSID AnonymousSid = NULL;
    PSID AdministratorsSid = NULL;
    PSID AccountOperatorsSid = NULL;
    PACL Dacl = NULL;
    PACL Sacl = NULL;
    ULONG DaclSize;
    ULONG SaclSize;
    ULONG RelSDSize = 0;
    NTSTATUS Status = STATUS_SUCCESS;


    /* Create the Everyone SID */
    Status = RtlAllocateAndInitializeSid(&WorldAuthority,
                                         1,
                                         SECURITY_WORLD_RID,
                                         0,
                                         0,
                                         0,
                                         0,
                                         0,
                                         0,
                                         0,
                                         &EveryoneSid);
    ASSERT(NT_SUCCESS(Status));
    if (!NT_SUCCESS(Status))
        goto done;

    /* Create the Anonymous SID */
    Status = RtlAllocateAndInitializeSid(&NtAuthority,
                                         1,
                                         SECURITY_ANONYMOUS_LOGON_RID,
                                         0,
                                         0,
                                         0,
                                         0,
                                         0,
                                         0,
                                         0,
                                         &AnonymousSid);
    ASSERT(NT_SUCCESS(Status));
    if (!NT_SUCCESS(Status))
        goto done;

    /* Create the Administrators SID */
    Status = RtlAllocateAndInitializeSid(&NtAuthority,
                                         2,
                                         SECURITY_BUILTIN_DOMAIN_RID,
                                         DOMAIN_ALIAS_RID_ADMINS,
                                         0,
                                         0,
                                         0,
                                         0,
                                         0,
                                         0,
                                         &AdministratorsSid);
    ASSERT(NT_SUCCESS(Status));
    if (!NT_SUCCESS(Status))
        goto done;

    /* Create the Account Operators SID */
    Status = RtlAllocateAndInitializeSid(&NtAuthority,
                                         2,
                                         SECURITY_BUILTIN_DOMAIN_RID,
                                         DOMAIN_ALIAS_RID_ACCOUNT_OPS,
                                         0,
                                         0,
                                         0,
                                         0,
                                         0,
                                         0,
                                         &AccountOperatorsSid);
    ASSERT(NT_SUCCESS(Status));
    if (!NT_SUCCESS(Status))
        goto done;

    /* Allocate a buffer for the absolute SD */
    AbsSD = RtlAllocateHeap(RtlGetProcessHeap(),
                            HEAP_ZERO_MEMORY,
                            sizeof(SECURITY_DESCRIPTOR));
    if (AbsSD == NULL)
    {
        Status = STATUS_INSUFFICIENT_RESOURCES;
    ASSERT(Status == STATUS_SUCCESS);
        goto done;
    }

    /* Create the absolute SD */
    Status = RtlCreateSecurityDescriptor(AbsSD,
                                         SECURITY_DESCRIPTOR_REVISION);
    ASSERT(NT_SUCCESS(Status));
    if (!NT_SUCCESS(Status))
        goto done;

    /* allocate and create the DACL */
    DaclSize = sizeof(ACL) + 
               3 * sizeof(ACE) +
               RtlLengthSid(EveryoneSid) +
               RtlLengthSid(AdministratorsSid) +
               RtlLengthSid(AccountOperatorsSid);

    Dacl = RtlAllocateHeap(RtlGetProcessHeap(),
                           HEAP_ZERO_MEMORY,
                           DaclSize);
    if (Dacl == NULL)
    {
        Status = STATUS_INSUFFICIENT_RESOURCES;
    ASSERT(Status == STATUS_SUCCESS);
        goto done;
    }

    Status = RtlCreateAcl(Dacl,
                          DaclSize,
                          ACL_REVISION);
    ASSERT(NT_SUCCESS(Status));
    if (!NT_SUCCESS(Status))
        goto done;

    Status = RtlAddAccessAllowedAce(Dacl,
                                    ACL_REVISION,
                                    READ_CONTROL | GROUP_LIST_MEMBERS | GROUP_READ_INFORMATION,
                                    EveryoneSid);
    ASSERT(NT_SUCCESS(Status));
    if (!NT_SUCCESS(Status))
        goto done;

    Status = RtlAddAccessAllowedAce(Dacl,
                                    ACL_REVISION,
                                    GROUP_ALL_ACCESS,
                                    AdministratorsSid);
    ASSERT(Status == STATUS_SUCCESS);
    if (!NT_SUCCESS(Status))
        goto done;

    Status = RtlAddAccessAllowedAce(Dacl,
                                    ACL_REVISION,
                                    GROUP_ALL_ACCESS,
                                    AccountOperatorsSid);
    ASSERT(Status == STATUS_SUCCESS);
    if (!NT_SUCCESS(Status))
        goto done;

    /* Set the DACL */
    Status = RtlSetDaclSecurityDescriptor(AbsSD,
                                          TRUE,
                                          Dacl,
                                          FALSE);
    ASSERT(Status == STATUS_SUCCESS);
    if (!NT_SUCCESS(Status))
        goto done;

    /* allocate and create the SACL */
    SaclSize = sizeof(ACL) + 
               2 * sizeof(ACE) +
               RtlLengthSid(EveryoneSid) +
               RtlLengthSid(AnonymousSid);

    Sacl = RtlAllocateHeap(RtlGetProcessHeap(),
                           HEAP_ZERO_MEMORY,
                           DaclSize);
    if (Sacl == NULL)
    {
        Status = STATUS_INSUFFICIENT_RESOURCES;
    ASSERT(Status == STATUS_SUCCESS);
        goto done;
    }

    Status = RtlCreateAcl(Sacl,
                          SaclSize,
                          ACL_REVISION);
    ASSERT(Status == STATUS_SUCCESS);
    if (!NT_SUCCESS(Status))
        goto done;

    Status = RtlAddAuditAccessAce(Sacl,
                                  ACL_REVISION,
                                  ACCESS_SYSTEM_SECURITY | WRITE_DAC | DELETE |
                                  GROUP_REMOVE_MEMBER | GROUP_ADD_MEMBER |
                                  GROUP_WRITE_ACCOUNT,
                                  EveryoneSid,
                                  TRUE,
                                  TRUE);
    ASSERT(Status == STATUS_SUCCESS);
    if (!NT_SUCCESS(Status))
        goto done;

    Status = RtlAddAuditAccessAce(Sacl,
                                  ACL_REVISION,
                                  STANDARD_RIGHTS_ALL | SPECIFIC_RIGHTS_ALL,
                                  AnonymousSid,
                                  TRUE,
                                  TRUE);
    ASSERT(Status == STATUS_SUCCESS);
    if (!NT_SUCCESS(Status))
        goto done;

    /* Set the SACL */
    Status = RtlSetSaclSecurityDescriptor(AbsSD,
                                          TRUE,
                                          Sacl,
                                          FALSE);
    ASSERT(Status == STATUS_SUCCESS);
    if (!NT_SUCCESS(Status))
        goto done;

    /* Set the owner SID */
    Status = RtlSetOwnerSecurityDescriptor(AbsSD,
                                           AdministratorsSid,
                                           FALSE);
    ASSERT(Status == STATUS_SUCCESS);
    if (!NT_SUCCESS(Status))
        goto done;

    /* Set the group SID */
    Status = RtlSetGroupSecurityDescriptor(AbsSD,
                                           AdministratorsSid,
                                           FALSE);
    ASSERT(Status == STATUS_SUCCESS);
    if (!NT_SUCCESS(Status))
        goto done;

    /* Get the reqired buffer size for the self-relative SD */
    Status = RtlAbsoluteToSelfRelativeSD(AbsSD,
                                         NULL,
                                         &RelSDSize);
    if (Status != STATUS_BUFFER_TOO_SMALL)
        goto done;

    /* Allocate a buffer for the self-relative SD */
    RelSD = RtlAllocateHeap(RtlGetProcessHeap(),
                            HEAP_ZERO_MEMORY,
                            RelSDSize);
    if (RelSD == NULL)
    {
        Status = STATUS_INSUFFICIENT_RESOURCES;
    ASSERT(Status == STATUS_SUCCESS);
        goto done;
    }

    /* Convert the absolute SD to self-relative format */
    Status = RtlAbsoluteToSelfRelativeSD(AbsSD,
                                         RelSD,
                                         &RelSDSize);
    if (Status == STATUS_BUFFER_TOO_SMALL)
    {
    ASSERT(Status == STATUS_SUCCESS);
        goto done;
    }

    *GroupSd = RelSD;
    *Size = RelSDSize;

done:
    if (!NT_SUCCESS(Status))
    {
        if (RelSD != NULL)
            RtlFreeHeap(RtlGetProcessHeap(), 0, RelSD);
    }

    if (EveryoneSid != NULL)
        RtlFreeSid(EveryoneSid);

    if (AnonymousSid != NULL)
        RtlFreeSid(AnonymousSid);

    if (AdministratorsSid != NULL)
        RtlFreeSid(AdministratorsSid);

    if (Dacl != NULL)
        RtlFreeHeap(RtlGetProcessHeap(), 0, Dacl);

    if (Sacl != NULL)
        RtlFreeHeap(RtlGetProcessHeap(), 0, Sacl);

    if (AbsSD != NULL)
        RtlFreeHeap(RtlGetProcessHeap(), 0, AbsSD);

    return Status;
}


NTSTATUS
SampCreateUserSD(IN PSID UserSid,
                 OUT PSECURITY_DESCRIPTOR *UserSd,
                 OUT PULONG Size)
{
    PSECURITY_DESCRIPTOR AbsSD = NULL;
    PSECURITY_DESCRIPTOR RelSD = NULL;
    PSID EveryoneSid = NULL;
    PSID AnonymousSid = NULL;
    PSID AdministratorsSid = NULL;
    PACL Dacl = NULL;
    PACL Sacl = NULL;
    ULONG DaclSize;
    ULONG SaclSize;
    ULONG RelSDSize = 0;
    NTSTATUS Status = STATUS_SUCCESS;


    /* Create the Everyone SID */
    Status = RtlAllocateAndInitializeSid(&WorldAuthority,
                                         1,
                                         SECURITY_WORLD_RID,
                                         0,
                                         0,
                                         0,
                                         0,
                                         0,
                                         0,
                                         0,
                                         &EveryoneSid);
    ASSERT(NT_SUCCESS(Status));
    if (!NT_SUCCESS(Status))
        goto done;

    /* Create the Anonymous SID */
    Status = RtlAllocateAndInitializeSid(&NtAuthority,
                                         1,
                                         SECURITY_ANONYMOUS_LOGON_RID,
                                         0,
                                         0,
                                         0,
                                         0,
                                         0,
                                         0,
                                         0,
                                         &AnonymousSid);
    ASSERT(NT_SUCCESS(Status));
    if (!NT_SUCCESS(Status))
        goto done;

    /* Create the Administrators SID */
    Status = RtlAllocateAndInitializeSid(&NtAuthority,
                                         2,
                                         SECURITY_BUILTIN_DOMAIN_RID,
                                         DOMAIN_ALIAS_RID_ADMINS,
                                         0,
                                         0,
                                         0,
                                         0,
                                         0,
                                         0,
                                         &AdministratorsSid);
    ASSERT(NT_SUCCESS(Status));
    if (!NT_SUCCESS(Status))
        goto done;

    /* Allocate a buffer for the absolute SD */
    AbsSD = RtlAllocateHeap(RtlGetProcessHeap(),
                            HEAP_ZERO_MEMORY,
                            sizeof(SECURITY_DESCRIPTOR));
    if (AbsSD == NULL)
    {
        Status = STATUS_INSUFFICIENT_RESOURCES;
    ASSERT(Status == STATUS_SUCCESS);
        goto done;
    }

    /* Create the absolute SD */
    Status = RtlCreateSecurityDescriptor(AbsSD,
                                         SECURITY_DESCRIPTOR_REVISION);
    ASSERT(NT_SUCCESS(Status));
    if (!NT_SUCCESS(Status))
        goto done;

    /* allocate and create the DACL */
    DaclSize = sizeof(ACL) + 
               3 * sizeof(ACE) +
               RtlLengthSid(EveryoneSid) +
               RtlLengthSid(AdministratorsSid) +
               RtlLengthSid(UserSid);

    Dacl = RtlAllocateHeap(RtlGetProcessHeap(),
                           HEAP_ZERO_MEMORY,
                           DaclSize);
    if (Dacl == NULL)
    {
        Status = STATUS_INSUFFICIENT_RESOURCES;
    ASSERT(Status == STATUS_SUCCESS);
        goto done;
    }

    Status = RtlCreateAcl(Dacl,
                          DaclSize,
                          ACL_REVISION);
    ASSERT(NT_SUCCESS(Status));
    if (!NT_SUCCESS(Status))
        goto done;

    Status = RtlAddAccessAllowedAce(Dacl,
                                    ACL_REVISION,
                                    READ_CONTROL | USER_READ_GROUP_INFORMATION | USER_LIST_GROUPS |
                                    USER_CHANGE_PASSWORD | USER_READ_ACCOUNT | USER_READ_LOGON |
                                    USER_READ_PREFERENCES | USER_READ_GENERAL,
                                    EveryoneSid);
    ASSERT(NT_SUCCESS(Status));
    if (!NT_SUCCESS(Status))
        goto done;

    Status = RtlAddAccessAllowedAce(Dacl,
                                    ACL_REVISION,
                                    USER_ALL_ACCESS,
                                    AdministratorsSid);
    ASSERT(Status == STATUS_SUCCESS);
    if (!NT_SUCCESS(Status))
        goto done;

    Status = RtlAddAccessAllowedAce(Dacl,
                                    ACL_REVISION,
                                    READ_CONTROL | USER_CHANGE_PASSWORD | USER_WRITE_PREFERENCES,
                                    UserSid);
    ASSERT(Status == STATUS_SUCCESS);
    if (!NT_SUCCESS(Status))
        goto done;

    /* Set the DACL */
    Status = RtlSetDaclSecurityDescriptor(AbsSD,
                                          TRUE,
                                          Dacl,
                                          FALSE);
    ASSERT(Status == STATUS_SUCCESS);
    if (!NT_SUCCESS(Status))
        goto done;

    /* allocate and create the SACL */
    SaclSize = sizeof(ACL) + 
               2 * sizeof(ACE) +
               RtlLengthSid(EveryoneSid) +
               RtlLengthSid(AnonymousSid);

    Sacl = RtlAllocateHeap(RtlGetProcessHeap(),
                           HEAP_ZERO_MEMORY,
                           DaclSize);
    if (Sacl == NULL)
    {
        Status = STATUS_INSUFFICIENT_RESOURCES;
    ASSERT(Status == STATUS_SUCCESS);
        goto done;
    }

    Status = RtlCreateAcl(Sacl,
                          SaclSize,
                          ACL_REVISION);
    ASSERT(Status == STATUS_SUCCESS);
    if (!NT_SUCCESS(Status))
        goto done;

    Status = RtlAddAuditAccessAce(Sacl,
                                  ACL_REVISION,
                                  ACCESS_SYSTEM_SECURITY | WRITE_DAC | DELETE |
                                  USER_CHANGE_PASSWORD | USER_WRITE_PREFERENCES,
                                  EveryoneSid,
                                  TRUE,
                                  TRUE);
    ASSERT(Status == STATUS_SUCCESS);
    if (!NT_SUCCESS(Status))
        goto done;

    Status = RtlAddAuditAccessAce(Sacl,
                                  ACL_REVISION,
                                  STANDARD_RIGHTS_ALL | SPECIFIC_RIGHTS_ALL,
                                  AnonymousSid,
                                  TRUE,
                                  TRUE);
    ASSERT(Status == STATUS_SUCCESS);
    if (!NT_SUCCESS(Status))
        goto done;

    /* Set the SACL */
    Status = RtlSetSaclSecurityDescriptor(AbsSD,
                                          TRUE,
                                          Sacl,
                                          FALSE);
    ASSERT(Status == STATUS_SUCCESS);
    if (!NT_SUCCESS(Status))
        goto done;

    /* Set the owner SID */
    Status = RtlSetOwnerSecurityDescriptor(AbsSD,
                                           AdministratorsSid,
                                           FALSE);
    ASSERT(Status == STATUS_SUCCESS);
    if (!NT_SUCCESS(Status))
        goto done;

    /* Set the group SID */
    Status = RtlSetGroupSecurityDescriptor(AbsSD,
                                           AdministratorsSid,
                                           FALSE);
    ASSERT(Status == STATUS_SUCCESS);
    if (!NT_SUCCESS(Status))
        goto done;

    /* Get the reqired buffer size for the self-relative SD */
    Status = RtlAbsoluteToSelfRelativeSD(AbsSD,
                                         NULL,
                                         &RelSDSize);
    if (Status != STATUS_BUFFER_TOO_SMALL)
        goto done;

    /* Allocate a buffer for the self-relative SD */
    RelSD = RtlAllocateHeap(RtlGetProcessHeap(),
                            HEAP_ZERO_MEMORY,
                            RelSDSize);
    if (RelSD == NULL)
    {
        Status = STATUS_INSUFFICIENT_RESOURCES;
    ASSERT(Status == STATUS_SUCCESS);
        goto done;
    }

    /* Convert the absolute SD to self-relative format */
    Status = RtlAbsoluteToSelfRelativeSD(AbsSD,
                                         RelSD,
                                         &RelSDSize);
    if (Status == STATUS_BUFFER_TOO_SMALL)
    {
    ASSERT(Status == STATUS_SUCCESS);
        goto done;
    }

    *UserSd = RelSD;
    *Size = RelSDSize;

done:
    if (!NT_SUCCESS(Status))
    {
        if (RelSD != NULL)
            RtlFreeHeap(RtlGetProcessHeap(), 0, RelSD);
    }

    if (EveryoneSid != NULL)
        RtlFreeSid(EveryoneSid);

    if (AnonymousSid != NULL)
        RtlFreeSid(AnonymousSid);

    if (AdministratorsSid != NULL)
        RtlFreeSid(AdministratorsSid);

    if (Dacl != NULL)
        RtlFreeHeap(RtlGetProcessHeap(), 0, Dacl);

    if (Sacl != NULL)
        RtlFreeHeap(RtlGetProcessHeap(), 0, Sacl);

    if (AbsSD != NULL)
        RtlFreeHeap(RtlGetProcessHeap(), 0, AbsSD);

    return Status;
}

/* EOF */
