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

#include "samsrv.h"

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

static SID_IDENTIFIER_AUTHORITY NtSidAuthority = {SECURITY_NT_AUTHORITY};

static GENERIC_MAPPING ServerMapping =
{
    SAM_SERVER_READ,
    SAM_SERVER_WRITE,
    SAM_SERVER_EXECUTE,
    SAM_SERVER_ALL_ACCESS
};

static GENERIC_MAPPING DomainMapping =
{
    DOMAIN_READ,
    DOMAIN_WRITE,
    DOMAIN_EXECUTE,
    DOMAIN_ALL_ACCESS
};

static GENERIC_MAPPING AliasMapping =
{
    ALIAS_READ,
    ALIAS_WRITE,
    ALIAS_EXECUTE,
    ALIAS_ALL_ACCESS
};

static GENERIC_MAPPING GroupMapping =
{
    GROUP_READ,
    GROUP_WRITE,
    GROUP_EXECUTE,
    GROUP_ALL_ACCESS
};

static GENERIC_MAPPING UserMapping =
{
    USER_READ,
    USER_WRITE,
    USER_EXECUTE,
    USER_ALL_ACCESS
};

PGENERIC_MAPPING pServerMapping = &ServerMapping;


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

static
LARGE_INTEGER
SampAddRelativeTimeToTime(IN LARGE_INTEGER AbsoluteTime,
                          IN LARGE_INTEGER RelativeTime)
{
    LARGE_INTEGER NewTime;

    NewTime.QuadPart = AbsoluteTime.QuadPart - RelativeTime.QuadPart;

    if (NewTime.QuadPart < 0)
        NewTime.QuadPart = 0;

    return NewTime;
}


VOID
SampStartRpcServer(VOID)
{
    RPC_STATUS Status;

    TRACE("SampStartRpcServer() called\n");

    Status = RpcServerUseProtseqEpW(L"ncacn_np",
                                    RPC_C_PROTSEQ_MAX_REQS_DEFAULT,
                                    L"\\pipe\\samr",
                                    NULL);
    if (Status != RPC_S_OK)
    {
        WARN("RpcServerUseProtseqEpW() failed (Status %lx)\n", Status);
        return;
    }

    Status = RpcServerRegisterIf(samr_v1_0_s_ifspec,
                                 NULL,
                                 NULL);
    if (Status != RPC_S_OK)
    {
        WARN("RpcServerRegisterIf() failed (Status %lx)\n", Status);
        return;
    }

    Status = RpcServerListen(1, 20, TRUE);
    if (Status != RPC_S_OK)
    {
        WARN("RpcServerListen() failed (Status %lx)\n", Status);
        return;
    }

    TRACE("SampStartRpcServer() done\n");
}


void __RPC_FAR * __RPC_USER midl_user_allocate(SIZE_T len)
{
    return HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, len);
}


void __RPC_USER midl_user_free(void __RPC_FAR * ptr)
{
    HeapFree(GetProcessHeap(), 0, ptr);
}


void __RPC_USER SAMPR_HANDLE_rundown(SAMPR_HANDLE hHandle)
{
    FIXME("SAMPR_HANDLE_rundown(%p)\n", hHandle);
}


/* Function 0 */
NTSTATUS
NTAPI
SamrConnect(IN PSAMPR_SERVER_NAME ServerName,
            OUT SAMPR_HANDLE *ServerHandle,
            IN ACCESS_MASK DesiredAccess)
{
    SAMPR_REVISION_INFO InRevisionInfo, OutRevisionInfo;
    ULONG OutVersion;

    TRACE("SamrConnect(%p %p %lx)\n",
          ServerName, ServerHandle, DesiredAccess);

    InRevisionInfo.V1.Revision = 0;
    InRevisionInfo.V1.SupportedFeatures = 0;

    return SamrConnect5(ServerName,
                        DesiredAccess,
                        1,
                        &InRevisionInfo,
                        &OutVersion,
                        &OutRevisionInfo,
                        ServerHandle);
}


/* Function 1 */
NTSTATUS
NTAPI
SamrCloseHandle(IN OUT SAMPR_HANDLE *SamHandle)
{
    PSAM_DB_OBJECT DbObject;
    NTSTATUS Status = STATUS_SUCCESS;

    TRACE("SamrCloseHandle(%p)\n", SamHandle);

    RtlAcquireResourceShared(&SampResource,
                             TRUE);

    Status = SampValidateDbObject(*SamHandle,
                                  SamDbIgnoreObject,
                                  0,
                                  &DbObject);
    if (Status == STATUS_SUCCESS)
    {
        Status = SampCloseDbObject(DbObject);
        *SamHandle = NULL;
    }

    RtlReleaseResource(&SampResource);

    TRACE("SamrCloseHandle done (Status 0x%08lx)\n", Status);

    return Status;
}


/* Function 2 */
NTSTATUS
NTAPI
SamrSetSecurityObject(IN SAMPR_HANDLE ObjectHandle,
                      IN SECURITY_INFORMATION SecurityInformation,
                      IN PSAMPR_SR_SECURITY_DESCRIPTOR SecurityDescriptor)
{
    PSAM_DB_OBJECT DbObject = NULL;
    ACCESS_MASK DesiredAccess = 0;
    PSECURITY_DESCRIPTOR RelativeSd = NULL;
    ULONG RelativeSdSize = 0;
    HANDLE TokenHandle = NULL;
    PGENERIC_MAPPING Mapping;
    NTSTATUS Status;

    TRACE("SamrSetSecurityObject(%p %lx %p)\n",
          ObjectHandle, SecurityInformation, SecurityDescriptor);

    if ((SecurityDescriptor == NULL) ||
        (SecurityDescriptor->SecurityDescriptor == NULL) ||
        !RtlValidSecurityDescriptor((PSECURITY_DESCRIPTOR)SecurityDescriptor->SecurityDescriptor))
        return ERROR_INVALID_PARAMETER;

    if (SecurityInformation == 0 ||
        SecurityInformation & ~(OWNER_SECURITY_INFORMATION | GROUP_SECURITY_INFORMATION
        | DACL_SECURITY_INFORMATION | SACL_SECURITY_INFORMATION))
        return ERROR_INVALID_PARAMETER;

    if (SecurityInformation & SACL_SECURITY_INFORMATION)
        DesiredAccess |= ACCESS_SYSTEM_SECURITY;

    if (SecurityInformation & DACL_SECURITY_INFORMATION)
        DesiredAccess |= WRITE_DAC;

    if (SecurityInformation & (OWNER_SECURITY_INFORMATION | GROUP_SECURITY_INFORMATION))
        DesiredAccess |= WRITE_OWNER;

    if ((SecurityInformation & OWNER_SECURITY_INFORMATION) &&
        (((PISECURITY_DESCRIPTOR)SecurityDescriptor)->Owner == NULL))
        return ERROR_INVALID_PARAMETER;

    if ((SecurityInformation & GROUP_SECURITY_INFORMATION) &&
        (((PISECURITY_DESCRIPTOR)SecurityDescriptor)->Group == NULL))
        return ERROR_INVALID_PARAMETER;

    /* Validate the server handle */
    Status = SampValidateDbObject(ObjectHandle,
                                  SamDbIgnoreObject,
                                  DesiredAccess,
                                  &DbObject);
    if (!NT_SUCCESS(Status))
        goto done;

    /* Get the mapping for the object type */
    switch (DbObject->ObjectType)
    {
        case SamDbServerObject:
            Mapping = &ServerMapping;
            break;

        case SamDbDomainObject:
            Mapping = &DomainMapping;
            break;

        case SamDbAliasObject:
            Mapping = &AliasMapping;
            break;

        case SamDbGroupObject:
            Mapping = &GroupMapping;
            break;

        case SamDbUserObject:
            Mapping = &UserMapping;
            break;

        default:
            return STATUS_INVALID_HANDLE;
    }

    /* Get the size of the SD */
    Status = SampGetObjectAttribute(DbObject,
                                    L"SecDesc",
                                    NULL,
                                    NULL,
                                    &RelativeSdSize);
    if (!NT_SUCCESS(Status))
        return Status;

    /* Allocate a buffer for the SD */
    RelativeSd = RtlAllocateHeap(RtlGetProcessHeap(), 0, RelativeSdSize);
    if (RelativeSd == NULL)
        return STATUS_INSUFFICIENT_RESOURCES;

    /* Get the SD */
    Status = SampGetObjectAttribute(DbObject,
                                    L"SecDesc",
                                    NULL,
                                    RelativeSd,
                                    &RelativeSdSize);
    if (!NT_SUCCESS(Status))
        goto done;

    /* Build the new security descriptor */
    Status = RtlSetSecurityObject(SecurityInformation,
                                  (PSECURITY_DESCRIPTOR)SecurityDescriptor->SecurityDescriptor,
                                  &RelativeSd,
                                  Mapping,
                                  TokenHandle);
    if (!NT_SUCCESS(Status))
    {
        ERR("RtlSetSecurityObject failed (Status 0x%08lx)\n", Status);
        goto done;
    }

    /* Set the modified SD */
    Status = SampSetObjectAttribute(DbObject,
                                    L"SecDesc",
                                    REG_BINARY,
                                    RelativeSd,
                                    RtlLengthSecurityDescriptor(RelativeSd));
    if (!NT_SUCCESS(Status))
    {
        ERR("SampSetObjectAttribute failed (Status 0x%08lx)\n", Status);
    }

done:
    if (TokenHandle != NULL)
        NtClose(TokenHandle);

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

    return Status;
}


/* Function 3 */
NTSTATUS
NTAPI
SamrQuerySecurityObject(IN SAMPR_HANDLE ObjectHandle,
                        IN SECURITY_INFORMATION SecurityInformation,
                        OUT PSAMPR_SR_SECURITY_DESCRIPTOR *SecurityDescriptor)
{
    PSAM_DB_OBJECT SamObject;
    PSAMPR_SR_SECURITY_DESCRIPTOR SdData = NULL;
    PISECURITY_DESCRIPTOR_RELATIVE RelativeSd = NULL;
    PSECURITY_DESCRIPTOR ResultSd = NULL;
    ACCESS_MASK DesiredAccess = 0;
    ULONG RelativeSdSize = 0;
    ULONG ResultSdSize = 0;
    NTSTATUS Status;

    TRACE("SamrQuerySecurityObject(%p %lx %p)\n",
          ObjectHandle, SecurityInformation, SecurityDescriptor);

    *SecurityDescriptor = NULL;

    RtlAcquireResourceShared(&SampResource,
                             TRUE);

    if (SecurityInformation & (DACL_SECURITY_INFORMATION |
                               OWNER_SECURITY_INFORMATION |
                               GROUP_SECURITY_INFORMATION))
        DesiredAccess |= READ_CONTROL;

    if (SecurityInformation & SACL_SECURITY_INFORMATION)
        DesiredAccess |= ACCESS_SYSTEM_SECURITY;

    /* Validate the server handle */
    Status = SampValidateDbObject(ObjectHandle,
                                  SamDbIgnoreObject,
                                  DesiredAccess,
                                  &SamObject);
    if (!NT_SUCCESS(Status))
        goto done;

    /* Get the size of the SD */
    Status = SampGetObjectAttribute(SamObject,
                                    L"SecDesc",
                                    NULL,
                                    NULL,
                                    &RelativeSdSize);
    if (!NT_SUCCESS(Status) && Status != STATUS_BUFFER_OVERFLOW)
    {
        TRACE("Status 0x%08lx\n", Status);
        goto done;
    }

    /* Allocate a buffer for the SD */
    RelativeSd = midl_user_allocate(RelativeSdSize);
    if (RelativeSd == NULL)
    {
        Status = STATUS_INSUFFICIENT_RESOURCES;
        goto done;
    }

    /* Get the SD */
    Status = SampGetObjectAttribute(SamObject,
                                    L"SecDesc",
                                    NULL,
                                    RelativeSd,
                                    &RelativeSdSize);
    if (!NT_SUCCESS(Status))
    {
        TRACE("Status 0x%08lx\n", Status);
        goto done;
    }

    /* Invalidate the SD information that was not requested */
    if (!(SecurityInformation & OWNER_SECURITY_INFORMATION))
        RelativeSd->Owner = 0;

    if (!(SecurityInformation & GROUP_SECURITY_INFORMATION))
        RelativeSd->Group = 0;

    if (!(SecurityInformation & DACL_SECURITY_INFORMATION))
        RelativeSd->Control &= ~SE_DACL_PRESENT;

    if (!(SecurityInformation & SACL_SECURITY_INFORMATION))
        RelativeSd->Control &= ~SE_SACL_PRESENT;

    /* Calculate the required SD size */
    Status = RtlMakeSelfRelativeSD(RelativeSd,
                                   NULL,
                                   &ResultSdSize);
    if (Status != STATUS_BUFFER_TOO_SMALL)
        goto done;

    /* Allocate a buffer for the new SD */
    ResultSd = MIDL_user_allocate(ResultSdSize);
    if (ResultSd == NULL)
    {
        Status = STATUS_INSUFFICIENT_RESOURCES;
        goto done;
    }

    /* Build the new SD */
    Status = RtlMakeSelfRelativeSD(RelativeSd,
                                   ResultSd,
                                   &ResultSdSize);
    if (!NT_SUCCESS(Status))
        goto done;

    /* Allocate the SD data buffer */
    SdData = midl_user_allocate(sizeof(SAMPR_SR_SECURITY_DESCRIPTOR));
    if (SdData == NULL)
    {
        Status = STATUS_INSUFFICIENT_RESOURCES;
        goto done;
    }

    /* Fill the SD data buffer and return it to the caller */
    SdData->Length = ResultSdSize;
    SdData->SecurityDescriptor = (PBYTE)ResultSd;

    *SecurityDescriptor = SdData;

done:
    RtlReleaseResource(&SampResource);

    if (!NT_SUCCESS(Status))
    {
        if (ResultSd != NULL)
            MIDL_user_free(ResultSd);
    }

    if (RelativeSd != NULL)
        MIDL_user_free(RelativeSd);

    return Status;
}


/* Function 4 */
NTSTATUS
NTAPI
SamrShutdownSamServer(IN SAMPR_HANDLE ServerHandle)
{
    PSAM_DB_OBJECT ServerObject;
    NTSTATUS Status;

    TRACE("SamrShutdownSamServer(%p)\n",
          ServerHandle);

    RtlAcquireResourceShared(&SampResource,
                             TRUE);

    /* Validate the server handle */
    Status = SampValidateDbObject(ServerHandle,
                                  SamDbServerObject,
                                  SAM_SERVER_SHUTDOWN,
                                  &ServerObject);

    RtlReleaseResource(&SampResource);

    if (!NT_SUCCESS(Status))
        return Status;

    /* Shut the server down */
    RpcMgmtStopServerListening(0);

    Status = SampShutdownDisplayCache();
    if (!NT_SUCCESS(Status))
    {
        ERR("SampShutdownDisplayCache() failed (Status 0x%08lx)\n", Status);
    }

    return STATUS_SUCCESS;
}


/* Function 5 */
NTSTATUS
NTAPI
SamrLookupDomainInSamServer(IN SAMPR_HANDLE ServerHandle,
                            IN PRPC_UNICODE_STRING Name,
                            OUT PRPC_SID *DomainId)
{
    PSAM_DB_OBJECT ServerObject;
    HANDLE DomainsKeyHandle = NULL;
    HANDLE DomainKeyHandle = NULL;
    WCHAR DomainKeyName[64];
    ULONG Index;
    WCHAR DomainNameString[MAX_COMPUTERNAME_LENGTH + 1];
    UNICODE_STRING DomainName;
    ULONG Length;
    BOOL Found = FALSE;
    NTSTATUS Status;

    TRACE("SamrLookupDomainInSamServer(%p %p %p)\n",
          ServerHandle, Name, DomainId);

    RtlAcquireResourceShared(&SampResource,
                             TRUE);

    /* Validate the server handle */
    Status = SampValidateDbObject(ServerHandle,
                                  SamDbServerObject,
                                  SAM_SERVER_LOOKUP_DOMAIN,
                                  &ServerObject);
    if (!NT_SUCCESS(Status))
        goto done;

    *DomainId = NULL;

    Status = SampRegOpenKey(ServerObject->KeyHandle,
                            L"Domains",
                            KEY_READ,
                            &DomainsKeyHandle);
    if (!NT_SUCCESS(Status))
        goto done;

    Index = 0;
    while (Found == FALSE)
    {
        Status = SampRegEnumerateSubKey(DomainsKeyHandle,
                                        Index,
                                        64,
                                        DomainKeyName);
        if (!NT_SUCCESS(Status))
        {
            if (Status == STATUS_NO_MORE_ENTRIES)
                Status = STATUS_NO_SUCH_DOMAIN;
            break;
        }

        TRACE("Domain key name: %S\n", DomainKeyName);

        Status = SampRegOpenKey(DomainsKeyHandle,
                                DomainKeyName,
                                KEY_READ,
                                &DomainKeyHandle);
        if (NT_SUCCESS(Status))
        {
            Length = (MAX_COMPUTERNAME_LENGTH + 1) * sizeof(WCHAR);
            Status = SampRegQueryValue(DomainKeyHandle,
                                       L"Name",
                                       NULL,
                                       (PVOID)&DomainNameString,
                                       &Length);
            if (NT_SUCCESS(Status))
            {
                TRACE("Domain name: %S\n", DomainNameString);

                RtlInitUnicodeString(&DomainName,
                                     DomainNameString);
                if (RtlEqualUnicodeString(&DomainName, (PUNICODE_STRING)Name, TRUE))
                {
                   TRACE("Found it!\n");
                   Found = TRUE;

                   Status = SampRegQueryValue(DomainKeyHandle,
                                              L"SID",
                                              NULL,
                                              NULL,
                                              &Length);
                   if (NT_SUCCESS(Status))
                   {
                       *DomainId = midl_user_allocate(Length);

                       SampRegQueryValue(DomainKeyHandle,
                                         L"SID",
                                         NULL,
                                         (PVOID)*DomainId,
                                         &Length);

                       Status = STATUS_SUCCESS;
                       break;
                   }
                }
            }

            SampRegCloseKey(&DomainKeyHandle);
        }

        Index++;
    }

done:
    SampRegCloseKey(&DomainKeyHandle);
    SampRegCloseKey(&DomainsKeyHandle);

    RtlReleaseResource(&SampResource);

    return Status;
}


/* Function 6 */
NTSTATUS
NTAPI
SamrEnumerateDomainsInSamServer(IN SAMPR_HANDLE ServerHandle,
                                IN OUT unsigned long *EnumerationContext,
                                OUT PSAMPR_ENUMERATION_BUFFER *Buffer,
                                IN ULONG PreferedMaximumLength,
                                OUT PULONG CountReturned)
{
    PSAM_DB_OBJECT ServerObject;
    WCHAR DomainKeyName[64];
    HANDLE DomainsKeyHandle = NULL;
    HANDLE DomainKeyHandle = NULL;
    ULONG EnumIndex;
    ULONG EnumCount;
    ULONG RequiredLength;
    ULONG DataLength;
    ULONG i;
    PSAMPR_ENUMERATION_BUFFER EnumBuffer = NULL;
    NTSTATUS Status;

    TRACE("SamrEnumerateDomainsInSamServer(%p %p %p %lu %p)\n",
          ServerHandle, EnumerationContext, Buffer, PreferedMaximumLength,
          CountReturned);

    RtlAcquireResourceShared(&SampResource,
                             TRUE);

    /* Validate the server handle */
    Status = SampValidateDbObject(ServerHandle,
                                  SamDbServerObject,
                                  SAM_SERVER_ENUMERATE_DOMAINS,
                                  &ServerObject);
    if (!NT_SUCCESS(Status))
        goto done;

    Status = SampRegOpenKey(ServerObject->KeyHandle,
                            L"Domains",
                            KEY_READ,
                            &DomainsKeyHandle);
    if (!NT_SUCCESS(Status))
        goto done;

    EnumIndex = *EnumerationContext;
    EnumCount = 0;
    RequiredLength = 0;

    while (TRUE)
    {
        Status = SampRegEnumerateSubKey(DomainsKeyHandle,
                                        EnumIndex,
                                        64 * sizeof(WCHAR),
                                        DomainKeyName);
        if (!NT_SUCCESS(Status))
            break;

        TRACE("EnumIndex: %lu\n", EnumIndex);
        TRACE("Domain key name: %S\n", DomainKeyName);

        Status = SampRegOpenKey(DomainsKeyHandle,
                                DomainKeyName,
                                KEY_READ,
                                &DomainKeyHandle);
        TRACE("SampRegOpenKey returned %08lX\n", Status);
        if (NT_SUCCESS(Status))
        {
            DataLength = 0;
            Status = SampRegQueryValue(DomainKeyHandle,
                                       L"Name",
                                       NULL,
                                       NULL,
                                       &DataLength);
            TRACE("SampRegQueryValue returned %08lX\n", Status);
            if (NT_SUCCESS(Status))
            {
                TRACE("Data length: %lu\n", DataLength);

                if ((RequiredLength + DataLength + sizeof(UNICODE_STRING)) > PreferedMaximumLength)
                    break;

                RequiredLength += (DataLength + sizeof(UNICODE_STRING));
                EnumCount++;
            }

            SampRegCloseKey(&DomainKeyHandle);
        }

        EnumIndex++;
    }

    TRACE("EnumCount: %lu\n", EnumCount);
    TRACE("RequiredLength: %lu\n", RequiredLength);

    EnumBuffer = midl_user_allocate(sizeof(SAMPR_ENUMERATION_BUFFER));
    if (EnumBuffer == NULL)
    {
        Status = STATUS_INSUFFICIENT_RESOURCES;
        goto done;
    }

    EnumBuffer->EntriesRead = EnumCount;
    EnumBuffer->Buffer = midl_user_allocate(EnumCount * sizeof(SAMPR_RID_ENUMERATION));
    if (EnumBuffer->Buffer == NULL)
    {
        Status = STATUS_INSUFFICIENT_RESOURCES;
        goto done;
    }

    EnumIndex = *EnumerationContext;
    for (i = 0; i < EnumCount; i++, EnumIndex++)
    {
        Status = SampRegEnumerateSubKey(DomainsKeyHandle,
                                        EnumIndex,
                                        64 * sizeof(WCHAR),
                                        DomainKeyName);
        if (!NT_SUCCESS(Status))
            break;

        TRACE("EnumIndex: %lu\n", EnumIndex);
        TRACE("Domain key name: %S\n", DomainKeyName);

        Status = SampRegOpenKey(DomainsKeyHandle,
                                DomainKeyName,
                                KEY_READ,
                                &DomainKeyHandle);
        TRACE("SampRegOpenKey returned %08lX\n", Status);
        if (NT_SUCCESS(Status))
        {
            DataLength = 0;
            Status = SampRegQueryValue(DomainKeyHandle,
                                       L"Name",
                                       NULL,
                                       NULL,
                                       &DataLength);
            TRACE("SampRegQueryValue returned %08lX\n", Status);
            if (NT_SUCCESS(Status))
            {
                EnumBuffer->Buffer[i].RelativeId = 0;
                EnumBuffer->Buffer[i].Name.Length = (USHORT)DataLength - sizeof(WCHAR);
                EnumBuffer->Buffer[i].Name.MaximumLength = (USHORT)DataLength;
                EnumBuffer->Buffer[i].Name.Buffer = midl_user_allocate(DataLength);
                if (EnumBuffer->Buffer[i].Name.Buffer == NULL)
                {
                    SampRegCloseKey(&DomainKeyHandle);
                    Status = STATUS_INSUFFICIENT_RESOURCES;
                    goto done;
                }

                Status = SampRegQueryValue(DomainKeyHandle,
                                           L"Name",
                                           NULL,
                                           EnumBuffer->Buffer[i].Name.Buffer,
                                           &DataLength);
                TRACE("SampRegQueryValue returned %08lX\n", Status);
                if (NT_SUCCESS(Status))
                {
                    TRACE("Domain name: %S\n", EnumBuffer->Buffer[i].Name.Buffer);
                }
            }

            SampRegCloseKey(&DomainKeyHandle);

            if (!NT_SUCCESS(Status))
                goto done;
        }
    }

    if (NT_SUCCESS(Status))
    {
        *EnumerationContext += EnumCount;
        *Buffer = EnumBuffer;
        *CountReturned = EnumCount;
    }

done:
    SampRegCloseKey(&DomainKeyHandle);
    SampRegCloseKey(&DomainsKeyHandle);

    if (!NT_SUCCESS(Status))
    {
        *EnumerationContext = 0;
        *Buffer = NULL;
        *CountReturned = 0;

        if (EnumBuffer != NULL)
        {
            if (EnumBuffer->Buffer != NULL)
            {
                if (EnumBuffer->EntriesRead != 0)
                {
                    for (i = 0; i < EnumBuffer->EntriesRead; i++)
                    {
                        if (EnumBuffer->Buffer[i].Name.Buffer != NULL)
                            midl_user_free(EnumBuffer->Buffer[i].Name.Buffer);
                    }
                }

                midl_user_free(EnumBuffer->Buffer);
            }

            midl_user_free(EnumBuffer);
        }
    }

    RtlReleaseResource(&SampResource);

    return Status;
}


/* Function 7 */
NTSTATUS
NTAPI
SamrOpenDomain(IN SAMPR_HANDLE ServerHandle,
               IN ACCESS_MASK DesiredAccess,
               IN PRPC_SID DomainId,
               OUT SAMPR_HANDLE *DomainHandle)
{
    PSAM_DB_OBJECT ServerObject;
    PSAM_DB_OBJECT DomainObject;
    NTSTATUS Status;

    TRACE("SamrOpenDomain(%p %lx %p %p)\n",
          ServerHandle, DesiredAccess, DomainId, DomainHandle);

    /* Map generic access rights */
    RtlMapGenericMask(&DesiredAccess,
                      &DomainMapping);

    RtlAcquireResourceShared(&SampResource,
                             TRUE);

    /* Validate the server handle */
    Status = SampValidateDbObject(ServerHandle,
                                  SamDbServerObject,
                                  SAM_SERVER_LOOKUP_DOMAIN,
                                  &ServerObject);
    if (!NT_SUCCESS(Status))
        return Status;

    /* Validate the Domain SID */
    if ((DomainId->Revision != SID_REVISION) ||
        (DomainId->SubAuthorityCount > SID_MAX_SUB_AUTHORITIES) ||
        (memcmp(&DomainId->IdentifierAuthority, &NtSidAuthority, sizeof(SID_IDENTIFIER_AUTHORITY)) != 0))
        return STATUS_INVALID_PARAMETER;

    /* Open the domain object */
    if ((DomainId->SubAuthorityCount == 1) &&
        (DomainId->SubAuthority[0] == SECURITY_BUILTIN_DOMAIN_RID))
    {
        /* Builtin domain object */
        TRACE("Opening the builtin domain object.\n");

        Status = SampOpenDbObject(ServerObject,
                                  L"Domains",
                                  L"Builtin",
                                  0,
                                  SamDbDomainObject,
                                  DesiredAccess,
                                  &DomainObject);
    }
    else if ((DomainId->SubAuthorityCount == 4) &&
             (DomainId->SubAuthority[0] == SECURITY_NT_NON_UNIQUE))
    {
        /* Account domain object */
        TRACE("Opening the account domain object.\n");

        /* FIXME: Check the account domain sub authorities!!! */

        Status = SampOpenDbObject(ServerObject,
                                  L"Domains",
                                  L"Account",
                                  0,
                                  SamDbDomainObject,
                                  DesiredAccess,
                                  &DomainObject);
    }
    else
    {
        /* No valid domain SID */
        Status = STATUS_INVALID_PARAMETER;
    }

    if (NT_SUCCESS(Status))
        *DomainHandle = (SAMPR_HANDLE)DomainObject;

    RtlReleaseResource(&SampResource);

    TRACE("SamrOpenDomain done (Status 0x%08lx)\n", Status);

    return Status;
}


static NTSTATUS
SampQueryDomainPassword(PSAM_DB_OBJECT DomainObject,
                        PSAMPR_DOMAIN_INFO_BUFFER *Buffer)
{
    PSAMPR_DOMAIN_INFO_BUFFER InfoBuffer = NULL;
    SAM_DOMAIN_FIXED_DATA FixedData;
    ULONG Length = 0;
    NTSTATUS Status;

    *Buffer = NULL;

    InfoBuffer = midl_user_allocate(sizeof(SAMPR_DOMAIN_INFO_BUFFER));
    if (InfoBuffer == NULL)
        return STATUS_INSUFFICIENT_RESOURCES;

    Length = sizeof(SAM_DOMAIN_FIXED_DATA);
    Status = SampGetObjectAttribute(DomainObject,
                                    L"F",
                                    NULL,
                                    (PVOID)&FixedData,
                                    &Length);
    if (!NT_SUCCESS(Status))
        goto done;

    InfoBuffer->Password.MinPasswordLength = FixedData.MinPasswordLength;
    InfoBuffer->Password.PasswordHistoryLength = FixedData.PasswordHistoryLength;
    InfoBuffer->Password.PasswordProperties = FixedData.PasswordProperties;
    InfoBuffer->Password.MaxPasswordAge.LowPart = FixedData.MaxPasswordAge.LowPart;
    InfoBuffer->Password.MaxPasswordAge.HighPart = FixedData.MaxPasswordAge.HighPart;
    InfoBuffer->Password.MinPasswordAge.LowPart = FixedData.MinPasswordAge.LowPart;
    InfoBuffer->Password.MinPasswordAge.HighPart = FixedData.MinPasswordAge.HighPart;

    *Buffer = InfoBuffer;

done:
    if (!NT_SUCCESS(Status))
    {
        if (InfoBuffer != NULL)
        {
            midl_user_free(InfoBuffer);
        }
    }

    return Status;
}


static NTSTATUS
SampGetNumberOfAccounts(PSAM_DB_OBJECT DomainObject,
                        LPCWSTR AccountType,
                        PULONG Count)
{
    HANDLE AccountKeyHandle = NULL;
    HANDLE NamesKeyHandle = NULL;
    NTSTATUS Status;

    *Count = 0;

    Status = SampRegOpenKey(DomainObject->KeyHandle,
                            AccountType,
                            KEY_READ,
                            &AccountKeyHandle);
    if (!NT_SUCCESS(Status))
        return Status;

    Status = SampRegOpenKey(AccountKeyHandle,
                            L"Names",
                            KEY_READ,
                            &NamesKeyHandle);
    if (!NT_SUCCESS(Status))
        goto done;

    Status = SampRegQueryKeyInfo(NamesKeyHandle,
                                 NULL,
                                 Count);

done:
    SampRegCloseKey(&NamesKeyHandle);
    SampRegCloseKey(&AccountKeyHandle);

    return Status;
}


static NTSTATUS
SampQueryDomainGeneral(PSAM_DB_OBJECT DomainObject,
                       PSAMPR_DOMAIN_INFO_BUFFER *Buffer)
{
    PSAMPR_DOMAIN_INFO_BUFFER InfoBuffer = NULL;
    SAM_DOMAIN_FIXED_DATA FixedData;
    ULONG Length = 0;
    NTSTATUS Status;

    *Buffer = NULL;

    InfoBuffer = midl_user_allocate(sizeof(SAMPR_DOMAIN_INFO_BUFFER));
    if (InfoBuffer == NULL)
        return STATUS_INSUFFICIENT_RESOURCES;

    Length = sizeof(SAM_DOMAIN_FIXED_DATA);
    Status = SampGetObjectAttribute(DomainObject,
                                    L"F",
                                    NULL,
                                    (PVOID)&FixedData,
                                    &Length);
    if (!NT_SUCCESS(Status))
        goto done;

    InfoBuffer->General.ForceLogoff.LowPart = FixedData.ForceLogoff.LowPart;
    InfoBuffer->General.ForceLogoff.HighPart = FixedData.ForceLogoff.HighPart;
    InfoBuffer->General.DomainModifiedCount.LowPart = FixedData.DomainModifiedCount.LowPart;
    InfoBuffer->General.DomainModifiedCount.HighPart = FixedData.DomainModifiedCount.HighPart;
    InfoBuffer->General.DomainServerState = FixedData.DomainServerState;
    InfoBuffer->General.DomainServerRole = FixedData.DomainServerRole;
    InfoBuffer->General.UasCompatibilityRequired = FixedData.UasCompatibilityRequired;

    /* Get the OemInformation string */
    Status = SampGetObjectAttributeString(DomainObject,
                                          L"OemInformation",
                                          &InfoBuffer->General.OemInformation);
    if (!NT_SUCCESS(Status))
    {
        TRACE("Status 0x%08lx\n", Status);
        goto done;
    }

    /* Get the Name string */
    Status = SampGetObjectAttributeString(DomainObject,
                                          L"Name",
                                          &InfoBuffer->General.DomainName);
    if (!NT_SUCCESS(Status))
    {
        TRACE("Status 0x%08lx\n", Status);
        goto done;
    }

    /* Get the ReplicaSourceNodeName string */
    Status = SampGetObjectAttributeString(DomainObject,
                                          L"ReplicaSourceNodeName",
                                          &InfoBuffer->General.ReplicaSourceNodeName);
    if (!NT_SUCCESS(Status))
    {
        TRACE("Status 0x%08lx\n", Status);
        goto done;
    }

    /* Get the number of Users in the Domain */
    Status = SampGetNumberOfAccounts(DomainObject,
                                     L"Users",
                                     &InfoBuffer->General.UserCount);
    if (!NT_SUCCESS(Status))
    {
        TRACE("Status 0x%08lx\n", Status);
        goto done;
    }

    /* Get the number of Groups in the Domain */
    Status = SampGetNumberOfAccounts(DomainObject,
                                     L"Groups",
                                     &InfoBuffer->General.GroupCount);
    if (!NT_SUCCESS(Status))
    {
        TRACE("Status 0x%08lx\n", Status);
        goto done;
    }

    /* Get the number of Aliases in the Domain */
    Status = SampGetNumberOfAccounts(DomainObject,
                                     L"Aliases",
                                     &InfoBuffer->General.AliasCount);
    if (!NT_SUCCESS(Status))
    {
        TRACE("Status 0x%08lx\n", Status);
        goto done;
    }

    *Buffer = InfoBuffer;

done:
    if (!NT_SUCCESS(Status))
    {
        if (InfoBuffer != NULL)
        {
            if (InfoBuffer->General.OemInformation.Buffer != NULL)
                midl_user_free(InfoBuffer->General.OemInformation.Buffer);

            if (InfoBuffer->General.DomainName.Buffer != NULL)
                midl_user_free(InfoBuffer->General.DomainName.Buffer);

            if (InfoBuffer->General.ReplicaSourceNodeName.Buffer != NULL)
                midl_user_free(InfoBuffer->General.ReplicaSourceNodeName.Buffer);

            midl_user_free(InfoBuffer);
        }
    }

    return Status;
}


static NTSTATUS
SampQueryDomainLogoff(PSAM_DB_OBJECT DomainObject,
                      PSAMPR_DOMAIN_INFO_BUFFER *Buffer)
{
    PSAMPR_DOMAIN_INFO_BUFFER InfoBuffer = NULL;
    SAM_DOMAIN_FIXED_DATA FixedData;
    ULONG Length = 0;
    NTSTATUS Status;

    *Buffer = NULL;

    InfoBuffer = midl_user_allocate(sizeof(SAMPR_DOMAIN_INFO_BUFFER));
    if (InfoBuffer == NULL)
        return STATUS_INSUFFICIENT_RESOURCES;

    Length = sizeof(SAM_DOMAIN_FIXED_DATA);
    Status = SampGetObjectAttribute(DomainObject,
                                    L"F",
                                    NULL,
                                    (PVOID)&FixedData,
                                    &Length);
    if (!NT_SUCCESS(Status))
        goto done;

    InfoBuffer->Logoff.ForceLogoff.LowPart = FixedData.ForceLogoff.LowPart;
    InfoBuffer->Logoff.ForceLogoff.HighPart = FixedData.ForceLogoff.HighPart;

    *Buffer = InfoBuffer;

done:
    if (!NT_SUCCESS(Status))
    {
        if (InfoBuffer != NULL)
        {
            midl_user_free(InfoBuffer);
        }
    }

    return Status;
}


static NTSTATUS
SampQueryDomainOem(PSAM_DB_OBJECT DomainObject,
                   PSAMPR_DOMAIN_INFO_BUFFER *Buffer)
{
    PSAMPR_DOMAIN_INFO_BUFFER InfoBuffer = NULL;
    NTSTATUS Status;

    *Buffer = NULL;

    InfoBuffer = midl_user_allocate(sizeof(SAMPR_DOMAIN_INFO_BUFFER));
    if (InfoBuffer == NULL)
        return STATUS_INSUFFICIENT_RESOURCES;

    /* Get the OemInformation string */
    Status = SampGetObjectAttributeString(DomainObject,
                                          L"OemInformation",
                                          &InfoBuffer->Oem.OemInformation);
    if (!NT_SUCCESS(Status))
    {
        TRACE("Status 0x%08lx\n", Status);
        goto done;
    }

    *Buffer = InfoBuffer;

done:
    if (!NT_SUCCESS(Status))
    {
        if (InfoBuffer != NULL)
        {
            if (InfoBuffer->Oem.OemInformation.Buffer != NULL)
                midl_user_free(InfoBuffer->Oem.OemInformation.Buffer);

            midl_user_free(InfoBuffer);
        }
    }

    return Status;
}


static NTSTATUS
SampQueryDomainName(PSAM_DB_OBJECT DomainObject,
                    PSAMPR_DOMAIN_INFO_BUFFER *Buffer)
{
    PSAMPR_DOMAIN_INFO_BUFFER InfoBuffer = NULL;
    NTSTATUS Status;

    *Buffer = NULL;

    InfoBuffer = midl_user_allocate(sizeof(SAMPR_DOMAIN_INFO_BUFFER));
    if (InfoBuffer == NULL)
        return STATUS_INSUFFICIENT_RESOURCES;

    /* Get the Name string */
    Status = SampGetObjectAttributeString(DomainObject,
                                          L"Name",
                                          &InfoBuffer->Name.DomainName);
    if (!NT_SUCCESS(Status))
    {
        TRACE("Status 0x%08lx\n", Status);
        goto done;
    }

    *Buffer = InfoBuffer;

done:
    if (!NT_SUCCESS(Status))
    {
        if (InfoBuffer != NULL)
        {
            if (InfoBuffer->Name.DomainName.Buffer != NULL)
                midl_user_free(InfoBuffer->Name.DomainName.Buffer);

            midl_user_free(InfoBuffer);
        }
    }

    return Status;
}


static NTSTATUS
SampQueryDomainReplication(PSAM_DB_OBJECT DomainObject,
                           PSAMPR_DOMAIN_INFO_BUFFER *Buffer)
{
    PSAMPR_DOMAIN_INFO_BUFFER InfoBuffer = NULL;
    NTSTATUS Status;

    *Buffer = NULL;

    InfoBuffer = midl_user_allocate(sizeof(SAMPR_DOMAIN_INFO_BUFFER));
    if (InfoBuffer == NULL)
        return STATUS_INSUFFICIENT_RESOURCES;

    /* Get the ReplicaSourceNodeName string */
    Status = SampGetObjectAttributeString(DomainObject,
                                          L"ReplicaSourceNodeName",
                                          &InfoBuffer->Replication.ReplicaSourceNodeName);
    if (!NT_SUCCESS(Status))
    {
        TRACE("Status 0x%08lx\n", Status);
        goto done;
    }

    *Buffer = InfoBuffer;

done:
    if (!NT_SUCCESS(Status))
    {
        if (InfoBuffer != NULL)
        {
            if (InfoBuffer->Replication.ReplicaSourceNodeName.Buffer != NULL)
                midl_user_free(InfoBuffer->Replication.ReplicaSourceNodeName.Buffer);

            midl_user_free(InfoBuffer);
        }
    }

    return Status;
}


static NTSTATUS
SampQueryDomainServerRole(PSAM_DB_OBJECT DomainObject,
                          PSAMPR_DOMAIN_INFO_BUFFER *Buffer)
{
    PSAMPR_DOMAIN_INFO_BUFFER InfoBuffer = NULL;
    SAM_DOMAIN_FIXED_DATA FixedData;
    ULONG Length = 0;
    NTSTATUS Status;

    *Buffer = NULL;

    InfoBuffer = midl_user_allocate(sizeof(SAMPR_DOMAIN_INFO_BUFFER));
    if (InfoBuffer == NULL)
        return STATUS_INSUFFICIENT_RESOURCES;

    Length = sizeof(SAM_DOMAIN_FIXED_DATA);
    Status = SampGetObjectAttribute(DomainObject,
                                    L"F",
                                    NULL,
                                    (PVOID)&FixedData,
                                    &Length);
    if (!NT_SUCCESS(Status))
        goto done;

    InfoBuffer->Role.DomainServerRole = FixedData.DomainServerRole;

    *Buffer = InfoBuffer;

done:
    if (!NT_SUCCESS(Status))
    {
        if (InfoBuffer != NULL)
        {
            midl_user_free(InfoBuffer);
        }
    }

    return Status;
}


static NTSTATUS
SampQueryDomainModified(PSAM_DB_OBJECT DomainObject,
                        PSAMPR_DOMAIN_INFO_BUFFER *Buffer)
{
    PSAMPR_DOMAIN_INFO_BUFFER InfoBuffer = NULL;
    SAM_DOMAIN_FIXED_DATA FixedData;
    ULONG Length = 0;
    NTSTATUS Status;

    *Buffer = NULL;

    InfoBuffer = midl_user_allocate(sizeof(SAMPR_DOMAIN_INFO_BUFFER));
    if (InfoBuffer == NULL)
        return STATUS_INSUFFICIENT_RESOURCES;

    Length = sizeof(SAM_DOMAIN_FIXED_DATA);
    Status = SampGetObjectAttribute(DomainObject,
                                    L"F",
                                    NULL,
                                    (PVOID)&FixedData,
                                    &Length);
    if (!NT_SUCCESS(Status))
        goto done;

    InfoBuffer->Modified.DomainModifiedCount.LowPart = FixedData.DomainModifiedCount.LowPart;
    InfoBuffer->Modified.DomainModifiedCount.HighPart = FixedData.DomainModifiedCount.HighPart;
    InfoBuffer->Modified.CreationTime.LowPart = FixedData.CreationTime.LowPart;
    InfoBuffer->Modified.CreationTime.HighPart = FixedData.CreationTime.HighPart;

    *Buffer = InfoBuffer;

done:
    if (!NT_SUCCESS(Status))
    {
        if (InfoBuffer != NULL)
        {
            midl_user_free(InfoBuffer);
        }
    }

    return Status;
}


static NTSTATUS
SampQueryDomainState(PSAM_DB_OBJECT DomainObject,
                     PSAMPR_DOMAIN_INFO_BUFFER *Buffer)
{
    PSAMPR_DOMAIN_INFO_BUFFER InfoBuffer = NULL;
    SAM_DOMAIN_FIXED_DATA FixedData;
    ULONG Length = 0;
    NTSTATUS Status;

    *Buffer = NULL;

    InfoBuffer = midl_user_allocate(sizeof(SAMPR_DOMAIN_INFO_BUFFER));
    if (InfoBuffer == NULL)
        return STATUS_INSUFFICIENT_RESOURCES;

    Length = sizeof(SAM_DOMAIN_FIXED_DATA);
    Status = SampGetObjectAttribute(DomainObject,
                                    L"F",
                                    NULL,
                                    (PVOID)&FixedData,
                                    &Length);
    if (!NT_SUCCESS(Status))
        goto done;

    InfoBuffer->State.DomainServerState = FixedData.DomainServerState;

    *Buffer = InfoBuffer;

done:
    if (!NT_SUCCESS(Status))
    {
        if (InfoBuffer != NULL)
        {
            midl_user_free(InfoBuffer);
        }
    }

    return Status;
}


static NTSTATUS
SampQueryDomainGeneral2(PSAM_DB_OBJECT DomainObject,
                        PSAMPR_DOMAIN_INFO_BUFFER *Buffer)
{
    PSAMPR_DOMAIN_INFO_BUFFER InfoBuffer = NULL;
    SAM_DOMAIN_FIXED_DATA FixedData;
    ULONG Length = 0;
    NTSTATUS Status;

    *Buffer = NULL;

    InfoBuffer = midl_user_allocate(sizeof(SAMPR_DOMAIN_INFO_BUFFER));
    if (InfoBuffer == NULL)
        return STATUS_INSUFFICIENT_RESOURCES;

    Length = sizeof(SAM_DOMAIN_FIXED_DATA);
    Status = SampGetObjectAttribute(DomainObject,
                                    L"F",
                                    NULL,
                                    (PVOID)&FixedData,
                                    &Length);
    if (!NT_SUCCESS(Status))
        goto done;

    InfoBuffer->General2.I1.ForceLogoff.LowPart = FixedData.ForceLogoff.LowPart;
    InfoBuffer->General2.I1.ForceLogoff.HighPart = FixedData.ForceLogoff.HighPart;
    InfoBuffer->General2.I1.DomainModifiedCount.LowPart = FixedData.DomainModifiedCount.LowPart;
    InfoBuffer->General2.I1.DomainModifiedCount.HighPart = FixedData.DomainModifiedCount.HighPart;
    InfoBuffer->General2.I1.DomainServerState = FixedData.DomainServerState;
    InfoBuffer->General2.I1.DomainServerRole = FixedData.DomainServerRole;
    InfoBuffer->General2.I1.UasCompatibilityRequired = FixedData.UasCompatibilityRequired;

    InfoBuffer->General2.LockoutDuration = FixedData.LockoutDuration;
    InfoBuffer->General2.LockoutObservationWindow = FixedData.LockoutObservationWindow;
    InfoBuffer->General2.LockoutThreshold = FixedData.LockoutThreshold;

    /* Get the OemInformation string */
    Status = SampGetObjectAttributeString(DomainObject,
                                          L"OemInformation",
                                          &InfoBuffer->General2.I1.OemInformation);
    if (!NT_SUCCESS(Status))
    {
        TRACE("Status 0x%08lx\n", Status);
        goto done;
    }

    /* Get the Name string */
    Status = SampGetObjectAttributeString(DomainObject,
                                          L"Name",
                                          &InfoBuffer->General2.I1.DomainName);
    if (!NT_SUCCESS(Status))
    {
        TRACE("Status 0x%08lx\n", Status);
        goto done;
    }

    /* Get the ReplicaSourceNodeName string */
    Status = SampGetObjectAttributeString(DomainObject,
                                          L"ReplicaSourceNodeName",
                                          &InfoBuffer->General2.I1.ReplicaSourceNodeName);
    if (!NT_SUCCESS(Status))
    {
        TRACE("Status 0x%08lx\n", Status);
        goto done;
    }

    /* Get the number of Users in the Domain */
    Status = SampGetNumberOfAccounts(DomainObject,
                                     L"Users",
                                     &InfoBuffer->General2.I1.UserCount);
    if (!NT_SUCCESS(Status))
    {
        TRACE("Status 0x%08lx\n", Status);
        goto done;
    }

    /* Get the number of Groups in the Domain */
    Status = SampGetNumberOfAccounts(DomainObject,
                                     L"Groups",
                                     &InfoBuffer->General2.I1.GroupCount);
    if (!NT_SUCCESS(Status))
    {
        TRACE("Status 0x%08lx\n", Status);
        goto done;
    }

    /* Get the number of Aliases in the Domain */
    Status = SampGetNumberOfAccounts(DomainObject,
                                     L"Aliases",
                                     &InfoBuffer->General2.I1.AliasCount);
    if (!NT_SUCCESS(Status))
    {
        TRACE("Status 0x%08lx\n", Status);
        goto done;
    }

    *Buffer = InfoBuffer;

done:
    if (!NT_SUCCESS(Status))
    {
        if (InfoBuffer != NULL)
        {
            if (InfoBuffer->General2.I1.OemInformation.Buffer != NULL)
                midl_user_free(InfoBuffer->General2.I1.OemInformation.Buffer);

            if (InfoBuffer->General2.I1.DomainName.Buffer != NULL)
                midl_user_free(InfoBuffer->General2.I1.DomainName.Buffer);

            if (InfoBuffer->General2.I1.ReplicaSourceNodeName.Buffer != NULL)
                midl_user_free(InfoBuffer->General2.I1.ReplicaSourceNodeName.Buffer);

            midl_user_free(InfoBuffer);
        }
    }

    return Status;
}


static NTSTATUS
SampQueryDomainLockout(PSAM_DB_OBJECT DomainObject,
                       PSAMPR_DOMAIN_INFO_BUFFER *Buffer)
{
    PSAMPR_DOMAIN_INFO_BUFFER InfoBuffer = NULL;
    SAM_DOMAIN_FIXED_DATA FixedData;
    ULONG Length = 0;
    NTSTATUS Status;

    *Buffer = NULL;

    InfoBuffer = midl_user_allocate(sizeof(SAMPR_DOMAIN_INFO_BUFFER));
    if (InfoBuffer == NULL)
        return STATUS_INSUFFICIENT_RESOURCES;

    Length = sizeof(SAM_DOMAIN_FIXED_DATA);
    Status = SampGetObjectAttribute(DomainObject,
                                    L"F",
                                    NULL,
                                    (PVOID)&FixedData,
                                    &Length);
    if (!NT_SUCCESS(Status))
        goto done;

    InfoBuffer->Lockout.LockoutDuration = FixedData.LockoutDuration;
    InfoBuffer->Lockout.LockoutObservationWindow = FixedData.LockoutObservationWindow;
    InfoBuffer->Lockout.LockoutThreshold = FixedData.LockoutThreshold;

    *Buffer = InfoBuffer;

done:
    if (!NT_SUCCESS(Status))
    {
        if (InfoBuffer != NULL)
        {
            midl_user_free(InfoBuffer);
        }
    }

    return Status;
}


static NTSTATUS
SampQueryDomainModified2(PSAM_DB_OBJECT DomainObject,
                        PSAMPR_DOMAIN_INFO_BUFFER *Buffer)
{
    PSAMPR_DOMAIN_INFO_BUFFER InfoBuffer = NULL;
    SAM_DOMAIN_FIXED_DATA FixedData;
    ULONG Length = 0;
    NTSTATUS Status;

    *Buffer = NULL;

    InfoBuffer = midl_user_allocate(sizeof(SAMPR_DOMAIN_INFO_BUFFER));
    if (InfoBuffer == NULL)
        return STATUS_INSUFFICIENT_RESOURCES;

    Length = sizeof(SAM_DOMAIN_FIXED_DATA);
    Status = SampGetObjectAttribute(DomainObject,
                                    L"F",
                                    NULL,
                                    (PVOID)&FixedData,
                                    &Length);
    if (!NT_SUCCESS(Status))
        goto done;

    InfoBuffer->Modified2.DomainModifiedCount.LowPart = FixedData.DomainModifiedCount.LowPart;
    InfoBuffer->Modified2.DomainModifiedCount.HighPart = FixedData.DomainModifiedCount.HighPart;
    InfoBuffer->Modified2.CreationTime.LowPart = FixedData.CreationTime.LowPart;
    InfoBuffer->Modified2.CreationTime.HighPart = FixedData.CreationTime.HighPart;
    InfoBuffer->Modified2.ModifiedCountAtLastPromotion.LowPart = FixedData.ModifiedCountAtLastPromotion.LowPart;
    InfoBuffer->Modified2.ModifiedCountAtLastPromotion.HighPart = FixedData.ModifiedCountAtLastPromotion.HighPart;

    *Buffer = InfoBuffer;

done:
    if (!NT_SUCCESS(Status))
    {
        if (InfoBuffer != NULL)
        {
            midl_user_free(InfoBuffer);
        }
    }

    return Status;
}


/* Function 8 */
NTSTATUS
NTAPI
SamrQueryInformationDomain(IN SAMPR_HANDLE DomainHandle,
                           IN DOMAIN_INFORMATION_CLASS DomainInformationClass,
                           OUT PSAMPR_DOMAIN_INFO_BUFFER *Buffer)
{
    TRACE("SamrQueryInformationDomain(%p %lu %p)\n",
          DomainHandle, DomainInformationClass, Buffer);

    return SamrQueryInformationDomain2(DomainHandle,
                                       DomainInformationClass,
                                       Buffer);
}


static NTSTATUS
SampSetDomainPassword(PSAM_DB_OBJECT DomainObject,
                      PSAMPR_DOMAIN_INFO_BUFFER Buffer)
{
    SAM_DOMAIN_FIXED_DATA FixedData;
    ULONG Length = 0;
    NTSTATUS Status;

    Length = sizeof(SAM_DOMAIN_FIXED_DATA);
    Status = SampGetObjectAttribute(DomainObject,
                                    L"F",
                                    NULL,
                                    (PVOID)&FixedData,
                                    &Length);
    if (!NT_SUCCESS(Status))
        goto done;

    FixedData.MinPasswordLength = Buffer->Password.MinPasswordLength;
    FixedData.PasswordHistoryLength = Buffer->Password.PasswordHistoryLength;
    FixedData.PasswordProperties = Buffer->Password.PasswordProperties;
    FixedData.MaxPasswordAge.LowPart = Buffer->Password.MaxPasswordAge.LowPart;
    FixedData.MaxPasswordAge.HighPart = Buffer->Password.MaxPasswordAge.HighPart;
    FixedData.MinPasswordAge.LowPart = Buffer->Password.MinPasswordAge.LowPart;
    FixedData.MinPasswordAge.HighPart = Buffer->Password.MinPasswordAge.HighPart;

    Status = SampSetObjectAttribute(DomainObject,
                                    L"F",
                                    REG_BINARY,
                                    &FixedData,
                                    Length);

done:
    return Status;
}


static NTSTATUS
SampSetDomainLogoff(PSAM_DB_OBJECT DomainObject,
                    PSAMPR_DOMAIN_INFO_BUFFER Buffer)
{
    SAM_DOMAIN_FIXED_DATA FixedData;
    ULONG Length = 0;
    NTSTATUS Status;

    Length = sizeof(SAM_DOMAIN_FIXED_DATA);
    Status = SampGetObjectAttribute(DomainObject,
                                    L"F",
                                    NULL,
                                    (PVOID)&FixedData,
                                    &Length);
    if (!NT_SUCCESS(Status))
        goto done;

    FixedData.ForceLogoff.LowPart = Buffer->Logoff.ForceLogoff.LowPart;
    FixedData.ForceLogoff.HighPart = Buffer->Logoff.ForceLogoff.HighPart;

    Status = SampSetObjectAttribute(DomainObject,
                                    L"F",
                                    REG_BINARY,
                                    &FixedData,
                                    Length);

done:
    return Status;
}


static NTSTATUS
SampSetDomainServerRole(PSAM_DB_OBJECT DomainObject,
                        PSAMPR_DOMAIN_INFO_BUFFER Buffer)
{
    SAM_DOMAIN_FIXED_DATA FixedData;
    ULONG Length = 0;
    NTSTATUS Status;

    Length = sizeof(SAM_DOMAIN_FIXED_DATA);
    Status = SampGetObjectAttribute(DomainObject,
                                    L"F",
                                    NULL,
                                    (PVOID)&FixedData,
                                    &Length);
    if (!NT_SUCCESS(Status))
        goto done;

    FixedData.DomainServerRole = Buffer->Role.DomainServerRole;

    Status = SampSetObjectAttribute(DomainObject,
                                    L"F",
                                    REG_BINARY,
                                    &FixedData,
                                    Length);

done:
    return Status;
}


static NTSTATUS
SampSetDomainState(PSAM_DB_OBJECT DomainObject,
                   PSAMPR_DOMAIN_INFO_BUFFER Buffer)
{
    SAM_DOMAIN_FIXED_DATA FixedData;
    ULONG Length = 0;
    NTSTATUS Status;

    Length = sizeof(SAM_DOMAIN_FIXED_DATA);
    Status = SampGetObjectAttribute(DomainObject,
                                    L"F",
                                    NULL,
                                    (PVOID)&FixedData,
                                    &Length);
    if (!NT_SUCCESS(Status))
        goto done;

    FixedData.DomainServerState = Buffer->State.DomainServerState;

    Status = SampSetObjectAttribute(DomainObject,
                                    L"F",
                                    REG_BINARY,
                                    &FixedData,
                                    Length);

done:
    return Status;
}


static NTSTATUS
SampSetDomainLockout(PSAM_DB_OBJECT DomainObject,
                     PSAMPR_DOMAIN_INFO_BUFFER Buffer)
{
    SAM_DOMAIN_FIXED_DATA FixedData;
    ULONG Length = 0;
    NTSTATUS Status;

    Length = sizeof(SAM_DOMAIN_FIXED_DATA);
    Status = SampGetObjectAttribute(DomainObject,
                                    L"F",
                                    NULL,
                                    (PVOID)&FixedData,
                                    &Length);
    if (!NT_SUCCESS(Status))
        goto done;

    FixedData.LockoutDuration = Buffer->Lockout.LockoutDuration;
    FixedData.LockoutObservationWindow = Buffer->Lockout.LockoutObservationWindow;
    FixedData.LockoutThreshold = Buffer->Lockout.LockoutThreshold;

    Status = SampSetObjectAttribute(DomainObject,
                                    L"F",
                                    REG_BINARY,
                                    &FixedData,
                                    Length);

done:
    return Status;
}


/* Function 9 */
NTSTATUS
NTAPI
SamrSetInformationDomain(IN SAMPR_HANDLE DomainHandle,
                         IN DOMAIN_INFORMATION_CLASS DomainInformationClass,
                         IN PSAMPR_DOMAIN_INFO_BUFFER DomainInformation)
{
    PSAM_DB_OBJECT DomainObject;
    ACCESS_MASK DesiredAccess;
    NTSTATUS Status;

    TRACE("SamrSetInformationDomain(%p %lu %p)\n",
          DomainHandle, DomainInformationClass, DomainInformation);

    switch (DomainInformationClass)
    {
        case DomainPasswordInformation:
        case DomainLockoutInformation:
            DesiredAccess = DOMAIN_WRITE_PASSWORD_PARAMS;
            break;

        case DomainLogoffInformation:
        case DomainOemInformation:
        case DomainNameInformation:
            DesiredAccess = DOMAIN_WRITE_OTHER_PARAMETERS;
            break;

        case DomainReplicationInformation:
        case DomainServerRoleInformation:
        case DomainStateInformation:
            DesiredAccess = DOMAIN_ADMINISTER_SERVER;
            break;

        default:
            return STATUS_INVALID_INFO_CLASS;
    }

    RtlAcquireResourceExclusive(&SampResource,
                                TRUE);

    /* Validate the server handle */
    Status = SampValidateDbObject(DomainHandle,
                                  SamDbDomainObject,
                                  DesiredAccess,
                                  &DomainObject);
    if (!NT_SUCCESS(Status))
        goto done;

    switch (DomainInformationClass)
    {
        case DomainPasswordInformation:
            Status = SampSetDomainPassword(DomainObject,
                                           DomainInformation);
            break;

        case DomainLogoffInformation:
            Status = SampSetDomainLogoff(DomainObject,
                                         DomainInformation);
            break;

        case DomainOemInformation:
            Status = SampSetObjectAttributeString(DomainObject,
                                                  L"OemInformation",
                                                  &DomainInformation->Oem.OemInformation);
            break;

        case DomainNameInformation:
            Status = SampSetObjectAttributeString(DomainObject,
                                                  L"Name",
                                                  &DomainInformation->Name.DomainName);
            break;

        case DomainReplicationInformation:
            Status = SampSetObjectAttributeString(DomainObject,
                                                  L"ReplicaSourceNodeName",
                                                  &DomainInformation->Replication.ReplicaSourceNodeName);
            break;

        case DomainServerRoleInformation:
            Status = SampSetDomainServerRole(DomainObject,
                                             DomainInformation);
            break;

        case DomainStateInformation:
            Status = SampSetDomainState(DomainObject,
                                        DomainInformation);
            break;

        case DomainLockoutInformation:
            Status = SampSetDomainLockout(DomainObject,
                                          DomainInformation);
            break;

        default:
            Status = STATUS_NOT_IMPLEMENTED;
    }

done:
    RtlReleaseResource(&SampResource);

    return Status;
}


/* Function 10 */
NTSTATUS
NTAPI
SamrCreateGroupInDomain(IN SAMPR_HANDLE DomainHandle,
                        IN PRPC_UNICODE_STRING Name,
                        IN ACCESS_MASK DesiredAccess,
                        OUT SAMPR_HANDLE *GroupHandle,
                        OUT unsigned long *RelativeId)
{
    SAM_DOMAIN_FIXED_DATA FixedDomainData;
    SAM_GROUP_FIXED_DATA FixedGroupData;
    PSAM_DB_OBJECT DomainObject;
    PSAM_DB_OBJECT GroupObject;
    PSECURITY_DESCRIPTOR Sd = NULL;
    ULONG SdSize = 0;
    ULONG ulSize;
    ULONG ulRid;
    WCHAR szRid[9];
    NTSTATUS Status;

    TRACE("SamrCreateGroupInDomain(%p %p %lx %p %p)\n",
          DomainHandle, Name, DesiredAccess, GroupHandle, RelativeId);

    /* Map generic access rights */
    RtlMapGenericMask(&DesiredAccess,
                      &GroupMapping);

    RtlAcquireResourceExclusive(&SampResource,
                                TRUE);

    /* Validate the domain handle */
    Status = SampValidateDbObject(DomainHandle,
                                  SamDbDomainObject,
                                  DOMAIN_CREATE_GROUP,
                                  &DomainObject);
    if (!NT_SUCCESS(Status))
    {
        TRACE("failed with status 0x%08lx\n", Status);
        goto done;
    }

    /* Check the group account name */
    Status = SampCheckAccountName(Name, 256);
    if (!NT_SUCCESS(Status))
    {
        TRACE("SampCheckAccountName failed (Status 0x%08lx)\n", Status);
        goto done;
    }

    /* Check if the group name already exists in the domain */
    Status = SampCheckAccountNameInDomain(DomainObject,
                                          Name->Buffer);
    if (!NT_SUCCESS(Status))
    {
        TRACE("Group name \'%S\' already exists in domain (Status 0x%08lx)\n",
              Name->Buffer, Status);
        goto done;
    }

    /* Create the security descriptor */
    Status = SampCreateGroupSD(&Sd,
                               &SdSize);
    if (!NT_SUCCESS(Status))
    {
        TRACE("SampCreateGroupSD failed (Status 0x%08lx)\n", Status);
        goto done;
    }

    /* Get the fixed domain attributes */
    ulSize = sizeof(SAM_DOMAIN_FIXED_DATA);
    Status = SampGetObjectAttribute(DomainObject,
                                    L"F",
                                    NULL,
                                    (PVOID)&FixedDomainData,
                                    &ulSize);
    if (!NT_SUCCESS(Status))
    {
        TRACE("failed with status 0x%08lx\n", Status);
        goto done;
    }

    /* Increment the NextRid attribute */
    ulRid = FixedDomainData.NextRid;
    FixedDomainData.NextRid++;

    /* Store the fixed domain attributes */
    Status = SampSetObjectAttribute(DomainObject,
                                    L"F",
                                    REG_BINARY,
                                    &FixedDomainData,
                                    ulSize);
    if (!NT_SUCCESS(Status))
    {
        TRACE("failed with status 0x%08lx\n", Status);
        goto done;
    }

    TRACE("RID: %lx\n", ulRid);

    /* Convert the RID into a string (hex) */
    swprintf(szRid, L"%08lX", ulRid);

    /* Create the group object */
    Status = SampCreateDbObject(DomainObject,
                                L"Groups",
                                szRid,
                                ulRid,
                                SamDbGroupObject,
                                DesiredAccess,
                                &GroupObject);
    if (!NT_SUCCESS(Status))
    {
        TRACE("failed with status 0x%08lx\n", Status);
        goto done;
    }

    /* Add the account name of the user object */
    Status = SampSetAccountNameInDomain(DomainObject,
                                        L"Groups",
                                        Name->Buffer,
                                        ulRid);
    if (!NT_SUCCESS(Status))
    {
        TRACE("failed with status 0x%08lx\n", Status);
        goto done;
    }

    /* Initialize fixed user data */
    memset(&FixedGroupData, 0, sizeof(SAM_GROUP_FIXED_DATA));
    FixedGroupData.Version = 1;
    FixedGroupData.GroupId = ulRid;

    /* Set fixed user data attribute */
    Status = SampSetObjectAttribute(GroupObject,
                                    L"F",
                                    REG_BINARY,
                                    (LPVOID)&FixedGroupData,
                                    sizeof(SAM_GROUP_FIXED_DATA));
    if (!NT_SUCCESS(Status))
    {
        TRACE("failed with status 0x%08lx\n", Status);
        goto done;
    }

    /* Set the Name attribute */
    Status = SampSetObjectAttributeString(GroupObject,
                                          L"Name",
                                          Name);
    if (!NT_SUCCESS(Status))
    {
        TRACE("failed with status 0x%08lx\n", Status);
        goto done;
    }

    /* Set the AdminComment attribute */
    Status = SampSetObjectAttributeString(GroupObject,
                                          L"AdminComment",
                                          NULL);
    if (!NT_SUCCESS(Status))
    {
        TRACE("failed with status 0x%08lx\n", Status);
        goto done;
    }

    /* Set the SecDesc attribute*/
    Status = SampSetObjectAttribute(GroupObject,
                                    L"SecDesc",
                                    REG_BINARY,
                                    Sd,
                                    SdSize);
    if (!NT_SUCCESS(Status))
    {
        TRACE("failed with status 0x%08lx\n", Status);
        goto done;
    }

    if (NT_SUCCESS(Status))
    {
        *GroupHandle = (SAMPR_HANDLE)GroupObject;
        *RelativeId = ulRid;
    }

done:
    if (Sd != NULL)
        RtlFreeHeap(RtlGetProcessHeap(), 0, Sd);

    RtlReleaseResource(&SampResource);

    TRACE("returns with status 0x%08lx\n", Status);

    return Status;
}


/* Function 11 */
NTSTATUS
NTAPI
SamrEnumerateGroupsInDomain(IN SAMPR_HANDLE DomainHandle,
                            IN OUT unsigned long *EnumerationContext,
                            OUT PSAMPR_ENUMERATION_BUFFER *Buffer,
                            IN unsigned long PreferedMaximumLength,
                            OUT unsigned long *CountReturned)
{
    PSAMPR_ENUMERATION_BUFFER EnumBuffer = NULL;
    PSAM_DB_OBJECT DomainObject;
    HANDLE GroupsKeyHandle = NULL;
    HANDLE NamesKeyHandle = NULL;
    WCHAR GroupName[64];
    ULONG EnumIndex;
    ULONG EnumCount = 0;
    ULONG RequiredLength = 0;
    ULONG NameLength;
    ULONG DataLength;
    ULONG Rid;
    ULONG i;
    BOOLEAN MoreEntries = FALSE;
    NTSTATUS Status;

    TRACE("SamrEnumerateUsersInDomain(%p %p %p %lu %p)\n",
          DomainHandle, EnumerationContext, Buffer,
          PreferedMaximumLength, CountReturned);

    RtlAcquireResourceShared(&SampResource,
                             TRUE);

    /* Validate the domain handle */
    Status = SampValidateDbObject(DomainHandle,
                                  SamDbDomainObject,
                                  DOMAIN_LIST_ACCOUNTS,
                                  &DomainObject);
    if (!NT_SUCCESS(Status))
        goto done;

    Status = SampRegOpenKey(DomainObject->KeyHandle,
                            L"Groups",
                            KEY_READ,
                            &GroupsKeyHandle);
    if (!NT_SUCCESS(Status))
        goto done;

    Status = SampRegOpenKey(GroupsKeyHandle,
                            L"Names",
                            KEY_READ,
                            &NamesKeyHandle);
    if (!NT_SUCCESS(Status))
        goto done;

    TRACE("Part 1\n");

    EnumIndex = *EnumerationContext;

    while (TRUE)
    {
        NameLength = 64 * sizeof(WCHAR);
        Status = SampRegEnumerateValue(NamesKeyHandle,
                                       EnumIndex,
                                       GroupName,
                                       &NameLength,
                                       NULL,
                                       NULL,
                                       NULL);
        if (!NT_SUCCESS(Status))
        {
            if (Status == STATUS_NO_MORE_ENTRIES)
                Status = STATUS_SUCCESS;
            break;
        }

        TRACE("EnumIndex: %lu\n", EnumIndex);
        TRACE("Group name: %S\n", GroupName);
        TRACE("Name length: %lu\n", NameLength);

        if ((RequiredLength + NameLength + sizeof(UNICODE_NULL) + sizeof(SAMPR_RID_ENUMERATION)) > PreferedMaximumLength)
        {
            MoreEntries = TRUE;
            break;
        }

        RequiredLength += (NameLength + sizeof(UNICODE_NULL) + sizeof(SAMPR_RID_ENUMERATION));
        EnumCount++;

        EnumIndex++;
    }

    TRACE("EnumCount: %lu\n", EnumCount);
    TRACE("RequiredLength: %lu\n", RequiredLength);

    if (!NT_SUCCESS(Status))
        goto done;

    EnumBuffer = midl_user_allocate(sizeof(SAMPR_ENUMERATION_BUFFER));
    if (EnumBuffer == NULL)
    {
        Status = STATUS_INSUFFICIENT_RESOURCES;
        goto done;
    }

    EnumBuffer->EntriesRead = EnumCount;
    if (EnumCount == 0)
    {
        Status = STATUS_NO_MORE_ENTRIES;
        goto done;
    }

    EnumBuffer->Buffer = midl_user_allocate(EnumCount * sizeof(SAMPR_RID_ENUMERATION));
    if (EnumBuffer->Buffer == NULL)
    {
        Status = STATUS_INSUFFICIENT_RESOURCES;
        goto done;
    }

    TRACE("Part 2\n");

    EnumIndex = *EnumerationContext;
    for (i = 0; i < EnumCount; i++, EnumIndex++)
    {
        NameLength = 64 * sizeof(WCHAR);
        DataLength = sizeof(ULONG);
        Status = SampRegEnumerateValue(NamesKeyHandle,
                                       EnumIndex,
                                       GroupName,
                                       &NameLength,
                                       NULL,
                                       &Rid,
                                       &DataLength);
        if (!NT_SUCCESS(Status))
        {
            if (Status == STATUS_NO_MORE_ENTRIES)
                Status = STATUS_SUCCESS;
            break;
        }

        TRACE("EnumIndex: %lu\n", EnumIndex);
        TRACE("Group name: %S\n", GroupName);
        TRACE("Name length: %lu\n", NameLength);
        TRACE("RID: %lu\n", Rid);

        EnumBuffer->Buffer[i].RelativeId = Rid;

        EnumBuffer->Buffer[i].Name.Length = (USHORT)NameLength;
        EnumBuffer->Buffer[i].Name.MaximumLength = (USHORT)(NameLength + sizeof(UNICODE_NULL));

/* FIXME: Disabled because of bugs in widl and rpcrt4 */
#if 0
        EnumBuffer->Buffer[i].Name.Buffer = midl_user_allocate(EnumBuffer->Buffer[i].Name.MaximumLength);
        if (EnumBuffer->Buffer[i].Name.Buffer == NULL)
        {
            Status = STATUS_INSUFFICIENT_RESOURCES;
            goto done;
        }

        memcpy(EnumBuffer->Buffer[i].Name.Buffer,
               GroupName,
               EnumBuffer->Buffer[i].Name.Length);
#endif
    }

done:
    if (NT_SUCCESS(Status))
    {
        *EnumerationContext += EnumCount;
        *Buffer = EnumBuffer;
        *CountReturned = EnumCount;
    }
    else
    {
        *EnumerationContext = 0;
        *Buffer = NULL;
        *CountReturned = 0;

        if (EnumBuffer != NULL)
        {
            if (EnumBuffer->Buffer != NULL)
            {
                if (EnumBuffer->EntriesRead != 0)
                {
                    for (i = 0; i < EnumBuffer->EntriesRead; i++)
                    {
                        if (EnumBuffer->Buffer[i].Name.Buffer != NULL)
                            midl_user_free(EnumBuffer->Buffer[i].Name.Buffer);
                    }
                }

                midl_user_free(EnumBuffer->Buffer);
            }

            midl_user_free(EnumBuffer);
        }
    }

    SampRegCloseKey(&NamesKeyHandle);
    SampRegCloseKey(&GroupsKeyHandle);

    if ((Status == STATUS_SUCCESS) && (MoreEntries != FALSE))
        Status = STATUS_MORE_ENTRIES;

    RtlReleaseResource(&SampResource);

    return Status;
}


/* Function 12 */
NTSTATUS
NTAPI
SamrCreateUserInDomain(IN SAMPR_HANDLE DomainHandle,
                       IN PRPC_UNICODE_STRING Name,
                       IN ACCESS_MASK DesiredAccess,
                       OUT SAMPR_HANDLE *UserHandle,
                       OUT unsigned long *RelativeId)
{
    SAM_DOMAIN_FIXED_DATA FixedDomainData;
    SAM_USER_FIXED_DATA FixedUserData;
    PSAM_DB_OBJECT DomainObject;
    PSAM_DB_OBJECT UserObject;
    GROUP_MEMBERSHIP GroupMembership;
    UCHAR LogonHours[23];
    ULONG ulSize;
    ULONG ulRid;
    WCHAR szRid[9];
    PSECURITY_DESCRIPTOR Sd = NULL;
    ULONG SdSize = 0;
    PSID UserSid = NULL;
    NTSTATUS Status;

    TRACE("SamrCreateUserInDomain(%p %p %lx %p %p)\n",
          DomainHandle, Name, DesiredAccess, UserHandle, RelativeId);

    if (Name == NULL ||
        Name->Length == 0 ||
        Name->Buffer == NULL ||
        UserHandle == NULL ||
        RelativeId == NULL)
        return STATUS_INVALID_PARAMETER;

    /* Map generic access rights */
    RtlMapGenericMask(&DesiredAccess,
                      &UserMapping);

    RtlAcquireResourceExclusive(&SampResource,
                                TRUE);

    /* Validate the domain handle */
    Status = SampValidateDbObject(DomainHandle,
                                  SamDbDomainObject,
                                  DOMAIN_CREATE_USER,
                                  &DomainObject);
    if (!NT_SUCCESS(Status))
    {
        TRACE("failed with status 0x%08lx\n", Status);
        goto done;
    }

    /* Check the user account name */
    Status = SampCheckAccountName(Name, 20);
    if (!NT_SUCCESS(Status))
    {
        TRACE("SampCheckAccountName failed (Status 0x%08lx)\n", Status);
        goto done;
    }

    /* Check if the user name already exists in the domain */
    Status = SampCheckAccountNameInDomain(DomainObject,
                                          Name->Buffer);
    if (!NT_SUCCESS(Status))
    {
        TRACE("User name \'%S\' already exists in domain (Status 0x%08lx)\n",
              Name->Buffer, Status);
        goto done;
    }

    /* Get the fixed domain attributes */
    ulSize = sizeof(SAM_DOMAIN_FIXED_DATA);
    Status = SampGetObjectAttribute(DomainObject,
                                    L"F",
                                    NULL,
                                    (PVOID)&FixedDomainData,
                                    &ulSize);
    if (!NT_SUCCESS(Status))
    {
        TRACE("failed with status 0x%08lx\n", Status);
        goto done;
    }

    /* Increment the NextRid attribute */
    ulRid = FixedDomainData.NextRid;
    FixedDomainData.NextRid++;

    TRACE("RID: %lx\n", ulRid);

    /* Create the user SID */
    Status = SampCreateAccountSid(DomainObject,
                                  ulRid,
                                  &UserSid);
    if (!NT_SUCCESS(Status))
    {
        TRACE("SampCreateAccountSid failed (Status 0x%08lx)\n", Status);
        goto done;
    }

    /* Create the security descriptor */
    Status = SampCreateUserSD(UserSid,
                              &Sd,
                              &SdSize);
    if (!NT_SUCCESS(Status))
    {
        TRACE("SampCreateUserSD failed (Status 0x%08lx)\n", Status);
        goto done;
    }

    /* Store the fixed domain attributes */
    Status = SampSetObjectAttribute(DomainObject,
                                    L"F",
                                    REG_BINARY,
                                    &FixedDomainData,
                                    ulSize);
    if (!NT_SUCCESS(Status))
    {
        TRACE("failed with status 0x%08lx\n", Status);
        goto done;
    }

    /* Convert the RID into a string (hex) */
    swprintf(szRid, L"%08lX", ulRid);

    /* Create the user object */
    Status = SampCreateDbObject(DomainObject,
                                L"Users",
                                szRid,
                                ulRid,
                                SamDbUserObject,
                                DesiredAccess,
                                &UserObject);
    if (!NT_SUCCESS(Status))
    {
        TRACE("failed with status 0x%08lx\n", Status);
        goto done;
    }

    /* Add the account name for the user object */
    Status = SampSetAccountNameInDomain(DomainObject,
                                        L"Users",
                                        Name->Buffer,
                                        ulRid);
    if (!NT_SUCCESS(Status))
    {
        TRACE("failed with status 0x%08lx\n", Status);
        goto done;
    }

    /* Initialize fixed user data */
    memset(&FixedUserData, 0, sizeof(SAM_USER_FIXED_DATA));
    FixedUserData.Version = 1;
    FixedUserData.Reserved = 0;
    FixedUserData.LastLogon.QuadPart = 0;
    FixedUserData.LastLogoff.QuadPart = 0;
    FixedUserData.PasswordLastSet.QuadPart = 0;
    FixedUserData.AccountExpires.QuadPart = MAXLONGLONG;
    FixedUserData.LastBadPasswordTime.QuadPart = 0;
    FixedUserData.UserId = ulRid;
    FixedUserData.PrimaryGroupId = DOMAIN_GROUP_RID_USERS;
    FixedUserData.UserAccountControl = USER_ACCOUNT_DISABLED |
                                       USER_PASSWORD_NOT_REQUIRED |
                                       USER_NORMAL_ACCOUNT;
    FixedUserData.CountryCode = 0;
    FixedUserData.CodePage = 0;
    FixedUserData.BadPasswordCount = 0;
    FixedUserData.LogonCount = 0;
    FixedUserData.AdminCount = 0;
    FixedUserData.OperatorCount = 0;

    /* Set fixed user data attribute */
    Status = SampSetObjectAttribute(UserObject,
                                    L"F",
                                    REG_BINARY,
                                    (LPVOID)&FixedUserData,
                                    sizeof(SAM_USER_FIXED_DATA));
    if (!NT_SUCCESS(Status))
    {
        TRACE("failed with status 0x%08lx\n", Status);
        goto done;
    }

    /* Set the Name attribute */
    Status = SampSetObjectAttributeString(UserObject,
                                          L"Name",
                                          Name);
    if (!NT_SUCCESS(Status))
    {
        TRACE("failed with status 0x%08lx\n", Status);
        goto done;
    }

    /* Set the FullName attribute */
    Status = SampSetObjectAttributeString(UserObject,
                                          L"FullName",
                                          NULL);
    if (!NT_SUCCESS(Status))
    {
        TRACE("failed with status 0x%08lx\n", Status);
        goto done;
    }

    /* Set the HomeDirectory attribute */
    Status = SampSetObjectAttributeString(UserObject,
                                          L"HomeDirectory",
                                          NULL);
    if (!NT_SUCCESS(Status))
    {
        TRACE("failed with status 0x%08lx\n", Status);
        goto done;
    }

    /* Set the HomeDirectoryDrive attribute */
    Status = SampSetObjectAttributeString(UserObject,
                                          L"HomeDirectoryDrive",
                                          NULL);
    if (!NT_SUCCESS(Status))
    {
        TRACE("failed with status 0x%08lx\n", Status);
        goto done;
    }

    /* Set the ScriptPath attribute */
    Status = SampSetObjectAttributeString(UserObject,
                                          L"ScriptPath",
                                          NULL);
    if (!NT_SUCCESS(Status))
    {
        TRACE("failed with status 0x%08lx\n", Status);
        goto done;
    }

    /* Set the ProfilePath attribute */
    Status = SampSetObjectAttributeString(UserObject,
                                          L"ProfilePath",
                                          NULL);
    if (!NT_SUCCESS(Status))
    {
        TRACE("failed with status 0x%08lx\n", Status);
        goto done;
    }

    /* Set the AdminComment attribute */
    Status = SampSetObjectAttributeString(UserObject,
                                          L"AdminComment",
                                          NULL);
    if (!NT_SUCCESS(Status))
    {
        TRACE("failed with status 0x%08lx\n", Status);
        goto done;
    }

    /* Set the UserComment attribute */
    Status = SampSetObjectAttributeString(UserObject,
                                          L"UserComment",
                                          NULL);
    if (!NT_SUCCESS(Status))
    {
        TRACE("failed with status 0x%08lx\n", Status);
        goto done;
    }

    /* Set the WorkStations attribute */
    Status = SampSetObjectAttributeString(UserObject,
                                          L"WorkStations",
                                          NULL);
    if (!NT_SUCCESS(Status))
    {
        TRACE("failed with status 0x%08lx\n", Status);
        goto done;
    }

    /* Set the Parameters attribute */
    Status = SampSetObjectAttributeString(UserObject,
                                          L"Parameters",
                                          NULL);
    if (!NT_SUCCESS(Status))
    {
        TRACE("failed with status 0x%08lx\n", Status);
        goto done;
    }

    /* Set LogonHours attribute*/
    *((PUSHORT)LogonHours) = 168;
    memset(&(LogonHours[2]), 0xff, 21);

    Status = SampSetObjectAttribute(UserObject,
                                    L"LogonHours",
                                    REG_BINARY,
                                    &LogonHours,
                                    sizeof(LogonHours));
    if (!NT_SUCCESS(Status))
    {
        TRACE("failed with status 0x%08lx\n", Status);
        goto done;
    }

    /* Set Groups attribute*/
    GroupMembership.RelativeId = DOMAIN_GROUP_RID_USERS;
    GroupMembership.Attributes = SE_GROUP_MANDATORY |
                                 SE_GROUP_ENABLED |
                                 SE_GROUP_ENABLED_BY_DEFAULT;

    Status = SampSetObjectAttribute(UserObject,
                                    L"Groups",
                                    REG_BINARY,
                                    &GroupMembership,
                                    sizeof(GROUP_MEMBERSHIP));
    if (!NT_SUCCESS(Status))
    {
        TRACE("failed with status 0x%08lx\n", Status);
        goto done;
    }

    /* Set LMPwd attribute*/
    Status = SampSetObjectAttribute(UserObject,
                                    L"LMPwd",
                                    REG_BINARY,
                                    &EmptyLmHash,
                                    sizeof(ENCRYPTED_LM_OWF_PASSWORD));
    if (!NT_SUCCESS(Status))
    {
        TRACE("failed with status 0x%08lx\n", Status);
        goto done;
    }

    /* Set NTPwd attribute*/
    Status = SampSetObjectAttribute(UserObject,
                                    L"NTPwd",
                                    REG_BINARY,
                                    &EmptyNtHash,
                                    sizeof(ENCRYPTED_NT_OWF_PASSWORD));
    if (!NT_SUCCESS(Status))
    {
        TRACE("failed with status 0x%08lx\n", Status);
        goto done;
    }

    /* Set LMPwdHistory attribute*/
    Status = SampSetObjectAttribute(UserObject,
                                    L"LMPwdHistory",
                                    REG_BINARY,
                                    NULL,
                                    0);
    if (!NT_SUCCESS(Status))
    {
        TRACE("failed with status 0x%08lx\n", Status);
        goto done;
    }

    /* Set NTPwdHistory attribute*/
    Status = SampSetObjectAttribute(UserObject,
                                    L"NTPwdHistory",
                                    REG_BINARY,
                                    NULL,
                                    0);
    if (!NT_SUCCESS(Status))
    {
        TRACE("failed with status 0x%08lx\n", Status);
        goto done;
    }

    /* Set the PrivateData attribute */
    Status = SampSetObjectAttributeString(UserObject,
                                          L"PrivateData",
                                          NULL);
    if (!NT_SUCCESS(Status))
    {
        TRACE("failed with status 0x%08lx\n", Status);
        goto done;
    }

    /* Set the SecDesc attribute*/
    Status = SampSetObjectAttribute(UserObject,
                                    L"SecDesc",
                                    REG_BINARY,
                                    Sd,
                                    SdSize);
    if (!NT_SUCCESS(Status))
    {
        TRACE("failed with status 0x%08lx\n", Status);
        goto done;
    }

    if (NT_SUCCESS(Status))
    {
        *UserHandle = (SAMPR_HANDLE)UserObject;
        *RelativeId = ulRid;
    }

done:
    if (Sd != NULL)
        RtlFreeHeap(RtlGetProcessHeap(), 0, Sd);

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

    RtlReleaseResource(&SampResource);

    TRACE("returns with status 0x%08lx\n", Status);

    return Status;
}


/* Function 13 */
NTSTATUS
NTAPI
SamrEnumerateUsersInDomain(IN SAMPR_HANDLE DomainHandle,
                           IN OUT unsigned long *EnumerationContext,
                           IN unsigned long UserAccountControl,
                           OUT PSAMPR_ENUMERATION_BUFFER *Buffer,
                           IN unsigned long PreferedMaximumLength,
                           OUT unsigned long *CountReturned)
{
    PSAMPR_ENUMERATION_BUFFER EnumBuffer = NULL;
    PSAM_DB_OBJECT DomainObject;
    HANDLE UsersKeyHandle = NULL;
    HANDLE NamesKeyHandle = NULL;
    WCHAR UserName[64];
    ULONG EnumIndex;
    ULONG EnumCount = 0;
    ULONG RequiredLength = 0;
    ULONG NameLength;
    ULONG DataLength;
    ULONG Rid;
    ULONG i;
    BOOLEAN MoreEntries = FALSE;
    NTSTATUS Status;

    TRACE("SamrEnumerateUsersInDomain(%p %p %lx %p %lu %p)\n",
          DomainHandle, EnumerationContext, UserAccountControl, Buffer,
          PreferedMaximumLength, CountReturned);

    RtlAcquireResourceShared(&SampResource,
                             TRUE);

    /* Validate the domain handle */
    Status = SampValidateDbObject(DomainHandle,
                                  SamDbDomainObject,
                                  DOMAIN_LIST_ACCOUNTS,
                                  &DomainObject);
    if (!NT_SUCCESS(Status))
        goto done;

    Status = SampRegOpenKey(DomainObject->KeyHandle,
                            L"Users",
                            KEY_READ,
                            &UsersKeyHandle);
    if (!NT_SUCCESS(Status))
        goto done;

    Status = SampRegOpenKey(UsersKeyHandle,
                            L"Names",
                            KEY_READ,
                            &NamesKeyHandle);
    if (!NT_SUCCESS(Status))
        goto done;

    TRACE("Part 1\n");

    EnumIndex = *EnumerationContext;

    while (TRUE)
    {
        NameLength = 64 * sizeof(WCHAR);
        Status = SampRegEnumerateValue(NamesKeyHandle,
                                       EnumIndex,
                                       UserName,
                                       &NameLength,
                                       NULL,
                                       NULL,
                                       NULL);
        if (!NT_SUCCESS(Status))
        {
            if (Status == STATUS_NO_MORE_ENTRIES)
                Status = STATUS_SUCCESS;
            break;
        }

        TRACE("EnumIndex: %lu\n", EnumIndex);
        TRACE("User name: %S\n", UserName);
        TRACE("Name length: %lu\n", NameLength);

        if ((RequiredLength + NameLength + sizeof(UNICODE_NULL) + sizeof(SAMPR_RID_ENUMERATION)) > PreferedMaximumLength)
        {
            MoreEntries = TRUE;
            break;
        }

        RequiredLength += (NameLength + sizeof(UNICODE_NULL) + sizeof(SAMPR_RID_ENUMERATION));
        EnumCount++;

        EnumIndex++;
    }

    TRACE("EnumCount: %lu\n", EnumCount);
    TRACE("RequiredLength: %lu\n", RequiredLength);

    if (!NT_SUCCESS(Status))
        goto done;

    EnumBuffer = midl_user_allocate(sizeof(SAMPR_ENUMERATION_BUFFER));
    if (EnumBuffer == NULL)
    {
        Status = STATUS_INSUFFICIENT_RESOURCES;
        goto done;
    }

    EnumBuffer->EntriesRead = EnumCount;
    if (EnumCount == 0)
    {
        Status = STATUS_NO_MORE_ENTRIES;
        goto done;
    }

    EnumBuffer->Buffer = midl_user_allocate(EnumCount * sizeof(SAMPR_RID_ENUMERATION));
    if (EnumBuffer->Buffer == NULL)
    {
        Status = STATUS_INSUFFICIENT_RESOURCES;
        goto done;
    }

    TRACE("Part 2\n");

    EnumIndex = *EnumerationContext;
    for (i = 0; i < EnumCount; i++, EnumIndex++)
    {
        NameLength = 64 * sizeof(WCHAR);
        DataLength = sizeof(ULONG);
        Status = SampRegEnumerateValue(NamesKeyHandle,
                                       EnumIndex,
                                       UserName,
                                       &NameLength,
                                       NULL,
                                       &Rid,
                                       &DataLength);
        if (!NT_SUCCESS(Status))
        {
            if (Status == STATUS_NO_MORE_ENTRIES)
                Status = STATUS_SUCCESS;
            break;
        }

        TRACE("EnumIndex: %lu\n", EnumIndex);
        TRACE("User name: %S\n", UserName);
        TRACE("Name length: %lu\n", NameLength);
        TRACE("RID: %lu\n", Rid);

        EnumBuffer->Buffer[i].RelativeId = Rid;

        EnumBuffer->Buffer[i].Name.Length = (USHORT)NameLength;
        EnumBuffer->Buffer[i].Name.MaximumLength = (USHORT)(NameLength + sizeof(UNICODE_NULL));

/* FIXME: Disabled because of bugs in widl and rpcrt4 */
#if 0
        EnumBuffer->Buffer[i].Name.Buffer = midl_user_allocate(EnumBuffer->Buffer[i].Name.MaximumLength);
        if (EnumBuffer->Buffer[i].Name.Buffer == NULL)
        {
            Status = STATUS_INSUFFICIENT_RESOURCES;
            goto done;
        }

        memcpy(EnumBuffer->Buffer[i].Name.Buffer,
               UserName,
               EnumBuffer->Buffer[i].Name.Length);
#endif
    }

done:
    if (NT_SUCCESS(Status))
    {
        *EnumerationContext += EnumCount;
        *Buffer = EnumBuffer;
        *CountReturned = EnumCount;
    }
    else
    {
        *EnumerationContext = 0;
        *Buffer = NULL;
        *CountReturned = 0;

        if (EnumBuffer != NULL)
        {
            if (EnumBuffer->Buffer != NULL)
            {
                if (EnumBuffer->EntriesRead != 0)
                {
                    for (i = 0; i < EnumBuffer->EntriesRead; i++)
                    {
                        if (EnumBuffer->Buffer[i].Name.Buffer != NULL)
                            midl_user_free(EnumBuffer->Buffer[i].Name.Buffer);
                    }
                }

                midl_user_free(EnumBuffer->Buffer);
            }

            midl_user_free(EnumBuffer);
        }
    }

    SampRegCloseKey(&NamesKeyHandle);
    SampRegCloseKey(&UsersKeyHandle);

    if ((Status == STATUS_SUCCESS) && (MoreEntries != FALSE))
        Status = STATUS_MORE_ENTRIES;

    RtlReleaseResource(&SampResource);

    return Status;
}


/* Function 14 */
NTSTATUS
NTAPI
SamrCreateAliasInDomain(IN SAMPR_HANDLE DomainHandle,
                        IN PRPC_UNICODE_STRING AccountName,
                        IN ACCESS_MASK DesiredAccess,
                        OUT SAMPR_HANDLE *AliasHandle,
                        OUT unsigned long *RelativeId)
{
    SAM_DOMAIN_FIXED_DATA FixedDomainData;
    PSAM_DB_OBJECT DomainObject;
    PSAM_DB_OBJECT AliasObject;
    PSECURITY_DESCRIPTOR Sd = NULL;
    ULONG SdSize = 0;
    ULONG ulSize;
    ULONG ulRid;
    WCHAR szRid[9];
    NTSTATUS Status;

    TRACE("SamrCreateAliasInDomain(%p %p %lx %p %p)\n",
          DomainHandle, AccountName, DesiredAccess, AliasHandle, RelativeId);

    /* Map generic access rights */
    RtlMapGenericMask(&DesiredAccess,
                      &AliasMapping);

    RtlAcquireResourceExclusive(&SampResource,
                                TRUE);

    /* Validate the domain handle */
    Status = SampValidateDbObject(DomainHandle,
                                  SamDbDomainObject,
                                  DOMAIN_CREATE_ALIAS,
                                  &DomainObject);
    if (!NT_SUCCESS(Status))
    {
        TRACE("failed with status 0x%08lx\n", Status);
        goto done;
    }

    /* Check the alias account name */
    Status = SampCheckAccountName(AccountName, 256);
    if (!NT_SUCCESS(Status))
    {
        TRACE("SampCheckAccountName failed (Status 0x%08lx)\n", Status);
        goto done;
    }

    /* Check if the alias name already exists in the domain */
    Status = SampCheckAccountNameInDomain(DomainObject,
                                          AccountName->Buffer);
    if (!NT_SUCCESS(Status))
    {
        TRACE("Alias name \'%S\' already exists in domain (Status 0x%08lx)\n",
              AccountName->Buffer, Status);
        goto done;
    }

    /* Create the security descriptor */
    Status = SampCreateAliasSD(&Sd,
                               &SdSize);
    if (!NT_SUCCESS(Status))
    {
        TRACE("SampCreateAliasSD failed (Status 0x%08lx)\n", Status);
        goto done;
    }

    /* Get the fixed domain attributes */
    ulSize = sizeof(SAM_DOMAIN_FIXED_DATA);
    Status = SampGetObjectAttribute(DomainObject,
                                    L"F",
                                    NULL,
                                    (PVOID)&FixedDomainData,
                                    &ulSize);
    if (!NT_SUCCESS(Status))
    {
        TRACE("failed with status 0x%08lx\n", Status);
        goto done;
    }

    /* Increment the NextRid attribute */
    ulRid = FixedDomainData.NextRid;
    FixedDomainData.NextRid++;

    /* Store the fixed domain attributes */
    Status = SampSetObjectAttribute(DomainObject,
                           L"F",
                           REG_BINARY,
                           &FixedDomainData,
                           ulSize);
    if (!NT_SUCCESS(Status))
    {
        TRACE("failed with status 0x%08lx\n", Status);
        goto done;
    }

    TRACE("RID: %lx\n", ulRid);

    /* Convert the RID into a string (hex) */
    swprintf(szRid, L"%08lX", ulRid);

    /* Create the alias object */
    Status = SampCreateDbObject(DomainObject,
                                L"Aliases",
                                szRid,
                                ulRid,
                                SamDbAliasObject,
                                DesiredAccess,
                                &AliasObject);
    if (!NT_SUCCESS(Status))
    {
        TRACE("failed with status 0x%08lx\n", Status);
        goto done;
    }

    /* Add the account name for the alias object */
    Status = SampSetAccountNameInDomain(DomainObject,
                                        L"Aliases",
                                        AccountName->Buffer,
                                        ulRid);
    if (!NT_SUCCESS(Status))
    {
        TRACE("failed with status 0x%08lx\n", Status);
        goto done;
    }

    /* Set the Name attribute */
    Status = SampSetObjectAttributeString(AliasObject,
                                          L"Name",
                                          AccountName);
    if (!NT_SUCCESS(Status))
    {
        TRACE("failed with status 0x%08lx\n", Status);
        goto done;
    }

    /* Set the Description attribute */
    Status = SampSetObjectAttributeString(AliasObject,
                                          L"Description",
                                          NULL);
    if (!NT_SUCCESS(Status))
    {
        TRACE("failed with status 0x%08lx\n", Status);
        goto done;
    }

    /* Set the SecDesc attribute*/
    Status = SampSetObjectAttribute(AliasObject,
                                    L"SecDesc",
                                    REG_BINARY,
                                    Sd,
                                    SdSize);
    if (!NT_SUCCESS(Status))
    {
        TRACE("failed with status 0x%08lx\n", Status);
        goto done;
    }

    if (NT_SUCCESS(Status))
    {
        *AliasHandle = (SAMPR_HANDLE)AliasObject;
        *RelativeId = ulRid;
    }

done:
    if (Sd != NULL)
        RtlFreeHeap(RtlGetProcessHeap(), 0, Sd);

    RtlReleaseResource(&SampResource);

    TRACE("returns with status 0x%08lx\n", Status);

    return Status;
}


/* Function 15 */
NTSTATUS
NTAPI
SamrEnumerateAliasesInDomain(IN SAMPR_HANDLE DomainHandle,
                             IN OUT unsigned long *EnumerationContext,
                             OUT PSAMPR_ENUMERATION_BUFFER *Buffer,
                             IN unsigned long PreferedMaximumLength,
                             OUT unsigned long *CountReturned)
{
    PSAMPR_ENUMERATION_BUFFER EnumBuffer = NULL;
    PSAM_DB_OBJECT DomainObject;
    HANDLE AliasesKeyHandle = NULL;
    HANDLE NamesKeyHandle = NULL;
    WCHAR AliasName[64];
    ULONG EnumIndex;
    ULONG EnumCount = 0;
    ULONG RequiredLength = 0;
    ULONG NameLength;
    ULONG DataLength;
    ULONG Rid;
    ULONG i;
    BOOLEAN MoreEntries = FALSE;
    NTSTATUS Status;

    TRACE("SamrEnumerateAliasesInDomain(%p %p %p %lu %p)\n",
          DomainHandle, EnumerationContext, Buffer,
          PreferedMaximumLength, CountReturned);

    RtlAcquireResourceShared(&SampResource,
                             TRUE);

    /* Validate the domain handle */
    Status = SampValidateDbObject(DomainHandle,
                                  SamDbDomainObject,
                                  DOMAIN_LIST_ACCOUNTS,
                                  &DomainObject);
    if (!NT_SUCCESS(Status))
        goto done;

    Status = SampRegOpenKey(DomainObject->KeyHandle,
                            L"Aliases",
                            KEY_READ,
                            &AliasesKeyHandle);
    if (!NT_SUCCESS(Status))
        goto done;

    Status = SampRegOpenKey(AliasesKeyHandle,
                            L"Names",
                            KEY_READ,
                            &NamesKeyHandle);
    if (!NT_SUCCESS(Status))
        goto done;

    TRACE("Part 1\n");

    EnumIndex = *EnumerationContext;

    while (TRUE)
    {
        NameLength = 64 * sizeof(WCHAR);
        Status = SampRegEnumerateValue(NamesKeyHandle,
                                       EnumIndex,
                                       AliasName,
                                       &NameLength,
                                       NULL,
                                       NULL,
                                       NULL);
        if (!NT_SUCCESS(Status))
        {
            if (Status == STATUS_NO_MORE_ENTRIES)
                Status = STATUS_SUCCESS;
            break;
        }

        TRACE("EnumIndex: %lu\n", EnumIndex);
        TRACE("Alias name: %S\n", AliasName);
        TRACE("Name length: %lu\n", NameLength);

        if ((RequiredLength + NameLength + sizeof(UNICODE_NULL) + sizeof(SAMPR_RID_ENUMERATION)) > PreferedMaximumLength)
        {
            MoreEntries = TRUE;
            break;
        }

        RequiredLength += (NameLength + sizeof(UNICODE_NULL) + sizeof(SAMPR_RID_ENUMERATION));
        EnumCount++;

        EnumIndex++;
    }

    TRACE("EnumCount: %lu\n", EnumCount);
    TRACE("RequiredLength: %lu\n", RequiredLength);

    if (!NT_SUCCESS(Status))
        goto done;

    EnumBuffer = midl_user_allocate(sizeof(SAMPR_ENUMERATION_BUFFER));
    if (EnumBuffer == NULL)
    {
        Status = STATUS_INSUFFICIENT_RESOURCES;
        goto done;
    }

    EnumBuffer->EntriesRead = EnumCount;
    if (EnumCount == 0)
    {
        Status = STATUS_NO_MORE_ENTRIES;
        goto done;
    }

    EnumBuffer->Buffer = midl_user_allocate(EnumCount * sizeof(SAMPR_RID_ENUMERATION));
    if (EnumBuffer->Buffer == NULL)
    {
        Status = STATUS_INSUFFICIENT_RESOURCES;
        goto done;
    }

    TRACE("Part 2\n");

    EnumIndex = *EnumerationContext;
    for (i = 0; i < EnumCount; i++, EnumIndex++)
    {
        NameLength = 64 * sizeof(WCHAR);
        DataLength = sizeof(ULONG);
        Status = SampRegEnumerateValue(NamesKeyHandle,
                                       EnumIndex,
                                       AliasName,
                                       &NameLength,
                                       NULL,
                                       &Rid,
                                       &DataLength);
        if (!NT_SUCCESS(Status))
        {
            if (Status == STATUS_NO_MORE_ENTRIES)
                Status = STATUS_SUCCESS;
            break;
        }

        TRACE("EnumIndex: %lu\n", EnumIndex);
        TRACE("Alias name: %S\n", AliasName);
        TRACE("Name length: %lu\n", NameLength);
        TRACE("RID: %lu\n", Rid);

        EnumBuffer->Buffer[i].RelativeId = Rid;

        EnumBuffer->Buffer[i].Name.Length = (USHORT)NameLength;
        EnumBuffer->Buffer[i].Name.MaximumLength = (USHORT)(NameLength + sizeof(UNICODE_NULL));

/* FIXME: Disabled because of bugs in widl and rpcrt4 */
#if 0
        EnumBuffer->Buffer[i].Name.Buffer = midl_user_allocate(EnumBuffer->Buffer[i].Name.MaximumLength);
        if (EnumBuffer->Buffer[i].Name.Buffer == NULL)
        {
            Status = STATUS_INSUFFICIENT_RESOURCES;
            goto done;
        }

        memcpy(EnumBuffer->Buffer[i].Name.Buffer,
               AliasName,
               EnumBuffer->Buffer[i].Name.Length);
#endif
    }

done:
    if (NT_SUCCESS(Status))
    {
        *EnumerationContext += EnumCount;
        *Buffer = EnumBuffer;
        *CountReturned = EnumCount;
    }
    else
    {
        *EnumerationContext = 0;
        *Buffer = NULL;
        *CountReturned = 0;

        if (EnumBuffer != NULL)
        {
            if (EnumBuffer->Buffer != NULL)
            {
                if (EnumBuffer->EntriesRead != 0)
                {
                    for (i = 0; i < EnumBuffer->EntriesRead; i++)
                    {
                        if (EnumBuffer->Buffer[i].Name.Buffer != NULL)
                            midl_user_free(EnumBuffer->Buffer[i].Name.Buffer);
                    }
                }

                midl_user_free(EnumBuffer->Buffer);
            }

            midl_user_free(EnumBuffer);
        }
    }

    SampRegCloseKey(&NamesKeyHandle);
    SampRegCloseKey(&AliasesKeyHandle);

    if ((Status == STATUS_SUCCESS) && (MoreEntries != FALSE))
        Status = STATUS_MORE_ENTRIES;

    RtlReleaseResource(&SampResource);

    return Status;
}


/* Function 16 */
NTSTATUS
NTAPI
SamrGetAliasMembership(IN SAMPR_HANDLE DomainHandle,
                       IN PSAMPR_PSID_ARRAY SidArray,
                       OUT PSAMPR_ULONG_ARRAY Membership)
{
    PSAM_DB_OBJECT DomainObject;
    HANDLE AliasesKeyHandle = NULL;
    HANDLE MembersKeyHandle = NULL;
    HANDLE MemberKeyHandle = NULL;
    LPWSTR MemberSidString = NULL;
    PULONG RidArray = NULL;
    ULONG MaxSidCount = 0;
    ULONG ValueCount;
    ULONG DataLength;
    ULONG i, j;
    ULONG RidIndex;
    NTSTATUS Status;
    WCHAR NameBuffer[9];

    TRACE("SamrGetAliasMembership(%p %p %p)\n",
          DomainHandle, SidArray, Membership);

    RtlAcquireResourceShared(&SampResource,
                             TRUE);

    /* Validate the domain handle */
    Status = SampValidateDbObject(DomainHandle,
                                  SamDbDomainObject,
                                  DOMAIN_GET_ALIAS_MEMBERSHIP,
                                  &DomainObject);
    if (!NT_SUCCESS(Status))
        goto done;

    Status = SampRegOpenKey(DomainObject->KeyHandle,
                            L"Aliases",
                            KEY_READ,
                            &AliasesKeyHandle);
    TRACE("SampRegOpenKey returned %08lX\n", Status);
    if (!NT_SUCCESS(Status))
        goto done;

    Status = SampRegOpenKey(AliasesKeyHandle,
                            L"Members",
                            KEY_READ,
                            &MembersKeyHandle);
    TRACE("SampRegOpenKey returned %08lX\n", Status);

    if (Status == STATUS_OBJECT_NAME_NOT_FOUND)
    {
        Status = STATUS_SUCCESS;
        goto done;
    }

    if (!NT_SUCCESS(Status))
        goto done;

    for (i = 0; i < SidArray->Count; i++)
    {
        ConvertSidToStringSid(SidArray->Sids[i].SidPointer, &MemberSidString);
TRACE("Open %S\n", MemberSidString);

        Status = SampRegOpenKey(MembersKeyHandle,
                                MemberSidString,
                                KEY_READ,
                                &MemberKeyHandle);
        TRACE("SampRegOpenKey returned %08lX\n", Status);
        if (NT_SUCCESS(Status))
        {
            Status = SampRegQueryKeyInfo(MemberKeyHandle,
                                         NULL,
                                         &ValueCount);
            if (NT_SUCCESS(Status))
            {
                TRACE("Found %lu values\n", ValueCount);
                MaxSidCount += ValueCount;
            }

            SampRegCloseKey(&MemberKeyHandle);
        }

        if (Status == STATUS_OBJECT_NAME_NOT_FOUND)
            Status = STATUS_SUCCESS;

        LocalFree(MemberSidString);
    }

    if (MaxSidCount == 0)
    {
        Status = STATUS_SUCCESS;
        goto done;
    }

    TRACE("Maximum sid count: %lu\n", MaxSidCount);
    RidArray = midl_user_allocate(MaxSidCount * sizeof(ULONG));
    if (RidArray == NULL)
    {
        Status = STATUS_INSUFFICIENT_RESOURCES;
        goto done;
    }

    RidIndex = 0;
    for (i = 0; i < SidArray->Count; i++)
    {
        ConvertSidToStringSid(SidArray->Sids[i].SidPointer, &MemberSidString);
TRACE("Open %S\n", MemberSidString);

        Status = SampRegOpenKey(MembersKeyHandle,
                                MemberSidString,
                                KEY_READ,
                                &MemberKeyHandle);
        TRACE("SampRegOpenKey returned %08lX\n", Status);
        if (NT_SUCCESS(Status))
        {
            Status = SampRegQueryKeyInfo(MemberKeyHandle,
                                         NULL,
                                         &ValueCount);
            if (NT_SUCCESS(Status))
            {
                TRACE("Found %lu values\n", ValueCount);

                for (j = 0; j < ValueCount; j++)
                {
                    DataLength = 9 * sizeof(WCHAR);
                    Status = SampRegEnumerateValue(MemberKeyHandle,
                                                   j,
                                                   NameBuffer,
                                                   &DataLength,
                                                   NULL,
                                                   NULL,
                                                   NULL);
                    if (NT_SUCCESS(Status))
                    {
                        /* FIXME: Do not return each RID more than once. */
                        RidArray[RidIndex] = wcstoul(NameBuffer, NULL, 16);
                        RidIndex++;
                    }
                }
            }

            SampRegCloseKey(&MemberKeyHandle);
        }

        if (Status == STATUS_OBJECT_NAME_NOT_FOUND)
            Status = STATUS_SUCCESS;

        LocalFree(MemberSidString);
    }

done:
    SampRegCloseKey(&MembersKeyHandle);
    SampRegCloseKey(&AliasesKeyHandle);

    if (NT_SUCCESS(Status))
    {
        Membership->Count = MaxSidCount;
        Membership->Element = RidArray;
    }
    else
    {
        if (RidArray != NULL)
            midl_user_free(RidArray);
    }

    RtlReleaseResource(&SampResource);

    return Status;
}


/* Function 17 */
NTSTATUS
NTAPI
SamrLookupNamesInDomain(IN SAMPR_HANDLE DomainHandle,
                        IN ULONG Count,
                        IN RPC_UNICODE_STRING Names[],
                        OUT PSAMPR_ULONG_ARRAY RelativeIds,
                        OUT PSAMPR_ULONG_ARRAY Use)
{
    PSAM_DB_OBJECT DomainObject;
    HANDLE AccountsKeyHandle = NULL;
    HANDLE NamesKeyHandle = NULL;
    ULONG MappedCount = 0;
    ULONG DataLength;
    ULONG i;
    ULONG RelativeId;
    NTSTATUS Status;

    TRACE("SamrLookupNamesInDomain(%p %lu %p %p %p)\n",
          DomainHandle, Count, Names, RelativeIds, Use);

    RtlAcquireResourceShared(&SampResource,
                             TRUE);

    /* Validate the domain handle */
    Status = SampValidateDbObject(DomainHandle,
                                  SamDbDomainObject,
                                  DOMAIN_LOOKUP,
                                  &DomainObject);
    if (!NT_SUCCESS(Status))
    {
        TRACE("failed with status 0x%08lx\n", Status);
        goto done;
    }

    RelativeIds->Count = 0;
    Use->Count = 0;

    if (Count == 0)
    {
        Status = STATUS_SUCCESS;
        goto done;
    }

    /* Allocate the relative IDs array */
    RelativeIds->Element = midl_user_allocate(Count * sizeof(ULONG));
    if (RelativeIds->Element == NULL)
    {
        Status = STATUS_INSUFFICIENT_RESOURCES;
        goto done;
    }

    /* Allocate the use array */
    Use->Element = midl_user_allocate(Count * sizeof(ULONG));
    if (Use->Element == NULL)
    {
        Status = STATUS_INSUFFICIENT_RESOURCES;
        goto done;
    }

    RelativeIds->Count = Count;
    Use->Count = Count;

    for (i = 0; i < Count; i++)
    {
        TRACE("Name: %S\n", Names[i].Buffer);

        RelativeId = 0;

        /* Lookup aliases */
        Status = SampRegOpenKey(DomainObject->KeyHandle,
                                L"Aliases",
                                KEY_READ,
                                &AccountsKeyHandle);
        if (NT_SUCCESS(Status))
        {
            Status = SampRegOpenKey(AccountsKeyHandle,
                                    L"Names",
                                    KEY_READ,
                                    &NamesKeyHandle);
            if (NT_SUCCESS(Status))
            {
                DataLength = sizeof(ULONG);
                Status = SampRegQueryValue(NamesKeyHandle,
                                           Names[i].Buffer,
                                           NULL,
                                           &RelativeId,
                                           &DataLength);

                SampRegCloseKey(&NamesKeyHandle);
            }

            SampRegCloseKey(&AccountsKeyHandle);
        }

        if (!NT_SUCCESS(Status) && Status != STATUS_OBJECT_NAME_NOT_FOUND)
            break;

        /* Return alias account */
        if (NT_SUCCESS(Status) && RelativeId != 0)
        {
            TRACE("Rid: %lu\n", RelativeId);
            RelativeIds->Element[i] = RelativeId;
            Use->Element[i] = SidTypeAlias;
            MappedCount++;
            continue;
        }

        /* Lookup groups */
        Status = SampRegOpenKey(DomainObject->KeyHandle,
                                L"Groups",
                                KEY_READ,
                                &AccountsKeyHandle);
        if (NT_SUCCESS(Status))
        {
            Status = SampRegOpenKey(AccountsKeyHandle,
                                    L"Names",
                                    KEY_READ,
                                    &NamesKeyHandle);
            if (NT_SUCCESS(Status))
            {
                DataLength = sizeof(ULONG);
                Status = SampRegQueryValue(NamesKeyHandle,
                                           Names[i].Buffer,
                                           NULL,
                                           &RelativeId,
                                           &DataLength);

                SampRegCloseKey(&NamesKeyHandle);
            }

            SampRegCloseKey(&AccountsKeyHandle);
        }

        if (!NT_SUCCESS(Status) && Status != STATUS_OBJECT_NAME_NOT_FOUND)
            break;

        /* Return group account */
        if (NT_SUCCESS(Status) && RelativeId != 0)
        {
            TRACE("Rid: %lu\n", RelativeId);
            RelativeIds->Element[i] = RelativeId;
            Use->Element[i] = SidTypeGroup;
            MappedCount++;
            continue;
        }

        /* Lookup users */
        Status = SampRegOpenKey(DomainObject->KeyHandle,
                                L"Users",
                                KEY_READ,
                                &AccountsKeyHandle);
        if (NT_SUCCESS(Status))
        {
            Status = SampRegOpenKey(AccountsKeyHandle,
                                    L"Names",
                                    KEY_READ,
                                    &NamesKeyHandle);
            if (NT_SUCCESS(Status))
            {
                DataLength = sizeof(ULONG);
                Status = SampRegQueryValue(NamesKeyHandle,
                                           Names[i].Buffer,
                                           NULL,
                                           &RelativeId,
                                           &DataLength);

                SampRegCloseKey(&NamesKeyHandle);
            }

            SampRegCloseKey(&AccountsKeyHandle);
        }

        if (!NT_SUCCESS(Status) && Status != STATUS_OBJECT_NAME_NOT_FOUND)
            break;

        /* Return user account */
        if (NT_SUCCESS(Status) && RelativeId != 0)
        {
            TRACE("Rid: %lu\n", RelativeId);
            RelativeIds->Element[i] = RelativeId;
            Use->Element[i] = SidTypeUser;
            MappedCount++;
            continue;
        }

        /* Return unknown account */
        RelativeIds->Element[i] = 0;
        Use->Element[i] = SidTypeUnknown;
    }

done:
    if (Status == STATUS_OBJECT_NAME_NOT_FOUND)
        Status = STATUS_SUCCESS;

    if (NT_SUCCESS(Status))
    {
        if (MappedCount == 0)
            Status = STATUS_NONE_MAPPED;
        else if (MappedCount < Count)
            Status = STATUS_SOME_NOT_MAPPED;
    }
    else
    {
        if (RelativeIds->Element != NULL)
        {
            midl_user_free(RelativeIds->Element);
            RelativeIds->Element = NULL;
        }

        RelativeIds->Count = 0;

        if (Use->Element != NULL)
        {
            midl_user_free(Use->Element);
            Use->Element = NULL;
        }

        Use->Count = 0;
    }

    RtlReleaseResource(&SampResource);

    TRACE("Returned Status %lx\n", Status);

    return Status;
}


/* Function 18 */
NTSTATUS
NTAPI
SamrLookupIdsInDomain(IN SAMPR_HANDLE DomainHandle,
                      IN ULONG Count,
                      IN ULONG *RelativeIds,
                      OUT PSAMPR_RETURNED_USTRING_ARRAY Names,
                      OUT PSAMPR_ULONG_ARRAY Use)
{
    PSAM_DB_OBJECT DomainObject;
    WCHAR RidString[9];
    HANDLE AccountsKeyHandle = NULL;
    HANDLE AccountKeyHandle = NULL;
    ULONG MappedCount = 0;
    ULONG DataLength;
    ULONG i;
    NTSTATUS Status;

    TRACE("SamrLookupIdsInDomain(%p %lu %p %p %p)\n",
          DomainHandle, Count, RelativeIds, Names, Use);

    RtlAcquireResourceShared(&SampResource,
                             TRUE);

    /* Validate the domain handle */
    Status = SampValidateDbObject(DomainHandle,
                                  SamDbDomainObject,
                                  DOMAIN_LOOKUP,
                                  &DomainObject);
    if (!NT_SUCCESS(Status))
    {
        TRACE("failed with status 0x%08lx\n", Status);
        goto done;
    }

    Names->Count = 0;
    Use->Count = 0;

    if (Count == 0)
    {
        Status = STATUS_SUCCESS;
        goto done;
    }

    /* Allocate the names array */
    Names->Element = midl_user_allocate(Count * sizeof(*Names->Element));
    if (Names->Element == NULL)
    {
        Status = STATUS_INSUFFICIENT_RESOURCES;
        goto done;
    }

    /* Allocate the use array */
    Use->Element = midl_user_allocate(Count * sizeof(*Use->Element));
    if (Use->Element == NULL)
    {
        Status = STATUS_INSUFFICIENT_RESOURCES;
        goto done;
    }

    Names->Count = Count;
    Use->Count = Count;

    for (i = 0; i < Count; i++)
    {
        TRACE("RID: %lu\n", RelativeIds[i]);

        swprintf(RidString, L"%08lx", RelativeIds[i]);

        /* Lookup aliases */
        Status = SampRegOpenKey(DomainObject->KeyHandle,
                                L"Aliases",
                                KEY_READ,
                                &AccountsKeyHandle);
        if (NT_SUCCESS(Status))
        {
            Status = SampRegOpenKey(AccountsKeyHandle,
                                    RidString,
                                    KEY_READ,
                                    &AccountKeyHandle);
            if (NT_SUCCESS(Status))
            {
                DataLength = 0;
                Status = SampRegQueryValue(AccountKeyHandle,
                                           L"Name",
                                           NULL,
                                           NULL,
                                           &DataLength);
                if (NT_SUCCESS(Status))
                {
                    Names->Element[i].Buffer = midl_user_allocate(DataLength);
                    if (Names->Element[i].Buffer == NULL)
                        Status = STATUS_INSUFFICIENT_RESOURCES;

                    if (NT_SUCCESS(Status))
                    {
                        Names->Element[i].MaximumLength = (USHORT)DataLength;
                        Names->Element[i].Length = (USHORT)(DataLength - sizeof(WCHAR));

                        Status = SampRegQueryValue(AccountKeyHandle,
                                                   L"Name",
                                                   NULL,
                                                   Names->Element[i].Buffer,
                                                   &DataLength);
                    }
                }

                SampRegCloseKey(&AccountKeyHandle);
            }

            SampRegCloseKey(&AccountsKeyHandle);
        }

        if (!NT_SUCCESS(Status) && Status != STATUS_OBJECT_NAME_NOT_FOUND)
            break;

        /* Return alias account */
        if (NT_SUCCESS(Status) && Names->Element[i].Buffer != NULL)
        {
            TRACE("Name: %S\n", Names->Element[i].Buffer);
            Use->Element[i] = SidTypeAlias;
            MappedCount++;
            continue;
        }

        /* Lookup groups */
        Status = SampRegOpenKey(DomainObject->KeyHandle,
                                L"Groups",
                                KEY_READ,
                                &AccountsKeyHandle);
        if (NT_SUCCESS(Status))
        {
            Status = SampRegOpenKey(AccountsKeyHandle,
                                    RidString,
                                    KEY_READ,
                                    &AccountKeyHandle);
            if (NT_SUCCESS(Status))
            {
                DataLength = 0;
                Status = SampRegQueryValue(AccountKeyHandle,
                                           L"Name",
                                           NULL,
                                           NULL,
                                           &DataLength);
                if (NT_SUCCESS(Status))
                {
                    Names->Element[i].Buffer = midl_user_allocate(DataLength);
                    if (Names->Element[i].Buffer == NULL)
                        Status = STATUS_INSUFFICIENT_RESOURCES;

                    if (NT_SUCCESS(Status))
                    {
                        Names->Element[i].MaximumLength = (USHORT)DataLength;
                        Names->Element[i].Length = (USHORT)(DataLength - sizeof(WCHAR));

                        Status = SampRegQueryValue(AccountKeyHandle,
                                                   L"Name",
                                                   NULL,
                                                   Names->Element[i].Buffer,
                                                   &DataLength);
                    }
                }

                SampRegCloseKey(&AccountKeyHandle);
            }

            SampRegCloseKey(&AccountsKeyHandle);
        }

        if (!NT_SUCCESS(Status) && Status != STATUS_OBJECT_NAME_NOT_FOUND)
            break;

        /* Return group account */
        if (NT_SUCCESS(Status) && Names->Element[i].Buffer != NULL)
        {
            TRACE("Name: %S\n", Names->Element[i].Buffer);
            Use->Element[i] = SidTypeGroup;
            MappedCount++;
            continue;
        }

        /* Lookup users */
        Status = SampRegOpenKey(DomainObject->KeyHandle,
                                L"Users",
                                KEY_READ,
                                &AccountsKeyHandle);
        if (NT_SUCCESS(Status))
        {
            Status = SampRegOpenKey(AccountsKeyHandle,
                                    RidString,
                                    KEY_READ,
                                    &AccountKeyHandle);
            if (NT_SUCCESS(Status))
            {
                DataLength = 0;
                Status = SampRegQueryValue(AccountKeyHandle,
                                           L"Name",
                                           NULL,
                                           NULL,
                                           &DataLength);
                if (NT_SUCCESS(Status))
                {
                    TRACE("DataLength: %lu\n", DataLength);

                    Names->Element[i].Buffer = midl_user_allocate(DataLength);
                    if (Names->Element[i].Buffer == NULL)
                        Status = STATUS_INSUFFICIENT_RESOURCES;

                    if (NT_SUCCESS(Status))
                    {
                        Names->Element[i].MaximumLength = (USHORT)DataLength;
                        Names->Element[i].Length = (USHORT)(DataLength - sizeof(WCHAR));

                        Status = SampRegQueryValue(AccountKeyHandle,
                                                   L"Name",
                                                   NULL,
                                                   Names->Element[i].Buffer,
                                                   &DataLength);
                    }
                }

                SampRegCloseKey(&AccountKeyHandle);
            }

            SampRegCloseKey(&AccountsKeyHandle);
        }

        if (!NT_SUCCESS(Status) && Status != STATUS_OBJECT_NAME_NOT_FOUND)
            break;

        /* Return user account */
        if (NT_SUCCESS(Status) && Names->Element[i].Buffer != NULL)
        {
            TRACE("Name: %S\n", Names->Element[i].Buffer);
            Use->Element[i] = SidTypeUser;
            MappedCount++;
            continue;
        }

        /* Return unknown account */
        Use->Element[i] = SidTypeUnknown;
    }

done:
    if (Status == STATUS_OBJECT_NAME_NOT_FOUND)
        Status = STATUS_SUCCESS;

    if (NT_SUCCESS(Status))
    {
        if (MappedCount == 0)
            Status = STATUS_NONE_MAPPED;
        else if (MappedCount < Count)
            Status = STATUS_SOME_NOT_MAPPED;
    }
    else
    {
        if (Names->Element != NULL)
        {
            for (i = 0; i < Count; i++)
            {
                if (Names->Element[i].Buffer != NULL)
                    midl_user_free(Names->Element[i].Buffer);
            }

            midl_user_free(Names->Element);
            Names->Element = NULL;
        }

        Names->Count = 0;

        if (Use->Element != NULL)
        {
            midl_user_free(Use->Element);
            Use->Element = NULL;
        }

        Use->Count = 0;
    }

    RtlReleaseResource(&SampResource);

    return Status;
}


/* Function 19 */
NTSTATUS
NTAPI
SamrOpenGroup(IN SAMPR_HANDLE DomainHandle,
              IN ACCESS_MASK DesiredAccess,
              IN unsigned long GroupId,
              OUT SAMPR_HANDLE *GroupHandle)
{
    PSAM_DB_OBJECT DomainObject;
    PSAM_DB_OBJECT GroupObject;
    WCHAR szRid[9];
    NTSTATUS Status;

    TRACE("SamrOpenGroup(%p %lx %lx %p)\n",
          DomainHandle, DesiredAccess, GroupId, GroupHandle);

    /* Map generic access rights */
    RtlMapGenericMask(&DesiredAccess,
                      &GroupMapping);

    RtlAcquireResourceShared(&SampResource,
                             TRUE);

    /* Validate the domain handle */
    Status = SampValidateDbObject(DomainHandle,
                                  SamDbDomainObject,
                                  DOMAIN_LOOKUP,
                                  &DomainObject);
    if (!NT_SUCCESS(Status))
    {
        TRACE("failed with status 0x%08lx\n", Status);
        goto done;
    }

    /* Convert the RID into a string (hex) */
    swprintf(szRid, L"%08lX", GroupId);

    /* Create the group object */
    Status = SampOpenDbObject(DomainObject,
                              L"Groups",
                              szRid,
                              GroupId,
                              SamDbGroupObject,
                              DesiredAccess,
                              &GroupObject);
    if (!NT_SUCCESS(Status))
    {
        TRACE("failed with status 0x%08lx\n", Status);
        goto done;
    }

    *GroupHandle = (SAMPR_HANDLE)GroupObject;

done:
    RtlReleaseResource(&SampResource);

    return Status;
}


static NTSTATUS
SampQueryGroupGeneral(PSAM_DB_OBJECT GroupObject,
                      PSAMPR_GROUP_INFO_BUFFER *Buffer)
{
    PSAMPR_GROUP_INFO_BUFFER InfoBuffer = NULL;
    SAM_GROUP_FIXED_DATA FixedData;
    ULONG MembersLength = 0;
    ULONG Length = 0;
    NTSTATUS Status;

    *Buffer = NULL;

    InfoBuffer = midl_user_allocate(sizeof(SAMPR_GROUP_INFO_BUFFER));
    if (InfoBuffer == NULL)
        return STATUS_INSUFFICIENT_RESOURCES;

    Status = SampGetObjectAttributeString(GroupObject,
                                          L"Name",
                                          &InfoBuffer->General.Name);
    if (!NT_SUCCESS(Status))
    {
        TRACE("Status 0x%08lx\n", Status);
        goto done;
    }

    Status = SampGetObjectAttributeString(GroupObject,
                                          L"AdminComment",
                                          &InfoBuffer->General.AdminComment);
    if (!NT_SUCCESS(Status))
    {
        TRACE("Status 0x%08lx\n", Status);
        goto done;
    }

    Length = sizeof(SAM_GROUP_FIXED_DATA);
    Status = SampGetObjectAttribute(GroupObject,
                                    L"F",
                                    NULL,
                                    (PVOID)&FixedData,
                                    &Length);
    if (!NT_SUCCESS(Status))
    {
        TRACE("Status 0x%08lx\n", Status);
        goto done;
    }

    InfoBuffer->General.Attributes = FixedData.Attributes;

    Status = SampGetObjectAttribute(GroupObject,
                                    L"Members",
                                    NULL,
                                    NULL,
                                    &MembersLength);
    if (!NT_SUCCESS(Status) && Status != STATUS_OBJECT_NAME_NOT_FOUND)
    {
        TRACE("Status 0x%08lx\n", Status);
        goto done;
    }

    if (Status == STATUS_OBJECT_NAME_NOT_FOUND)
    {
        InfoBuffer->General.MemberCount = 0;
        Status = STATUS_SUCCESS;
    }
    else
    {
        InfoBuffer->General.MemberCount = MembersLength / sizeof(ULONG);
    }

    *Buffer = InfoBuffer;

done:
    if (!NT_SUCCESS(Status))
    {
        if (InfoBuffer != NULL)
        {
            if (InfoBuffer->General.Name.Buffer != NULL)
                midl_user_free(InfoBuffer->General.Name.Buffer);

            if (InfoBuffer->General.AdminComment.Buffer != NULL)
                midl_user_free(InfoBuffer->General.AdminComment.Buffer);

            midl_user_free(InfoBuffer);
        }
    }

    return Status;
}


static NTSTATUS
SampQueryGroupName(PSAM_DB_OBJECT GroupObject,
                   PSAMPR_GROUP_INFO_BUFFER *Buffer)
{
    PSAMPR_GROUP_INFO_BUFFER InfoBuffer = NULL;
    NTSTATUS Status;

    *Buffer = NULL;

    InfoBuffer = midl_user_allocate(sizeof(SAMPR_GROUP_INFO_BUFFER));
    if (InfoBuffer == NULL)
        return STATUS_INSUFFICIENT_RESOURCES;

    Status = SampGetObjectAttributeString(GroupObject,
                                          L"Name",
                                          &InfoBuffer->Name.Name);
    if (!NT_SUCCESS(Status))
    {
        TRACE("Status 0x%08lx\n", Status);
        goto done;
    }

    *Buffer = InfoBuffer;

done:
    if (!NT_SUCCESS(Status))
    {
        if (InfoBuffer != NULL)
        {
            if (InfoBuffer->Name.Name.Buffer != NULL)
                midl_user_free(InfoBuffer->Name.Name.Buffer);

            midl_user_free(InfoBuffer);
        }
    }

    return Status;
}


static NTSTATUS
SampQueryGroupAttribute(PSAM_DB_OBJECT GroupObject,
                        PSAMPR_GROUP_INFO_BUFFER *Buffer)
{
    PSAMPR_GROUP_INFO_BUFFER InfoBuffer = NULL;
    SAM_GROUP_FIXED_DATA FixedData;
    ULONG Length = 0;
    NTSTATUS Status;

    *Buffer = NULL;

    InfoBuffer = midl_user_allocate(sizeof(SAMPR_GROUP_INFO_BUFFER));
    if (InfoBuffer == NULL)
        return STATUS_INSUFFICIENT_RESOURCES;

    Length = sizeof(SAM_GROUP_FIXED_DATA);
    Status = SampGetObjectAttribute(GroupObject,
                                    L"F",
                                    NULL,
                                    (PVOID)&FixedData,
                                    &Length);
    if (!NT_SUCCESS(Status))
    {
        TRACE("Status 0x%08lx\n", Status);
        goto done;
    }

    InfoBuffer->Attribute.Attributes = FixedData.Attributes;

    *Buffer = InfoBuffer;

done:
    if (!NT_SUCCESS(Status))
    {
        if (InfoBuffer != NULL)
        {
            midl_user_free(InfoBuffer);
        }
    }

    return Status;
}


static NTSTATUS
SampQueryGroupAdminComment(PSAM_DB_OBJECT GroupObject,
                           PSAMPR_GROUP_INFO_BUFFER *Buffer)
{
    PSAMPR_GROUP_INFO_BUFFER InfoBuffer = NULL;
    NTSTATUS Status;

    *Buffer = NULL;

    InfoBuffer = midl_user_allocate(sizeof(SAMPR_GROUP_INFO_BUFFER));
    if (InfoBuffer == NULL)
        return STATUS_INSUFFICIENT_RESOURCES;

    Status = SampGetObjectAttributeString(GroupObject,
                                          L"AdminComment",
                                          &InfoBuffer->AdminComment.AdminComment);
    if (!NT_SUCCESS(Status))
    {
        TRACE("Status 0x%08lx\n", Status);
        goto done;
    }

    *Buffer = InfoBuffer;

done:
    if (!NT_SUCCESS(Status))
    {
        if (InfoBuffer != NULL)
        {
            if (InfoBuffer->AdminComment.AdminComment.Buffer != NULL)
                midl_user_free(InfoBuffer->AdminComment.AdminComment.Buffer);

            midl_user_free(InfoBuffer);
        }
    }

    return Status;
}


/* Function 20 */
NTSTATUS
NTAPI
SamrQueryInformationGroup(IN SAMPR_HANDLE GroupHandle,
                          IN GROUP_INFORMATION_CLASS GroupInformationClass,
                          OUT PSAMPR_GROUP_INFO_BUFFER *Buffer)
{
    PSAM_DB_OBJECT GroupObject;
    NTSTATUS Status;

    TRACE("SamrQueryInformationGroup(%p %lu %p)\n",
          GroupHandle, GroupInformationClass, Buffer);

    RtlAcquireResourceShared(&SampResource,
                             TRUE);

    /* Validate the group handle */
    Status = SampValidateDbObject(GroupHandle,
                                  SamDbGroupObject,
                                  GROUP_READ_INFORMATION,
                                  &GroupObject);
    if (!NT_SUCCESS(Status))
        goto done;

    switch (GroupInformationClass)
    {
        case GroupGeneralInformation:
            Status = SampQueryGroupGeneral(GroupObject,
                                           Buffer);
            break;

        case GroupNameInformation:
            Status = SampQueryGroupName(GroupObject,
                                        Buffer);
            break;

        case GroupAttributeInformation:
            Status = SampQueryGroupAttribute(GroupObject,
                                             Buffer);
            break;

        case GroupAdminCommentInformation:
            Status = SampQueryGroupAdminComment(GroupObject,
                                                Buffer);
            break;

        default:
            Status = STATUS_INVALID_INFO_CLASS;
            break;
    }

done:
    RtlReleaseResource(&SampResource);

    return Status;
}


static NTSTATUS
SampSetGroupName(PSAM_DB_OBJECT GroupObject,
                 PSAMPR_GROUP_INFO_BUFFER Buffer)
{
    UNICODE_STRING OldGroupName = {0, 0, NULL};
    UNICODE_STRING NewGroupName;
    NTSTATUS Status;

    Status = SampGetObjectAttributeString(GroupObject,
                                          L"Name",
                                          (PRPC_UNICODE_STRING)&OldGroupName);
    if (!NT_SUCCESS(Status))
    {
        TRACE("SampGetObjectAttributeString failed (Status 0x%08lx)\n", Status);
        goto done;
    }

    /* Check the new account name */
    Status = SampCheckAccountName(&Buffer->Name.Name, 256);
    if (!NT_SUCCESS(Status))
    {
        TRACE("SampCheckAccountName failed (Status 0x%08lx)\n", Status);
        return Status;
    }

    NewGroupName.Length = Buffer->Name.Name.Length;
    NewGroupName.MaximumLength = Buffer->Name.Name.MaximumLength;
    NewGroupName.Buffer = Buffer->Name.Name.Buffer;

    if (!RtlEqualUnicodeString(&OldGroupName, &NewGroupName, TRUE))
    {
        Status = SampCheckAccountNameInDomain(GroupObject->ParentObject,
                                              NewGroupName.Buffer);
        if (!NT_SUCCESS(Status))
        {
            TRACE("Group name \'%S\' already exists in domain (Status 0x%08lx)\n",
                  NewGroupName.Buffer, Status);
            goto done;
        }
    }

    Status = SampSetAccountNameInDomain(GroupObject->ParentObject,
                                        L"Groups",
                                        NewGroupName.Buffer,
                                        GroupObject->RelativeId);
    if (!NT_SUCCESS(Status))
    {
        TRACE("SampSetAccountNameInDomain failed (Status 0x%08lx)\n", Status);
        goto done;
    }

    Status = SampRemoveAccountNameFromDomain(GroupObject->ParentObject,
                                             L"Groups",
                                             OldGroupName.Buffer);
    if (!NT_SUCCESS(Status))
    {
        TRACE("SampRemoveAccountNameFromDomain failed (Status 0x%08lx)\n", Status);
        goto done;
    }

    Status = SampSetObjectAttributeString(GroupObject,
                                          L"Name",
                                          (PRPC_UNICODE_STRING)&NewGroupName);
    if (!NT_SUCCESS(Status))
    {
        TRACE("SampSetObjectAttribute failed (Status 0x%08lx)\n", Status);
    }

done:
    if (OldGroupName.Buffer != NULL)
        midl_user_free(OldGroupName.Buffer);

    return Status;
}


static NTSTATUS
SampSetGroupAttribute(PSAM_DB_OBJECT GroupObject,
                      PSAMPR_GROUP_INFO_BUFFER Buffer)
{
    SAM_GROUP_FIXED_DATA FixedData;
    ULONG Length = 0;
    NTSTATUS Status;

    Length = sizeof(SAM_GROUP_FIXED_DATA);
    Status = SampGetObjectAttribute(GroupObject,
                                    L"F",
                                    NULL,
                                    (PVOID)&FixedData,
                                    &Length);
    if (!NT_SUCCESS(Status))
        goto done;

    FixedData.Attributes = Buffer->Attribute.Attributes;

    Status = SampSetObjectAttribute(GroupObject,
                                    L"F",
                                    REG_BINARY,
                                    &FixedData,
                                    Length);

done:
    return Status;
}


/* Function 21 */
NTSTATUS
NTAPI
SamrSetInformationGroup(IN SAMPR_HANDLE GroupHandle,
                        IN GROUP_INFORMATION_CLASS GroupInformationClass,
                        IN PSAMPR_GROUP_INFO_BUFFER Buffer)
{
    PSAM_DB_OBJECT GroupObject;
    NTSTATUS Status;

    TRACE("SamrSetInformationGroup(%p %lu %p)\n",
          GroupHandle, GroupInformationClass, Buffer);

    RtlAcquireResourceExclusive(&SampResource,
                                TRUE);

    /* Validate the group handle */
    Status = SampValidateDbObject(GroupHandle,
                                  SamDbGroupObject,
                                  GROUP_WRITE_ACCOUNT,
                                  &GroupObject);
    if (!NT_SUCCESS(Status))
        goto done;

    switch (GroupInformationClass)
    {
        case GroupNameInformation:
            Status = SampSetGroupName(GroupObject,
                                      Buffer);
            break;

        case GroupAttributeInformation:
            Status = SampSetGroupAttribute(GroupObject,
                                           Buffer);
            break;

        case GroupAdminCommentInformation:
            Status = SampSetObjectAttributeString(GroupObject,
                                                  L"AdminComment",
                                                  &Buffer->AdminComment.AdminComment);
            break;

        default:
            Status = STATUS_INVALID_INFO_CLASS;
            break;
    }

done:
    RtlReleaseResource(&SampResource);

    return Status;
}


/* Function 22 */
NTSTATUS
NTAPI
SamrAddMemberToGroup(IN SAMPR_HANDLE GroupHandle,
                     IN unsigned long MemberId,
                     IN unsigned long Attributes)
{
    PSAM_DB_OBJECT GroupObject;
    PSAM_DB_OBJECT UserObject = NULL;
    NTSTATUS Status;

    TRACE("SamrAddMemberToGroup(%p %lu %lx)\n",
          GroupHandle, MemberId, Attributes);

    RtlAcquireResourceExclusive(&SampResource,
                                TRUE);

    /* Validate the group handle */
    Status = SampValidateDbObject(GroupHandle,
                                  SamDbGroupObject,
                                  GROUP_ADD_MEMBER,
                                  &GroupObject);
    if (!NT_SUCCESS(Status))
        goto done;

    /* Open the user object in the same domain */
    Status = SampOpenUserObject(GroupObject->ParentObject,
                                MemberId,
                                0,
                                &UserObject);
    if (!NT_SUCCESS(Status))
    {
        TRACE("SampOpenUserObject() failed (Status 0x%08lx)\n", Status);
        goto done;
    }

    /* Add group membership to the user object */
    Status = SampAddGroupMembershipToUser(UserObject,
                                          GroupObject->RelativeId,
                                          Attributes);
    if (!NT_SUCCESS(Status))
    {
        TRACE("SampAddGroupMembershipToUser() failed (Status 0x%08lx)\n", Status);
        goto done;
    }

    /* Add the member to the group object */
    Status = SampAddMemberToGroup(GroupObject,
                                  MemberId);
    if (!NT_SUCCESS(Status))
    {
        TRACE("SampAddMemberToGroup() failed (Status 0x%08lx)\n", Status);
    }

done:
    if (UserObject)
        SampCloseDbObject(UserObject);

    RtlReleaseResource(&SampResource);

    return Status;
}


/* Function 23 */
NTSTATUS
NTAPI
SamrDeleteGroup(IN OUT SAMPR_HANDLE *GroupHandle)
{
    PSAM_DB_OBJECT GroupObject;
    ULONG Length = 0;
    NTSTATUS Status;

    TRACE("SamrDeleteGroup(%p)\n", GroupHandle);

    RtlAcquireResourceExclusive(&SampResource,
                                TRUE);

    /* Validate the group handle */
    Status = SampValidateDbObject(*GroupHandle,
                                  SamDbGroupObject,
                                  DELETE,
                                  &GroupObject);
    if (!NT_SUCCESS(Status))
    {
        TRACE("SampValidateDbObject() failed (Status 0x%08lx)\n", Status);
        goto done;
    }

    /* Fail, if the group is built-in */
    if (GroupObject->RelativeId < 1000)
    {
        TRACE("You can not delete a special account!\n");
        Status = STATUS_SPECIAL_ACCOUNT;
        goto done;
    }

    /* Get the length of the Members attribute */
    SampGetObjectAttribute(GroupObject,
                           L"Members",
                           NULL,
                           NULL,
                           &Length);

    /* Fail, if the group has members */
    if (Length != 0)
    {
        TRACE("There are still members in the group!\n");
        Status = STATUS_MEMBER_IN_GROUP;
        goto done;
    }

    /* FIXME: Remove the group from all aliases */

    /* Delete the group from the database */
    Status = SampDeleteAccountDbObject(GroupObject);
    if (!NT_SUCCESS(Status))
    {
        TRACE("SampDeleteAccountDbObject() failed (Status 0x%08lx)\n", Status);
        goto done;
    }

    /* Invalidate the handle */
    *GroupHandle = NULL;

done:
    RtlReleaseResource(&SampResource);

    return Status;
}


/* Function 24 */
NTSTATUS
NTAPI
SamrRemoveMemberFromGroup(IN SAMPR_HANDLE GroupHandle,
                          IN unsigned long MemberId)
{
    PSAM_DB_OBJECT GroupObject;
    PSAM_DB_OBJECT UserObject = NULL;
    NTSTATUS Status;

    TRACE("SamrRemoveMemberFromGroup(%p %lu)\n",
          GroupHandle, MemberId);

    RtlAcquireResourceExclusive(&SampResource,
                                TRUE);

    /* Validate the group handle */
    Status = SampValidateDbObject(GroupHandle,
                                  SamDbGroupObject,
                                  GROUP_REMOVE_MEMBER,
                                  &GroupObject);
    if (!NT_SUCCESS(Status))
        goto done;

    /* Open the user object in the same domain */
    Status = SampOpenUserObject(GroupObject->ParentObject,
                                MemberId,
                                0,
                                &UserObject);
    if (!NT_SUCCESS(Status))
    {
        ERR("SampOpenUserObject() failed (Status 0x%08lx)\n", Status);
        goto done;
    }

    /* Remove group membership from the user object */
    Status = SampRemoveGroupMembershipFromUser(UserObject,
                                               GroupObject->RelativeId);
    if (!NT_SUCCESS(Status))
    {
        ERR("SampAddGroupMembershipToUser() failed (Status 0x%08lx)\n", Status);
        goto done;
    }

    /* Remove the member from the group object */
    Status = SampRemoveMemberFromGroup(GroupObject,
                                       MemberId);
    if (!NT_SUCCESS(Status))
    {
        ERR("SampRemoveMemberFromGroup() failed (Status 0x%08lx)\n", Status);
    }

done:
    if (UserObject)
        SampCloseDbObject(UserObject);

    RtlReleaseResource(&SampResource);

    return Status;
}


/* Function 25 */
NTSTATUS
NTAPI
SamrGetMembersInGroup(IN SAMPR_HANDLE GroupHandle,
                      OUT PSAMPR_GET_MEMBERS_BUFFER *Members)
{
    PSAMPR_GET_MEMBERS_BUFFER MembersBuffer = NULL;
    PSAM_DB_OBJECT GroupObject;
    ULONG Length = 0;
    ULONG i;
    NTSTATUS Status;

    TRACE("SamrGetMembersInGroup(%p %p)\n",
          GroupHandle, Members);

    RtlAcquireResourceShared(&SampResource,
                             TRUE);

    /* Validate the group handle */
    Status = SampValidateDbObject(GroupHandle,
                                  SamDbGroupObject,
                                  GROUP_LIST_MEMBERS,
                                  &GroupObject);
    if (!NT_SUCCESS(Status))
        goto done;

    MembersBuffer = midl_user_allocate(sizeof(SAMPR_GET_MEMBERS_BUFFER));
    if (MembersBuffer == NULL)
    {
        Status = STATUS_INSUFFICIENT_RESOURCES;
        goto done;
    }

    SampGetObjectAttribute(GroupObject,
                           L"Members",
                           NULL,
                           NULL,
                           &Length);

    if (Length == 0)
    {
        MembersBuffer->MemberCount = 0;
        MembersBuffer->Members = NULL;
        MembersBuffer->Attributes = NULL;

        *Members = MembersBuffer;

        Status = STATUS_SUCCESS;
        goto done;
    }

    MembersBuffer->Members = midl_user_allocate(Length);
    if (MembersBuffer->Members == NULL)
    {
        Status = STATUS_INSUFFICIENT_RESOURCES;
        goto done;
    }

    MembersBuffer->Attributes = midl_user_allocate(Length);
    if (MembersBuffer->Attributes == NULL)
    {
        Status = STATUS_INSUFFICIENT_RESOURCES;
        goto done;
    }

    Status = SampGetObjectAttribute(GroupObject,
                                    L"Members",
                                    NULL,
                                    MembersBuffer->Members,
                                    &Length);
    if (!NT_SUCCESS(Status))
    {
        TRACE("SampGetObjectAttributes() failed (Status 0x%08lx)\n", Status);
        goto done;
    }

    MembersBuffer->MemberCount = Length / sizeof(ULONG);

    for (i = 0; i < MembersBuffer->MemberCount; i++)
    {
        Status = SampGetUserGroupAttributes(GroupObject->ParentObject,
                                            MembersBuffer->Members[i],
                                            GroupObject->RelativeId,
                                            &(MembersBuffer->Attributes[i]));
        if (!NT_SUCCESS(Status))
        {
            TRACE("SampGetUserGroupAttributes() failed (Status 0x%08lx)\n", Status);
            goto done;
        }
    }

    *Members = MembersBuffer;

done:
    if (!NT_SUCCESS(Status))
    {
        if (MembersBuffer != NULL)
        {
            if (MembersBuffer->Members != NULL)
                midl_user_free(MembersBuffer->Members);

            if (MembersBuffer->Attributes != NULL)
                midl_user_free(MembersBuffer->Attributes);

            midl_user_free(MembersBuffer);
        }
    }

    RtlReleaseResource(&SampResource);

    return Status;
}


/* Function 26 */
NTSTATUS
NTAPI
SamrSetMemberAttributesOfGroup(IN SAMPR_HANDLE GroupHandle,
                               IN unsigned long MemberId,
                               IN unsigned long Attributes)
{
    PSAM_DB_OBJECT GroupObject;
    NTSTATUS Status;

    TRACE("SamrSetMemberAttributesOfGroup(%p %lu %lx)\n",
          GroupHandle, MemberId, Attributes);

    RtlAcquireResourceExclusive(&SampResource,
                                TRUE);

    /* Validate the group handle */
    Status = SampValidateDbObject(GroupHandle,
                                  SamDbGroupObject,
                                  GROUP_ADD_MEMBER,
                                  &GroupObject);
    if (!NT_SUCCESS(Status))
    {
        TRACE("SampValidateDbObject failed with status 0x%08lx\n", Status);
        goto done;
    }

    Status = SampSetUserGroupAttributes(GroupObject->ParentObject,
                                        MemberId,
                                        GroupObject->RelativeId,
                                        Attributes);
    if (!NT_SUCCESS(Status))
    {
        TRACE("SampSetUserGroupAttributes failed with status 0x%08lx\n", Status);
    }

done:
    RtlReleaseResource(&SampResource);

    return Status;
}


/* Function 27 */
NTSTATUS
NTAPI
SamrOpenAlias(IN SAMPR_HANDLE DomainHandle,
              IN ACCESS_MASK DesiredAccess,
              IN ULONG AliasId,
              OUT SAMPR_HANDLE *AliasHandle)
{
    PSAM_DB_OBJECT DomainObject;
    PSAM_DB_OBJECT AliasObject;
    WCHAR szRid[9];
    NTSTATUS Status;

    TRACE("SamrOpenAlias(%p %lx %lx %p)\n",
          DomainHandle, DesiredAccess, AliasId, AliasHandle);

    /* Map generic access rights */
    RtlMapGenericMask(&DesiredAccess,
                      &AliasMapping);

    RtlAcquireResourceShared(&SampResource,
                             TRUE);

    /* Validate the domain handle */
    Status = SampValidateDbObject(DomainHandle,
                                  SamDbDomainObject,
                                  DOMAIN_LOOKUP,
                                  &DomainObject);
    if (!NT_SUCCESS(Status))
    {
        TRACE("failed with status 0x%08lx\n", Status);
        goto done;
    }

    /* Convert the RID into a string (hex) */
    swprintf(szRid, L"%08lX", AliasId);

    /* Create the alias object */
    Status = SampOpenDbObject(DomainObject,
                              L"Aliases",
                              szRid,
                              AliasId,
                              SamDbAliasObject,
                              DesiredAccess,
                              &AliasObject);
    if (!NT_SUCCESS(Status))
    {
        TRACE("failed with status 0x%08lx\n", Status);
        goto done;
    }

    *AliasHandle = (SAMPR_HANDLE)AliasObject;

done:
    RtlReleaseResource(&SampResource);

    return Status;
}


static NTSTATUS
SampQueryAliasGeneral(PSAM_DB_OBJECT AliasObject,
                      PSAMPR_ALIAS_INFO_BUFFER *Buffer)
{
    PSAMPR_ALIAS_INFO_BUFFER InfoBuffer = NULL;
    HANDLE MembersKeyHandle = NULL;
    NTSTATUS Status;

    *Buffer = NULL;

    InfoBuffer = midl_user_allocate(sizeof(SAMPR_ALIAS_INFO_BUFFER));
    if (InfoBuffer == NULL)
        return STATUS_INSUFFICIENT_RESOURCES;

    Status = SampGetObjectAttributeString(AliasObject,
                                          L"Name",
                                          &InfoBuffer->General.Name);
    if (!NT_SUCCESS(Status))
    {
        TRACE("Status 0x%08lx\n", Status);
        goto done;
    }

    Status = SampGetObjectAttributeString(AliasObject,
                                          L"Description",
                                          &InfoBuffer->General.AdminComment);
    if (!NT_SUCCESS(Status))
    {
        TRACE("Status 0x%08lx\n", Status);
        goto done;
    }

    /* Open the Members subkey */
    Status = SampRegOpenKey(AliasObject->KeyHandle,
                            L"Members",
                            KEY_READ,
                            &MembersKeyHandle);
    if (NT_SUCCESS(Status))
    {
        /* Retrieve the number of members of the alias */
        Status = SampRegQueryKeyInfo(MembersKeyHandle,
                                     NULL,
                                     &InfoBuffer->General.MemberCount);
        if (!NT_SUCCESS(Status))
        {
            TRACE("Status 0x%08lx\n", Status);
            goto done;
        }
    }
    else if (Status == STATUS_OBJECT_NAME_NOT_FOUND)
    {
        InfoBuffer->General.MemberCount = 0;
        Status = STATUS_SUCCESS;
    }
    else
    {
        TRACE("Status 0x%08lx\n", Status);
        goto done;
    }

    *Buffer = InfoBuffer;

done:
    SampRegCloseKey(&MembersKeyHandle);

    if (!NT_SUCCESS(Status))
    {
        if (InfoBuffer != NULL)
        {
            if (InfoBuffer->General.Name.Buffer != NULL)
                midl_user_free(InfoBuffer->General.Name.Buffer);

            if (InfoBuffer->General.AdminComment.Buffer != NULL)
                midl_user_free(InfoBuffer->General.AdminComment.Buffer);

            midl_user_free(InfoBuffer);
        }
    }

    return Status;
}


static NTSTATUS
SampQueryAliasName(PSAM_DB_OBJECT AliasObject,
                   PSAMPR_ALIAS_INFO_BUFFER *Buffer)
{
    PSAMPR_ALIAS_INFO_BUFFER InfoBuffer = NULL;
    NTSTATUS Status;

    *Buffer = NULL;

    InfoBuffer = midl_user_allocate(sizeof(SAMPR_ALIAS_INFO_BUFFER));
    if (InfoBuffer == NULL)
        return STATUS_INSUFFICIENT_RESOURCES;

    Status = SampGetObjectAttributeString(AliasObject,
                                          L"Name",
                                          &InfoBuffer->Name.Name);
    if (!NT_SUCCESS(Status))
    {
        TRACE("Status 0x%08lx\n", Status);
        goto done;
    }

    *Buffer = InfoBuffer;

done:
    if (!NT_SUCCESS(Status))
    {
        if (InfoBuffer != NULL)
        {
            if (InfoBuffer->Name.Name.Buffer != NULL)
                midl_user_free(InfoBuffer->Name.Name.Buffer);

            midl_user_free(InfoBuffer);
        }
    }

    return Status;
}


static NTSTATUS
SampQueryAliasAdminComment(PSAM_DB_OBJECT AliasObject,
                           PSAMPR_ALIAS_INFO_BUFFER *Buffer)
{
    PSAMPR_ALIAS_INFO_BUFFER InfoBuffer = NULL;
    NTSTATUS Status;

    *Buffer = NULL;

    InfoBuffer = midl_user_allocate(sizeof(SAMPR_ALIAS_INFO_BUFFER));
    if (InfoBuffer == NULL)
        return STATUS_INSUFFICIENT_RESOURCES;

    Status = SampGetObjectAttributeString(AliasObject,
                                          L"Description",
                                          &InfoBuffer->AdminComment.AdminComment);
    if (!NT_SUCCESS(Status))
    {
        TRACE("Status 0x%08lx\n", Status);
        goto done;
    }

    *Buffer = InfoBuffer;

done:
    if (!NT_SUCCESS(Status))
    {
        if (InfoBuffer != NULL)
        {
            if (InfoBuffer->AdminComment.AdminComment.Buffer != NULL)
                midl_user_free(InfoBuffer->AdminComment.AdminComment.Buffer);

            midl_user_free(InfoBuffer);
        }
    }

    return Status;
}


/* Function 28 */
NTSTATUS
NTAPI
SamrQueryInformationAlias(IN SAMPR_HANDLE AliasHandle,
                          IN ALIAS_INFORMATION_CLASS AliasInformationClass,
                          OUT PSAMPR_ALIAS_INFO_BUFFER *Buffer)
{
    PSAM_DB_OBJECT AliasObject;
    NTSTATUS Status;

    TRACE("SamrQueryInformationAlias(%p %lu %p)\n",
          AliasHandle, AliasInformationClass, Buffer);

    RtlAcquireResourceShared(&SampResource,
                             TRUE);

    /* Validate the alias handle */
    Status = SampValidateDbObject(AliasHandle,
                                  SamDbAliasObject,
                                  ALIAS_READ_INFORMATION,
                                  &AliasObject);
    if (!NT_SUCCESS(Status))
        goto done;

    switch (AliasInformationClass)
    {
        case AliasGeneralInformation:
            Status = SampQueryAliasGeneral(AliasObject,
                                           Buffer);
            break;

        case AliasNameInformation:
            Status = SampQueryAliasName(AliasObject,
                                        Buffer);
            break;

        case AliasAdminCommentInformation:
            Status = SampQueryAliasAdminComment(AliasObject,
                                                Buffer);
            break;

        default:
            Status = STATUS_INVALID_INFO_CLASS;
            break;
    }

done:
    RtlReleaseResource(&SampResource);

    return Status;
}


static NTSTATUS
SampSetAliasName(PSAM_DB_OBJECT AliasObject,
                 PSAMPR_ALIAS_INFO_BUFFER Buffer)
{
    UNICODE_STRING OldAliasName = {0, 0, NULL};
    UNICODE_STRING NewAliasName;
    NTSTATUS Status;

    Status = SampGetObjectAttributeString(AliasObject,
                                          L"Name",
                                          (PRPC_UNICODE_STRING)&OldAliasName);
    if (!NT_SUCCESS(Status))
    {
        TRACE("SampGetObjectAttributeString failed (Status 0x%08lx)\n", Status);
        goto done;
    }

    /* Check the new account name */
    Status = SampCheckAccountName(&Buffer->Name.Name, 256);
    if (!NT_SUCCESS(Status))
    {
        TRACE("SampCheckAccountName failed (Status 0x%08lx)\n", Status);
        return Status;
    }

    NewAliasName.Length = Buffer->Name.Name.Length;
    NewAliasName.MaximumLength = Buffer->Name.Name.MaximumLength;
    NewAliasName.Buffer = Buffer->Name.Name.Buffer;

    if (!RtlEqualUnicodeString(&OldAliasName, &NewAliasName, TRUE))
    {
        Status = SampCheckAccountNameInDomain(AliasObject->ParentObject,
                                              NewAliasName.Buffer);
        if (!NT_SUCCESS(Status))
        {
            TRACE("Alias name \'%S\' already exists in domain (Status 0x%08lx)\n",
                  NewAliasName.Buffer, Status);
            goto done;
        }
    }

    Status = SampSetAccountNameInDomain(AliasObject->ParentObject,
                                        L"Aliases",
                                        NewAliasName.Buffer,
                                        AliasObject->RelativeId);
    if (!NT_SUCCESS(Status))
    {
        TRACE("SampSetAccountNameInDomain failed (Status 0x%08lx)\n", Status);
        goto done;
    }

    Status = SampRemoveAccountNameFromDomain(AliasObject->ParentObject,
                                             L"Aliases",
                                             OldAliasName.Buffer);
    if (!NT_SUCCESS(Status))
    {
        TRACE("SampRemoveAccountNameFromDomain failed (Status 0x%08lx)\n", Status);
        goto done;
    }

    Status = SampSetObjectAttributeString(AliasObject,
                                          L"Name",
                                          (PRPC_UNICODE_STRING)&NewAliasName);
    if (!NT_SUCCESS(Status))
    {
        TRACE("SampSetObjectAttribute failed (Status 0x%08lx)\n", Status);
    }

done:
    if (OldAliasName.Buffer != NULL)
        midl_user_free(OldAliasName.Buffer);

    return Status;
}


/* Function 29 */
NTSTATUS
NTAPI
SamrSetInformationAlias(IN SAMPR_HANDLE AliasHandle,
                        IN ALIAS_INFORMATION_CLASS AliasInformationClass,
                        IN PSAMPR_ALIAS_INFO_BUFFER Buffer)
{
    PSAM_DB_OBJECT AliasObject;
    NTSTATUS Status;

    TRACE("SamrSetInformationAlias(%p %lu %p)\n",
          AliasHandle, AliasInformationClass, Buffer);

    RtlAcquireResourceExclusive(&SampResource,
                                TRUE);

    /* Validate the alias handle */
    Status = SampValidateDbObject(AliasHandle,
                                  SamDbAliasObject,
                                  ALIAS_WRITE_ACCOUNT,
                                  &AliasObject);
    if (!NT_SUCCESS(Status))
        goto done;

    switch (AliasInformationClass)
    {
        case AliasNameInformation:
            Status = SampSetAliasName(AliasObject,
                                      Buffer);
            break;

        case AliasAdminCommentInformation:
            Status = SampSetObjectAttributeString(AliasObject,
                                                  L"Description",
                                                  &Buffer->AdminComment.AdminComment);
            break;

        default:
            Status = STATUS_INVALID_INFO_CLASS;
            break;
    }

done:
    RtlReleaseResource(&SampResource);

    return Status;
}


/* Function 30 */
NTSTATUS
NTAPI
SamrDeleteAlias(IN OUT SAMPR_HANDLE *AliasHandle)
{
    PSAM_DB_OBJECT AliasObject;
    NTSTATUS Status;

    TRACE("SamrDeleteAlias(%p)\n", AliasHandle);

    RtlAcquireResourceExclusive(&SampResource,
                                TRUE);

    /* Validate the alias handle */
    Status = SampValidateDbObject(*AliasHandle,
                                  SamDbAliasObject,
                                  DELETE,
                                  &AliasObject);
    if (!NT_SUCCESS(Status))
    {
        TRACE("SampValidateDbObject failed (Status 0x%08lx)\n", Status);
        goto done;
    }

    /* Fail, if the alias is built-in */
    if (AliasObject->RelativeId < 1000)
    {
        TRACE("You can not delete a special account!\n");
        Status = STATUS_SPECIAL_ACCOUNT;
        goto done;
    }

    /* Remove all members from the alias */
    Status = SampRemoveAllMembersFromAlias(AliasObject);
    if (!NT_SUCCESS(Status))
    {
        TRACE("SampRemoveAllMembersFromAlias() failed (Status 0x%08lx)\n", Status);
        goto done;
    }

    /* Delete the alias from the database */
    Status = SampDeleteAccountDbObject(AliasObject);
    if (!NT_SUCCESS(Status))
    {
        TRACE("SampDeleteAccountDbObject() failed (Status 0x%08lx)\n", Status);
        goto done;
    }

    /* Invalidate the handle */
    *AliasHandle = NULL;

done:
    RtlReleaseResource(&SampResource);

    return Status;
}


/* Function 31 */
NTSTATUS
NTAPI
SamrAddMemberToAlias(IN SAMPR_HANDLE AliasHandle,
                     IN PRPC_SID MemberId)
{
    PSAM_DB_OBJECT AliasObject;
    NTSTATUS Status;

    TRACE("SamrAddMemberToAlias(%p %p)\n",
          AliasHandle, MemberId);

    RtlAcquireResourceExclusive(&SampResource,
                                TRUE);

    /* Validate the alias handle */
    Status = SampValidateDbObject(AliasHandle,
                                  SamDbAliasObject,
                                  ALIAS_ADD_MEMBER,
                                  &AliasObject);
    if (!NT_SUCCESS(Status))
    {
        TRACE("failed with status 0x%08lx\n", Status);
        goto done;
    }

    Status = SampAddMemberToAlias(AliasObject,
                                  MemberId);
    if (!NT_SUCCESS(Status))
    {
        TRACE("failed with status 0x%08lx\n", Status);
    }

done:
    RtlReleaseResource(&SampResource);

    return Status;
}


/* Function 32 */
NTSTATUS
NTAPI
SamrRemoveMemberFromAlias(IN SAMPR_HANDLE AliasHandle,
                          IN PRPC_SID MemberId)
{
    PSAM_DB_OBJECT AliasObject;
    NTSTATUS Status;

    TRACE("SamrRemoveMemberFromAlias(%p %p)\n",
          AliasHandle, MemberId);

    RtlAcquireResourceExclusive(&SampResource,
                                TRUE);

    /* Validate the alias handle */
    Status = SampValidateDbObject(AliasHandle,
                                  SamDbAliasObject,
                                  ALIAS_REMOVE_MEMBER,
                                  &AliasObject);
    if (!NT_SUCCESS(Status))
    {
        TRACE("failed with status 0x%08lx\n", Status);
        goto done;
    }

    Status = SampRemoveMemberFromAlias(AliasObject,
                                       MemberId);
    if (!NT_SUCCESS(Status))
    {
        TRACE("failed with status 0x%08lx\n", Status);
    }

done:
    RtlReleaseResource(&SampResource);

    return Status;
}


/* Function 33 */
NTSTATUS
NTAPI
SamrGetMembersInAlias(IN SAMPR_HANDLE AliasHandle,
                      OUT PSAMPR_PSID_ARRAY_OUT Members)
{
    PSAM_DB_OBJECT AliasObject;
    PSAMPR_SID_INFORMATION MemberArray = NULL;
    ULONG MemberCount = 0;
    ULONG Index;
    NTSTATUS Status;

    TRACE("SamrGetMembersInAlias(%p %p %p)\n",
          AliasHandle, Members);

    RtlAcquireResourceShared(&SampResource,
                             TRUE);

    /* Validate the alias handle */
    Status = SampValidateDbObject(AliasHandle,
                                  SamDbAliasObject,
                                  ALIAS_LIST_MEMBERS,
                                  &AliasObject);
    if (!NT_SUCCESS(Status))
    {
        ERR("failed with status 0x%08lx\n", Status);
        goto done;
    }

    Status = SampGetMembersInAlias(AliasObject,
                                   &MemberCount,
                                   &MemberArray);

    /* Return the number of members and the member array */
    if (NT_SUCCESS(Status))
    {
        Members->Count = MemberCount;
        Members->Sids = MemberArray;
    }

done:
    /* Clean up the members array and the SID buffers if something failed */
    if (!NT_SUCCESS(Status))
    {
        if (MemberArray != NULL)
        {
            for (Index = 0; Index < MemberCount; Index++)
            {
                if (MemberArray[Index].SidPointer != NULL)
                    midl_user_free(MemberArray[Index].SidPointer);
            }

            midl_user_free(MemberArray);
        }
    }

    RtlReleaseResource(&SampResource);

    return Status;
}


/* Function 34 */
NTSTATUS
NTAPI
SamrOpenUser(IN SAMPR_HANDLE DomainHandle,
             IN ACCESS_MASK DesiredAccess,
             IN unsigned long UserId,
             OUT SAMPR_HANDLE *UserHandle)
{
    PSAM_DB_OBJECT DomainObject;
    PSAM_DB_OBJECT UserObject;
    WCHAR szRid[9];
    NTSTATUS Status;

    TRACE("SamrOpenUser(%p %lx %lx %p)\n",
          DomainHandle, DesiredAccess, UserId, UserHandle);

    /* Map generic access rights */
    RtlMapGenericMask(&DesiredAccess,
                      &UserMapping);

    RtlAcquireResourceShared(&SampResource,
                             TRUE);

    /* Validate the domain handle */
    Status = SampValidateDbObject(DomainHandle,
                                  SamDbDomainObject,
                                  DOMAIN_LOOKUP,
                                  &DomainObject);
    if (!NT_SUCCESS(Status))
    {
        TRACE("failed with status 0x%08lx\n", Status);
        goto done;
    }

    /* Convert the RID into a string (hex) */
    swprintf(szRid, L"%08lX", UserId);

    /* Create the user object */
    Status = SampOpenDbObject(DomainObject,
                              L"Users",
                              szRid,
                              UserId,
                              SamDbUserObject,
                              DesiredAccess,
                              &UserObject);
    if (!NT_SUCCESS(Status))
    {
        TRACE("failed with status 0x%08lx\n", Status);
        goto done;
    }

    *UserHandle = (SAMPR_HANDLE)UserObject;

done:
    RtlReleaseResource(&SampResource);

    return Status;
}


/* Function 35 */
NTSTATUS
NTAPI
SamrDeleteUser(IN OUT SAMPR_HANDLE *UserHandle)
{
    PSAM_DB_OBJECT UserObject;
    NTSTATUS Status;

    TRACE("SamrDeleteUser(%p)\n", UserHandle);

    RtlAcquireResourceExclusive(&SampResource,
                                TRUE);

    /* Validate the user handle */
    Status = SampValidateDbObject(*UserHandle,
                                  SamDbUserObject,
                                  DELETE,
                                  &UserObject);
    if (!NT_SUCCESS(Status))
    {
        TRACE("SampValidateDbObject() failed (Status 0x%08lx)\n", Status);
        goto done;
    }

    /* Fail, if the user is built-in */
    if (UserObject->RelativeId < 1000)
    {
        TRACE("You can not delete a special account!\n");
        Status = STATUS_SPECIAL_ACCOUNT;
        goto done;
    }

    /* Remove the user from all groups */
    Status = SampRemoveUserFromAllGroups(UserObject);
    if (!NT_SUCCESS(Status))
    {
        TRACE("SampRemoveUserFromAllGroups() failed (Status 0x%08lx)\n", Status);
        goto done;
    }

    /* Remove the user from all aliases */
    Status = SampRemoveUserFromAllAliases(UserObject);
    if (!NT_SUCCESS(Status))
    {
        TRACE("SampRemoveUserFromAllAliases() failed (Status 0x%08lx)\n", Status);
        goto done;
    }

    /* Delete the user from the database */
    Status = SampDeleteAccountDbObject(UserObject);
    if (!NT_SUCCESS(Status))
    {
        TRACE("SampDeleteAccountDbObject() failed (Status 0x%08lx)\n", Status);
        goto done;
    }

    /* Invalidate the handle */
    *UserHandle = NULL;

done:
    RtlReleaseResource(&SampResource);

    return Status;
}


static
NTSTATUS
SampQueryUserGeneral(PSAM_DB_OBJECT UserObject,
                     PSAMPR_USER_INFO_BUFFER *Buffer)
{
    PSAMPR_USER_INFO_BUFFER InfoBuffer = NULL;
    SAM_USER_FIXED_DATA FixedData;
    ULONG Length = 0;
    NTSTATUS Status;

    *Buffer = NULL;

    InfoBuffer = midl_user_allocate(sizeof(SAMPR_USER_INFO_BUFFER));
    if (InfoBuffer == NULL)
        return STATUS_INSUFFICIENT_RESOURCES;

    Length = sizeof(SAM_USER_FIXED_DATA);
    Status = SampGetObjectAttribute(UserObject,
                                    L"F",
                                    NULL,
                                    (PVOID)&FixedData,
                                    &Length);
    if (!NT_SUCCESS(Status))
        goto done;

    InfoBuffer->General.PrimaryGroupId = FixedData.PrimaryGroupId;

    /* Get the Name string */
    Status = SampGetObjectAttributeString(UserObject,
                                          L"Name",
                                          &InfoBuffer->General.UserName);
    if (!NT_SUCCESS(Status))
    {
        TRACE("Status 0x%08lx\n", Status);
        goto done;
    }

    /* Get the FullName string */
    Status = SampGetObjectAttributeString(UserObject,
                                          L"FullName",
                                          &InfoBuffer->General.FullName);
    if (!NT_SUCCESS(Status))
    {
        TRACE("Status 0x%08lx\n", Status);
        goto done;
    }

    /* Get the AdminComment string */
    Status = SampGetObjectAttributeString(UserObject,
                                          L"AdminComment",
                                          &InfoBuffer->General.AdminComment);
    if (!NT_SUCCESS(Status))
    {
        TRACE("Status 0x%08lx\n", Status);
        goto done;
    }

    /* Get the UserComment string */
    Status = SampGetObjectAttributeString(UserObject,
                                          L"UserComment",
                                          &InfoBuffer->General.UserComment);
    if (!NT_SUCCESS(Status))
    {
        TRACE("Status 0x%08lx\n", Status);
        goto done;
    }

    *Buffer = InfoBuffer;

done:
    if (!NT_SUCCESS(Status))
    {
        if (InfoBuffer != NULL)
        {
            if (InfoBuffer->General.UserName.Buffer != NULL)
                midl_user_free(InfoBuffer->General.UserName.Buffer);

            if (InfoBuffer->General.FullName.Buffer != NULL)
                midl_user_free(InfoBuffer->General.FullName.Buffer);

            if (InfoBuffer->General.AdminComment.Buffer != NULL)
                midl_user_free(InfoBuffer->General.AdminComment.Buffer);

            if (InfoBuffer->General.UserComment.Buffer != NULL)
                midl_user_free(InfoBuffer->General.UserComment.Buffer);

            midl_user_free(InfoBuffer);
        }
    }

    return Status;
}


static
NTSTATUS
SampQueryUserPreferences(PSAM_DB_OBJECT UserObject,
                         PSAMPR_USER_INFO_BUFFER *Buffer)
{
    PSAMPR_USER_INFO_BUFFER InfoBuffer = NULL;
    SAM_USER_FIXED_DATA FixedData;
    ULONG Length = 0;
    NTSTATUS Status;

    *Buffer = NULL;

    InfoBuffer = midl_user_allocate(sizeof(SAMPR_USER_INFO_BUFFER));
    if (InfoBuffer == NULL)
        return STATUS_INSUFFICIENT_RESOURCES;

    Length = sizeof(SAM_USER_FIXED_DATA);
    Status = SampGetObjectAttribute(UserObject,
                                    L"F",
                                    NULL,
                                    (PVOID)&FixedData,
                                    &Length);
    if (!NT_SUCCESS(Status))
        goto done;

    InfoBuffer->Preferences.CountryCode = FixedData.CountryCode;
    InfoBuffer->Preferences.CodePage = FixedData.CodePage;

    /* Get the UserComment string */
    Status = SampGetObjectAttributeString(UserObject,
                                          L"UserComment",
                                          &InfoBuffer->Preferences.UserComment);
    if (!NT_SUCCESS(Status))
    {
        TRACE("Status 0x%08lx\n", Status);
        goto done;
    }

    *Buffer = InfoBuffer;

done:
    if (!NT_SUCCESS(Status))
    {
        if (InfoBuffer != NULL)
        {
            if (InfoBuffer->Preferences.UserComment.Buffer != NULL)
                midl_user_free(InfoBuffer->Preferences.UserComment.Buffer);

            midl_user_free(InfoBuffer);
        }
    }

    return Status;
}


static
NTSTATUS
SampQueryUserLogon(PSAM_DB_OBJECT UserObject,
                   PSAMPR_USER_INFO_BUFFER *Buffer)
{
    PSAMPR_USER_INFO_BUFFER InfoBuffer = NULL;
    SAM_DOMAIN_FIXED_DATA DomainFixedData;
    SAM_USER_FIXED_DATA FixedData;
    LARGE_INTEGER PasswordCanChange;
    LARGE_INTEGER PasswordMustChange;
    ULONG Length = 0;
    NTSTATUS Status;

    *Buffer = NULL;

    InfoBuffer = midl_user_allocate(sizeof(SAMPR_USER_INFO_BUFFER));
    if (InfoBuffer == NULL)
        return STATUS_INSUFFICIENT_RESOURCES;

    /* Get the fixed size domain data */
    Length = sizeof(SAM_DOMAIN_FIXED_DATA);
    Status = SampGetObjectAttribute(UserObject->ParentObject,
                                    L"F",
                                    NULL,
                                    (PVOID)&DomainFixedData,
                                    &Length);
    if (!NT_SUCCESS(Status))
        goto done;

    /* Get the fixed size user data */
    Length = sizeof(SAM_USER_FIXED_DATA);
    Status = SampGetObjectAttribute(UserObject,
                                    L"F",
                                    NULL,
                                    (PVOID)&FixedData,
                                    &Length);
    if (!NT_SUCCESS(Status))
        goto done;

    InfoBuffer->Logon.UserId = FixedData.UserId;
    InfoBuffer->Logon.PrimaryGroupId = FixedData.PrimaryGroupId;
    InfoBuffer->Logon.LastLogon.LowPart = FixedData.LastLogon.LowPart;
    InfoBuffer->Logon.LastLogon.HighPart = FixedData.LastLogon.HighPart;
    InfoBuffer->Logon.LastLogoff.LowPart = FixedData.LastLogoff.LowPart;
    InfoBuffer->Logon.LastLogoff.HighPart = FixedData.LastLogoff.HighPart;
    InfoBuffer->Logon.PasswordLastSet.LowPart = FixedData.PasswordLastSet.LowPart;
    InfoBuffer->Logon.PasswordLastSet.HighPart = FixedData.PasswordLastSet.HighPart;
    InfoBuffer->Logon.BadPasswordCount = FixedData.BadPasswordCount;
    InfoBuffer->Logon.LogonCount = FixedData.LogonCount;
    InfoBuffer->Logon.UserAccountControl = FixedData.UserAccountControl;

    PasswordCanChange = SampAddRelativeTimeToTime(FixedData.PasswordLastSet,
                                                  DomainFixedData.MinPasswordAge);
    InfoBuffer->Logon.PasswordCanChange.LowPart = PasswordCanChange.LowPart;
    InfoBuffer->Logon.PasswordCanChange.HighPart = PasswordCanChange.HighPart;

    PasswordMustChange = SampAddRelativeTimeToTime(FixedData.PasswordLastSet,
                                                   DomainFixedData.MaxPasswordAge);
    InfoBuffer->Logon.PasswordMustChange.LowPart = PasswordMustChange.LowPart;
    InfoBuffer->Logon.PasswordMustChange.HighPart = PasswordMustChange.HighPart;

    /* Get the Name string */
    Status = SampGetObjectAttributeString(UserObject,
                                          L"Name",
                                          &InfoBuffer->Logon.UserName);
    if (!NT_SUCCESS(Status))
    {
        TRACE("Status 0x%08lx\n", Status);
        goto done;
    }

    /* Get the FullName string */
    Status = SampGetObjectAttributeString(UserObject,
                                          L"FullName",
                                          &InfoBuffer->Logon.FullName);
    if (!NT_SUCCESS(Status))
    {
        TRACE("Status 0x%08lx\n", Status);
        goto done;
    }

    /* Get the HomeDirectory string */
    Status = SampGetObjectAttributeString(UserObject,
                                          L"HomeDirectory",
                                          &InfoBuffer->Logon.HomeDirectory);
    if (!NT_SUCCESS(Status))
    {
        TRACE("Status 0x%08lx\n", Status);
        goto done;
    }

    /* Get the HomeDirectoryDrive string */
    Status = SampGetObjectAttributeString(UserObject,
                                          L"HomeDirectoryDrive",
                                          &InfoBuffer->Logon.HomeDirectoryDrive);
    if (!NT_SUCCESS(Status))
    {
        TRACE("Status 0x%08lx\n", Status);
        goto done;
    }

    /* Get the ScriptPath string */
    Status = SampGetObjectAttributeString(UserObject,
                                          L"ScriptPath",
                                          &InfoBuffer->Logon.ScriptPath);
    if (!NT_SUCCESS(Status))
    {
        TRACE("Status 0x%08lx\n", Status);
        goto done;
    }

    /* Get the ProfilePath string */
    Status = SampGetObjectAttributeString(UserObject,
                                          L"ProfilePath",
                                          &InfoBuffer->Logon.ProfilePath);
    if (!NT_SUCCESS(Status))
    {
        TRACE("Status 0x%08lx\n", Status);
        goto done;
    }

    /* Get the WorkStations string */
    Status = SampGetObjectAttributeString(UserObject,
                                          L"WorkStations",
                                          &InfoBuffer->Logon.WorkStations);
    if (!NT_SUCCESS(Status))
    {
        TRACE("Status 0x%08lx\n", Status);
        goto done;
    }

    /* Get the LogonHours attribute */
    Status = SampGetLogonHoursAttribute(UserObject,
                                       &InfoBuffer->Logon.LogonHours);
    if (!NT_SUCCESS(Status))
    {
        TRACE("Status 0x%08lx\n", Status);
        goto done;
    }

    *Buffer = InfoBuffer;

done:
    if (!NT_SUCCESS(Status))
    {
        if (InfoBuffer != NULL)
        {
            if (InfoBuffer->Logon.UserName.Buffer != NULL)
                midl_user_free(InfoBuffer->Logon.UserName.Buffer);

            if (InfoBuffer->Logon.FullName.Buffer != NULL)
                midl_user_free(InfoBuffer->Logon.FullName.Buffer);

            if (InfoBuffer->Logon.HomeDirectory.Buffer != NULL)
                midl_user_free(InfoBuffer->Logon.HomeDirectory.Buffer);

            if (InfoBuffer->Logon.HomeDirectoryDrive.Buffer != NULL)
                midl_user_free(InfoBuffer->Logon.HomeDirectoryDrive.Buffer);

            if (InfoBuffer->Logon.ScriptPath.Buffer != NULL)
                midl_user_free(InfoBuffer->Logon.ScriptPath.Buffer);

            if (InfoBuffer->Logon.ProfilePath.Buffer != NULL)
                midl_user_free(InfoBuffer->Logon.ProfilePath.Buffer);

            if (InfoBuffer->Logon.WorkStations.Buffer != NULL)
                midl_user_free(InfoBuffer->Logon.WorkStations.Buffer);

            if (InfoBuffer->Logon.LogonHours.LogonHours != NULL)
                midl_user_free(InfoBuffer->Logon.LogonHours.LogonHours);

            midl_user_free(InfoBuffer);
        }
    }

    return Status;
}


static
NTSTATUS
SampQueryUserAccount(PSAM_DB_OBJECT UserObject,
                     PSAMPR_USER_INFO_BUFFER *Buffer)
{
    PSAMPR_USER_INFO_BUFFER InfoBuffer = NULL;
    SAM_USER_FIXED_DATA FixedData;
    ULONG Length = 0;
    NTSTATUS Status;

    *Buffer = NULL;

    InfoBuffer = midl_user_allocate(sizeof(SAMPR_USER_INFO_BUFFER));
    if (InfoBuffer == NULL)
        return STATUS_INSUFFICIENT_RESOURCES;

    Length = sizeof(SAM_USER_FIXED_DATA);
    Status = SampGetObjectAttribute(UserObject,
                                    L"F",
                                    NULL,
                                    (PVOID)&FixedData,
                                    &Length);
    if (!NT_SUCCESS(Status))
        goto done;

    InfoBuffer->Account.UserId = FixedData.UserId;
    InfoBuffer->Account.PrimaryGroupId = FixedData.PrimaryGroupId;
    InfoBuffer->Account.LastLogon.LowPart = FixedData.LastLogon.LowPart;
    InfoBuffer->Account.LastLogon.HighPart = FixedData.LastLogon.HighPart;
    InfoBuffer->Account.LastLogoff.LowPart = FixedData.LastLogoff.LowPart;
    InfoBuffer->Account.LastLogoff.HighPart = FixedData.LastLogoff.HighPart;
    InfoBuffer->Account.PasswordLastSet.LowPart = FixedData.PasswordLastSet.LowPart;
    InfoBuffer->Account.PasswordLastSet.HighPart = FixedData.PasswordLastSet.HighPart;
    InfoBuffer->Account.AccountExpires.LowPart = FixedData.AccountExpires.LowPart;
    InfoBuffer->Account.AccountExpires.HighPart = FixedData.AccountExpires.HighPart;
    InfoBuffer->Account.BadPasswordCount = FixedData.BadPasswordCount;
    InfoBuffer->Account.LogonCount = FixedData.LogonCount;
    InfoBuffer->Account.UserAccountControl = FixedData.UserAccountControl;

    /* Get the Name string */
    Status = SampGetObjectAttributeString(UserObject,
                                          L"Name",
                                          &InfoBuffer->Account.UserName);
    if (!NT_SUCCESS(Status))
    {
        TRACE("Status 0x%08lx\n", Status);
        goto done;
    }

    /* Get the FullName string */
    Status = SampGetObjectAttributeString(UserObject,
                                          L"FullName",
                                          &InfoBuffer->Account.FullName);
    if (!NT_SUCCESS(Status))
    {
        TRACE("Status 0x%08lx\n", Status);
        goto done;
    }

    /* Get the HomeDirectory string */
    Status = SampGetObjectAttributeString(UserObject,
                                          L"HomeDirectory",
                                          &InfoBuffer->Account.HomeDirectory);
    if (!NT_SUCCESS(Status))
    {
        TRACE("Status 0x%08lx\n", Status);
        goto done;
    }

    /* Get the HomeDirectoryDrive string */
    Status = SampGetObjectAttributeString(UserObject,
                                          L"HomeDirectoryDrive",
                                          &InfoBuffer->Account.HomeDirectoryDrive);
    if (!NT_SUCCESS(Status))
    {
        TRACE("Status 0x%08lx\n", Status);
        goto done;
    }

    /* Get the ScriptPath string */
    Status = SampGetObjectAttributeString(UserObject,
                                          L"ScriptPath",
                                          &InfoBuffer->Account.ScriptPath);
    if (!NT_SUCCESS(Status))
    {
        TRACE("Status 0x%08lx\n", Status);
        goto done;
    }

    /* Get the ProfilePath string */
    Status = SampGetObjectAttributeString(UserObject,
                                          L"ProfilePath",
                                          &InfoBuffer->Account.ProfilePath);
    if (!NT_SUCCESS(Status))
    {
        TRACE("Status 0x%08lx\n", Status);
        goto done;
    }

    /* Get the AdminComment string */
    Status = SampGetObjectAttributeString(UserObject,
                                          L"AdminComment",
                                          &InfoBuffer->Account.AdminComment);
    if (!NT_SUCCESS(Status))
    {
        TRACE("Status 0x%08lx\n", Status);
        goto done;
    }

    /* Get the WorkStations string */
    Status = SampGetObjectAttributeString(UserObject,
                                          L"WorkStations",
                                          &InfoBuffer->Account.WorkStations);
    if (!NT_SUCCESS(Status))
    {
        TRACE("Status 0x%08lx\n", Status);
        goto done;
    }

    /* Get the LogonHours attribute */
    Status = SampGetLogonHoursAttribute(UserObject,
                                       &InfoBuffer->Account.LogonHours);
    if (!NT_SUCCESS(Status))
    {
        TRACE("Status 0x%08lx\n", Status);
        goto done;
    }

    *Buffer = InfoBuffer;

done:
    if (!NT_SUCCESS(Status))
    {
        if (InfoBuffer != NULL)
        {
            if (InfoBuffer->Account.UserName.Buffer != NULL)
                midl_user_free(InfoBuffer->Account.UserName.Buffer);

            if (InfoBuffer->Account.FullName.Buffer != NULL)
                midl_user_free(InfoBuffer->Account.FullName.Buffer);

            if (InfoBuffer->Account.HomeDirectory.Buffer != NULL)
                midl_user_free(InfoBuffer->Account.HomeDirectory.Buffer);

            if (InfoBuffer->Account.HomeDirectoryDrive.Buffer != NULL)
                midl_user_free(InfoBuffer->Account.HomeDirectoryDrive.Buffer);

            if (InfoBuffer->Account.ScriptPath.Buffer != NULL)
                midl_user_free(InfoBuffer->Account.ScriptPath.Buffer);

            if (InfoBuffer->Account.ProfilePath.Buffer != NULL)
                midl_user_free(InfoBuffer->Account.ProfilePath.Buffer);

            if (InfoBuffer->Account.AdminComment.Buffer != NULL)
                midl_user_free(InfoBuffer->Account.AdminComment.Buffer);

            if (InfoBuffer->Account.WorkStations.Buffer != NULL)
                midl_user_free(InfoBuffer->Account.WorkStations.Buffer);

            if (InfoBuffer->Account.LogonHours.LogonHours != NULL)
                midl_user_free(InfoBuffer->Account.LogonHours.LogonHours);

            midl_user_free(InfoBuffer);
        }
    }

    return Status;
}


static
NTSTATUS
SampQueryUserLogonHours(PSAM_DB_OBJECT UserObject,
                        PSAMPR_USER_INFO_BUFFER *Buffer)
{
    PSAMPR_USER_INFO_BUFFER InfoBuffer = NULL;
    NTSTATUS Status;

    TRACE("(%p %p)\n", UserObject, Buffer);

    *Buffer = NULL;

    InfoBuffer = midl_user_allocate(sizeof(SAMPR_USER_INFO_BUFFER));
    if (InfoBuffer == NULL)
    {
        TRACE("Failed to allocate InfoBuffer!\n");
        return STATUS_INSUFFICIENT_RESOURCES;
    }

    Status = SampGetLogonHoursAttribute(UserObject,
                                        &InfoBuffer->LogonHours.LogonHours);
    if (!NT_SUCCESS(Status))
    {
        TRACE("SampGetLogonHoursAttribute failed (Status 0x%08lx)\n", Status);
        goto done;
    }

    *Buffer = InfoBuffer;

done:
    if (!NT_SUCCESS(Status))
    {
        if (InfoBuffer != NULL)
        {
            if (InfoBuffer->LogonHours.LogonHours.LogonHours != NULL)
                midl_user_free(InfoBuffer->LogonHours.LogonHours.LogonHours);

            midl_user_free(InfoBuffer);
        }
    }

    return Status;
}


static
NTSTATUS
SampQueryUserName(PSAM_DB_OBJECT UserObject,
                  PSAMPR_USER_INFO_BUFFER *Buffer)
{
    PSAMPR_USER_INFO_BUFFER InfoBuffer = NULL;
    NTSTATUS Status;

    *Buffer = NULL;

    InfoBuffer = midl_user_allocate(sizeof(SAMPR_USER_INFO_BUFFER));
    if (InfoBuffer == NULL)
        return STATUS_INSUFFICIENT_RESOURCES;

    /* Get the Name string */
    Status = SampGetObjectAttributeString(UserObject,
                                          L"Name",
                                          &InfoBuffer->Name.UserName);
    if (!NT_SUCCESS(Status))
    {
        TRACE("Status 0x%08lx\n", Status);
        goto done;
    }

    /* Get the FullName string */
    Status = SampGetObjectAttributeString(UserObject,
                                          L"FullName",
                                          &InfoBuffer->Name.FullName);
    if (!NT_SUCCESS(Status))
    {
        TRACE("Status 0x%08lx\n", Status);
        goto done;
    }

    *Buffer = InfoBuffer;

done:
    if (!NT_SUCCESS(Status))
    {
        if (InfoBuffer != NULL)
        {
            if (InfoBuffer->Name.UserName.Buffer != NULL)
                midl_user_free(InfoBuffer->Name.UserName.Buffer);

            if (InfoBuffer->Name.FullName.Buffer != NULL)
                midl_user_free(InfoBuffer->Name.FullName.Buffer);

            midl_user_free(InfoBuffer);
        }
    }

    return Status;
}


static NTSTATUS
SampQueryUserAccountName(PSAM_DB_OBJECT UserObject,
                         PSAMPR_USER_INFO_BUFFER *Buffer)
{
    PSAMPR_USER_INFO_BUFFER InfoBuffer = NULL;
    NTSTATUS Status;

    *Buffer = NULL;

    InfoBuffer = midl_user_allocate(sizeof(SAMPR_USER_INFO_BUFFER));
    if (InfoBuffer == NULL)
        return STATUS_INSUFFICIENT_RESOURCES;

    /* Get the Name string */
    Status = SampGetObjectAttributeString(UserObject,
                                          L"Name",
                                          &InfoBuffer->AccountName.UserName);
    if (!NT_SUCCESS(Status))
    {
        TRACE("Status 0x%08lx\n", Status);
        goto done;
    }

    *Buffer = InfoBuffer;

done:
    if (!NT_SUCCESS(Status))
    {
        if (InfoBuffer != NULL)
        {
            if (InfoBuffer->AccountName.UserName.Buffer != NULL)
                midl_user_free(InfoBuffer->AccountName.UserName.Buffer);

            midl_user_free(InfoBuffer);
        }
    }

    return Status;
}


static NTSTATUS
SampQueryUserFullName(PSAM_DB_OBJECT UserObject,
                      PSAMPR_USER_INFO_BUFFER *Buffer)
{
    PSAMPR_USER_INFO_BUFFER InfoBuffer = NULL;
    NTSTATUS Status;

    *Buffer = NULL;

    InfoBuffer = midl_user_allocate(sizeof(SAMPR_USER_INFO_BUFFER));
    if (InfoBuffer == NULL)
        return STATUS_INSUFFICIENT_RESOURCES;

    /* Get the FullName string */
    Status = SampGetObjectAttributeString(UserObject,
                                          L"FullName",
                                          &InfoBuffer->FullName.FullName);
    if (!NT_SUCCESS(Status))
    {
        TRACE("Status 0x%08lx\n", Status);
        goto done;
    }

    *Buffer = InfoBuffer;

done:
    if (!NT_SUCCESS(Status))
    {
        if (InfoBuffer != NULL)
        {
            if (InfoBuffer->FullName.FullName.Buffer != NULL)
                midl_user_free(InfoBuffer->FullName.FullName.Buffer);

            midl_user_free(InfoBuffer);
        }
    }

    return Status;
}


static
NTSTATUS
SampQueryUserPrimaryGroup(PSAM_DB_OBJECT UserObject,
                          PSAMPR_USER_INFO_BUFFER *Buffer)
{
    PSAMPR_USER_INFO_BUFFER InfoBuffer = NULL;
    SAM_USER_FIXED_DATA FixedData;
    ULONG Length = 0;
    NTSTATUS Status;

    *Buffer = NULL;

    InfoBuffer = midl_user_allocate(sizeof(SAMPR_USER_INFO_BUFFER));
    if (InfoBuffer == NULL)
        return STATUS_INSUFFICIENT_RESOURCES;

    Length = sizeof(SAM_USER_FIXED_DATA);
    Status = SampGetObjectAttribute(UserObject,
                                    L"F",
                                    NULL,
                                    (PVOID)&FixedData,
                                    &Length);
    if (!NT_SUCCESS(Status))
        goto done;

    InfoBuffer->PrimaryGroup.PrimaryGroupId = FixedData.PrimaryGroupId;

    *Buffer = InfoBuffer;

done:
    if (!NT_SUCCESS(Status))
    {
        if (InfoBuffer != NULL)
        {
            midl_user_free(InfoBuffer);
        }
    }

    return Status;
}


static NTSTATUS
SampQueryUserHome(PSAM_DB_OBJECT UserObject,
                  PSAMPR_USER_INFO_BUFFER *Buffer)
{
    PSAMPR_USER_INFO_BUFFER InfoBuffer = NULL;
    NTSTATUS Status;

    *Buffer = NULL;

    InfoBuffer = midl_user_allocate(sizeof(SAMPR_USER_INFO_BUFFER));
    if (InfoBuffer == NULL)
        return STATUS_INSUFFICIENT_RESOURCES;

    /* Get the HomeDirectory string */
    Status = SampGetObjectAttributeString(UserObject,
                                          L"HomeDirectory",
                                          &InfoBuffer->Home.HomeDirectory);
    if (!NT_SUCCESS(Status))
    {
        TRACE("Status 0x%08lx\n", Status);
        goto done;
    }

    /* Get the HomeDirectoryDrive string */
    Status = SampGetObjectAttributeString(UserObject,
                                          L"HomeDirectoryDrive",
                                          &InfoBuffer->Home.HomeDirectoryDrive);
    if (!NT_SUCCESS(Status))
    {
        TRACE("Status 0x%08lx\n", Status);
        goto done;
    }

    *Buffer = InfoBuffer;

done:
    if (!NT_SUCCESS(Status))
    {
        if (InfoBuffer != NULL)
        {
            if (InfoBuffer->Home.HomeDirectory.Buffer != NULL)
                midl_user_free(InfoBuffer->Home.HomeDirectory.Buffer);

            if (InfoBuffer->Home.HomeDirectoryDrive.Buffer != NULL)
                midl_user_free(InfoBuffer->Home.HomeDirectoryDrive.Buffer);

            midl_user_free(InfoBuffer);
        }
    }

    return Status;
}


static NTSTATUS
SampQueryUserScript(PSAM_DB_OBJECT UserObject,
                    PSAMPR_USER_INFO_BUFFER *Buffer)
{
    PSAMPR_USER_INFO_BUFFER InfoBuffer = NULL;
    NTSTATUS Status;

    *Buffer = NULL;

    InfoBuffer = midl_user_allocate(sizeof(SAMPR_USER_INFO_BUFFER));
    if (InfoBuffer == NULL)
        return STATUS_INSUFFICIENT_RESOURCES;

    /* Get the ScriptPath string */
    Status = SampGetObjectAttributeString(UserObject,
                                          L"ScriptPath",
                                          &InfoBuffer->Script.ScriptPath);
    if (!NT_SUCCESS(Status))
    {
        TRACE("Status 0x%08lx\n", Status);
        goto done;
    }

    *Buffer = InfoBuffer;

done:
    if (!NT_SUCCESS(Status))
    {
        if (InfoBuffer != NULL)
        {
            if (InfoBuffer->Script.ScriptPath.Buffer != NULL)
                midl_user_free(InfoBuffer->Script.ScriptPath.Buffer);

            midl_user_free(InfoBuffer);
        }
    }

    return Status;
}


static NTSTATUS
SampQueryUserProfile(PSAM_DB_OBJECT UserObject,
                     PSAMPR_USER_INFO_BUFFER *Buffer)
{
    PSAMPR_USER_INFO_BUFFER InfoBuffer = NULL;
    NTSTATUS Status;

    *Buffer = NULL;

    InfoBuffer = midl_user_allocate(sizeof(SAMPR_USER_INFO_BUFFER));
    if (InfoBuffer == NULL)
        return STATUS_INSUFFICIENT_RESOURCES;

    /* Get the ProfilePath string */
    Status = SampGetObjectAttributeString(UserObject,
                                          L"ProfilePath",
                                          &InfoBuffer->Profile.ProfilePath);
    if (!NT_SUCCESS(Status))
    {
        TRACE("Status 0x%08lx\n", Status);
        goto done;
    }

    *Buffer = InfoBuffer;

done:
    if (!NT_SUCCESS(Status))
    {
        if (InfoBuffer != NULL)
        {
            if (InfoBuffer->Profile.ProfilePath.Buffer != NULL)
                midl_user_free(InfoBuffer->Profile.ProfilePath.Buffer);

            midl_user_free(InfoBuffer);
        }
    }

    return Status;
}


static NTSTATUS
SampQueryUserAdminComment(PSAM_DB_OBJECT UserObject,
                          PSAMPR_USER_INFO_BUFFER *Buffer)
{
    PSAMPR_USER_INFO_BUFFER InfoBuffer = NULL;
    NTSTATUS Status;

    *Buffer = NULL;

    InfoBuffer = midl_user_allocate(sizeof(SAMPR_USER_INFO_BUFFER));
    if (InfoBuffer == NULL)
        return STATUS_INSUFFICIENT_RESOURCES;

    /* Get the AdminComment string */
    Status = SampGetObjectAttributeString(UserObject,
                                          L"AdminComment",
                                          &InfoBuffer->AdminComment.AdminComment);
    if (!NT_SUCCESS(Status))
    {
        TRACE("Status 0x%08lx\n", Status);
        goto done;
    }

    *Buffer = InfoBuffer;

done:
    if (!NT_SUCCESS(Status))
    {
        if (InfoBuffer != NULL)
        {
            if (InfoBuffer->AdminComment.AdminComment.Buffer != NULL)
                midl_user_free(InfoBuffer->AdminComment.AdminComment.Buffer);

            midl_user_free(InfoBuffer);
        }
    }

    return Status;
}


static NTSTATUS
SampQueryUserWorkStations(PSAM_DB_OBJECT UserObject,
                          PSAMPR_USER_INFO_BUFFER *Buffer)
{
    PSAMPR_USER_INFO_BUFFER InfoBuffer = NULL;
    NTSTATUS Status;

    *Buffer = NULL;

    InfoBuffer = midl_user_allocate(sizeof(SAMPR_USER_INFO_BUFFER));
    if (InfoBuffer == NULL)
        return STATUS_INSUFFICIENT_RESOURCES;

    /* Get the WorkStations string */
    Status = SampGetObjectAttributeString(UserObject,
                                          L"WorkStations",
                                          &InfoBuffer->WorkStations.WorkStations);
    if (!NT_SUCCESS(Status))
    {
        TRACE("Status 0x%08lx\n", Status);
        goto done;
    }

    *Buffer = InfoBuffer;

done:
    if (!NT_SUCCESS(Status))
    {
        if (InfoBuffer != NULL)
        {
            if (InfoBuffer->WorkStations.WorkStations.Buffer != NULL)
                midl_user_free(InfoBuffer->WorkStations.WorkStations.Buffer);

            midl_user_free(InfoBuffer);
        }
    }

    return Status;
}


static
NTSTATUS
SampQueryUserControl(PSAM_DB_OBJECT UserObject,
                     PSAMPR_USER_INFO_BUFFER *Buffer)
{
    PSAMPR_USER_INFO_BUFFER InfoBuffer = NULL;
    SAM_USER_FIXED_DATA FixedData;
    ULONG Length = 0;
    NTSTATUS Status;

    *Buffer = NULL;

    InfoBuffer = midl_user_allocate(sizeof(SAMPR_USER_INFO_BUFFER));
    if (InfoBuffer == NULL)
        return STATUS_INSUFFICIENT_RESOURCES;

    Length = sizeof(SAM_USER_FIXED_DATA);
    Status = SampGetObjectAttribute(UserObject,
                                    L"F",
                                    NULL,
                                    (PVOID)&FixedData,
                                    &Length);
    if (!NT_SUCCESS(Status))
        goto done;

    InfoBuffer->Control.UserAccountControl = FixedData.UserAccountControl;

    *Buffer = InfoBuffer;

done:
    if (!NT_SUCCESS(Status))
    {
        if (InfoBuffer != NULL)
        {
            midl_user_free(InfoBuffer);
        }
    }

    return Status;
}


static
NTSTATUS
SampQueryUserExpires(PSAM_DB_OBJECT UserObject,
                     PSAMPR_USER_INFO_BUFFER *Buffer)
{
    PSAMPR_USER_INFO_BUFFER InfoBuffer = NULL;
    SAM_USER_FIXED_DATA FixedData;
    ULONG Length = 0;
    NTSTATUS Status;

    *Buffer = NULL;

    InfoBuffer = midl_user_allocate(sizeof(SAMPR_USER_INFO_BUFFER));
    if (InfoBuffer == NULL)
        return STATUS_INSUFFICIENT_RESOURCES;

    Length = sizeof(SAM_USER_FIXED_DATA);
    Status = SampGetObjectAttribute(UserObject,
                                    L"F",
                                    NULL,
                                    (PVOID)&FixedData,
                                    &Length);
    if (!NT_SUCCESS(Status))
        goto done;

    InfoBuffer->Expires.AccountExpires.LowPart = FixedData.AccountExpires.LowPart;
    InfoBuffer->Expires.AccountExpires.HighPart = FixedData.AccountExpires.HighPart;

    *Buffer = InfoBuffer;

done:
    if (!NT_SUCCESS(Status))
    {
        if (InfoBuffer != NULL)
        {
            midl_user_free(InfoBuffer);
        }
    }

    return Status;
}


static
NTSTATUS
SampQueryUserInternal1(PSAM_DB_OBJECT UserObject,
                       PSAMPR_USER_INFO_BUFFER *Buffer)
{
    PSAMPR_USER_INFO_BUFFER InfoBuffer = NULL;
    ULONG Length = 0;
    NTSTATUS Status = STATUS_SUCCESS;

    /* Fail, if the caller is not a trusted caller */
    if (UserObject->Trusted == FALSE)
        return STATUS_INVALID_INFO_CLASS;

    *Buffer = NULL;

    InfoBuffer = midl_user_allocate(sizeof(SAMPR_USER_INFO_BUFFER));
    if (InfoBuffer == NULL)
        return STATUS_INSUFFICIENT_RESOURCES;

    InfoBuffer->Internal1.LmPasswordPresent = FALSE;
    InfoBuffer->Internal1.NtPasswordPresent = FALSE;

    /* Get the NT password */
    Length = 0;
    SampGetObjectAttribute(UserObject,
                           L"NTPwd",
                           NULL,
                           NULL,
                           &Length);

    if (Length == sizeof(ENCRYPTED_NT_OWF_PASSWORD))
    {
        Status = SampGetObjectAttribute(UserObject,
                                        L"NTPwd",
                                        NULL,
                                        (PVOID)&InfoBuffer->Internal1.EncryptedNtOwfPassword,
                                        &Length);
        if (!NT_SUCCESS(Status))
            goto done;

        if (memcmp(&InfoBuffer->Internal1.EncryptedNtOwfPassword,
                   &EmptyNtHash,
                   sizeof(ENCRYPTED_NT_OWF_PASSWORD)))
            InfoBuffer->Internal1.NtPasswordPresent = TRUE;
    }


    /* Get the LM password */
    Length = 0;
    SampGetObjectAttribute(UserObject,
                           L"LMPwd",
                           NULL,
                           NULL,
                           &Length);

    if (Length == sizeof(ENCRYPTED_LM_OWF_PASSWORD))
    {
        Status = SampGetObjectAttribute(UserObject,
                                        L"LMPwd",
                                        NULL,
                                        (PVOID)&InfoBuffer->Internal1.EncryptedLmOwfPassword,
                                        &Length);
        if (!NT_SUCCESS(Status))
            goto done;

        if (memcmp(&InfoBuffer->Internal1.EncryptedLmOwfPassword,
                   &EmptyLmHash,
                   sizeof(ENCRYPTED_LM_OWF_PASSWORD)))
            InfoBuffer->Internal1.LmPasswordPresent = TRUE;
    }

    InfoBuffer->Internal1.PasswordExpired = FALSE;

    *Buffer = InfoBuffer;

done:
    if (!NT_SUCCESS(Status))
    {
        if (InfoBuffer != NULL)
        {
            midl_user_free(InfoBuffer);
        }
    }

    return Status;
}


static
NTSTATUS
SampQueryUserInternal2(PSAM_DB_OBJECT UserObject,
                       PSAMPR_USER_INFO_BUFFER *Buffer)
{
    PSAMPR_USER_INFO_BUFFER InfoBuffer = NULL;
    SAM_USER_FIXED_DATA FixedData;
    ULONG Length = 0;
    NTSTATUS Status;

    *Buffer = NULL;

    InfoBuffer = midl_user_allocate(sizeof(SAMPR_USER_INFO_BUFFER));
    if (InfoBuffer == NULL)
        return STATUS_INSUFFICIENT_RESOURCES;

    Length = sizeof(SAM_USER_FIXED_DATA);
    Status = SampGetObjectAttribute(UserObject,
                                    L"F",
                                    NULL,
                                    (PVOID)&FixedData,
                                    &Length);
    if (!NT_SUCCESS(Status))
        goto done;

    InfoBuffer->Internal2.Flags = 0;
    InfoBuffer->Internal2.LastLogon.LowPart = FixedData.LastLogon.LowPart;
    InfoBuffer->Internal2.LastLogon.HighPart = FixedData.LastLogon.HighPart;
    InfoBuffer->Internal2.LastLogoff.LowPart = FixedData.LastLogoff.LowPart;
    InfoBuffer->Internal2.LastLogoff.HighPart = FixedData.LastLogoff.HighPart;
    InfoBuffer->Internal2.BadPasswordCount = FixedData.BadPasswordCount;
    InfoBuffer->Internal2.LogonCount = FixedData.LogonCount;

    *Buffer = InfoBuffer;

done:
    if (!NT_SUCCESS(Status))
    {
        if (InfoBuffer != NULL)
        {
            midl_user_free(InfoBuffer);
        }
    }

    return Status;
}


static NTSTATUS
SampQueryUserParameters(PSAM_DB_OBJECT UserObject,
                        PSAMPR_USER_INFO_BUFFER *Buffer)
{
    PSAMPR_USER_INFO_BUFFER InfoBuffer = NULL;
    NTSTATUS Status;

    *Buffer = NULL;

    InfoBuffer = midl_user_allocate(sizeof(SAMPR_USER_INFO_BUFFER));
    if (InfoBuffer == NULL)
        return STATUS_INSUFFICIENT_RESOURCES;

    /* Get the Parameters string */
    Status = SampGetObjectAttributeString(UserObject,
                                          L"Parameters",
                                          &InfoBuffer->Parameters.Parameters);
    if (!NT_SUCCESS(Status))
    {
        TRACE("Status 0x%08lx\n", Status);
        goto done;
    }

    *Buffer = InfoBuffer;

done:
    if (!NT_SUCCESS(Status))
    {
        if (InfoBuffer != NULL)
        {
            if (InfoBuffer->Parameters.Parameters.Buffer != NULL)
                midl_user_free(InfoBuffer->Parameters.Parameters.Buffer);

            midl_user_free(InfoBuffer);
        }
    }

    return Status;
}


static NTSTATUS
SampQueryUserAll(PSAM_DB_OBJECT UserObject,
                 PSAMPR_USER_INFO_BUFFER *Buffer)
{
    PSAMPR_USER_INFO_BUFFER InfoBuffer = NULL;
    SAM_DOMAIN_FIXED_DATA DomainFixedData;
    SAM_USER_FIXED_DATA FixedData;
    LARGE_INTEGER PasswordCanChange;
    LARGE_INTEGER PasswordMustChange;
    ULONG Length = 0;
    NTSTATUS Status;

    *Buffer = NULL;

    InfoBuffer = midl_user_allocate(sizeof(SAMPR_USER_INFO_BUFFER));
    if (InfoBuffer == NULL)
        return STATUS_INSUFFICIENT_RESOURCES;

    /* Get the fixed size domain data */
    Length = sizeof(SAM_DOMAIN_FIXED_DATA);
    Status = SampGetObjectAttribute(UserObject->ParentObject,
                                    L"F",
                                    NULL,
                                    (PVOID)&DomainFixedData,
                                    &Length);
    if (!NT_SUCCESS(Status))
        goto done;

    /* Get the fixed size user data */
    Length = sizeof(SAM_USER_FIXED_DATA);
    Status = SampGetObjectAttribute(UserObject,
                                    L"F",
                                    NULL,
                                    (PVOID)&FixedData,
                                    &Length);
    if (!NT_SUCCESS(Status))
        goto done;

    /* Set the fields to be returned */
    if (UserObject->Trusted)
    {
        InfoBuffer->All.WhichFields = USER_ALL_READ_GENERAL_MASK |
                                      USER_ALL_READ_LOGON_MASK |
                                      USER_ALL_READ_ACCOUNT_MASK |
                                      USER_ALL_READ_PREFERENCES_MASK |
                                      USER_ALL_READ_TRUSTED_MASK;
    }
    else
    {
        InfoBuffer->All.WhichFields = 0;

        if (UserObject->Access & USER_READ_GENERAL)
            InfoBuffer->All.WhichFields |= USER_ALL_READ_GENERAL_MASK;

        if (UserObject->Access & USER_READ_LOGON)
            InfoBuffer->All.WhichFields |= USER_ALL_READ_LOGON_MASK;

        if (UserObject->Access & USER_READ_ACCOUNT)
            InfoBuffer->All.WhichFields |= USER_ALL_READ_ACCOUNT_MASK;

        if (UserObject->Access & USER_READ_PREFERENCES)
            InfoBuffer->All.WhichFields |= USER_ALL_READ_PREFERENCES_MASK;
    }

    /* Fail, if no fields are to be returned */
    if (InfoBuffer->All.WhichFields == 0)
    {
        Status = STATUS_ACCESS_DENIED;
        goto done;
    }

    /* Get the UserName attribute */
    if (InfoBuffer->All.WhichFields & USER_ALL_USERNAME)
    {
        Status = SampGetObjectAttributeString(UserObject,
                                              L"Name",
                                              &InfoBuffer->All.UserName);
        if (!NT_SUCCESS(Status))
        {
            TRACE("Status 0x%08lx\n", Status);
            goto done;
        }
    }

    /* Get the FullName attribute */
    if (InfoBuffer->All.WhichFields & USER_ALL_FULLNAME)
    {
        Status = SampGetObjectAttributeString(UserObject,
                                              L"FullName",
                                              &InfoBuffer->All.FullName);
        if (!NT_SUCCESS(Status))
        {
            TRACE("Status 0x%08lx\n", Status);
            goto done;
        }
    }

    /* Get the UserId attribute */
    if (InfoBuffer->All.WhichFields & USER_ALL_USERID)
    {
        InfoBuffer->All.UserId = FixedData.UserId;
    }

    /* Get the PrimaryGroupId attribute */
    if (InfoBuffer->All.WhichFields & USER_ALL_PRIMARYGROUPID)
    {
        InfoBuffer->All.PrimaryGroupId = FixedData.PrimaryGroupId;
    }

    /* Get the AdminComment attribute */
    if (InfoBuffer->All.WhichFields & USER_ALL_ADMINCOMMENT)
    {
        Status = SampGetObjectAttributeString(UserObject,
                                              L"AdminComment",
                                              &InfoBuffer->All.AdminComment);
        if (!NT_SUCCESS(Status))
        {
            TRACE("Status 0x%08lx\n", Status);
            goto done;
        }
    }

    /* Get the UserComment attribute */
    if (InfoBuffer->All.WhichFields & USER_ALL_USERCOMMENT)
    {
        Status = SampGetObjectAttributeString(UserObject,
                                              L"UserComment",
                                              &InfoBuffer->All.UserComment);
        if (!NT_SUCCESS(Status))
        {
            TRACE("Status 0x%08lx\n", Status);
            goto done;
        }
    }

    /* Get the HomeDirectory attribute */
    if (InfoBuffer->All.WhichFields & USER_ALL_HOMEDIRECTORY)
    {
        Status = SampGetObjectAttributeString(UserObject,
                                              L"HomeDirectory",
                                              &InfoBuffer->All.HomeDirectory);
        if (!NT_SUCCESS(Status))
        {
            TRACE("Status 0x%08lx\n", Status);
            goto done;
        }
    }

    /* Get the HomeDirectoryDrive attribute */
    if (InfoBuffer->All.WhichFields & USER_ALL_HOMEDIRECTORYDRIVE)
    {
        Status = SampGetObjectAttributeString(UserObject,
                                              L"HomeDirectoryDrive",
                                              &InfoBuffer->Home.HomeDirectoryDrive);
        if (!NT_SUCCESS(Status))
        {
            TRACE("Status 0x%08lx\n", Status);
            goto done;
        }
    }

    /* Get the ScriptPath attribute */
    if (InfoBuffer->All.WhichFields & USER_ALL_SCRIPTPATH)
    {
        Status = SampGetObjectAttributeString(UserObject,
                                              L"ScriptPath",
                                              &InfoBuffer->All.ScriptPath);
        if (!NT_SUCCESS(Status))
        {
            TRACE("Status 0x%08lx\n", Status);
            goto done;
        }
    }

    /* Get the ProfilePath attribute */
    if (InfoBuffer->All.WhichFields & USER_ALL_PROFILEPATH)
    {
        Status = SampGetObjectAttributeString(UserObject,
                                              L"ProfilePath",
                                              &InfoBuffer->All.ProfilePath);
        if (!NT_SUCCESS(Status))
        {
            TRACE("Status 0x%08lx\n", Status);
            goto done;
        }
    }

    /* Get the WorkStations attribute */
    if (InfoBuffer->All.WhichFields & USER_ALL_WORKSTATIONS)
    {
        Status = SampGetObjectAttributeString(UserObject,
                                              L"WorkStations",
                                              &InfoBuffer->All.WorkStations);
        if (!NT_SUCCESS(Status))
        {
            TRACE("Status 0x%08lx\n", Status);
            goto done;
        }
    }

    /* Get the LastLogon attribute */
    if (InfoBuffer->All.WhichFields & USER_ALL_LASTLOGON)
    {
        InfoBuffer->All.LastLogon.LowPart = FixedData.LastLogon.LowPart;
        InfoBuffer->All.LastLogon.HighPart = FixedData.LastLogon.HighPart;
    }

    /* Get the LastLogoff attribute */
    if (InfoBuffer->All.WhichFields & USER_ALL_LASTLOGOFF)
    {
        InfoBuffer->All.LastLogoff.LowPart = FixedData.LastLogoff.LowPart;
        InfoBuffer->All.LastLogoff.HighPart = FixedData.LastLogoff.HighPart;
    }

    /* Get the LogonHours attribute */
    if (InfoBuffer->All.WhichFields & USER_ALL_LOGONHOURS)
    {
        Status = SampGetLogonHoursAttribute(UserObject,
                                            &InfoBuffer->All.LogonHours);
        if (!NT_SUCCESS(Status))
        {
            TRACE("Status 0x%08lx\n", Status);
            goto done;
        }
    }

    /* Get the BadPasswordCount attribute */
    if (InfoBuffer->All.WhichFields & USER_ALL_BADPASSWORDCOUNT)
    {
        InfoBuffer->All.BadPasswordCount = FixedData.BadPasswordCount;
    }

    /* Get the LogonCount attribute */
    if (InfoBuffer->All.WhichFields & USER_ALL_LOGONCOUNT)
    {
        InfoBuffer->All.LogonCount = FixedData.LogonCount;
    }

    /* Get the PasswordCanChange attribute */
    if (InfoBuffer->All.WhichFields & USER_ALL_PASSWORDCANCHANGE)
    {
        PasswordCanChange = SampAddRelativeTimeToTime(FixedData.PasswordLastSet,
                                                      DomainFixedData.MinPasswordAge);
        InfoBuffer->All.PasswordCanChange.LowPart = PasswordCanChange.LowPart;
        InfoBuffer->All.PasswordCanChange.HighPart = PasswordCanChange.HighPart;
    }

    /* Get the PasswordMustChange attribute */
    if (InfoBuffer->All.WhichFields & USER_ALL_PASSWORDMUSTCHANGE)
    {
        PasswordMustChange = SampAddRelativeTimeToTime(FixedData.PasswordLastSet,
                                                       DomainFixedData.MaxPasswordAge);
        InfoBuffer->All.PasswordMustChange.LowPart = PasswordMustChange.LowPart;
        InfoBuffer->All.PasswordMustChange.HighPart = PasswordMustChange.HighPart;
    }

    /* Get the PasswordLastSet attribute */
    if (InfoBuffer->All.WhichFields & USER_ALL_PASSWORDLASTSET)
    {
        InfoBuffer->All.PasswordLastSet.LowPart = FixedData.PasswordLastSet.LowPart;
        InfoBuffer->All.PasswordLastSet.HighPart = FixedData.PasswordLastSet.HighPart;
    }

    /* Get the AccountExpires attribute */
    if (InfoBuffer->All.WhichFields & USER_ALL_ACCOUNTEXPIRES)
    {
        InfoBuffer->All.AccountExpires.LowPart = FixedData.AccountExpires.LowPart;
        InfoBuffer->All.AccountExpires.HighPart = FixedData.AccountExpires.HighPart;
    }

    /* Get the UserAccountControl attribute */
    if (InfoBuffer->All.WhichFields & USER_ALL_USERACCOUNTCONTROL)
    {
        InfoBuffer->All.UserAccountControl = FixedData.UserAccountControl;
    }

    /* Get the Parameters attribute */
    if (InfoBuffer->All.WhichFields & USER_ALL_PARAMETERS)
    {
        Status = SampGetObjectAttributeString(UserObject,
                                              L"Parameters",
                                              &InfoBuffer->All.Parameters);
        if (!NT_SUCCESS(Status))
        {
            TRACE("Status 0x%08lx\n", Status);
            goto done;
        }
    }

    /* Get the CountryCode attribute */
    if (InfoBuffer->All.WhichFields & USER_ALL_COUNTRYCODE)
    {
        InfoBuffer->All.CountryCode = FixedData.CountryCode;
    }

    /* Get the CodePage attribute */
    if (InfoBuffer->All.WhichFields & USER_ALL_CODEPAGE)
    {
        InfoBuffer->All.CodePage = FixedData.CodePage;
    }

    /* Get the LmPassword and NtPassword attributes */
    if (InfoBuffer->All.WhichFields & (USER_ALL_NTPASSWORDPRESENT | USER_ALL_LMPASSWORDPRESENT))
    {
        InfoBuffer->All.LmPasswordPresent = FALSE;
        InfoBuffer->All.NtPasswordPresent = FALSE;

        /* Get the NT password */
        Length = 0;
        SampGetObjectAttribute(UserObject,
                               L"NTPwd",
                               NULL,
                               NULL,
                               &Length);

        if (Length == sizeof(ENCRYPTED_NT_OWF_PASSWORD))
        {
            InfoBuffer->All.NtOwfPassword.Buffer = midl_user_allocate(sizeof(ENCRYPTED_NT_OWF_PASSWORD));
            if (InfoBuffer->All.NtOwfPassword.Buffer == NULL)
            {
                Status = STATUS_INSUFFICIENT_RESOURCES;
                goto done;
            }

            InfoBuffer->All.NtOwfPassword.Length = sizeof(ENCRYPTED_NT_OWF_PASSWORD);
            InfoBuffer->All.NtOwfPassword.MaximumLength = sizeof(ENCRYPTED_NT_OWF_PASSWORD);

            Status = SampGetObjectAttribute(UserObject,
                                            L"NTPwd",
                                            NULL,
                                            (PVOID)InfoBuffer->All.NtOwfPassword.Buffer,
                                            &Length);
            if (!NT_SUCCESS(Status))
                goto done;

            if (memcmp(InfoBuffer->All.NtOwfPassword.Buffer,
                       &EmptyNtHash,
                       sizeof(ENCRYPTED_NT_OWF_PASSWORD)))
                InfoBuffer->All.NtPasswordPresent = TRUE;
        }

        /* Get the LM password */
        Length = 0;
        SampGetObjectAttribute(UserObject,
                               L"LMPwd",
                               NULL,
                               NULL,
                               &Length);

        if (Length == sizeof(ENCRYPTED_LM_OWF_PASSWORD))
        {
            InfoBuffer->All.LmOwfPassword.Buffer = midl_user_allocate(sizeof(ENCRYPTED_LM_OWF_PASSWORD));
            if (InfoBuffer->All.LmOwfPassword.Buffer == NULL)
            {
                Status = STATUS_INSUFFICIENT_RESOURCES;
                goto done;
            }

            InfoBuffer->All.LmOwfPassword.Length = sizeof(ENCRYPTED_LM_OWF_PASSWORD);
            InfoBuffer->All.LmOwfPassword.MaximumLength = sizeof(ENCRYPTED_LM_OWF_PASSWORD);

            Status = SampGetObjectAttribute(UserObject,
                                            L"LMPwd",
                                            NULL,
                                            (PVOID)InfoBuffer->All.LmOwfPassword.Buffer,
                                            &Length);
            if (!NT_SUCCESS(Status))
                goto done;

            if (memcmp(InfoBuffer->All.LmOwfPassword.Buffer,
                       &EmptyLmHash,
                       sizeof(ENCRYPTED_LM_OWF_PASSWORD)))
                InfoBuffer->All.LmPasswordPresent = TRUE;
        }
    }

    if (InfoBuffer->All.WhichFields & USER_ALL_PRIVATEDATA)
    {
        Status = SampGetObjectAttributeString(UserObject,
                                              L"PrivateData",
                                              &InfoBuffer->All.PrivateData);
        if (!NT_SUCCESS(Status))
        {
            TRACE("Status 0x%08lx\n", Status);
            goto done;
        }
    }

    if (InfoBuffer->All.WhichFields & USER_ALL_PASSWORDEXPIRED)
    {
        /* FIXME */
    }

    if (InfoBuffer->All.WhichFields & USER_ALL_SECURITYDESCRIPTOR)
    {
        Length = 0;
        SampGetObjectAttribute(UserObject,
                               L"SecDesc",
                               NULL,
                               NULL,
                               &Length);

        if (Length > 0)
        {
            InfoBuffer->All.SecurityDescriptor.SecurityDescriptor = midl_user_allocate(Length);
            if (InfoBuffer->All.SecurityDescriptor.SecurityDescriptor == NULL)
            {
                Status = STATUS_INSUFFICIENT_RESOURCES;
                goto done;
            }

            InfoBuffer->All.SecurityDescriptor.Length = Length;

            Status = SampGetObjectAttribute(UserObject,
                                            L"SecDesc",
                                            NULL,
                                            (PVOID)InfoBuffer->All.SecurityDescriptor.SecurityDescriptor,
                                            &Length);
            if (!NT_SUCCESS(Status))
                goto done;
        }
    }

    *Buffer = InfoBuffer;

done:
    if (!NT_SUCCESS(Status))
    {
        if (InfoBuffer != NULL)
        {
            if (InfoBuffer->All.UserName.Buffer != NULL)
                midl_user_free(InfoBuffer->All.UserName.Buffer);

            if (InfoBuffer->All.FullName.Buffer != NULL)
                midl_user_free(InfoBuffer->All.FullName.Buffer);

            if (InfoBuffer->All.AdminComment.Buffer != NULL)
                midl_user_free(InfoBuffer->All.AdminComment.Buffer);

            if (InfoBuffer->All.UserComment.Buffer != NULL)
                midl_user_free(InfoBuffer->All.UserComment.Buffer);

            if (InfoBuffer->All.HomeDirectory.Buffer != NULL)
                midl_user_free(InfoBuffer->All.HomeDirectory.Buffer);

            if (InfoBuffer->All.HomeDirectoryDrive.Buffer != NULL)
                midl_user_free(InfoBuffer->All.HomeDirectoryDrive.Buffer);

            if (InfoBuffer->All.ScriptPath.Buffer != NULL)
                midl_user_free(InfoBuffer->All.ScriptPath.Buffer);

            if (InfoBuffer->All.ProfilePath.Buffer != NULL)
                midl_user_free(InfoBuffer->All.ProfilePath.Buffer);

            if (InfoBuffer->All.WorkStations.Buffer != NULL)
                midl_user_free(InfoBuffer->All.WorkStations.Buffer);

            if (InfoBuffer->All.LogonHours.LogonHours != NULL)
                midl_user_free(InfoBuffer->All.LogonHours.LogonHours);

            if (InfoBuffer->All.Parameters.Buffer != NULL)
                midl_user_free(InfoBuffer->All.Parameters.Buffer);

            if (InfoBuffer->All.LmOwfPassword.Buffer != NULL)
                midl_user_free(InfoBuffer->All.LmOwfPassword.Buffer);

            if (InfoBuffer->All.NtOwfPassword.Buffer != NULL)
                midl_user_free(InfoBuffer->All.NtOwfPassword.Buffer);

            if (InfoBuffer->All.PrivateData.Buffer != NULL)
                midl_user_free(InfoBuffer->All.PrivateData.Buffer);

            if (InfoBuffer->All.SecurityDescriptor.SecurityDescriptor != NULL)
                midl_user_free(InfoBuffer->All.SecurityDescriptor.SecurityDescriptor);

            midl_user_free(InfoBuffer);
        }
    }

    return Status;
}


/* Function 36 */
NTSTATUS
NTAPI
SamrQueryInformationUser(IN SAMPR_HANDLE UserHandle,
                         IN USER_INFORMATION_CLASS UserInformationClass,
                         OUT PSAMPR_USER_INFO_BUFFER *Buffer)
{
    TRACE("SamrQueryInformationUser(%p %lu %p)\n",
          UserHandle, UserInformationClass, Buffer);

    return SamrQueryInformationUser2(UserHandle,
                                     UserInformationClass,
                                     Buffer);
}


static NTSTATUS
SampSetUserName(PSAM_DB_OBJECT UserObject,
                PRPC_UNICODE_STRING NewUserName)
{
    UNICODE_STRING OldUserName = {0, 0, NULL};
    NTSTATUS Status;

    /* Check the account name */
    Status = SampCheckAccountName(NewUserName, 20);
    if (!NT_SUCCESS(Status))
    {
        TRACE("SampCheckAccountName failed (Status 0x%08lx)\n", Status);
        return Status;
    }

    Status = SampGetObjectAttributeString(UserObject,
                                          L"Name",
                                          (PRPC_UNICODE_STRING)&OldUserName);
    if (!NT_SUCCESS(Status))
    {
        TRACE("SampGetObjectAttributeString failed (Status 0x%08lx)\n", Status);
        goto done;
    }

    if (!RtlEqualUnicodeString(&OldUserName, (PCUNICODE_STRING)NewUserName, TRUE))
    {
        Status = SampCheckAccountNameInDomain(UserObject->ParentObject,
                                              NewUserName->Buffer);
        if (!NT_SUCCESS(Status))
        {
            TRACE("User name \'%S\' already exists in domain (Status 0x%08lx)\n",
                  NewUserName->Buffer, Status);
            goto done;
        }
    }

    Status = SampSetAccountNameInDomain(UserObject->ParentObject,
                                        L"Users",
                                        NewUserName->Buffer,
                                        UserObject->RelativeId);
    if (!NT_SUCCESS(Status))
    {
        TRACE("SampSetAccountNameInDomain failed (Status 0x%08lx)\n", Status);
        goto done;
    }

    Status = SampRemoveAccountNameFromDomain(UserObject->ParentObject,
                                             L"Users",
                                             OldUserName.Buffer);
    if (!NT_SUCCESS(Status))
    {
        TRACE("SampRemoveAccountNameFromDomain failed (Status 0x%08lx)\n", Status);
        goto done;
    }

    Status = SampSetObjectAttributeString(UserObject,
                                          L"Name",
                                          NewUserName);
    if (!NT_SUCCESS(Status))
    {
        TRACE("SampSetObjectAttribute failed (Status 0x%08lx)\n", Status);
    }

done:
    if (OldUserName.Buffer != NULL)
        midl_user_free(OldUserName.Buffer);

    return Status;
}


static NTSTATUS
SampSetUserGeneral(PSAM_DB_OBJECT UserObject,
                   PSAMPR_USER_INFO_BUFFER Buffer)
{
    SAM_USER_FIXED_DATA FixedData;
    ULONG Length = 0;
    NTSTATUS Status;

    Length = sizeof(SAM_USER_FIXED_DATA);
    Status = SampGetObjectAttribute(UserObject,
                                    L"F",
                                    NULL,
                                    (PVOID)&FixedData,
                                    &Length);
    if (!NT_SUCCESS(Status))
        goto done;

    FixedData.PrimaryGroupId = Buffer->General.PrimaryGroupId;

    Status = SampSetObjectAttribute(UserObject,
                                    L"F",
                                    REG_BINARY,
                                    &FixedData,
                                    Length);
    if (!NT_SUCCESS(Status))
        goto done;

    Status = SampSetUserName(UserObject,
                             &Buffer->General.UserName);
    if (!NT_SUCCESS(Status))
        goto done;

    Status = SampSetObjectAttributeString(UserObject,
                                          L"FullName",
                                          &Buffer->General.FullName);
    if (!NT_SUCCESS(Status))
        goto done;

    Status = SampSetObjectAttributeString(UserObject,
                                          L"AdminComment",
                                          &Buffer->General.AdminComment);
    if (!NT_SUCCESS(Status))
        goto done;

    Status = SampSetObjectAttributeString(UserObject,
                                          L"UserComment",
                                          &Buffer->General.UserComment);

done:
    return Status;
}


static NTSTATUS
SampSetUserPreferences(PSAM_DB_OBJECT UserObject,
                       PSAMPR_USER_INFO_BUFFER Buffer)
{
    SAM_USER_FIXED_DATA FixedData;
    ULONG Length = 0;
    NTSTATUS Status;

    Length = sizeof(SAM_USER_FIXED_DATA);
    Status = SampGetObjectAttribute(UserObject,
                                    L"F",
                                    NULL,
                                    (PVOID)&FixedData,
                                    &Length);
    if (!NT_SUCCESS(Status))
        goto done;

    FixedData.CountryCode = Buffer->Preferences.CountryCode;
    FixedData.CodePage = Buffer->Preferences.CodePage;

    Status = SampSetObjectAttribute(UserObject,
                                    L"F",
                                    REG_BINARY,
                                    &FixedData,
                                    Length);
    if (!NT_SUCCESS(Status))
        goto done;

    Status = SampSetObjectAttributeString(UserObject,
                                          L"UserComment",
                                          &Buffer->Preferences.UserComment);

done:
    return Status;
}


static NTSTATUS
SampSetUserPrimaryGroup(PSAM_DB_OBJECT UserObject,
                        PSAMPR_USER_INFO_BUFFER Buffer)
{
    SAM_USER_FIXED_DATA FixedData;
    ULONG Length = 0;
    NTSTATUS Status;

    Length = sizeof(SAM_USER_FIXED_DATA);
    Status = SampGetObjectAttribute(UserObject,
                                    L"F",
                                    NULL,
                                    (PVOID)&FixedData,
                                    &Length);
    if (!NT_SUCCESS(Status))
        goto done;

    FixedData.PrimaryGroupId = Buffer->PrimaryGroup.PrimaryGroupId;

    Status = SampSetObjectAttribute(UserObject,
                                    L"F",
                                    REG_BINARY,
                                    &FixedData,
                                    Length);

done:
    return Status;
}


static NTSTATUS
SampSetUserControl(PSAM_DB_OBJECT UserObject,
                   PSAMPR_USER_INFO_BUFFER Buffer)
{
    SAM_USER_FIXED_DATA FixedData;
    ULONG Length = 0;
    NTSTATUS Status;

    Length = sizeof(SAM_USER_FIXED_DATA);
    Status = SampGetObjectAttribute(UserObject,
                                    L"F",
                                    NULL,
                                    (PVOID)&FixedData,
                                    &Length);
    if (!NT_SUCCESS(Status))
        goto done;

    FixedData.UserAccountControl = Buffer->Control.UserAccountControl;

    Status = SampSetObjectAttribute(UserObject,
                                    L"F",
                                    REG_BINARY,
                                    &FixedData,
                                    Length);

done:
    return Status;
}


static NTSTATUS
SampSetUserExpires(PSAM_DB_OBJECT UserObject,
                   PSAMPR_USER_INFO_BUFFER Buffer)
{
    SAM_USER_FIXED_DATA FixedData;
    ULONG Length = 0;
    NTSTATUS Status;

    Length = sizeof(SAM_USER_FIXED_DATA);
    Status = SampGetObjectAttribute(UserObject,
                                    L"F",
                                    NULL,
                                    (PVOID)&FixedData,
                                    &Length);
    if (!NT_SUCCESS(Status))
        goto done;

    FixedData.AccountExpires.LowPart = Buffer->Expires.AccountExpires.LowPart;
    FixedData.AccountExpires.HighPart = Buffer->Expires.AccountExpires.HighPart;

    Status = SampSetObjectAttribute(UserObject,
                                    L"F",
                                    REG_BINARY,
                                    &FixedData,
                                    Length);

done:
    return Status;
}


static NTSTATUS
SampSetUserInternal1(PSAM_DB_OBJECT UserObject,
                     PSAMPR_USER_INFO_BUFFER Buffer)
{
    SAM_USER_FIXED_DATA FixedData;
    ULONG Length = 0;
    NTSTATUS Status = STATUS_SUCCESS;

    /* FIXME: Decrypt NT password */
    /* FIXME: Decrypt LM password */

    Status = SampSetUserPassword(UserObject,
                                 &Buffer->Internal1.EncryptedNtOwfPassword,
                                 Buffer->Internal1.NtPasswordPresent,
                                 &Buffer->Internal1.EncryptedLmOwfPassword,
                                 Buffer->Internal1.LmPasswordPresent);
    if (!NT_SUCCESS(Status))
        goto done;

    /* Get the fixed user attributes */
    Length = sizeof(SAM_USER_FIXED_DATA);
    Status = SampGetObjectAttribute(UserObject,
                                    L"F",
                                    NULL,
                                    (PVOID)&FixedData,
                                    &Length);
    if (!NT_SUCCESS(Status))
        goto done;

    if (Buffer->Internal1.PasswordExpired)
    {
        /* The password was last set ages ago */
        FixedData.PasswordLastSet.LowPart = 0;
        FixedData.PasswordLastSet.HighPart = 0;
    }
    else
    {
        /* The password was last set right now */
        Status = NtQuerySystemTime(&FixedData.PasswordLastSet);
        if (!NT_SUCCESS(Status))
            goto done;
    }

    /* Set the fixed user attributes */
    Status = SampSetObjectAttribute(UserObject,
                                    L"F",
                                    REG_BINARY,
                                    &FixedData,
                                    Length);

done:
    return Status;
}


static NTSTATUS
SampSetUserInternal2(PSAM_DB_OBJECT UserObject,
                     PSAMPR_USER_INFO_BUFFER Buffer)
{
    SAM_USER_FIXED_DATA FixedData;
    ULONG Length = 0;
    NTSTATUS Status = STATUS_SUCCESS;

    /* Get the fixed user attributes */
    Length = sizeof(SAM_USER_FIXED_DATA);
    Status = SampGetObjectAttribute(UserObject,
                                    L"F",
                                    NULL,
                                    (PVOID)&FixedData,
                                    &Length);
    if (!NT_SUCCESS(Status))
        goto done;

    if ((Buffer->Internal2.Flags & USER_LOGON_SUCCESS) &&
        ((Buffer->Internal2.Flags & ~USER_LOGON_SUCCESS) == 0))
    {
        /* Update the LastLogon time */
        Status = NtQuerySystemTime(&FixedData.LastLogon);
        if (!NT_SUCCESS(Status))
            goto done;

        FixedData.LogonCount++;
        FixedData.BadPasswordCount = 0;
    }

    if ((Buffer->Internal2.Flags & USER_LOGON_BAD_PASSWORD) &&
        ((Buffer->Internal2.Flags & ~USER_LOGON_BAD_PASSWORD) == 0))
    {
        /* Update the LastBadPasswordTime */
        Status = NtQuerySystemTime(&FixedData.LastBadPasswordTime);
        if (!NT_SUCCESS(Status))
            goto done;

        FixedData.BadPasswordCount++;
    }

    /* Set the fixed user attributes */
    Status = SampSetObjectAttribute(UserObject,
                                    L"F",
                                    REG_BINARY,
                                    &FixedData,
                                    Length);

done:
    return Status;
}


static NTSTATUS
SampSetUserAll(PSAM_DB_OBJECT UserObject,
               PSAMPR_USER_INFO_BUFFER Buffer)
{
    SAM_USER_FIXED_DATA FixedData;
    ULONG Length = 0;
    ULONG WhichFields;
    PENCRYPTED_NT_OWF_PASSWORD NtPassword = NULL;
    PENCRYPTED_LM_OWF_PASSWORD LmPassword = NULL;
    BOOLEAN NtPasswordPresent = FALSE;
    BOOLEAN LmPasswordPresent = FALSE;
    BOOLEAN WriteFixedData = FALSE;
    NTSTATUS Status = STATUS_SUCCESS;

    WhichFields = Buffer->All.WhichFields;

    /* Get the fixed size attributes */
    Length = sizeof(SAM_USER_FIXED_DATA);
    Status = SampGetObjectAttribute(UserObject,
                                    L"F",
                                    NULL,
                                    (PVOID)&FixedData,
                                    &Length);
    if (!NT_SUCCESS(Status))
        goto done;

    if (WhichFields & USER_ALL_USERNAME)
    {
        Status = SampSetUserName(UserObject,
                                 &Buffer->All.UserName);
        if (!NT_SUCCESS(Status))
            goto done;
    }

    if (WhichFields & USER_ALL_FULLNAME)
    {
        Status = SampSetObjectAttributeString(UserObject,
                                              L"FullName",
                                              &Buffer->All.FullName);
        if (!NT_SUCCESS(Status))
            goto done;
    }

    if (WhichFields & USER_ALL_ADMINCOMMENT)
    {
        Status = SampSetObjectAttributeString(UserObject,
                                              L"AdminComment",
                                              &Buffer->All.AdminComment);
        if (!NT_SUCCESS(Status))
            goto done;
    }

    if (WhichFields & USER_ALL_USERCOMMENT)
    {
        Status = SampSetObjectAttributeString(UserObject,
                                              L"UserComment",
                                              &Buffer->All.UserComment);
        if (!NT_SUCCESS(Status))
            goto done;
    }

    if (WhichFields & USER_ALL_HOMEDIRECTORY)
    {
        Status = SampSetObjectAttributeString(UserObject,
                                              L"HomeDirectory",
                                              &Buffer->All.HomeDirectory);
        if (!NT_SUCCESS(Status))
            goto done;
    }

    if (WhichFields & USER_ALL_HOMEDIRECTORYDRIVE)
    {
        Status = SampSetObjectAttributeString(UserObject,
                                              L"HomeDirectoryDrive",
                                              &Buffer->All.HomeDirectoryDrive);
        if (!NT_SUCCESS(Status))
            goto done;
    }

    if (WhichFields & USER_ALL_SCRIPTPATH)
    {
        Status = SampSetObjectAttributeString(UserObject,
                                              L"ScriptPath",
                                              &Buffer->All.ScriptPath);
        if (!NT_SUCCESS(Status))
            goto done;
    }

    if (WhichFields & USER_ALL_PROFILEPATH)
    {
        Status = SampSetObjectAttributeString(UserObject,
                                              L"ProfilePath",
                                              &Buffer->All.ProfilePath);
        if (!NT_SUCCESS(Status))
            goto done;
    }

    if (WhichFields & USER_ALL_WORKSTATIONS)
    {
        Status = SampSetObjectAttributeString(UserObject,
                                              L"WorkStations",
                                              &Buffer->All.WorkStations);
        if (!NT_SUCCESS(Status))
            goto done;
    }

    if (WhichFields & USER_ALL_PARAMETERS)
    {
        Status = SampSetObjectAttributeString(UserObject,
                                              L"Parameters",
                                              &Buffer->All.Parameters);
        if (!NT_SUCCESS(Status))
            goto done;
    }

    if (WhichFields & USER_ALL_LOGONHOURS)
    {
        Status = SampSetLogonHoursAttribute(UserObject,
                                           &Buffer->All.LogonHours);
        if (!NT_SUCCESS(Status))
            goto done;
    }

    if (WhichFields & USER_ALL_PRIMARYGROUPID)
    {
        FixedData.PrimaryGroupId = Buffer->All.PrimaryGroupId;
        WriteFixedData = TRUE;
    }

    if (WhichFields & USER_ALL_ACCOUNTEXPIRES)
    {
        FixedData.AccountExpires.LowPart = Buffer->All.AccountExpires.LowPart;
        FixedData.AccountExpires.HighPart = Buffer->All.AccountExpires.HighPart;
        WriteFixedData = TRUE;
    }

    if (WhichFields & USER_ALL_USERACCOUNTCONTROL)
    {
        FixedData.UserAccountControl = Buffer->All.UserAccountControl;
        WriteFixedData = TRUE;
    }

    if (WhichFields & USER_ALL_COUNTRYCODE)
    {
        FixedData.CountryCode = Buffer->All.CountryCode;
        WriteFixedData = TRUE;
    }

    if (WhichFields & USER_ALL_CODEPAGE)
    {
        FixedData.CodePage = Buffer->All.CodePage;
        WriteFixedData = TRUE;
    }

    if (WhichFields & (USER_ALL_NTPASSWORDPRESENT |
                       USER_ALL_LMPASSWORDPRESENT))
    {
        if (WhichFields & USER_ALL_NTPASSWORDPRESENT)
        {
            NtPassword = (PENCRYPTED_NT_OWF_PASSWORD)Buffer->All.NtOwfPassword.Buffer;
            NtPasswordPresent = Buffer->All.NtPasswordPresent;
        }

        if (WhichFields & USER_ALL_LMPASSWORDPRESENT)
        {
            LmPassword = (PENCRYPTED_LM_OWF_PASSWORD)Buffer->All.LmOwfPassword.Buffer;
            LmPasswordPresent = Buffer->All.LmPasswordPresent;
        }

        Status = SampSetUserPassword(UserObject,
                                     NtPassword,
                                     NtPasswordPresent,
                                     LmPassword,
                                     LmPasswordPresent);
        if (!NT_SUCCESS(Status))
            goto done;

        /* The password has just been set */
        Status = NtQuerySystemTime(&FixedData.PasswordLastSet);
        if (!NT_SUCCESS(Status))
            goto done;

        WriteFixedData = TRUE;
    }

    if (WhichFields & USER_ALL_PRIVATEDATA)
    {
        Status = SampSetObjectAttributeString(UserObject,
                                              L"PrivateData",
                                              &Buffer->All.PrivateData);
        if (!NT_SUCCESS(Status))
            goto done;
    }

    if (WhichFields & USER_ALL_PASSWORDEXPIRED)
    {
        if (Buffer->All.PasswordExpired)
        {
            /* The password was last set ages ago */
            FixedData.PasswordLastSet.LowPart = 0;
            FixedData.PasswordLastSet.HighPart = 0;
        }
        else
        {
            /* The password was last set right now */
            Status = NtQuerySystemTime(&FixedData.PasswordLastSet);
            if (!NT_SUCCESS(Status))
                goto done;
        }

        WriteFixedData = TRUE;
    }

    if (WhichFields & USER_ALL_SECURITYDESCRIPTOR)
    {
        Status = SampSetObjectAttribute(UserObject,
                                        L"SecDesc",
                                        REG_BINARY,
                                        Buffer->All.SecurityDescriptor.SecurityDescriptor,
                                        Buffer->All.SecurityDescriptor.Length);
    }

    if (WriteFixedData != FALSE)
    {
        Status = SampSetObjectAttribute(UserObject,
                                        L"F",
                                        REG_BINARY,
                                        &FixedData,
                                        Length);
        if (!NT_SUCCESS(Status))
            goto done;
    }

done:
    return Status;
}


/* Function 37 */
NTSTATUS
NTAPI
SamrSetInformationUser(IN SAMPR_HANDLE UserHandle,
                       IN USER_INFORMATION_CLASS UserInformationClass,
                       IN PSAMPR_USER_INFO_BUFFER Buffer)
{
    TRACE("SamrSetInformationUser(%p %lu %p)\n",
          UserHandle, UserInformationClass, Buffer);

    return SamrSetInformationUser2(UserHandle,
                                   UserInformationClass,
                                   Buffer);
}


/* Function 38 */
NTSTATUS
NTAPI
SamrChangePasswordUser(IN SAMPR_HANDLE UserHandle,
                       IN unsigned char LmPresent,
                       IN PENCRYPTED_LM_OWF_PASSWORD OldLmEncryptedWithNewLm,
                       IN PENCRYPTED_LM_OWF_PASSWORD NewLmEncryptedWithOldLm,
                       IN unsigned char NtPresent,
                       IN PENCRYPTED_NT_OWF_PASSWORD OldNtEncryptedWithNewNt,
                       IN PENCRYPTED_NT_OWF_PASSWORD NewNtEncryptedWithOldNt,
                       IN unsigned char NtCrossEncryptionPresent,
                       IN PENCRYPTED_NT_OWF_PASSWORD NewNtEncryptedWithNewLm,
                       IN unsigned char LmCrossEncryptionPresent,
                       IN PENCRYPTED_LM_OWF_PASSWORD NewLmEncryptedWithNewNt)
{
    ENCRYPTED_LM_OWF_PASSWORD StoredLmPassword;
    ENCRYPTED_NT_OWF_PASSWORD StoredNtPassword;
    LM_OWF_PASSWORD OldLmPassword;
    LM_OWF_PASSWORD NewLmPassword;
    NT_OWF_PASSWORD OldNtPassword;
    NT_OWF_PASSWORD NewNtPassword;
    BOOLEAN StoredLmPresent = FALSE;
    BOOLEAN StoredNtPresent = FALSE;
    BOOLEAN StoredLmEmpty = TRUE;
    BOOLEAN StoredNtEmpty = TRUE;
    PSAM_DB_OBJECT UserObject;
    ULONG Length;
    SAM_USER_FIXED_DATA UserFixedData;
    SAM_DOMAIN_FIXED_DATA DomainFixedData;
    LARGE_INTEGER SystemTime;
    NTSTATUS Status;

    DBG_UNREFERENCED_LOCAL_VARIABLE(StoredLmPresent);
    DBG_UNREFERENCED_LOCAL_VARIABLE(StoredNtPresent);
    DBG_UNREFERENCED_LOCAL_VARIABLE(StoredLmEmpty);

    TRACE("SamrChangePasswordUser(%p %u %p %p %u %p %p %u %p %u %p)\n",
          UserHandle, LmPresent, OldLmEncryptedWithNewLm, NewLmEncryptedWithOldLm,
          NtPresent, OldNtEncryptedWithNewNt, NewNtEncryptedWithOldNt, NtCrossEncryptionPresent,
          NewNtEncryptedWithNewLm, LmCrossEncryptionPresent, NewLmEncryptedWithNewNt);

    RtlAcquireResourceExclusive(&SampResource,
                                TRUE);

    /* Validate the user handle */
    Status = SampValidateDbObject(UserHandle,
                                  SamDbUserObject,
                                  USER_CHANGE_PASSWORD,
                                  &UserObject);
    if (!NT_SUCCESS(Status))
    {
        TRACE("SampValidateDbObject failed with status 0x%08lx\n", Status);
        goto done;
    }

    /* Get the current time */
    Status = NtQuerySystemTime(&SystemTime);
    if (!NT_SUCCESS(Status))
    {
        TRACE("NtQuerySystemTime failed (Status 0x%08lx)\n", Status);
        goto done;
    }

    /* Retrieve the LM password */
    Length = sizeof(ENCRYPTED_LM_OWF_PASSWORD);
    Status = SampGetObjectAttribute(UserObject,
                                    L"LMPwd",
                                    NULL,
                                    &StoredLmPassword,
                                    &Length);
    if (NT_SUCCESS(Status))
    {
        if (Length == sizeof(ENCRYPTED_LM_OWF_PASSWORD))
        {
            StoredLmPresent = TRUE;
            if (!RtlEqualMemory(&StoredLmPassword,
                                &EmptyLmHash,
                                sizeof(ENCRYPTED_LM_OWF_PASSWORD)))
                StoredLmEmpty = FALSE;
        }
    }

    /* Retrieve the NT password */
    Length = sizeof(ENCRYPTED_NT_OWF_PASSWORD);
    Status = SampGetObjectAttribute(UserObject,
                                    L"NTPwd",
                                    NULL,
                                    &StoredNtPassword,
                                    &Length);
    if (NT_SUCCESS(Status))
    {
        if (Length == sizeof(ENCRYPTED_NT_OWF_PASSWORD))
        {
            StoredNtPresent = TRUE;
            if (!RtlEqualMemory(&StoredNtPassword,
                                &EmptyNtHash,
                                sizeof(ENCRYPTED_NT_OWF_PASSWORD)))
                StoredNtEmpty = FALSE;
        }
    }

    /* Retrieve the fixed size user data */
    Length = sizeof(SAM_USER_FIXED_DATA);
    Status = SampGetObjectAttribute(UserObject,
                                    L"F",
                                    NULL,
                                    &UserFixedData,
                                    &Length);
    if (!NT_SUCCESS(Status))
    {
        TRACE("SampGetObjectAttribute failed to retrieve the fixed user data (Status 0x%08lx)\n", Status);
        goto done;
    }

    /* Check if we can change the password at this time */
    if ((StoredLmEmpty == FALSE) || (StoredNtEmpty == FALSE))
    {
        /* Get fixed domain data */
        Length = sizeof(SAM_DOMAIN_FIXED_DATA);
        Status = SampGetObjectAttribute(UserObject->ParentObject,
                                        L"F",
                                        NULL,
                                        &DomainFixedData,
                                        &Length);
        if (!NT_SUCCESS(Status))
        {
            TRACE("SampGetObjectAttribute failed to retrieve the fixed domain data (Status 0x%08lx)\n", Status);
            goto done;
        }

        if (DomainFixedData.MinPasswordAge.QuadPart > 0)
        {
            if (SystemTime.QuadPart < (UserFixedData.PasswordLastSet.QuadPart + DomainFixedData.MinPasswordAge.QuadPart))
            {
                Status = STATUS_ACCOUNT_RESTRICTION;
                goto done;
            }
        }
    }

    /* Decrypt the LM passwords, if present */
    if (LmPresent)
    {
        Status = SystemFunction013((const BYTE *)NewLmEncryptedWithOldLm,
                                   (const BYTE *)&StoredLmPassword,
                                   (LPBYTE)&NewLmPassword);
        if (!NT_SUCCESS(Status))
        {
            TRACE("SystemFunction013 failed (Status 0x%08lx)\n", Status);
            goto done;
        }

        Status = SystemFunction013((const BYTE *)OldLmEncryptedWithNewLm,
                                   (const BYTE *)&NewLmPassword,
                                   (LPBYTE)&OldLmPassword);
        if (!NT_SUCCESS(Status))
        {
            TRACE("SystemFunction013 failed (Status 0x%08lx)\n", Status);
            goto done;
        }
    }

    /* Decrypt the NT passwords, if present */
    if (NtPresent)
    {
        Status = SystemFunction013((const BYTE *)NewNtEncryptedWithOldNt,
                                   (const BYTE *)&StoredNtPassword,
                                   (LPBYTE)&NewNtPassword);
        if (!NT_SUCCESS(Status))
        {
            TRACE("SystemFunction013 failed (Status 0x%08lx)\n", Status);
            goto done;
        }

        Status = SystemFunction013((const BYTE *)OldNtEncryptedWithNewNt,
                                   (const BYTE *)&NewNtPassword,
                                   (LPBYTE)&OldNtPassword);
        if (!NT_SUCCESS(Status))
        {
            TRACE("SystemFunction013 failed (Status 0x%08lx)\n", Status);
            goto done;
        }
    }

    /* Check if the old passwords match the stored ones */
    if (NtPresent)
    {
        if (LmPresent)
        {
            if (!RtlEqualMemory(&StoredLmPassword,
                                &OldLmPassword,
                                sizeof(ENCRYPTED_LM_OWF_PASSWORD)))
            {
                TRACE("Old LM Password does not match!\n");
                Status = STATUS_WRONG_PASSWORD;
            }
            else
            {
                if (!RtlEqualMemory(&StoredNtPassword,
                                    &OldNtPassword,
                                    sizeof(ENCRYPTED_LM_OWF_PASSWORD)))
                {
                    TRACE("Old NT Password does not match!\n");
                    Status = STATUS_WRONG_PASSWORD;
                }
            }
        }
        else
        {
            if (!RtlEqualMemory(&StoredNtPassword,
                                &OldNtPassword,
                                sizeof(ENCRYPTED_LM_OWF_PASSWORD)))
            {
                TRACE("Old NT Password does not match!\n");
                Status = STATUS_WRONG_PASSWORD;
            }
        }
    }
    else
    {
        if (LmPresent)
        {
            if (!RtlEqualMemory(&StoredLmPassword,
                                &OldLmPassword,
                                sizeof(ENCRYPTED_LM_OWF_PASSWORD)))
            {
                TRACE("Old LM Password does not match!\n");
                Status = STATUS_WRONG_PASSWORD;
            }
        }
        else
        {
            Status = STATUS_INVALID_PARAMETER;
        }
    }

    /* Store the new password hashes */
    if (NT_SUCCESS(Status))
    {
        Status = SampSetUserPassword(UserObject,
                                     &NewNtPassword,
                                     NtPresent,
                                     &NewLmPassword,
                                     LmPresent);
        if (NT_SUCCESS(Status))
        {
            /* Update PasswordLastSet */
            UserFixedData.PasswordLastSet.QuadPart = SystemTime.QuadPart;

            /* Set the fixed size user data */
            Length = sizeof(SAM_USER_FIXED_DATA);
            Status = SampSetObjectAttribute(UserObject,
                                            L"F",
                                            REG_BINARY,
                                            &UserFixedData,
                                            Length);
        }
    }

    if (Status == STATUS_WRONG_PASSWORD)
    {
        /* Update BadPasswordCount and LastBadPasswordTime */
        UserFixedData.BadPasswordCount++;
        UserFixedData.LastBadPasswordTime.QuadPart = SystemTime.QuadPart;

        /* Set the fixed size user data */
        Length = sizeof(SAM_USER_FIXED_DATA);
        Status = SampSetObjectAttribute(UserObject,
                                        L"F",
                                        REG_BINARY,
                                        &UserFixedData,
                                        Length);
    }

done:
    RtlReleaseResource(&SampResource);

    return Status;
}


/* Function 39 */
NTSTATUS
NTAPI
SamrGetGroupsForUser(IN SAMPR_HANDLE UserHandle,
                     OUT PSAMPR_GET_GROUPS_BUFFER *Groups)
{
    PSAMPR_GET_GROUPS_BUFFER GroupsBuffer = NULL;
    PSAM_DB_OBJECT UserObject;
    ULONG Length = 0;
    NTSTATUS Status;

    TRACE("SamrGetGroupsForUser(%p %p)\n",
          UserHandle, Groups);

    RtlAcquireResourceShared(&SampResource,
                             TRUE);

    /* Validate the user handle */
    Status = SampValidateDbObject(UserHandle,
                                  SamDbUserObject,
                                  USER_LIST_GROUPS,
                                  &UserObject);
    if (!NT_SUCCESS(Status))
    {
        TRACE("SampValidateDbObject failed with status 0x%08lx\n", Status);
        goto done;
    }

    /* Allocate the groups buffer */
    GroupsBuffer = midl_user_allocate(sizeof(SAMPR_GET_GROUPS_BUFFER));
    if (GroupsBuffer == NULL)
    {
        Status = STATUS_INSUFFICIENT_RESOURCES;
        goto done;
    }

    /*
     * Get the size of the Groups attribute.
     * Do not check the status code because in case of an error
     * Length will be 0. And that is all we need.
     */
    SampGetObjectAttribute(UserObject,
                           L"Groups",
                           NULL,
                           NULL,
                           &Length);

    /* If there is no Groups attribute, return a groups buffer without an array */
    if (Length == 0)
    {
        GroupsBuffer->MembershipCount = 0;
        GroupsBuffer->Groups = NULL;

        *Groups = GroupsBuffer;

        Status = STATUS_SUCCESS;
        goto done;
    }

    /* Allocate a buffer for the Groups attribute */
    GroupsBuffer->Groups = midl_user_allocate(Length);
    if (GroupsBuffer->Groups == NULL)
    {
        Status = STATUS_INSUFFICIENT_RESOURCES;
        goto done;
    }

    /* Retrieve the Grous attribute */
    Status = SampGetObjectAttribute(UserObject,
                                    L"Groups",
                                    NULL,
                                    GroupsBuffer->Groups,
                                    &Length);
    if (!NT_SUCCESS(Status))
    {
        TRACE("SampGetObjectAttribute failed with status 0x%08lx\n", Status);
        goto done;
    }

    /* Calculate the membership count */
    GroupsBuffer->MembershipCount = Length / sizeof(GROUP_MEMBERSHIP);

    /* Return the groups buffer to the caller */
    *Groups = GroupsBuffer;

done:
    if (!NT_SUCCESS(Status))
    {
        if (GroupsBuffer != NULL)
        {
            if (GroupsBuffer->Groups != NULL)
                midl_user_free(GroupsBuffer->Groups);

            midl_user_free(GroupsBuffer);
        }
    }

    RtlReleaseResource(&SampResource);

    return Status;
}


/* Function 40 */
NTSTATUS
NTAPI
SamrQueryDisplayInformation(IN SAMPR_HANDLE DomainHandle,
                            IN DOMAIN_DISPLAY_INFORMATION DisplayInformationClass,
                            IN unsigned long Index,
                            IN unsigned long EntryCount,
                            IN unsigned long PreferredMaximumLength,
                            OUT unsigned long *TotalAvailable,
                            OUT unsigned long *TotalReturned,
                            OUT PSAMPR_DISPLAY_INFO_BUFFER Buffer)
{
    TRACE("SamrQueryDisplayInformation(%p %lu %lu %lu %lu %p %p %p)\n",
          DomainHandle, DisplayInformationClass, Index,
          EntryCount, PreferredMaximumLength, TotalAvailable,
          TotalReturned, Buffer);

    return SamrQueryDisplayInformation3(DomainHandle,
                                        DisplayInformationClass,
                                        Index,
                                        EntryCount,
                                        PreferredMaximumLength,
                                        TotalAvailable,
                                        TotalReturned,
                                        Buffer);
}


/* Function 41 */
NTSTATUS
NTAPI
SamrGetDisplayEnumerationIndex(IN SAMPR_HANDLE DomainHandle,
                               IN DOMAIN_DISPLAY_INFORMATION DisplayInformationClass,
                               IN PRPC_UNICODE_STRING Prefix,
                               OUT unsigned long *Index)
{
    TRACE("SamrGetDisplayEnumerationIndex(%p %lu %p %p)\n",
          DomainHandle, DisplayInformationClass, Prefix, Index);

    return SamrGetDisplayEnumerationIndex2(DomainHandle,
                                           DisplayInformationClass,
                                           Prefix,
                                           Index);
}


/* Function 42 */
NTSTATUS
NTAPI
SamrTestPrivateFunctionsDomain(IN SAMPR_HANDLE DomainHandle)
{
    UNIMPLEMENTED;
    return STATUS_NOT_IMPLEMENTED;
}


/* Function 43 */
NTSTATUS
NTAPI
SamrTestPrivateFunctionsUser(IN SAMPR_HANDLE UserHandle)
{
    UNIMPLEMENTED;
    return STATUS_NOT_IMPLEMENTED;
}


/* Function 44 */
NTSTATUS
NTAPI
SamrGetUserDomainPasswordInformation(IN SAMPR_HANDLE UserHandle,
                                     OUT PUSER_DOMAIN_PASSWORD_INFORMATION PasswordInformation)
{
    SAM_DOMAIN_FIXED_DATA DomainFixedData;
    SAM_USER_FIXED_DATA UserFixedData;
    PSAM_DB_OBJECT DomainObject;
    PSAM_DB_OBJECT UserObject;
    ULONG Length = 0;
    NTSTATUS Status;

    TRACE("SamrGetUserDomainPasswordInformation(%p %p)\n",
          UserHandle, PasswordInformation);

    RtlAcquireResourceShared(&SampResource,
                             TRUE);

    /* Validate the user handle */
    Status = SampValidateDbObject(UserHandle,
                                  SamDbUserObject,
                                  0,
                                  &UserObject);
    if (!NT_SUCCESS(Status))
    {
        TRACE("SampValidateDbObject failed with status 0x%08lx\n", Status);
        goto done;
    }

    /* Validate the domain object */
    Status = SampValidateDbObject((SAMPR_HANDLE)UserObject->ParentObject,
                                  SamDbDomainObject,
                                  DOMAIN_READ_PASSWORD_PARAMETERS,
                                  &DomainObject);
    if (!NT_SUCCESS(Status))
    {
        TRACE("SampValidateDbObject failed with status 0x%08lx\n", Status);
        goto done;
    }

    /* Get fixed user data */
    Length = sizeof(SAM_USER_FIXED_DATA);
    Status = SampGetObjectAttribute(UserObject,
                                    L"F",
                                    NULL,
                                    (PVOID)&UserFixedData,
                                    &Length);
    if (!NT_SUCCESS(Status))
    {
        TRACE("SampGetObjectAttribute failed with status 0x%08lx\n", Status);
        goto done;
    }

    if ((UserObject->RelativeId == DOMAIN_USER_RID_KRBTGT) ||
        (UserFixedData.UserAccountControl & (USER_INTERDOMAIN_TRUST_ACCOUNT |
                                             USER_WORKSTATION_TRUST_ACCOUNT |
                                             USER_SERVER_TRUST_ACCOUNT)))
    {
        PasswordInformation->MinPasswordLength = 0;
        PasswordInformation->PasswordProperties = 0;
    }
    else
    {
        /* Get fixed domain data */
        Length = sizeof(SAM_DOMAIN_FIXED_DATA);
        Status = SampGetObjectAttribute(DomainObject,
                                        L"F",
                                        NULL,
                                        (PVOID)&DomainFixedData,
                                        &Length);
        if (!NT_SUCCESS(Status))
        {
            TRACE("SampGetObjectAttribute failed with status 0x%08lx\n", Status);
            goto done;
        }

        PasswordInformation->MinPasswordLength = DomainFixedData.MinPasswordLength;
        PasswordInformation->PasswordProperties = DomainFixedData.PasswordProperties;
    }

done:
    RtlReleaseResource(&SampResource);

    return STATUS_SUCCESS;
}


/* Function 45 */
NTSTATUS
NTAPI
SamrRemoveMemberFromForeignDomain(IN SAMPR_HANDLE DomainHandle,
                                  IN PRPC_SID MemberSid)
{
    PSAM_DB_OBJECT DomainObject;
    ULONG Rid = 0;
    NTSTATUS Status;

    TRACE("SamrRemoveMemberFromForeignDomain(%p %p)\n",
          DomainHandle, MemberSid);

    RtlAcquireResourceExclusive(&SampResource,
                                TRUE);

    /* Validate the domain object */
    Status = SampValidateDbObject(DomainHandle,
                                  SamDbDomainObject,
                                  DOMAIN_LOOKUP,
                                  &DomainObject);
    if (!NT_SUCCESS(Status))
    {
        TRACE("SampValidateDbObject failed with status 0x%08lx\n", Status);
        goto done;
    }

    /* Retrieve the RID from the MemberSID */
    Status = SampGetRidFromSid((PSID)MemberSid,
                               &Rid);
    if (!NT_SUCCESS(Status))
    {
        TRACE("SampGetRidFromSid failed with status 0x%08lx\n", Status);
        goto done;
    }

    /* Fail, if the RID represents a special account */
    if (Rid < 1000)
    {
        TRACE("Cannot remove a special account (RID: %lu)\n", Rid);
        Status = STATUS_SPECIAL_ACCOUNT;
        goto done;
    }

    /* Remove the member from all aliases in the domain */
    Status = SampRemoveMemberFromAllAliases(DomainObject,
                                            MemberSid);
    if (!NT_SUCCESS(Status))
    {
        TRACE("SampRemoveMemberFromAllAliases failed with status 0x%08lx\n", Status);
    }

done:
    RtlReleaseResource(&SampResource);

    return Status;
}


/* Function 46 */
NTSTATUS
NTAPI
SamrQueryInformationDomain2(IN SAMPR_HANDLE DomainHandle,
                            IN DOMAIN_INFORMATION_CLASS DomainInformationClass,
                            OUT PSAMPR_DOMAIN_INFO_BUFFER *Buffer)
{
    PSAM_DB_OBJECT DomainObject;
    ACCESS_MASK DesiredAccess;
    NTSTATUS Status;

    TRACE("SamrQueryInformationDomain2(%p %lu %p)\n",
          DomainHandle, DomainInformationClass, Buffer);

    switch (DomainInformationClass)
    {
        case DomainPasswordInformation:
        case DomainLockoutInformation:
            DesiredAccess = DOMAIN_READ_PASSWORD_PARAMETERS;
            break;

        case DomainGeneralInformation:
        case DomainLogoffInformation:
        case DomainOemInformation:
        case DomainNameInformation:
        case DomainReplicationInformation:
        case DomainServerRoleInformation:
        case DomainModifiedInformation:
        case DomainStateInformation:
        case DomainModifiedInformation2:
            DesiredAccess = DOMAIN_READ_OTHER_PARAMETERS;
            break;

        case DomainGeneralInformation2:
            DesiredAccess = DOMAIN_READ_PASSWORD_PARAMETERS |
                            DOMAIN_READ_OTHER_PARAMETERS;
            break;

        default:
            return STATUS_INVALID_INFO_CLASS;
    }

    RtlAcquireResourceShared(&SampResource,
                             TRUE);

    /* Validate the server handle */
    Status = SampValidateDbObject(DomainHandle,
                                  SamDbDomainObject,
                                  DesiredAccess,
                                  &DomainObject);
    if (!NT_SUCCESS(Status))
        goto done;

    switch (DomainInformationClass)
    {
        case DomainPasswordInformation:
            Status = SampQueryDomainPassword(DomainObject,
                                             Buffer);
            break;

        case DomainGeneralInformation:
            Status = SampQueryDomainGeneral(DomainObject,
                                            Buffer);
            break;

        case DomainLogoffInformation:
            Status = SampQueryDomainLogoff(DomainObject,
                                           Buffer);
            break;

        case DomainOemInformation:
            Status = SampQueryDomainOem(DomainObject,
                                        Buffer);
            break;

        case DomainNameInformation:
            Status = SampQueryDomainName(DomainObject,
                                         Buffer);
            break;

        case DomainReplicationInformation:
            Status = SampQueryDomainReplication(DomainObject,
                                                Buffer);
            break;

        case DomainServerRoleInformation:
            Status = SampQueryDomainServerRole(DomainObject,
                                               Buffer);
            break;

        case DomainModifiedInformation:
            Status = SampQueryDomainModified(DomainObject,
                                             Buffer);
            break;

        case DomainStateInformation:
            Status = SampQueryDomainState(DomainObject,
                                          Buffer);
            break;

        case DomainGeneralInformation2:
            Status = SampQueryDomainGeneral2(DomainObject,
                                             Buffer);
            break;

        case DomainLockoutInformation:
            Status = SampQueryDomainLockout(DomainObject,
                                            Buffer);
            break;

        case DomainModifiedInformation2:
            Status = SampQueryDomainModified2(DomainObject,
                                              Buffer);
            break;

        default:
            Status = STATUS_NOT_IMPLEMENTED;
    }

done:
    RtlReleaseResource(&SampResource);

    return Status;
}


/* Function 47 */
NTSTATUS
NTAPI
SamrQueryInformationUser2(IN SAMPR_HANDLE UserHandle,
                          IN USER_INFORMATION_CLASS UserInformationClass,
                          OUT PSAMPR_USER_INFO_BUFFER *Buffer)
{
    PSAM_DB_OBJECT UserObject;
    ACCESS_MASK DesiredAccess;
    NTSTATUS Status;

    TRACE("SamrQueryInformationUser2(%p %lu %p)\n",
          UserHandle, UserInformationClass, Buffer);

    switch (UserInformationClass)
    {
        case UserGeneralInformation:
        case UserNameInformation:
        case UserAccountNameInformation:
        case UserFullNameInformation:
        case UserPrimaryGroupInformation:
        case UserAdminCommentInformation:
            DesiredAccess = USER_READ_GENERAL;
            break;

        case UserLogonHoursInformation:
        case UserHomeInformation:
        case UserScriptInformation:
        case UserProfileInformation:
        case UserWorkStationsInformation:
            DesiredAccess = USER_READ_LOGON;
            break;

        case UserControlInformation:
        case UserExpiresInformation:
        case UserParametersInformation:
            DesiredAccess = USER_READ_ACCOUNT;
            break;

        case UserPreferencesInformation:
            DesiredAccess = USER_READ_GENERAL |
                            USER_READ_PREFERENCES;
            break;

        case UserLogonInformation:
        case UserAccountInformation:
            DesiredAccess = USER_READ_GENERAL |
                            USER_READ_PREFERENCES |
                            USER_READ_LOGON |
                            USER_READ_ACCOUNT;
            break;

        case UserInternal1Information:
        case UserInternal2Information:
        case UserAllInformation:
            DesiredAccess = 0;
            break;

        default:
            return STATUS_INVALID_INFO_CLASS;
    }

    RtlAcquireResourceShared(&SampResource,
                             TRUE);

    /* Validate the domain handle */
    Status = SampValidateDbObject(UserHandle,
                                  SamDbUserObject,
                                  DesiredAccess,
                                  &UserObject);
    if (!NT_SUCCESS(Status))
    {
        TRACE("failed with status 0x%08lx\n", Status);
        goto done;
    }

    switch (UserInformationClass)
    {
        case UserGeneralInformation:
            Status = SampQueryUserGeneral(UserObject,
                                          Buffer);
            break;

        case UserPreferencesInformation:
            Status = SampQueryUserPreferences(UserObject,
                                              Buffer);
            break;

        case UserLogonInformation:
            Status = SampQueryUserLogon(UserObject,
                                        Buffer);
            break;

        case UserLogonHoursInformation:
            Status = SampQueryUserLogonHours(UserObject,
                                             Buffer);
            break;

        case UserAccountInformation:
            Status = SampQueryUserAccount(UserObject,
                                          Buffer);
            break;

        case UserNameInformation:
            Status = SampQueryUserName(UserObject,
                                       Buffer);
            break;

        case UserAccountNameInformation:
            Status = SampQueryUserAccountName(UserObject,
                                              Buffer);
            break;

        case UserFullNameInformation:
            Status = SampQueryUserFullName(UserObject,
                                           Buffer);
            break;

        case UserPrimaryGroupInformation:
            Status = SampQueryUserPrimaryGroup(UserObject,
                                               Buffer);
            break;

        case UserHomeInformation:
            Status = SampQueryUserHome(UserObject,
                                       Buffer);
            break;

        case UserScriptInformation:
            Status = SampQueryUserScript(UserObject,
                                         Buffer);
            break;

        case UserProfileInformation:
            Status = SampQueryUserProfile(UserObject,
                                          Buffer);
            break;

        case UserAdminCommentInformation:
            Status = SampQueryUserAdminComment(UserObject,
                                               Buffer);
            break;

        case UserWorkStationsInformation:
            Status = SampQueryUserWorkStations(UserObject,
                                               Buffer);
            break;

        case UserControlInformation:
            Status = SampQueryUserControl(UserObject,
                                          Buffer);
            break;

        case UserExpiresInformation:
            Status = SampQueryUserExpires(UserObject,
                                          Buffer);
            break;

        case UserInternal1Information:
            Status = SampQueryUserInternal1(UserObject,
                                            Buffer);
            break;

        case UserInternal2Information:
            Status = SampQueryUserInternal2(UserObject,
                                            Buffer);
            break;

        case UserParametersInformation:
            Status = SampQueryUserParameters(UserObject,
                                             Buffer);
            break;

        case UserAllInformation:
            Status = SampQueryUserAll(UserObject,
                                      Buffer);
            break;

//        case UserInternal4Information:
//        case UserInternal5Information:
//        case UserInternal4InformationNew:
//        case UserInternal5InformationNew:

        default:
            Status = STATUS_INVALID_INFO_CLASS;
    }

done:
    RtlReleaseResource(&SampResource);

    return Status;
}


/* Function 48 */
NTSTATUS
NTAPI
SamrQueryDisplayInformation2(IN SAMPR_HANDLE DomainHandle,
                             IN DOMAIN_DISPLAY_INFORMATION DisplayInformationClass,
                             IN unsigned long Index,
                             IN unsigned long EntryCount,
                             IN unsigned long PreferredMaximumLength,
                             OUT unsigned long *TotalAvailable,
                             OUT unsigned long *TotalReturned,
                             OUT PSAMPR_DISPLAY_INFO_BUFFER Buffer)
{
    TRACE("SamrQueryDisplayInformation2(%p %lu %lu %lu %lu %p %p %p)\n",
          DomainHandle, DisplayInformationClass, Index,
          EntryCount, PreferredMaximumLength, TotalAvailable,
          TotalReturned, Buffer);

    return SamrQueryDisplayInformation3(DomainHandle,
                                        DisplayInformationClass,
                                        Index,
                                        EntryCount,
                                        PreferredMaximumLength,
                                        TotalAvailable,
                                        TotalReturned,
                                        Buffer);
}


/* Function 49 */
NTSTATUS
NTAPI
SamrGetDisplayEnumerationIndex2(IN SAMPR_HANDLE DomainHandle,
                                IN DOMAIN_DISPLAY_INFORMATION DisplayInformationClass,
                                IN PRPC_UNICODE_STRING Prefix,
                                OUT unsigned long *Index)
{
    TRACE("SamrGetDisplayEnumerationIndex2(%p %lu %p %p)\n",
          DomainHandle, DisplayInformationClass, Prefix, Index);

    UNIMPLEMENTED;
    return STATUS_NOT_IMPLEMENTED;
}


/* Function 50 */
NTSTATUS
NTAPI
SamrCreateUser2InDomain(IN SAMPR_HANDLE DomainHandle,
                        IN PRPC_UNICODE_STRING Name,
                        IN unsigned long AccountType,
                        IN ACCESS_MASK DesiredAccess,
                        OUT SAMPR_HANDLE *UserHandle,
                        OUT unsigned long *GrantedAccess,
                        OUT unsigned long *RelativeId)
{
    SAM_DOMAIN_FIXED_DATA FixedDomainData;
    SAM_USER_FIXED_DATA FixedUserData;
    PSAM_DB_OBJECT DomainObject;
    PSAM_DB_OBJECT UserObject;
    GROUP_MEMBERSHIP GroupMembership;
    UCHAR LogonHours[23];
    ULONG ulSize;
    ULONG ulRid;
    WCHAR szRid[9];
    PSECURITY_DESCRIPTOR Sd = NULL;
    ULONG SdSize = 0;
    PSID UserSid = NULL;
    NTSTATUS Status;

    TRACE("SamrCreateUserInDomain(%p %p %lx %p %p)\n",
          DomainHandle, Name, DesiredAccess, UserHandle, RelativeId);

    if (Name == NULL ||
        Name->Length == 0 ||
        Name->Buffer == NULL ||
        UserHandle == NULL ||
        RelativeId == NULL)
        return STATUS_INVALID_PARAMETER;

    /* Check for valid account type */
    if (AccountType != USER_NORMAL_ACCOUNT &&
        AccountType != USER_WORKSTATION_TRUST_ACCOUNT &&
        AccountType != USER_INTERDOMAIN_TRUST_ACCOUNT &&
        AccountType != USER_SERVER_TRUST_ACCOUNT &&
        AccountType != USER_TEMP_DUPLICATE_ACCOUNT)
        return STATUS_INVALID_PARAMETER;

    /* Map generic access rights */
    RtlMapGenericMask(&DesiredAccess,
                      &UserMapping);

    RtlAcquireResourceExclusive(&SampResource,
                                TRUE);

    /* Validate the domain handle */
    Status = SampValidateDbObject(DomainHandle,
                                  SamDbDomainObject,
                                  DOMAIN_CREATE_USER,
                                  &DomainObject);
    if (!NT_SUCCESS(Status))
    {
        TRACE("failed with status 0x%08lx\n", Status);
        goto done;
    }

    /* Check the user account name */
    Status = SampCheckAccountName(Name, 20);
    if (!NT_SUCCESS(Status))
    {
        TRACE("SampCheckAccountName failed (Status 0x%08lx)\n", Status);
        goto done;
    }

    /* Check if the user name already exists in the domain */
    Status = SampCheckAccountNameInDomain(DomainObject,
                                          Name->Buffer);
    if (!NT_SUCCESS(Status))
    {
        TRACE("User name \'%S\' already exists in domain (Status 0x%08lx)\n",
              Name->Buffer, Status);
        goto done;
    }

    /* Get the fixed domain attributes */
    ulSize = sizeof(SAM_DOMAIN_FIXED_DATA);
    Status = SampGetObjectAttribute(DomainObject,
                                    L"F",
                                    NULL,
                                    (PVOID)&FixedDomainData,
                                    &ulSize);
    if (!NT_SUCCESS(Status))
    {
        TRACE("failed with status 0x%08lx\n", Status);
        goto done;
    }

    /* Increment the NextRid attribute */
    ulRid = FixedDomainData.NextRid;
    FixedDomainData.NextRid++;

    TRACE("RID: %lx\n", ulRid);

    /* Create the user SID */
    Status = SampCreateAccountSid(DomainObject,
                                  ulRid,
                                  &UserSid);
    if (!NT_SUCCESS(Status))
    {
        TRACE("SampCreateAccountSid failed (Status 0x%08lx)\n", Status);
        goto done;
    }

    /* Create the security descriptor */
    Status = SampCreateUserSD(UserSid,
                              &Sd,
                              &SdSize);
    if (!NT_SUCCESS(Status))
    {
        TRACE("SampCreateUserSD failed (Status 0x%08lx)\n", Status);
        goto done;
    }

    /* Store the fixed domain attributes */
    Status = SampSetObjectAttribute(DomainObject,
                                    L"F",
                                    REG_BINARY,
                                    &FixedDomainData,
                                    ulSize);
    if (!NT_SUCCESS(Status))
    {
        TRACE("failed with status 0x%08lx\n", Status);
        goto done;
    }

    /* Convert the RID into a string (hex) */
    swprintf(szRid, L"%08lX", ulRid);

    /* Create the user object */
    Status = SampCreateDbObject(DomainObject,
                                L"Users",
                                szRid,
                                ulRid,
                                SamDbUserObject,
                                DesiredAccess,
                                &UserObject);
    if (!NT_SUCCESS(Status))
    {
        TRACE("failed with status 0x%08lx\n", Status);
        goto done;
    }

    /* Add the account name for the user object */
    Status = SampSetAccountNameInDomain(DomainObject,
                                        L"Users",
                                        Name->Buffer,
                                        ulRid);
    if (!NT_SUCCESS(Status))
    {
        TRACE("failed with status 0x%08lx\n", Status);
        goto done;
    }

    /* Initialize fixed user data */
    FixedUserData.Version = 1;
    FixedUserData.Reserved = 0;
    FixedUserData.LastLogon.QuadPart = 0;
    FixedUserData.LastLogoff.QuadPart = 0;
    FixedUserData.PasswordLastSet.QuadPart = 0;
    FixedUserData.AccountExpires.LowPart = MAXULONG;
    FixedUserData.AccountExpires.HighPart = MAXLONG;
    FixedUserData.LastBadPasswordTime.QuadPart = 0;
    FixedUserData.UserId = ulRid;
    FixedUserData.PrimaryGroupId = DOMAIN_GROUP_RID_USERS;
    FixedUserData.UserAccountControl = USER_ACCOUNT_DISABLED |
                                       USER_PASSWORD_NOT_REQUIRED |
                                       AccountType;
    FixedUserData.CountryCode = 0;
    FixedUserData.CodePage = 0;
    FixedUserData.BadPasswordCount = 0;
    FixedUserData.LogonCount = 0;
    FixedUserData.AdminCount = 0;
    FixedUserData.OperatorCount = 0;

    /* Set fixed user data attribute */
    Status = SampSetObjectAttribute(UserObject,
                                    L"F",
                                    REG_BINARY,
                                    (LPVOID)&FixedUserData,
                                    sizeof(SAM_USER_FIXED_DATA));
    if (!NT_SUCCESS(Status))
    {
        TRACE("failed with status 0x%08lx\n", Status);
        goto done;
    }

    /* Set the Name attribute */
    Status = SampSetObjectAttributeString(UserObject,
                                          L"Name",
                                          Name);
    if (!NT_SUCCESS(Status))
    {
        TRACE("failed with status 0x%08lx\n", Status);
        goto done;
    }

    /* Set the FullName attribute */
    Status = SampSetObjectAttributeString(UserObject,
                                          L"FullName",
                                          NULL);
    if (!NT_SUCCESS(Status))
    {
        TRACE("failed with status 0x%08lx\n", Status);
        goto done;
    }

    /* Set the HomeDirectory attribute */
    Status = SampSetObjectAttributeString(UserObject,
                                          L"HomeDirectory",
                                          NULL);
    if (!NT_SUCCESS(Status))
    {
        TRACE("failed with status 0x%08lx\n", Status);
        goto done;
    }

    /* Set the HomeDirectoryDrive attribute */
    Status = SampSetObjectAttributeString(UserObject,
                                          L"HomeDirectoryDrive",
                                          NULL);
    if (!NT_SUCCESS(Status))
    {
        TRACE("failed with status 0x%08lx\n", Status);
        goto done;
    }

    /* Set the ScriptPath attribute */
    Status = SampSetObjectAttributeString(UserObject,
                                          L"ScriptPath",
                                          NULL);
    if (!NT_SUCCESS(Status))
    {
        TRACE("failed with status 0x%08lx\n", Status);
        goto done;
    }

    /* Set the ProfilePath attribute */
    Status = SampSetObjectAttributeString(UserObject,
                                          L"ProfilePath",
                                          NULL);
    if (!NT_SUCCESS(Status))
    {
        TRACE("failed with status 0x%08lx\n", Status);
        goto done;
    }

    /* Set the AdminComment attribute */
    Status = SampSetObjectAttributeString(UserObject,
                                          L"AdminComment",
                                          NULL);
    if (!NT_SUCCESS(Status))
    {
        TRACE("failed with status 0x%08lx\n", Status);
        goto done;
    }

    /* Set the UserComment attribute */
    Status = SampSetObjectAttributeString(UserObject,
                                          L"UserComment",
                                          NULL);
    if (!NT_SUCCESS(Status))
    {
        TRACE("failed with status 0x%08lx\n", Status);
        goto done;
    }

    /* Set the WorkStations attribute */
    Status = SampSetObjectAttributeString(UserObject,
                                          L"WorkStations",
                                          NULL);
    if (!NT_SUCCESS(Status))
    {
        TRACE("failed with status 0x%08lx\n", Status);
        goto done;
    }

    /* Set the Parameters attribute */
    Status = SampSetObjectAttributeString(UserObject,
                                          L"Parameters",
                                          NULL);
    if (!NT_SUCCESS(Status))
    {
        TRACE("failed with status 0x%08lx\n", Status);
        goto done;
    }

    /* Set LogonHours attribute*/
    *((PUSHORT)LogonHours) = 168;
    memset(&(LogonHours[2]), 0xff, 21);

    Status = SampSetObjectAttribute(UserObject,
                                    L"LogonHours",
                                    REG_BINARY,
                                    &LogonHours,
                                    sizeof(LogonHours));
    if (!NT_SUCCESS(Status))
    {
        TRACE("failed with status 0x%08lx\n", Status);
        goto done;
    }

    /* Set Groups attribute*/
    GroupMembership.RelativeId = DOMAIN_GROUP_RID_USERS;
    GroupMembership.Attributes = SE_GROUP_MANDATORY |
                                 SE_GROUP_ENABLED |
                                 SE_GROUP_ENABLED_BY_DEFAULT;

    Status = SampSetObjectAttribute(UserObject,
                                    L"Groups",
                                    REG_BINARY,
                                    &GroupMembership,
                                    sizeof(GROUP_MEMBERSHIP));
    if (!NT_SUCCESS(Status))
    {
        TRACE("failed with status 0x%08lx\n", Status);
        goto done;
    }

    /* Set LMPwd attribute*/
    Status = SampSetObjectAttribute(UserObject,
                                    L"LMPwd",
                                    REG_BINARY,
                                    NULL,
                                    0);
    if (!NT_SUCCESS(Status))
    {
        TRACE("failed with status 0x%08lx\n", Status);
        goto done;
    }

    /* Set NTPwd attribute*/
    Status = SampSetObjectAttribute(UserObject,
                                    L"NTPwd",
                                    REG_BINARY,
                                    NULL,
                                    0);
    if (!NT_SUCCESS(Status))
    {
        TRACE("failed with status 0x%08lx\n", Status);
        goto done;
    }

    /* Set LMPwdHistory attribute*/
    Status = SampSetObjectAttribute(UserObject,
                                    L"LMPwdHistory",
                                    REG_BINARY,
                                    NULL,
                                    0);
    if (!NT_SUCCESS(Status))
    {
        TRACE("failed with status 0x%08lx\n", Status);
        goto done;
    }

    /* Set NTPwdHistory attribute*/
    Status = SampSetObjectAttribute(UserObject,
                                    L"NTPwdHistory",
                                    REG_BINARY,
                                    NULL,
                                    0);
    if (!NT_SUCCESS(Status))
    {
        TRACE("failed with status 0x%08lx\n", Status);
        goto done;
    }

    /* Set the PrivateData attribute */
    Status = SampSetObjectAttributeString(UserObject,
                                          L"PrivateData",
                                          NULL);
    if (!NT_SUCCESS(Status))
    {
        TRACE("failed with status 0x%08lx\n", Status);
        goto done;
    }

    /* Set the SecDesc attribute*/
    Status = SampSetObjectAttribute(UserObject,
                                    L"SecDesc",
                                    REG_BINARY,
                                    Sd,
                                    SdSize);
    if (!NT_SUCCESS(Status))
    {
        TRACE("failed with status 0x%08lx\n", Status);
        goto done;
    }

    if (NT_SUCCESS(Status))
    {
        *UserHandle = (SAMPR_HANDLE)UserObject;
        *RelativeId = ulRid;
        *GrantedAccess = UserObject->Access;
    }

done:
    if (Sd != NULL)
        RtlFreeHeap(RtlGetProcessHeap(), 0, Sd);

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

    RtlReleaseResource(&SampResource);

    TRACE("returns with status 0x%08lx\n", Status);

    return Status;
}


/* Function 51 */
NTSTATUS
NTAPI
SamrQueryDisplayInformation3(IN SAMPR_HANDLE DomainHandle,
                             IN DOMAIN_DISPLAY_INFORMATION DisplayInformationClass,
                             IN unsigned long Index,
                             IN unsigned long EntryCount,
                             IN unsigned long PreferredMaximumLength,
                             OUT unsigned long *TotalAvailable,
                             OUT unsigned long *TotalReturned,
                             OUT PSAMPR_DISPLAY_INFO_BUFFER Buffer)
{
    PSAM_DB_OBJECT DomainObject;
    NTSTATUS Status;

    FIXME("SamrQueryDisplayInformation3(%p %lu %lu %lu %lu %p %p %p)\n",
          DomainHandle, DisplayInformationClass, Index,
          EntryCount, PreferredMaximumLength, TotalAvailable,
          TotalReturned, Buffer);

    RtlAcquireResourceShared(&SampResource,
                             TRUE);

    /* Validate the domain handle */
    Status = SampValidateDbObject(DomainHandle,
                                  SamDbDomainObject,
                                  DOMAIN_LIST_ACCOUNTS,
                                  &DomainObject);
    if (!NT_SUCCESS(Status))
    {
        ERR("SampValidateDbObject() failed (Status 0x%08lx)\n", Status);
        goto done;
    }

    Status = SampFillDisplayCache(DomainObject,
                                  DisplayInformationClass);
    if (!NT_SUCCESS(Status))
    {
        ERR("SampFillDisplayCache() failed (Status 0x%08lx)\n", Status);
        goto done;
    }

done:
    TRACE("returns with status 0x%08lx\n", Status);

    RtlReleaseResource(&SampResource);

//    return Status;
    return STATUS_NOT_IMPLEMENTED;
}


/* Function 52 */
NTSTATUS
NTAPI
SamrAddMultipleMembersToAlias(IN SAMPR_HANDLE AliasHandle,
                              IN PSAMPR_PSID_ARRAY MembersBuffer)
{
    ULONG i;
    NTSTATUS Status = STATUS_SUCCESS;

    TRACE("SamrAddMultipleMembersToAlias(%p %p)\n",
          AliasHandle, MembersBuffer);

    for (i = 0; i < MembersBuffer->Count; i++)
    {
        Status = SamrAddMemberToAlias(AliasHandle,
                                      ((PSID *)MembersBuffer->Sids)[i]);

        if (Status == STATUS_MEMBER_IN_ALIAS)
            Status = STATUS_SUCCESS;

        if (!NT_SUCCESS(Status))
            break;
    }

    return Status;
}


/* Function 53 */
NTSTATUS
NTAPI
SamrRemoveMultipleMembersFromAlias(IN SAMPR_HANDLE AliasHandle,
                                   IN PSAMPR_PSID_ARRAY MembersBuffer)
{
    ULONG i;
    NTSTATUS Status = STATUS_SUCCESS;

    TRACE("SamrRemoveMultipleMembersFromAlias(%p %p)\n",
          AliasHandle, MembersBuffer);

    for (i = 0; i < MembersBuffer->Count; i++)
    {
        Status = SamrRemoveMemberFromAlias(AliasHandle,
                                           ((PSID *)MembersBuffer->Sids)[i]);

        if (Status == STATUS_MEMBER_IN_ALIAS)
            Status = STATUS_SUCCESS;

        if (!NT_SUCCESS(Status))
            break;
    }

    return Status;
}


/* Function 54 */
NTSTATUS
NTAPI
SamrOemChangePasswordUser2(IN handle_t BindingHandle,
                           IN PRPC_STRING ServerName,
                           IN PRPC_STRING UserName,
                           IN PSAMPR_ENCRYPTED_USER_PASSWORD NewPasswordEncryptedWithOldLm,
                           IN PENCRYPTED_LM_OWF_PASSWORD OldLmOwfPasswordEncryptedWithNewLm)
{
    UNIMPLEMENTED;
    return STATUS_NOT_IMPLEMENTED;
}

/* Function 55 */
NTSTATUS
NTAPI
SamrUnicodeChangePasswordUser2(IN handle_t BindingHandle,
                               IN PRPC_UNICODE_STRING ServerName,
                               IN PRPC_UNICODE_STRING UserName,
                               IN PSAMPR_ENCRYPTED_USER_PASSWORD NewPasswordEncryptedWithOldNt,
                               IN PENCRYPTED_NT_OWF_PASSWORD OldNtOwfPasswordEncryptedWithNewNt,
                               IN unsigned char LmPresent,
                               IN PSAMPR_ENCRYPTED_USER_PASSWORD NewPasswordEncryptedWithOldLm,
                               IN PENCRYPTED_LM_OWF_PASSWORD OldLmOwfPasswordEncryptedWithNewNt)
{
    UNIMPLEMENTED;
    return STATUS_NOT_IMPLEMENTED;
}


/* Function 56 */
NTSTATUS
NTAPI
SamrGetDomainPasswordInformation(IN handle_t BindingHandle,
                                 IN PRPC_UNICODE_STRING Unused,
                                 OUT PUSER_DOMAIN_PASSWORD_INFORMATION PasswordInformation)
{
    SAMPR_HANDLE ServerHandle = NULL;
    PSAM_DB_OBJECT DomainObject = NULL;
    SAM_DOMAIN_FIXED_DATA FixedData;
    ULONG Length;
    NTSTATUS Status;

    TRACE("SamrGetDomainPasswordInformation(%p %p %p)\n",
          BindingHandle, Unused, PasswordInformation);

    Status = SamrConnect(NULL,
                         &ServerHandle,
                         SAM_SERVER_LOOKUP_DOMAIN);
    if (!NT_SUCCESS(Status))
    {
        TRACE("SamrConnect() failed (Status 0x%08lx)\n", Status);
        goto done;
    }

    Status = SampOpenDbObject((PSAM_DB_OBJECT)ServerHandle,
                              L"Domains",
                              L"Account",
                              0,
                              SamDbDomainObject,
                              DOMAIN_READ_PASSWORD_PARAMETERS,
                              &DomainObject);
    if (!NT_SUCCESS(Status))
    {
        TRACE("SampOpenDbObject() failed (Status 0x%08lx)\n", Status);
        goto done;
    }

    Length = sizeof(SAM_DOMAIN_FIXED_DATA);
    Status = SampGetObjectAttribute(DomainObject,
                                    L"F",
                                    NULL,
                                    &FixedData,
                                    &Length);
    if (!NT_SUCCESS(Status))
    {
        TRACE("SampGetObjectAttribute() failed (Status 0x%08lx)\n", Status);
        goto done;
    }

    PasswordInformation->MinPasswordLength = FixedData.MinPasswordLength;
    PasswordInformation->PasswordProperties = FixedData.PasswordProperties;

done:
    if (DomainObject != NULL)
        SampCloseDbObject(DomainObject);

    if (ServerHandle != NULL)
        SamrCloseHandle(&ServerHandle);

    return Status;
}


/* Function 57 */
NTSTATUS
NTAPI
SamrConnect2(IN PSAMPR_SERVER_NAME ServerName,
             OUT SAMPR_HANDLE *ServerHandle,
             IN ACCESS_MASK DesiredAccess)
{
    SAMPR_REVISION_INFO InRevisionInfo, OutRevisionInfo;
    ULONG OutVersion;

    TRACE("SamrConnect2(%p %p %lx)\n",
          ServerName, ServerHandle, DesiredAccess);

    InRevisionInfo.V1.Revision = 1;
    InRevisionInfo.V1.SupportedFeatures = 0;

    return SamrConnect5(ServerName,
                        DesiredAccess,
                        1,
                        &InRevisionInfo,
                        &OutVersion,
                        &OutRevisionInfo,
                        ServerHandle);
}


/* Function 58 */
NTSTATUS
NTAPI
SamrSetInformationUser2(IN SAMPR_HANDLE UserHandle,
                        IN USER_INFORMATION_CLASS UserInformationClass,
                        IN PSAMPR_USER_INFO_BUFFER Buffer)
{
    PSAM_DB_OBJECT UserObject;
    ACCESS_MASK DesiredAccess;
    NTSTATUS Status;

    TRACE("SamrSetInformationUser2(%p %lu %p)\n",
          UserHandle, UserInformationClass, Buffer);

    switch (UserInformationClass)
    {
        case UserLogonHoursInformation:
        case UserNameInformation:
        case UserAccountNameInformation:
        case UserFullNameInformation:
        case UserPrimaryGroupInformation:
        case UserHomeInformation:
        case UserScriptInformation:
        case UserProfileInformation:
        case UserAdminCommentInformation:
        case UserWorkStationsInformation:
        case UserControlInformation:
        case UserExpiresInformation:
        case UserParametersInformation:
            DesiredAccess = USER_WRITE_ACCOUNT;
            break;

        case UserGeneralInformation:
            DesiredAccess = USER_WRITE_ACCOUNT |
                            USER_WRITE_PREFERENCES;
            break;

        case UserPreferencesInformation:
            DesiredAccess = USER_WRITE_PREFERENCES;
            break;

        case UserSetPasswordInformation:
        case UserInternal1Information:
            DesiredAccess = USER_FORCE_PASSWORD_CHANGE;
            break;

        case UserAllInformation:
        case UserInternal2Information:
            DesiredAccess = 0; /* FIXME */
            break;

        default:
            return STATUS_INVALID_INFO_CLASS;
    }

    RtlAcquireResourceExclusive(&SampResource,
                                TRUE);

    /* Validate the domain handle */
    Status = SampValidateDbObject(UserHandle,
                                  SamDbUserObject,
                                  DesiredAccess,
                                  &UserObject);
    if (!NT_SUCCESS(Status))
    {
        TRACE("failed with status 0x%08lx\n", Status);
        goto done;
    }

    switch (UserInformationClass)
    {
        case UserGeneralInformation:
            Status = SampSetUserGeneral(UserObject,
                                        Buffer);
            break;

        case UserPreferencesInformation:
            Status = SampSetUserPreferences(UserObject,
                                            Buffer);
            break;

        case UserLogonHoursInformation:
            Status = SampSetLogonHoursAttribute(UserObject,
                                               &Buffer->LogonHours.LogonHours);
            break;

        case UserNameInformation:
            Status = SampSetUserName(UserObject,
                                     &Buffer->Name.UserName);
            if (!NT_SUCCESS(Status))
                break;

            Status = SampSetObjectAttributeString(UserObject,
                                                  L"FullName",
                                                  &Buffer->Name.FullName);
            break;

        case UserAccountNameInformation:
            Status = SampSetUserName(UserObject,
                                     &Buffer->AccountName.UserName);
            break;

        case UserFullNameInformation:
            Status = SampSetObjectAttributeString(UserObject,
                                                  L"FullName",
                                                  &Buffer->FullName.FullName);
            break;

        case UserPrimaryGroupInformation:
            Status = SampSetUserPrimaryGroup(UserObject,
                                             Buffer);
            break;

        case UserHomeInformation:
            Status = SampSetObjectAttributeString(UserObject,
                                                  L"HomeDirectory",
                                                  &Buffer->Home.HomeDirectory);
            if (!NT_SUCCESS(Status))
                break;

            Status = SampSetObjectAttributeString(UserObject,
                                                  L"HomeDirectoryDrive",
                                                  &Buffer->Home.HomeDirectoryDrive);
            break;

        case UserScriptInformation:
            Status = SampSetObjectAttributeString(UserObject,
                                                  L"ScriptPath",
                                                  &Buffer->Script.ScriptPath);
            break;

        case UserProfileInformation:
            Status = SampSetObjectAttributeString(UserObject,
                                                  L"ProfilePath",
                                                  &Buffer->Profile.ProfilePath);
            break;

        case UserAdminCommentInformation:
            Status = SampSetObjectAttributeString(UserObject,
                                                  L"AdminComment",
                                                  &Buffer->AdminComment.AdminComment);
            break;

        case UserWorkStationsInformation:
            Status = SampSetObjectAttributeString(UserObject,
                                                  L"WorkStations",
                                                  &Buffer->WorkStations.WorkStations);
            break;

        case UserSetPasswordInformation:
            TRACE("Password: %S\n", Buffer->SetPassword.Password.Buffer);
            TRACE("PasswordExpired: %d\n", Buffer->SetPassword.PasswordExpired);

            Status = SampSetObjectAttributeString(UserObject,
                                                  L"Password",
                                                  &Buffer->SetPassword.Password);
            break;

        case UserControlInformation:
            Status = SampSetUserControl(UserObject,
                                        Buffer);
            break;

        case UserExpiresInformation:
            Status = SampSetUserExpires(UserObject,
                                        Buffer);
            break;

        case UserInternal1Information:
            Status = SampSetUserInternal1(UserObject,
                                          Buffer);
            break;

        case UserInternal2Information:
            Status = SampSetUserInternal2(UserObject,
                                          Buffer);
            break;

        case UserParametersInformation:
            Status = SampSetObjectAttributeString(UserObject,
                                                  L"Parameters",
                                                  &Buffer->Parameters.Parameters);
            break;

        case UserAllInformation:
            Status = SampSetUserAll(UserObject,
                                    Buffer);
            break;

//        case UserInternal4Information:
//        case UserInternal5Information:
//        case UserInternal4InformationNew:
//        case UserInternal5InformationNew:

        default:
            Status = STATUS_INVALID_INFO_CLASS;
    }

done:
    RtlReleaseResource(&SampResource);

    return Status;
}


/* Function 59 */
NTSTATUS
NTAPI
SamrSetBootKeyInformation(IN handle_t BindingHandle) /* FIXME */
{
    UNIMPLEMENTED;
    return STATUS_NOT_IMPLEMENTED;
}

/* Function 60 */
NTSTATUS
NTAPI
SamrGetBootKeyInformation(IN handle_t BindingHandle) /* FIXME */
{
    UNIMPLEMENTED;
    return STATUS_NOT_IMPLEMENTED;
}

/* Function 61 */
NTSTATUS
NTAPI
SamrConnect3(IN handle_t BindingHandle) /* FIXME */
{
    UNIMPLEMENTED;
    return STATUS_NOT_IMPLEMENTED;
}


/* Function 62 */
NTSTATUS
NTAPI
SamrConnect4(IN PSAMPR_SERVER_NAME ServerName,
             OUT SAMPR_HANDLE *ServerHandle,
             IN unsigned long ClientRevision,
             IN ACCESS_MASK DesiredAccess)
{
    SAMPR_REVISION_INFO InRevisionInfo, OutRevisionInfo;
    ULONG OutVersion;

    TRACE("SamrConnect4(%p %p %lu 0x%lx)\n",
          ServerName, ServerHandle, ClientRevision, DesiredAccess);

    InRevisionInfo.V1.Revision = 2;
    InRevisionInfo.V1.SupportedFeatures = 0;

    return SamrConnect5(ServerName,
                        DesiredAccess,
                        1,
                        &InRevisionInfo,
                        &OutVersion,
                        &OutRevisionInfo,
                        ServerHandle);
}


/* Function 63 */
NTSTATUS
NTAPI
SamrUnicodeChangePasswordUser3(IN handle_t BindingHandle) /* FIXME */
{
    UNIMPLEMENTED;
    return STATUS_NOT_IMPLEMENTED;
}


/* Function 64 */
NTSTATUS
NTAPI
SamrConnect5(IN PSAMPR_SERVER_NAME ServerName,
             IN ACCESS_MASK DesiredAccess,
             IN unsigned long InVersion,
             IN SAMPR_REVISION_INFO *InRevisionInfo,
             OUT unsigned long *OutVersion,
             OUT SAMPR_REVISION_INFO *OutRevisionInfo,
             OUT SAMPR_HANDLE *ServerHandle)
{
    PSAM_DB_OBJECT ServerObject;
    NTSTATUS Status;

    TRACE("SamrConnect5(%p 0x%lx %lu %p %p %p %p)\n",
          ServerName, DesiredAccess, InVersion, InRevisionInfo,
          OutVersion, OutRevisionInfo, ServerHandle);

    if (InVersion != 1)
        return STATUS_NOT_SUPPORTED;

    RtlAcquireResourceShared(&SampResource,
                             TRUE);

    /* Map generic access rights */
    RtlMapGenericMask(&DesiredAccess,
                      &ServerMapping);

    /* Open the Server Object */
    Status = SampOpenDbObject(NULL,
                              NULL,
                              L"SAM",
                              0,
                              SamDbServerObject,
                              DesiredAccess,
                              &ServerObject);
    if (NT_SUCCESS(Status))
    {
        *OutVersion = 1;

        OutRevisionInfo->V1.Revision = 3;
        OutRevisionInfo->V1.SupportedFeatures = 0;

        *ServerHandle = (SAMPR_HANDLE)ServerObject;
    }

    RtlReleaseResource(&SampResource);

    TRACE("SamrConnect5 done (Status 0x%08lx)\n", Status);

    return Status;
}


/* Function 65 */
NTSTATUS
NTAPI
SamrRidToSid(IN SAMPR_HANDLE ObjectHandle,
             IN unsigned long Rid,
             OUT PRPC_SID *Sid)
{
    UNIMPLEMENTED;
    return STATUS_NOT_IMPLEMENTED;
}

/* Function 66 */
NTSTATUS
NTAPI
SamrSetDSRMPassword(IN handle_t BindingHandle,
                    IN PRPC_UNICODE_STRING Unused,
                    IN unsigned long UserId,
                    IN PENCRYPTED_NT_OWF_PASSWORD EncryptedNtOwfPassword)
{
    UNIMPLEMENTED;
    return STATUS_NOT_IMPLEMENTED;
}

/* Function 67 */
NTSTATUS
NTAPI
SamrValidatePassword(IN handle_t Handle,
                     IN PASSWORD_POLICY_VALIDATION_TYPE ValidationType,
                     IN PSAM_VALIDATE_INPUT_ARG InputArg,
                     OUT PSAM_VALIDATE_OUTPUT_ARG *OutputArg)
{
    UNIMPLEMENTED;
    return STATUS_NOT_IMPLEMENTED;
}

/* EOF */
