[SAMSRV]
[reactos.git] / reactos / dll / win32 / samsrv / samrpc.c
index 3a2f94e..fc885fd 100644 (file)
@@ -7,17 +7,18 @@
  * PROGRAMMERS:     Eric Kohl
  */
 
-/* INCLUDES ****************************************************************/
+/* INCLUDES ******************************************************************/
 
 #include "samsrv.h"
 
 WINE_DEFAULT_DEBUG_CHANNEL(samsrv);
 
-/* GLOBALS ********************************************************************/
+/* GLOBALS *******************************************************************/
 
 static SID_IDENTIFIER_AUTHORITY NtSidAuthority = {SECURITY_NT_AUTHORITY};
 
-/* FUNCTIONS ***************************************************************/
+
+/* FUNCTIONS *****************************************************************/
 
 VOID
 SampStartRpcServer(VOID)
@@ -618,84 +619,36 @@ SampQueryDomainGeneral(PSAM_DB_OBJECT DomainObject,
     InfoBuffer->General.DomainServerRole = FixedData.DomainServerRole;
     InfoBuffer->General.UasCompatibilityRequired = FixedData.UasCompatibilityRequired;
 
-    Length = 0;
-    Status = SampGetObjectAttribute(DomainObject,
-                                    L"OemInformation",
-                                    NULL,
-                                    NULL,
-                                    &Length);
+    /* Get the OemInformation string */
+    Status = SampGetObjectAttributeString(DomainObject,
+                                          L"OemInformation",
+                                          &InfoBuffer->General.OemInformation);
     if (!NT_SUCCESS(Status))
-        goto done;
-
-    InfoBuffer->General.OemInformation.Length = Length - sizeof(WCHAR);
-    InfoBuffer->General.OemInformation.MaximumLength = Length;
-    InfoBuffer->General.OemInformation.Buffer = midl_user_allocate(Length);
-    if (InfoBuffer->General.OemInformation.Buffer == NULL)
     {
-        Status = STATUS_INSUFFICIENT_RESOURCES;
+        TRACE("Status 0x%08lx\n", Status);
         goto done;
     }
 
-    Status = SampGetObjectAttribute(DomainObject,
-                                    L"OemInformation",
-                                    NULL,
-                                    (PVOID)InfoBuffer->General.OemInformation.Buffer,
-                                    &Length);
-    if (!NT_SUCCESS(Status))
-        goto done;
-
-    Length = 0;
-    Status = SampGetObjectAttribute(DomainObject,
-                                    L"Name",
-                                    NULL,
-                                    NULL,
-                                    &Length);
+    /* Get the Name string */
+    Status = SampGetObjectAttributeString(DomainObject,
+                                          L"Name",
+                                          &InfoBuffer->General.DomainName);
     if (!NT_SUCCESS(Status))
-        goto done;
-
-    InfoBuffer->General.DomainName.Length = Length - sizeof(WCHAR);
-    InfoBuffer->General.DomainName.MaximumLength = Length;
-    InfoBuffer->General.DomainName.Buffer = midl_user_allocate(Length);
-    if (InfoBuffer->General.DomainName.Buffer == NULL)
     {
-        Status = STATUS_INSUFFICIENT_RESOURCES;
+        TRACE("Status 0x%08lx\n", Status);
         goto done;
     }
 
-    Status = SampGetObjectAttribute(DomainObject,
-                                    L"Name",
-                                    NULL,
-                                    (PVOID)InfoBuffer->General.DomainName.Buffer,
-                                    &Length);
-    if (!NT_SUCCESS(Status))
-        goto done;
-
-    Length = 0;
-    Status = SampGetObjectAttribute(DomainObject,
-                                    L"ReplicaSourceNodeName",
-                                    NULL,
-                                    NULL,
-                                    &Length);
+    /* Get the ReplicaSourceNodeName string */
+    Status = SampGetObjectAttributeString(DomainObject,
+                                          L"ReplicaSourceNodeName",
+                                          &InfoBuffer->General.ReplicaSourceNodeName);
     if (!NT_SUCCESS(Status))
-        goto done;
-
-    InfoBuffer->General.ReplicaSourceNodeName.Length = Length - sizeof(WCHAR);
-    InfoBuffer->General.ReplicaSourceNodeName.MaximumLength = Length;
-    InfoBuffer->General.ReplicaSourceNodeName.Buffer = midl_user_allocate(Length);
-    if (InfoBuffer->General.ReplicaSourceNodeName.Buffer == NULL)
     {
-        Status = STATUS_INSUFFICIENT_RESOURCES;
+        TRACE("Status 0x%08lx\n", Status);
         goto done;
     }
 
-    Status = SampGetObjectAttribute(DomainObject,
-                                    L"ReplicaSourceNodeName",
-                                    NULL,
-                                    (PVOID)InfoBuffer->General.ReplicaSourceNodeName.Buffer,
-                                    &Length);
-    if (!NT_SUCCESS(Status))
-        goto done;
-
     InfoBuffer->General.UserCount = 0;  /* FIXME */
     InfoBuffer->General.GroupCount = 0; /* FIXME */
     InfoBuffer->General.AliasCount = 0; /* FIXME */
@@ -771,7 +724,6 @@ SampQueryDomainOem(PSAM_DB_OBJECT DomainObject,
                    PSAMPR_DOMAIN_INFO_BUFFER *Buffer)
 {
     PSAMPR_DOMAIN_INFO_BUFFER InfoBuffer = NULL;
-    ULONG Length = 0;
     NTSTATUS Status;
 
     *Buffer = NULL;
@@ -780,31 +732,16 @@ SampQueryDomainOem(PSAM_DB_OBJECT DomainObject,
     if (InfoBuffer == NULL)
         return STATUS_INSUFFICIENT_RESOURCES;
 
-    Status = SampGetObjectAttribute(DomainObject,
-                                    L"OemInformation",
-                                    NULL,
-                                    NULL,
-                                    &Length);
+    /* Get the OemInformation string */
+    Status = SampGetObjectAttributeString(DomainObject,
+                                          L"OemInformation",
+                                          &InfoBuffer->Oem.OemInformation);
     if (!NT_SUCCESS(Status))
-        goto done;
-
-    InfoBuffer->Oem.OemInformation.Length = Length - sizeof(WCHAR);
-    InfoBuffer->Oem.OemInformation.MaximumLength = Length;
-    InfoBuffer->Oem.OemInformation.Buffer = midl_user_allocate(Length);
-    if (InfoBuffer->Oem.OemInformation.Buffer == NULL)
     {
-        Status = STATUS_INSUFFICIENT_RESOURCES;
+        TRACE("Status 0x%08lx\n", Status);
         goto done;
     }
 
-    Status = SampGetObjectAttribute(DomainObject,
-                                    L"OemInformation",
-                                    NULL,
-                                    (PVOID)InfoBuffer->Oem.OemInformation.Buffer,
-                                    &Length);
-    if (!NT_SUCCESS(Status))
-        goto done;
-
     *Buffer = InfoBuffer;
 
 done:
@@ -828,7 +765,6 @@ SampQueryDomainName(PSAM_DB_OBJECT DomainObject,
                     PSAMPR_DOMAIN_INFO_BUFFER *Buffer)
 {
     PSAMPR_DOMAIN_INFO_BUFFER InfoBuffer = NULL;
-    ULONG Length = 0;
     NTSTATUS Status;
 
     *Buffer = NULL;
@@ -837,31 +773,16 @@ SampQueryDomainName(PSAM_DB_OBJECT DomainObject,
     if (InfoBuffer == NULL)
         return STATUS_INSUFFICIENT_RESOURCES;
 
-    Status = SampGetObjectAttribute(DomainObject,
-                                    L"Name",
-                                    NULL,
-                                    NULL,
-                                    &Length);
+    /* Get the Name string */
+    Status = SampGetObjectAttributeString(DomainObject,
+                                          L"Name",
+                                          &InfoBuffer->Name.DomainName);
     if (!NT_SUCCESS(Status))
-        goto done;
-
-    InfoBuffer->Name.DomainName.Length = Length - sizeof(WCHAR);
-    InfoBuffer->Name.DomainName.MaximumLength = Length;
-    InfoBuffer->Name.DomainName.Buffer = midl_user_allocate(Length);
-    if (InfoBuffer->Name.DomainName.Buffer == NULL)
     {
-        Status = STATUS_INSUFFICIENT_RESOURCES;
+        TRACE("Status 0x%08lx\n", Status);
         goto done;
     }
 
-    Status = SampGetObjectAttribute(DomainObject,
-                                    L"Name",
-                                    NULL,
-                                    (PVOID)InfoBuffer->Name.DomainName.Buffer,
-                                    &Length);
-    if (!NT_SUCCESS(Status))
-        goto done;
-
     *Buffer = InfoBuffer;
 
 done:
@@ -885,7 +806,6 @@ SampQueryDomainReplication(PSAM_DB_OBJECT DomainObject,
                            PSAMPR_DOMAIN_INFO_BUFFER *Buffer)
 {
     PSAMPR_DOMAIN_INFO_BUFFER InfoBuffer = NULL;
-    ULONG Length = 0;
     NTSTATUS Status;
 
     *Buffer = NULL;
@@ -894,31 +814,16 @@ SampQueryDomainReplication(PSAM_DB_OBJECT DomainObject,
     if (InfoBuffer == NULL)
         return STATUS_INSUFFICIENT_RESOURCES;
 
-    Status = SampGetObjectAttribute(DomainObject,
-                                    L"ReplicaSourceNodeName",
-                                    NULL,
-                                    NULL,
-                                    &Length);
+    /* Get the ReplicaSourceNodeName string */
+    Status = SampGetObjectAttributeString(DomainObject,
+                                          L"ReplicaSourceNodeName",
+                                          &InfoBuffer->Replication.ReplicaSourceNodeName);
     if (!NT_SUCCESS(Status))
-        goto done;
-
-    InfoBuffer->Replication.ReplicaSourceNodeName.Length = Length - sizeof(WCHAR);
-    InfoBuffer->Replication.ReplicaSourceNodeName.MaximumLength = Length;
-    InfoBuffer->Replication.ReplicaSourceNodeName.Buffer = midl_user_allocate(Length);
-    if (InfoBuffer->Replication.ReplicaSourceNodeName.Buffer == NULL)
     {
-        Status = STATUS_INSUFFICIENT_RESOURCES;
+        TRACE("Status 0x%08lx\n", Status);
         goto done;
     }
 
-    Status = SampGetObjectAttribute(DomainObject,
-                                    L"ReplicaSourceNodeName",
-                                    NULL,
-                                    (PVOID)InfoBuffer->Replication.ReplicaSourceNodeName.Buffer,
-                                    &Length);
-    if (!NT_SUCCESS(Status))
-        goto done;
-
     *Buffer = InfoBuffer;
 
 done:
@@ -1099,84 +1004,36 @@ SampQueryDomainGeneral2(PSAM_DB_OBJECT DomainObject,
     InfoBuffer->General2.LockoutObservationWindow = FixedData.LockoutObservationWindow;
     InfoBuffer->General2.LockoutThreshold = FixedData.LockoutThreshold;
 
-    Length = 0;
-    Status = SampGetObjectAttribute(DomainObject,
-                                    L"OemInformation",
-                                    NULL,
-                                    NULL,
-                                    &Length);
+    /* Get the OemInformation string */
+    Status = SampGetObjectAttributeString(DomainObject,
+                                          L"OemInformation",
+                                          &InfoBuffer->General2.I1.OemInformation);
     if (!NT_SUCCESS(Status))
-        goto done;
-
-    InfoBuffer->General2.I1.OemInformation.Length = Length - sizeof(WCHAR);
-    InfoBuffer->General2.I1.OemInformation.MaximumLength = Length;
-    InfoBuffer->General2.I1.OemInformation.Buffer = midl_user_allocate(Length);
-    if (InfoBuffer->General2.I1.OemInformation.Buffer == NULL)
     {
-        Status = STATUS_INSUFFICIENT_RESOURCES;
+        TRACE("Status 0x%08lx\n", Status);
         goto done;
     }
 
-    Status = SampGetObjectAttribute(DomainObject,
-                                    L"OemInformation",
-                                    NULL,
-                                    (PVOID)InfoBuffer->General2.I1.OemInformation.Buffer,
-                                    &Length);
-    if (!NT_SUCCESS(Status))
-        goto done;
-
-    Length = 0;
-    Status = SampGetObjectAttribute(DomainObject,
-                                    L"Name",
-                                    NULL,
-                                    NULL,
-                                    &Length);
+    /* Get the Name string */
+    Status = SampGetObjectAttributeString(DomainObject,
+                                          L"Name",
+                                          &InfoBuffer->General2.I1.DomainName);
     if (!NT_SUCCESS(Status))
-        goto done;
-
-    InfoBuffer->General2.I1.DomainName.Length = Length - sizeof(WCHAR);
-    InfoBuffer->General2.I1.DomainName.MaximumLength = Length;
-    InfoBuffer->General2.I1.DomainName.Buffer = midl_user_allocate(Length);
-    if (InfoBuffer->General2.I1.DomainName.Buffer == NULL)
     {
-        Status = STATUS_INSUFFICIENT_RESOURCES;
+        TRACE("Status 0x%08lx\n", Status);
         goto done;
     }
 
-    Status = SampGetObjectAttribute(DomainObject,
-                                    L"Name",
-                                    NULL,
-                                    (PVOID)InfoBuffer->General2.I1.DomainName.Buffer,
-                                    &Length);
-    if (!NT_SUCCESS(Status))
-        goto done;
-
-    Length = 0;
-    Status = SampGetObjectAttribute(DomainObject,
-                                    L"ReplicaSourceNodeName",
-                                    NULL,
-                                    NULL,
-                                    &Length);
+    /* Get the ReplicaSourceNodeName string */
+    Status = SampGetObjectAttributeString(DomainObject,
+                                          L"ReplicaSourceNodeName",
+                                          &InfoBuffer->General2.I1.ReplicaSourceNodeName);
     if (!NT_SUCCESS(Status))
-        goto done;
-
-    InfoBuffer->General2.I1.ReplicaSourceNodeName.Length = Length - sizeof(WCHAR);
-    InfoBuffer->General2.I1.ReplicaSourceNodeName.MaximumLength = Length;
-    InfoBuffer->General2.I1.ReplicaSourceNodeName.Buffer = midl_user_allocate(Length);
-    if (InfoBuffer->General2.I1.ReplicaSourceNodeName.Buffer == NULL)
     {
-        Status = STATUS_INSUFFICIENT_RESOURCES;
+        TRACE("Status 0x%08lx\n", Status);
         goto done;
     }
 
-    Status = SampGetObjectAttribute(DomainObject,
-                                    L"ReplicaSourceNodeName",
-                                    NULL,
-                                    (PVOID)InfoBuffer->General2.I1.ReplicaSourceNodeName.Buffer,
-                                    &Length);
-    if (!NT_SUCCESS(Status))
-        goto done;
-
     InfoBuffer->General2.I1.UserCount = 0;  /* FIXME */
     InfoBuffer->General2.I1.GroupCount = 0; /* FIXME */
     InfoBuffer->General2.I1.AliasCount = 0; /* FIXME */
@@ -1685,49 +1542,23 @@ SamrCreateGroupInDomain(IN SAMPR_HANDLE DomainHandle,
                         OUT SAMPR_HANDLE *GroupHandle,
                         OUT unsigned long *RelativeId)
 {
-    UNIMPLEMENTED;
-    return STATUS_NOT_IMPLEMENTED;
-}
-
-/* Function 10 */
-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)
-{
-    UNIMPLEMENTED;
-    return STATUS_NOT_IMPLEMENTED;
-}
-
-/* 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)
-{
+    UNICODE_STRING EmptyString = RTL_CONSTANT_STRING(L"");
     SAM_DOMAIN_FIXED_DATA FixedDomainData;
-    SAM_USER_FIXED_DATA FixedUserData;
+    SAM_GROUP_FIXED_DATA FixedGroupData;
     PSAM_DB_OBJECT DomainObject;
-    PSAM_DB_OBJECT UserObject;
+    PSAM_DB_OBJECT GroupObject;
     ULONG ulSize;
     ULONG ulRid;
     WCHAR szRid[9];
-    BOOL bAliasExists = FALSE;
     NTSTATUS Status;
 
-    TRACE("SamrCreateUserInDomain(%p %p %lx %p %p)\n",
-          DomainHandle, Name, DesiredAccess, UserHandle, RelativeId);
+    TRACE("SamrCreateGroupInDomain(%p %p %lx %p %p)\n",
+          DomainHandle, Name, DesiredAccess, GroupHandle, RelativeId);
 
     /* Validate the domain handle */
     Status = SampValidateDbObject(DomainHandle,
                                   SamDbDomainObject,
-                                  DOMAIN_CREATE_USER,
+                                  DOMAIN_CREATE_GROUP,
                                   &DomainObject);
     if (!NT_SUCCESS(Status))
     {
@@ -1735,6 +1566,16 @@ SamrCreateUserInDomain(IN SAMPR_HANDLE DomainHandle,
         return Status;
     }
 
+    /* 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);
+        return Status;
+    }
+
     /* Get the fixed domain attributes */
     ulSize = sizeof(SAM_DOMAIN_FIXED_DATA);
     Status = SampGetObjectAttribute(DomainObject,
@@ -1769,41 +1610,24 @@ SamrCreateUserInDomain(IN SAMPR_HANDLE DomainHandle,
     /* Convert the RID into a string (hex) */
     swprintf(szRid, L"%08lX", ulRid);
 
-    /* Check whether the user name is already in use */
-    Status = SampCheckDbObjectNameAlias(DomainObject,
-                                        L"Users",
-                                        Name->Buffer,
-                                        &bAliasExists);
-    if (!NT_SUCCESS(Status))
-    {
-        TRACE("failed with status 0x%08lx\n", Status);
-        return Status;
-    }
-
-    if (bAliasExists)
-    {
-        TRACE("The user account %S already exists!\n", Name->Buffer);
-        return STATUS_USER_EXISTS;
-    }
-
-    /* Create the user object */
+    /* Create the group object */
     Status = SampCreateDbObject(DomainObject,
-                                L"Users",
+                                L"Groups",
                                 szRid,
-                                SamDbUserObject,
+                                SamDbGroupObject,
                                 DesiredAccess,
-                                &UserObject);
+                                &GroupObject);
     if (!NT_SUCCESS(Status))
     {
         TRACE("failed with status 0x%08lx\n", Status);
         return Status;
     }
 
-    /* Add the name alias for the user object */
-    Status = SampSetDbObjectNameAlias(DomainObject,
-                                      L"Users",
-                                      Name->Buffer,
-                                      ulRid);
+    /* 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);
@@ -1811,25 +1635,25 @@ SamrCreateUserInDomain(IN SAMPR_HANDLE DomainHandle,
     }
 
     /* Initialize fixed user data */
-    memset(&FixedUserData, 0, sizeof(SAM_USER_FIXED_DATA));
-    FixedUserData.Version = 1;
+    memset(&FixedGroupData, 0, sizeof(SAM_GROUP_FIXED_DATA));
+    FixedGroupData.Version = 1;
 
-    FixedUserData.UserId = ulRid;
+    FixedGroupData.GroupId = ulRid;
 
     /* Set fixed user data attribute */
-    Status = SampSetObjectAttribute(UserObject,
+    Status = SampSetObjectAttribute(GroupObject,
                                     L"F",
                                     REG_BINARY,
-                                    (LPVOID)&FixedUserData,
-                                    sizeof(SAM_USER_FIXED_DATA));
+                                    (LPVOID)&FixedGroupData,
+                                    sizeof(SAM_GROUP_FIXED_DATA));
     if (!NT_SUCCESS(Status))
     {
         TRACE("failed with status 0x%08lx\n", Status);
         return Status;
     }
 
-    /* Set the name attribute */
-    Status = SampSetObjectAttribute(UserObject,
+    /* Set the Name attribute */
+    Status = SampSetObjectAttribute(GroupObject,
                                     L"Name",
                                     REG_SZ,
                                     (LPVOID)Name->Buffer,
@@ -1840,11 +1664,21 @@ SamrCreateUserInDomain(IN SAMPR_HANDLE DomainHandle,
         return Status;
     }
 
-    /* FIXME: Set default user attributes */
+    /* Set the AdminComment attribute */
+    Status = SampSetObjectAttribute(GroupObject,
+                                    L"AdminComment",
+                                    REG_SZ,
+                                    EmptyString.Buffer,
+                                    EmptyString.MaximumLength);
+    if (!NT_SUCCESS(Status))
+    {
+        TRACE("failed with status 0x%08lx\n", Status);
+        return Status;
+    }
 
     if (NT_SUCCESS(Status))
     {
-        *UserHandle = (SAMPR_HANDLE)UserObject;
+        *GroupHandle = (SAMPR_HANDLE)GroupObject;
         *RelativeId = ulRid;
     }
 
@@ -1853,216 +1687,71 @@ SamrCreateUserInDomain(IN SAMPR_HANDLE DomainHandle,
     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)
-{
-    UNIMPLEMENTED;
-    return STATUS_NOT_IMPLEMENTED;
-}
 
-/* Function 14 */
+/* Function 11 */
 NTSTATUS
 NTAPI
-SamrCreateAliasInDomain(IN SAMPR_HANDLE DomainHandle,
-                        IN PRPC_UNICODE_STRING AccountName,
-                        IN ACCESS_MASK DesiredAccess,
-                        OUT SAMPR_HANDLE *AliasHandle,
-                        OUT unsigned long *RelativeId)
+SamrEnumerateGroupsInDomain(IN SAMPR_HANDLE DomainHandle,
+                            IN OUT unsigned long *EnumerationContext,
+                            OUT PSAMPR_ENUMERATION_BUFFER *Buffer,
+                            IN unsigned long PreferedMaximumLength,
+                            OUT unsigned long *CountReturned)
 {
-    SAM_DOMAIN_FIXED_DATA FixedDomainData;
+    PSAMPR_ENUMERATION_BUFFER EnumBuffer = NULL;
     PSAM_DB_OBJECT DomainObject;
-    PSAM_DB_OBJECT AliasObject;
-    UNICODE_STRING EmptyString = RTL_CONSTANT_STRING(L"");
-    ULONG ulSize;
-    ULONG ulRid;
-    WCHAR szRid[9];
-    BOOL bAliasExists = FALSE;
+    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("SamrCreateAliasInDomain(%p %p %lx %p %p)\n",
-          DomainHandle, AccountName, DesiredAccess, AliasHandle, RelativeId);
+    TRACE("SamrEnumerateUsersInDomain(%p %p %p %lu %p)\n",
+          DomainHandle, EnumerationContext, Buffer,
+          PreferedMaximumLength, CountReturned);
 
     /* Validate the domain handle */
     Status = SampValidateDbObject(DomainHandle,
                                   SamDbDomainObject,
-                                  DOMAIN_CREATE_ALIAS,
+                                  DOMAIN_LIST_ACCOUNTS,
                                   &DomainObject);
     if (!NT_SUCCESS(Status))
-    {
-        TRACE("failed with status 0x%08lx\n", Status);
         return Status;
-    }
 
-    /* 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);
-        return Status;
-    }
-
-    /* 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);
-        return Status;
-    }
-
-    TRACE("RID: %lx\n", ulRid);
-
-    /* Convert the RID into a string (hex) */
-    swprintf(szRid, L"%08lX", ulRid);
-
-    /* Check whether the user name is already in use */
-    Status = SampCheckDbObjectNameAlias(DomainObject,
-                                        L"Aliases",
-                                        AccountName->Buffer,
-                                        &bAliasExists);
-    if (!NT_SUCCESS(Status))
-    {
-        TRACE("failed with status 0x%08lx\n", Status);
-        return Status;
-    }
-
-    if (bAliasExists)
-    {
-        TRACE("The alias account %S already exists!\n", AccountName->Buffer);
-        return STATUS_ALIAS_EXISTS;
-    }
-
-    /* Create the user object */
-    Status = SampCreateDbObject(DomainObject,
-                                L"Aliases",
-                                szRid,
-                                SamDbAliasObject,
-                                DesiredAccess,
-                                &AliasObject);
-    if (!NT_SUCCESS(Status))
-    {
-        TRACE("failed with status 0x%08lx\n", Status);
-        return Status;
-    }
-
-    /* Add the name alias for the user object */
-    Status = SampSetDbObjectNameAlias(DomainObject,
-                                      L"Aliases",
-                                      AccountName->Buffer,
-                                      ulRid);
-    if (!NT_SUCCESS(Status))
-    {
-        TRACE("failed with status 0x%08lx\n", Status);
-        return Status;
-    }
-
-    /* Set the Name attribute */
-    Status = SampSetObjectAttribute(AliasObject,
-                                    L"Name",
-                                    REG_SZ,
-                                    (LPVOID)AccountName->Buffer,
-                                    AccountName->MaximumLength);
-    if (!NT_SUCCESS(Status))
-    {
-        TRACE("failed with status 0x%08lx\n", Status);
-        return Status;
-    }
-
-    /* Set the Description attribute */
-    Status = SampSetObjectAttribute(AliasObject,
-                                    L"Description",
-                                    REG_SZ,
-                                    EmptyString.Buffer,
-                                    EmptyString.MaximumLength);
-    if (!NT_SUCCESS(Status))
-    {
-        TRACE("failed with status 0x%08lx\n", Status);
-        return Status;
-    }
-
-    if (NT_SUCCESS(Status))
-    {
-        *AliasHandle = (SAMPR_HANDLE)AliasObject;
-        *RelativeId = ulRid;
-    }
-
-    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;
-    WCHAR AliasKeyName[64];
-    HANDLE AliasKeyHandle;
-    ULONG EnumIndex;
-    ULONG EnumCount;
-    ULONG RequiredLength;
-    ULONG DataLength;
-    ULONG i;
-    BOOLEAN MoreEntries = FALSE;
-    NTSTATUS Status;
-
-    TRACE("SamrEnumerateAliasesInDomain(%p %p %p %lu %p)\n",
-          DomainHandle, EnumerationContext, Buffer, PreferedMaximumLength,
-          CountReturned);
-
-    /* Validate the domain handle */
-    Status = SampValidateDbObject(DomainHandle,
-                                  SamDbDomainObject,
-                                  DOMAIN_LIST_ACCOUNTS,
-                                  &DomainObject);
+    Status = SampRegOpenKey(DomainObject->KeyHandle,
+                            L"Groups",
+                            KEY_READ,
+                            &GroupsKeyHandle);
     if (!NT_SUCCESS(Status))
         return Status;
 
-    Status = SampRegOpenKey(DomainObject->KeyHandle,
-                            L"Aliases",
+    Status = SampRegOpenKey(GroupsKeyHandle,
+                            L"Names",
                             KEY_READ,
-                            &AliasesKeyHandle);
+                            &NamesKeyHandle);
     if (!NT_SUCCESS(Status))
-        return Status;
+        goto done;
 
     TRACE("Part 1\n");
 
     EnumIndex = *EnumerationContext;
-    EnumCount = 0;
-    RequiredLength = 0;
 
     while (TRUE)
     {
-        Status = SampRegEnumerateSubKey(AliasesKeyHandle,
-                                        EnumIndex,
-                                        64 * sizeof(WCHAR),
-                                        AliasKeyName);
+        NameLength = 64 * sizeof(WCHAR);
+        Status = SampRegEnumerateValue(NamesKeyHandle,
+                                       EnumIndex,
+                                       GroupName,
+                                       &NameLength,
+                                       NULL,
+                                       NULL,
+                                       NULL);
         if (!NT_SUCCESS(Status))
         {
             if (Status == STATUS_NO_MORE_ENTRIES)
@@ -2071,47 +1760,27 @@ SamrEnumerateAliasesInDomain(IN SAMPR_HANDLE DomainHandle,
         }
 
         TRACE("EnumIndex: %lu\n", EnumIndex);
-        TRACE("Alias key name: %S\n", AliasKeyName);
+        TRACE("Group name: %S\n", GroupName);
+        TRACE("Name length: %lu\n", NameLength);
 
-        Status = SampRegOpenKey(AliasesKeyHandle,
-                                AliasKeyName,
-                                KEY_READ,
-                                &AliasKeyHandle);
-        TRACE("SampRegOpenKey returned %08lX\n", Status);
-        if (NT_SUCCESS(Status))
+        if ((RequiredLength + NameLength + sizeof(UNICODE_NULL) + sizeof(SAMPR_RID_ENUMERATION)) > PreferedMaximumLength)
         {
-            DataLength = 0;
-            Status = SampRegQueryValue(AliasKeyHandle,
-                                       L"Name",
-                                       NULL,
-                                       NULL,
-                                       &DataLength);
-
-            NtClose(AliasKeyHandle);
-
-            TRACE("SampRegQueryValue returned %08lX\n", Status);
-
-            if (NT_SUCCESS(Status))
-            {
-                TRACE("Data length: %lu\n", DataLength);
-
-                if ((RequiredLength + DataLength + sizeof(SAMPR_RID_ENUMERATION)) > PreferedMaximumLength)
-                {
-                    MoreEntries = TRUE;
-                    break;
-                }
-
-                RequiredLength += (DataLength + sizeof(SAMPR_RID_ENUMERATION));
-                EnumCount++;
-            }
+            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)
     {
@@ -2135,10 +1804,15 @@ SamrEnumerateAliasesInDomain(IN SAMPR_HANDLE DomainHandle,
     EnumIndex = *EnumerationContext;
     for (i = 0; i < EnumCount; i++, EnumIndex++)
     {
-        Status = SampRegEnumerateSubKey(AliasesKeyHandle,
-                                        EnumIndex,
-                                        64 * sizeof(WCHAR),
-                                        AliasKeyName);
+        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)
@@ -2147,53 +1821,28 @@ SamrEnumerateAliasesInDomain(IN SAMPR_HANDLE DomainHandle,
         }
 
         TRACE("EnumIndex: %lu\n", EnumIndex);
-        TRACE("Alias key name: %S\n", AliasKeyName);
-
-        Status = SampRegOpenKey(AliasesKeyHandle,
-                                AliasKeyName,
-                                KEY_READ,
-                                &AliasKeyHandle);
-        TRACE("SampRegOpenKey returned %08lX\n", Status);
-        if (NT_SUCCESS(Status))
-        {
-            DataLength = 0;
-            Status = SampRegQueryValue(AliasKeyHandle,
-                                       L"Name",
-                                       NULL,
-                                       NULL,
-                                       &DataLength);
-            TRACE("SampRegQueryValue returned %08lX\n", Status);
-            if (NT_SUCCESS(Status))
-            {
-                EnumBuffer->Buffer[i].RelativeId = wcstoul(AliasKeyName, NULL, 16);
-
-                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)
-                {
-                    NtClose(AliasKeyHandle);
-                    Status = STATUS_INSUFFICIENT_RESOURCES;
-                    goto done;
-                }
+        TRACE("Group name: %S\n", GroupName);
+        TRACE("Name length: %lu\n", NameLength);
+        TRACE("RID: %lu\n", Rid);
 
-                Status = SampRegQueryValue(AliasKeyHandle,
-                                           L"Name",
-                                           NULL,
-                                           EnumBuffer->Buffer[i].Name.Buffer,
-                                           &DataLength);
-                TRACE("SampRegQueryValue returned %08lX\n", Status);
-                if (NT_SUCCESS(Status))
-                {
-                    TRACE("Alias name: %S\n", EnumBuffer->Buffer[i].Name.Buffer);
-                }
-            }
+        EnumBuffer->Buffer[i].RelativeId = Rid;
 
-            NtClose(AliasKeyHandle);
+        EnumBuffer->Buffer[i].Name.Length = (USHORT)NameLength;
+        EnumBuffer->Buffer[i].Name.MaximumLength = (USHORT)(DataLength + sizeof(UNICODE_NULL));
 
-            if (!NT_SUCCESS(Status))
-                goto done;
+/* 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:
@@ -2203,8 +1852,7 @@ done:
         *Buffer = EnumBuffer;
         *CountReturned = EnumCount;
     }
-
-    if (!NT_SUCCESS(Status))
+    else
     {
         *EnumerationContext = 0;
         *Buffer = NULL;
@@ -2230,7 +1878,11 @@ done:
         }
     }
 
-    NtClose(AliasesKeyHandle);
+    if (NamesKeyHandle != NULL)
+        SampRegCloseKey(NamesKeyHandle);
+
+    if (GroupsKeyHandle != NULL)
+        SampRegCloseKey(GroupsKeyHandle);
 
     if ((Status == STATUS_SUCCESS) && (MoreEntries == TRUE))
         Status = STATUS_MORE_ENTRIES;
@@ -2238,200 +1890,1793 @@ done:
     return Status;
 }
 
-/* Function 16 */
+
+/* Function 12 */
 NTSTATUS
 NTAPI
-SamrGetAliasMembership(IN SAMPR_HANDLE DomainHandle,
-                       IN PSAMPR_PSID_ARRAY SidArray,
-                       OUT PSAMPR_ULONG_ARRAY Membership)
+SamrCreateUserInDomain(IN SAMPR_HANDLE DomainHandle,
+                       IN PRPC_UNICODE_STRING Name,
+                       IN ACCESS_MASK DesiredAccess,
+                       OUT SAMPR_HANDLE *UserHandle,
+                       OUT unsigned long *RelativeId)
 {
+    UNICODE_STRING EmptyString = RTL_CONSTANT_STRING(L"");
+    SAM_DOMAIN_FIXED_DATA FixedDomainData;
+    SAM_USER_FIXED_DATA FixedUserData;
     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;
+    PSAM_DB_OBJECT UserObject;
+    ULONG ulSize;
+    ULONG ulRid;
+    WCHAR szRid[9];
     NTSTATUS Status;
 
-    TRACE("SamrGetAliasMembership(%p %p %p)\n",
-          DomainHandle, SidArray, Membership);
+    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;
 
     /* Validate the domain handle */
     Status = SampValidateDbObject(DomainHandle,
                                   SamDbDomainObject,
-                                  DOMAIN_LOOKUP,
+                                  DOMAIN_CREATE_USER,
                                   &DomainObject);
     if (!NT_SUCCESS(Status))
+    {
+        TRACE("failed with status 0x%08lx\n", Status);
         return Status;
+    }
 
-    Status = SampRegOpenKey(DomainObject->KeyHandle,
-                            L"Aliases",
-                            KEY_READ,
-                            &AliasesKeyHandle);
-    TRACE("SampRegOpenKey returned %08lX\n", Status);
+    /* Check if the user name already exists in the domain */
+    Status = SampCheckAccountNameInDomain(DomainObject,
+                                          Name->Buffer);
     if (!NT_SUCCESS(Status))
-        goto done;
+    {
+        TRACE("User name \'%S\' already exists in domain (Status 0x%08lx)\n",
+              Name->Buffer, Status);
+        return Status;
+    }
 
-    Status = SampRegOpenKey(AliasesKeyHandle,
-                            L"Members",
-                            KEY_READ,
-                            &MembersKeyHandle);
+    /* 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);
+        return Status;
+    }
+
+    /* 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);
+        return Status;
+    }
+
+    TRACE("RID: %lx\n", ulRid);
+
+    /* Convert the RID into a string (hex) */
+    swprintf(szRid, L"%08lX", ulRid);
+
+    /* Create the user object */
+    Status = SampCreateDbObject(DomainObject,
+                                L"Users",
+                                szRid,
+                                SamDbUserObject,
+                                DesiredAccess,
+                                &UserObject);
+    if (!NT_SUCCESS(Status))
+    {
+        TRACE("failed with status 0x%08lx\n", Status);
+        return Status;
+    }
+
+    /* 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);
+        return Status;
+    }
+
+    /* Initialize fixed user data */
+    memset(&FixedUserData, 0, sizeof(SAM_USER_FIXED_DATA));
+    FixedUserData.Version = 1;
+    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 |
+                                       USER_NORMAL_ACCOUNT;
+
+    /* 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);
+        return Status;
+    }
+
+    /* Set the Name attribute */
+    Status = SampSetObjectAttribute(UserObject,
+                                    L"Name",
+                                    REG_SZ,
+                                    (LPVOID)Name->Buffer,
+                                    Name->MaximumLength);
+    if (!NT_SUCCESS(Status))
+    {
+        TRACE("failed with status 0x%08lx\n", Status);
+        return Status;
+    }
+
+    /* Set the FullName attribute */
+    Status = SampSetObjectAttribute(UserObject,
+                                    L"FullName",
+                                    REG_SZ,
+                                    EmptyString.Buffer,
+                                    EmptyString.MaximumLength);
+    if (!NT_SUCCESS(Status))
+    {
+        TRACE("failed with status 0x%08lx\n", Status);
+        return Status;
+    }
+
+    /* Set the HomeDirectory attribute */
+    Status = SampSetObjectAttribute(UserObject,
+                                    L"HomeDirectory",
+                                    REG_SZ,
+                                    EmptyString.Buffer,
+                                    EmptyString.MaximumLength);
+    if (!NT_SUCCESS(Status))
+    {
+        TRACE("failed with status 0x%08lx\n", Status);
+        return Status;
+    }
+
+    /* Set the HomeDirectoryDrive attribute */
+    Status = SampSetObjectAttribute(UserObject,
+                                    L"HomeDirectoryDrive",
+                                    REG_SZ,
+                                    EmptyString.Buffer,
+                                    EmptyString.MaximumLength);
+    if (!NT_SUCCESS(Status))
+    {
+        TRACE("failed with status 0x%08lx\n", Status);
+        return Status;
+    }
+
+    /* Set the ScriptPath attribute */
+    Status = SampSetObjectAttribute(UserObject,
+                                    L"ScriptPath",
+                                    REG_SZ,
+                                    EmptyString.Buffer,
+                                    EmptyString.MaximumLength);
+    if (!NT_SUCCESS(Status))
+    {
+        TRACE("failed with status 0x%08lx\n", Status);
+        return Status;
+    }
+
+    /* Set the ProfilePath attribute */
+    Status = SampSetObjectAttribute(UserObject,
+                                    L"ProfilePath",
+                                    REG_SZ,
+                                    EmptyString.Buffer,
+                                    EmptyString.MaximumLength);
+    if (!NT_SUCCESS(Status))
+    {
+        TRACE("failed with status 0x%08lx\n", Status);
+        return Status;
+    }
+
+    /* Set the AdminComment attribute */
+    Status = SampSetObjectAttribute(UserObject,
+                                    L"AdminComment",
+                                    REG_SZ,
+                                    EmptyString.Buffer,
+                                    EmptyString.MaximumLength);
+    if (!NT_SUCCESS(Status))
+    {
+        TRACE("failed with status 0x%08lx\n", Status);
+        return Status;
+    }
+
+    /* Set the UserComment attribute */
+    Status = SampSetObjectAttribute(UserObject,
+                                    L"UserComment",
+                                    REG_SZ,
+                                    EmptyString.Buffer,
+                                    EmptyString.MaximumLength);
+    if (!NT_SUCCESS(Status))
+    {
+        TRACE("failed with status 0x%08lx\n", Status);
+        return Status;
+    }
+
+    /* Set the WorkStations attribute */
+    Status = SampSetObjectAttribute(UserObject,
+                                    L"WorkStations",
+                                    REG_SZ,
+                                    EmptyString.Buffer,
+                                    EmptyString.MaximumLength);
+    if (!NT_SUCCESS(Status))
+    {
+        TRACE("failed with status 0x%08lx\n", Status);
+        return Status;
+    }
+
+    /* FIXME: Set default user attributes */
+
+    if (NT_SUCCESS(Status))
+    {
+        *UserHandle = (SAMPR_HANDLE)UserObject;
+        *RelativeId = ulRid;
+    }
+
+    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);
+
+    /* Validate the domain handle */
+    Status = SampValidateDbObject(DomainHandle,
+                                  SamDbDomainObject,
+                                  DOMAIN_LIST_ACCOUNTS,
+                                  &DomainObject);
+    if (!NT_SUCCESS(Status))
+        return Status;
+
+    Status = SampRegOpenKey(DomainObject->KeyHandle,
+                            L"Users",
+                            KEY_READ,
+                            &UsersKeyHandle);
+    if (!NT_SUCCESS(Status))
+        return Status;
+
+    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)
+        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)(DataLength + 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);
+        }
+    }
+
+    if (NamesKeyHandle != NULL)
+        SampRegCloseKey(NamesKeyHandle);
+
+    if (UsersKeyHandle != NULL)
+        SampRegCloseKey(UsersKeyHandle);
+
+    if ((Status == STATUS_SUCCESS) && (MoreEntries == TRUE))
+        Status = STATUS_MORE_ENTRIES;
+
+    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;
+    UNICODE_STRING EmptyString = RTL_CONSTANT_STRING(L"");
+    ULONG ulSize;
+    ULONG ulRid;
+    WCHAR szRid[9];
+    NTSTATUS Status;
+
+    TRACE("SamrCreateAliasInDomain(%p %p %lx %p %p)\n",
+          DomainHandle, AccountName, DesiredAccess, AliasHandle, RelativeId);
+
+    /* Validate the domain handle */
+    Status = SampValidateDbObject(DomainHandle,
+                                  SamDbDomainObject,
+                                  DOMAIN_CREATE_ALIAS,
+                                  &DomainObject);
+    if (!NT_SUCCESS(Status))
+    {
+        TRACE("failed with status 0x%08lx\n", Status);
+        return Status;
+    }
+
+    /* 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);
+        return Status;
+    }
+
+    /* 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);
+        return Status;
+    }
+
+    /* 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);
+        return Status;
+    }
+
+    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,
+                                SamDbAliasObject,
+                                DesiredAccess,
+                                &AliasObject);
+    if (!NT_SUCCESS(Status))
+    {
+        TRACE("failed with status 0x%08lx\n", Status);
+        return Status;
+    }
+
+    /* 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);
+        return Status;
+    }
+
+    /* Set the Name attribute */
+    Status = SampSetObjectAttribute(AliasObject,
+                                    L"Name",
+                                    REG_SZ,
+                                    (LPVOID)AccountName->Buffer,
+                                    AccountName->MaximumLength);
+    if (!NT_SUCCESS(Status))
+    {
+        TRACE("failed with status 0x%08lx\n", Status);
+        return Status;
+    }
+
+    /* Set the Description attribute */
+    Status = SampSetObjectAttribute(AliasObject,
+                                    L"Description",
+                                    REG_SZ,
+                                    EmptyString.Buffer,
+                                    EmptyString.MaximumLength);
+    if (!NT_SUCCESS(Status))
+    {
+        TRACE("failed with status 0x%08lx\n", Status);
+        return Status;
+    }
+
+    if (NT_SUCCESS(Status))
+    {
+        *AliasHandle = (SAMPR_HANDLE)AliasObject;
+        *RelativeId = ulRid;
+    }
+
+    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);
+
+    /* Validate the domain handle */
+    Status = SampValidateDbObject(DomainHandle,
+                                  SamDbDomainObject,
+                                  DOMAIN_LIST_ACCOUNTS,
+                                  &DomainObject);
+    if (!NT_SUCCESS(Status))
+        return Status;
+
+    Status = SampRegOpenKey(DomainObject->KeyHandle,
+                            L"Aliases",
+                            KEY_READ,
+                            &AliasesKeyHandle);
+    if (!NT_SUCCESS(Status))
+        return Status;
+
+    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)
+        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)(DataLength + 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);
+        }
+    }
+
+    if (NamesKeyHandle != NULL)
+        SampRegCloseKey(NamesKeyHandle);
+
+    if (AliasesKeyHandle != NULL)
+        SampRegCloseKey(AliasesKeyHandle);
+
+    if ((Status == STATUS_SUCCESS) && (MoreEntries == TRUE))
+        Status = STATUS_MORE_ENTRIES;
+
+    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;
+    NTSTATUS Status;
+
+    TRACE("SamrGetAliasMembership(%p %p %p)\n",
+          DomainHandle, SidArray, Membership);
+
+    /* Validate the domain handle */
+    Status = SampValidateDbObject(DomainHandle,
+                                  SamDbDomainObject,
+                                  DOMAIN_LOOKUP,
+                                  &DomainObject);
+    if (!NT_SUCCESS(Status))
+        return Status;
+
+    Status = SampRegOpenKey(DomainObject->KeyHandle,
+                            L"Aliases",
+                            KEY_READ,
+                            &AliasesKeyHandle);
     TRACE("SampRegOpenKey returned %08lX\n", Status);
     if (!NT_SUCCESS(Status))
-        goto done;
-
-    for (i = 0; i < SidArray->Count; i++)
+        goto done;
+
+    Status = SampRegOpenKey(AliasesKeyHandle,
+                            L"Members",
+                            KEY_READ,
+                            &MembersKeyHandle);
+    TRACE("SampRegOpenKey returned %08lX\n", Status);
+    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;
+            }
+
+
+            NtClose(MemberKeyHandle);
+        }
+
+        LocalFree(MemberSidString);
+    }
+
+    TRACE("Maximum sid count: %lu\n", MaxSidCount);
+    RidArray = midl_user_allocate(MaxSidCount * sizeof(ULONG));
+    if (RidArray == NULL)
+    {
+        Status = STATUS_INSUFFICIENT_RESOURCES;
+        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);
+
+                for (j = 0; j < ValueCount; j++)
+                {
+                    DataLength = sizeof(ULONG);
+                    Status = SampRegEnumerateValue(MemberKeyHandle,
+                                                   j,
+                      NULL,
+                      NULL,
+                      NULL,
+                      (PVOID)&RidArray[j],
+                      &DataLength);
+                }
+            }
+
+            NtClose(MemberKeyHandle);
+        }
+
+        LocalFree(MemberSidString);
+    }
+
+
+done:
+    if (NT_SUCCESS(Status))
+    {
+        Membership->Count = MaxSidCount;
+        Membership->Element = RidArray;
+    }
+    else
+    {
+        if (RidArray != NULL)
+            midl_user_free(RidArray);
+    }
+
+    if (MembersKeyHandle != NULL)
+        NtClose(MembersKeyHandle);
+
+    if (MembersKeyHandle != NULL)
+        NtClose(MembersKeyHandle);
+
+    if (AliasesKeyHandle != NULL)
+        NtClose(AliasesKeyHandle);
+
+    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;
+    HANDLE NamesKeyHandle;
+    ULONG MappedCount = 0;
+    ULONG DataLength;
+    ULONG i;
+    ULONG RelativeId;
+    NTSTATUS Status;
+
+    TRACE("SamrLookupNamesInDomain(%p %lu %p %p %p)\n",
+          DomainHandle, Count, Names, RelativeIds, Use);
+
+    /* Validate the domain handle */
+    Status = SampValidateDbObject(DomainHandle,
+                                  SamDbDomainObject,
+                                  DOMAIN_LOOKUP,
+                                  &DomainObject);
+    if (!NT_SUCCESS(Status))
+    {
+        TRACE("failed with status 0x%08lx\n", Status);
+        return Status;
+    }
+
+    RelativeIds->Count = 0;
+    Use->Count = 0;
+
+    if (Count == 0)
+        return STATUS_SUCCESS;
+
+    /* 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;
+    }
+
+    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;
+    HANDLE AccountKeyHandle;
+    ULONG MappedCount = 0;
+    ULONG DataLength;
+    ULONG i;
+    NTSTATUS Status;
+
+    TRACE("SamrLookupIdsInDomain(%p %lu %p %p %p)\n",
+          DomainHandle, Count, RelativeIds, Names, Use);
+
+    /* Validate the domain handle */
+    Status = SampValidateDbObject(DomainHandle,
+                                  SamDbDomainObject,
+                                  DOMAIN_LOOKUP,
+                                  &DomainObject);
+    if (!NT_SUCCESS(Status))
+    {
+        TRACE("failed with status 0x%08lx\n", Status);
+        return Status;
+    }
+
+    Names->Count = 0;
+    Use->Count = 0;
+
+    if (Count == 0)
+        return STATUS_SUCCESS;
+
+    /* Allocate the names array */
+    Names->Element = midl_user_allocate(Count * sizeof(ULONG));
+    if (Names->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;
+    }
+
+    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;
+    }
+
+    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);
+
+    /* Validate the domain handle */
+    Status = SampValidateDbObject(DomainHandle,
+                                  SamDbDomainObject,
+                                  DOMAIN_LOOKUP,
+                                  &DomainObject);
+    if (!NT_SUCCESS(Status))
+    {
+        TRACE("failed with status 0x%08lx\n", Status);
+        return Status;
+    }
+
+    /* Convert the RID into a string (hex) */
+    swprintf(szRid, L"%08lX", GroupId);
+
+    /* Create the group object */
+    Status = SampOpenDbObject(DomainObject,
+                              L"Groups",
+                              szRid,
+                              SamDbGroupObject,
+                              DesiredAccess,
+                              &GroupObject);
+    if (!NT_SUCCESS(Status))
     {
-        ConvertSidToStringSid(SidArray->Sids[i].SidPointer, &MemberSidString);
-TRACE("Open %S\n", MemberSidString);
+        TRACE("failed with status 0x%08lx\n", Status);
+        return Status;
+    }
 
-        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;
-            }
+    *GroupHandle = (SAMPR_HANDLE)GroupObject;
 
+    return STATUS_SUCCESS;
+}
 
-            NtClose(MemberKeyHandle);
-        }
 
-        LocalFree(MemberSidString);
+static NTSTATUS
+SampQueryGroupGeneral(PSAM_DB_OBJECT GroupObject,
+                      PSAMPR_GROUP_INFO_BUFFER *Buffer)
+{
+    PSAMPR_GROUP_INFO_BUFFER InfoBuffer = NULL;
+    HANDLE MembersKeyHandle = 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;
+
+    Status = SampGetObjectAttributeString(GroupObject,
+                                          L"Name",
+                                          &InfoBuffer->General.Name);
+    if (!NT_SUCCESS(Status))
+    {
+        TRACE("Status 0x%08lx\n", Status);
+        goto done;
     }
 
-    TRACE("Maximum sid count: %lu\n", MaxSidCount);
-    RidArray = midl_user_allocate(MaxSidCount * sizeof(ULONG));
-    if (RidArray == NULL)
+    Status = SampGetObjectAttributeString(GroupObject,
+                                          L"Description",
+                                          &InfoBuffer->General.AdminComment);
+    if (!NT_SUCCESS(Status))
     {
-        Status = STATUS_INSUFFICIENT_RESOURCES;
+        TRACE("Status 0x%08lx\n", Status);
         goto done;
     }
 
-    for (i = 0; i < SidArray->Count; i++)
+    Length = sizeof(SAM_GROUP_FIXED_DATA);
+    Status = SampGetObjectAttribute(GroupObject,
+                                    L"F",
+                                    NULL,
+                                    (PVOID)&FixedData,
+                                    &Length);
+    if (!NT_SUCCESS(Status))
+        goto done;
+
+    InfoBuffer->General.Attributes = FixedData.Attributes;
+
+    /* Open the Members subkey */
+    Status = SampRegOpenKey(GroupObject->KeyHandle,
+                            L"Members",
+                            KEY_READ,
+                            &MembersKeyHandle);
+    if (!NT_SUCCESS(Status))
     {
-        ConvertSidToStringSid(SidArray->Sids[i].SidPointer, &MemberSidString);
-TRACE("Open %S\n", MemberSidString);
+        TRACE("Status 0x%08lx\n", Status);
+        goto done;
+    }
 
-        Status = SampRegOpenKey(MembersKeyHandle,
-                                MemberSidString,
-                                KEY_READ,
-                                &MemberKeyHandle);
-        TRACE("SampRegOpenKey returned %08lX\n", Status);
-        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;
+    }
+
+    *Buffer = InfoBuffer;
+
+done:
+    if (MembersKeyHandle != NULL)
+        SampRegCloseKey(MembersKeyHandle);
+
+    if (!NT_SUCCESS(Status))
+    {
+        if (InfoBuffer != NULL)
         {
-            Status = SampRegQueryKeyInfo(MemberKeyHandle,
-                                         NULL,
-                                         &ValueCount);
-            if (NT_SUCCESS(Status))
-            {
-                TRACE("Found %lu values\n", ValueCount);
+            if (InfoBuffer->General.Name.Buffer != NULL)
+                midl_user_free(InfoBuffer->General.Name.Buffer);
 
-                for (j = 0; j < ValueCount; j++)
-                {
-                    DataLength = sizeof(ULONG);
-                    Status = SampRegEnumerateValue(MemberKeyHandle,
-                                                   j,
-                      NULL,
-                      NULL,
-                      NULL,
-                      (PVOID)&RidArray[j],
-                      &DataLength);
-                }
-            }
+            if (InfoBuffer->General.AdminComment.Buffer != NULL)
+                midl_user_free(InfoBuffer->General.AdminComment.Buffer);
 
-            NtClose(MemberKeyHandle);
+            midl_user_free(InfoBuffer);
         }
-
-        LocalFree(MemberSidString);
     }
 
+    return Status;
+}
+
 
-done:
-    if (NT_SUCCESS(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))
     {
-        Membership->Count = MaxSidCount;
-        Membership->Element = RidArray;
+        TRACE("Status 0x%08lx\n", Status);
+        goto done;
     }
-    else
+
+    *Buffer = InfoBuffer;
+
+done:
+    if (!NT_SUCCESS(Status))
     {
-        if (RidArray != NULL)
-            midl_user_free(RidArray);
+        if (InfoBuffer != NULL)
+        {
+            if (InfoBuffer->Name.Name.Buffer != NULL)
+                midl_user_free(InfoBuffer->Name.Name.Buffer);
+
+            midl_user_free(InfoBuffer);
+        }
     }
 
-    if (MembersKeyHandle != NULL)
-        NtClose(MembersKeyHandle);
+    return Status;
+}
 
-    if (MembersKeyHandle != NULL)
-        NtClose(MembersKeyHandle);
 
-    if (AliasesKeyHandle != NULL)
-        NtClose(AliasesKeyHandle);
+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))
+        goto done;
+
+    InfoBuffer->Attribute.Attributes = FixedData.Attributes;
+
+    *Buffer = InfoBuffer;
+
+done:
+    if (!NT_SUCCESS(Status))
+    {
+        if (InfoBuffer != NULL)
+        {
+            midl_user_free(InfoBuffer);
+        }
+    }
 
     return Status;
 }
 
-/* Function 17 */
-NTSTATUS
-NTAPI
-SamrLookupNamesInDomain(IN SAMPR_HANDLE DomainHandle,
-                        IN unsigned long Count,
-                        IN RPC_UNICODE_STRING Names[],
-                        OUT PSAMPR_ULONG_ARRAY RelativeIds,
-                        OUT PSAMPR_ULONG_ARRAY Use)
+
+static NTSTATUS
+SampQueryGroupAdminComment(PSAM_DB_OBJECT GroupObject,
+                           PSAMPR_GROUP_INFO_BUFFER *Buffer)
 {
-    UNIMPLEMENTED;
-    return STATUS_NOT_IMPLEMENTED;
+    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"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 18 */
+
+/* Function 20 */
 NTSTATUS
 NTAPI
-SamrLookupIdsInDomain(IN SAMPR_HANDLE DomainHandle,
-                      IN unsigned long Count,
-                      IN unsigned long *RelativeIds,
-                      OUT PSAMPR_RETURNED_USTRING_ARRAY Names,
-                      OUT PSAMPR_ULONG_ARRAY Use)
+SamrQueryInformationGroup(IN SAMPR_HANDLE GroupHandle,
+                          IN GROUP_INFORMATION_CLASS GroupInformationClass,
+                          OUT PSAMPR_GROUP_INFO_BUFFER *Buffer)
 {
-    UNIMPLEMENTED;
-    return STATUS_NOT_IMPLEMENTED;
-}
+    PSAM_DB_OBJECT GroupObject;
+    NTSTATUS Status;
+
+    TRACE("SamrQueryInformationGroup(%p %lu %p)\n",
+          GroupHandle, GroupInformationClass, Buffer);
+
+    /* Validate the group handle */
+    Status = SampValidateDbObject(GroupHandle,
+                                  SamDbGroupObject,
+                                  GROUP_READ_INFORMATION,
+                                  &GroupObject);
+    if (!NT_SUCCESS(Status))
+        return Status;
+
+    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;
 
-/* Function 19 */
-NTSTATUS
-NTAPI
-SamrOpenGroup(IN SAMPR_HANDLE DomainHandle,
-              IN ACCESS_MASK DesiredAccess,
-              IN unsigned long GroupId,
-              OUT SAMPR_HANDLE *GroupHandle)
-{
-    UNIMPLEMENTED;
-    return STATUS_NOT_IMPLEMENTED;
+        default:
+            Status = STATUS_INVALID_INFO_CLASS;
+            break;
+    }
+
+    return Status;
 }
 
-/* Function 20 */
-NTSTATUS
-NTAPI
-SamrQueryInformationGroup(IN SAMPR_HANDLE GroupHandle,
-                          IN GROUP_INFORMATION_CLASS GroupInformationClass,
-                          OUT PSAMPR_GROUP_INFO_BUFFER *Buffer)
+
+static NTSTATUS
+SampSetGroupAttribute(PSAM_DB_OBJECT GroupObject,
+                      PSAMPR_GROUP_INFO_BUFFER Buffer)
 {
-    UNIMPLEMENTED;
-    return STATUS_NOT_IMPLEMENTED;
+    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
@@ -2439,10 +3684,52 @@ SamrSetInformationGroup(IN SAMPR_HANDLE GroupHandle,
                         IN GROUP_INFORMATION_CLASS GroupInformationClass,
                         IN PSAMPR_GROUP_INFO_BUFFER Buffer)
 {
-    UNIMPLEMENTED;
-    return STATUS_NOT_IMPLEMENTED;
+    PSAM_DB_OBJECT GroupObject;
+    NTSTATUS Status;
+
+    TRACE("SamrSetInformationGroup(%p %lu %p)\n",
+          GroupHandle, GroupInformationClass, Buffer);
+
+    /* Validate the group handle */
+    Status = SampValidateDbObject(GroupHandle,
+                                  SamDbGroupObject,
+                                  GROUP_WRITE_ACCOUNT,
+                                  &GroupObject);
+    if (!NT_SUCCESS(Status))
+        return Status;
+
+    switch (GroupInformationClass)
+    {
+        case GroupNameInformation:
+            Status = SampSetObjectAttribute(GroupObject,
+                                            L"Name",
+                                            REG_SZ,
+                                            Buffer->Name.Name.Buffer,
+                                            Buffer->Name.Name.Length + sizeof(WCHAR));
+            break;
+
+        case GroupAttributeInformation:
+            Status = SampSetGroupAttribute(GroupObject,
+                                           Buffer);
+            break;
+
+        case GroupAdminCommentInformation:
+            Status = SampSetObjectAttribute(GroupObject,
+                                            L"Description",
+                                            REG_SZ,
+                                            Buffer->AdminComment.AdminComment.Buffer,
+                                            Buffer->AdminComment.AdminComment.Length + sizeof(WCHAR));
+            break;
+
+        default:
+            Status = STATUS_INVALID_INFO_CLASS;
+            break;
+    }
+
+    return Status;
 }
 
+
 /* Function 22 */
 NTSTATUS
 NTAPI
@@ -2494,6 +3781,7 @@ SamrSetMemberAttributesOfGroup(IN SAMPR_HANDLE GroupHandle,
     return STATUS_NOT_IMPLEMENTED;
 }
 
+
 /* Function 27 */
 NTSTATUS
 NTAPI
@@ -2549,8 +3837,6 @@ SampQueryAliasGeneral(PSAM_DB_OBJECT AliasObject,
 {
     PSAMPR_ALIAS_INFO_BUFFER InfoBuffer = NULL;
     HANDLE MembersKeyHandle = NULL;
-    ULONG NameLength = 0;
-    ULONG DescriptionLength = 0;
     NTSTATUS Status;
 
     *Buffer = NULL;
@@ -2559,65 +3845,18 @@ SampQueryAliasGeneral(PSAM_DB_OBJECT AliasObject,
     if (InfoBuffer == NULL)
         return STATUS_INSUFFICIENT_RESOURCES;
 
-    Status = SampGetObjectAttribute(AliasObject,
-                                    L"Name",
-                                    NULL,
-                                    NULL,
-                                    &NameLength);
-    TRACE("Status 0x%08lx\n", Status);
-    if (!NT_SUCCESS(Status) && Status != STATUS_BUFFER_OVERFLOW)
-    {
-        TRACE("Status 0x%08lx\n", Status);
-        goto done;
-    }
-
-    InfoBuffer->General.Name.Length = NameLength - sizeof(WCHAR);
-    InfoBuffer->General.Name.MaximumLength = NameLength;
-    InfoBuffer->General.Name.Buffer = midl_user_allocate(NameLength);
-    if (InfoBuffer->General.Name.Buffer == NULL)
-    {
-        Status = STATUS_INSUFFICIENT_RESOURCES;
-        goto done;
-    }
-
-    TRACE("Name Length: %lu\n", NameLength);
-    Status = SampGetObjectAttribute(AliasObject,
-                                    L"Name",
-                                    NULL,
-                                    (PVOID)InfoBuffer->General.Name.Buffer,
-                                    &NameLength);
+    Status = SampGetObjectAttributeString(AliasObject,
+                                          L"Name",
+                                          &InfoBuffer->General.Name);
     if (!NT_SUCCESS(Status))
     {
         TRACE("Status 0x%08lx\n", Status);
         goto done;
     }
 
-    Status = SampGetObjectAttribute(AliasObject,
-                                    L"Description",
-                                    NULL,
-                                    NULL,
-                                    &DescriptionLength);
-    if (!NT_SUCCESS(Status) && Status != STATUS_BUFFER_OVERFLOW)
-    {
-        TRACE("Status 0x%08lx\n", Status);
-        goto done;
-    }
-
-    InfoBuffer->General.AdminComment.Length = DescriptionLength - sizeof(WCHAR);
-    InfoBuffer->General.AdminComment.MaximumLength = DescriptionLength;
-    InfoBuffer->General.AdminComment.Buffer = midl_user_allocate(DescriptionLength);
-    if (InfoBuffer->General.AdminComment.Buffer == NULL)
-    {
-        Status = STATUS_INSUFFICIENT_RESOURCES;
-        goto done;
-    }
-
-    TRACE("Description Length: %lu\n", DescriptionLength);
-    Status = SampGetObjectAttribute(AliasObject,
-                                    L"Description",
-                                    NULL,
-                                    (PVOID)InfoBuffer->General.AdminComment.Buffer,
-                                    &DescriptionLength);
+    Status = SampGetObjectAttributeString(AliasObject,
+                                          L"Description",
+                                          &InfoBuffer->General.AdminComment);
     if (!NT_SUCCESS(Status))
     {
         TRACE("Status 0x%08lx\n", Status);
@@ -2674,7 +3913,6 @@ SampQueryAliasName(PSAM_DB_OBJECT AliasObject,
                    PSAMPR_ALIAS_INFO_BUFFER *Buffer)
 {
     PSAMPR_ALIAS_INFO_BUFFER InfoBuffer = NULL;
-    ULONG Length = 0;
     NTSTATUS Status;
 
     *Buffer = NULL;
@@ -2683,32 +3921,9 @@ SampQueryAliasName(PSAM_DB_OBJECT AliasObject,
     if (InfoBuffer == NULL)
         return STATUS_INSUFFICIENT_RESOURCES;
 
-    Status = SampGetObjectAttribute(AliasObject,
-                                    L"Name",
-                                    NULL,
-                                    NULL,
-                                    &Length);
-    if (!NT_SUCCESS(Status) && Status != STATUS_BUFFER_OVERFLOW)
-    {
-        TRACE("Status 0x%08lx\n", Status);
-        goto done;
-    }
-
-    InfoBuffer->Name.Name.Length = Length - sizeof(WCHAR);
-    InfoBuffer->Name.Name.MaximumLength = Length;
-    InfoBuffer->Name.Name.Buffer = midl_user_allocate(Length);
-    if (InfoBuffer->Name.Name.Buffer == NULL)
-    {
-        Status = STATUS_INSUFFICIENT_RESOURCES;
-        goto done;
-    }
-
-    TRACE("Length: %lu\n", Length);
-    Status = SampGetObjectAttribute(AliasObject,
-                                    L"Name",
-                                    NULL,
-                                    (PVOID)InfoBuffer->Name.Name.Buffer,
-                                    &Length);
+    Status = SampGetObjectAttributeString(AliasObject,
+                                          L"Name",
+                                          &InfoBuffer->Name.Name);
     if (!NT_SUCCESS(Status))
     {
         TRACE("Status 0x%08lx\n", Status);
@@ -2738,7 +3953,6 @@ SampQueryAliasAdminComment(PSAM_DB_OBJECT AliasObject,
                            PSAMPR_ALIAS_INFO_BUFFER *Buffer)
 {
     PSAMPR_ALIAS_INFO_BUFFER InfoBuffer = NULL;
-    ULONG Length = 0;
     NTSTATUS Status;
 
     *Buffer = NULL;
@@ -2747,31 +3961,15 @@ SampQueryAliasAdminComment(PSAM_DB_OBJECT AliasObject,
     if (InfoBuffer == NULL)
         return STATUS_INSUFFICIENT_RESOURCES;
 
-    Status = SampGetObjectAttribute(AliasObject,
-                                    L"Description",
-                                    NULL,
-                                    NULL,
-                                    &Length);
-    if (!NT_SUCCESS(Status) && Status != STATUS_BUFFER_OVERFLOW)
-        goto done;
-
-    InfoBuffer->AdminComment.AdminComment.Length = Length - sizeof(WCHAR);
-    InfoBuffer->AdminComment.AdminComment.MaximumLength = Length;
-    InfoBuffer->AdminComment.AdminComment.Buffer = midl_user_allocate(Length);
-    if (InfoBuffer->AdminComment.AdminComment.Buffer == NULL)
+    Status = SampGetObjectAttributeString(AliasObject,
+                                          L"Description",
+                                          &InfoBuffer->AdminComment.AdminComment);
+    if (!NT_SUCCESS(Status))
     {
-        Status = STATUS_INSUFFICIENT_RESOURCES;
+        TRACE("Status 0x%08lx\n", Status);
         goto done;
     }
 
-    Status = SampGetObjectAttribute(AliasObject,
-                                    L"Description",
-                                    NULL,
-                                    (PVOID)InfoBuffer->AdminComment.AdminComment.Buffer,
-                                    &Length);
-    if (!NT_SUCCESS(Status))
-        goto done;
-
     *Buffer = InfoBuffer;
 
 done:
@@ -2836,6 +4034,7 @@ SamrQueryInformationAlias(IN SAMPR_HANDLE AliasHandle,
     return Status;
 }
 
+
 /* Function 29 */
 NTSTATUS
 NTAPI
@@ -2883,6 +4082,7 @@ SamrSetInformationAlias(IN SAMPR_HANDLE AliasHandle,
     return Status;
 }
 
+
 /* Function 30 */
 NTSTATUS
 NTAPI
@@ -2892,6 +4092,7 @@ SamrDeleteAlias(IN OUT SAMPR_HANDLE *AliasHandle)
     return STATUS_NOT_IMPLEMENTED;
 }
 
+
 /* Function 31 */
 NTSTATUS
 NTAPI
@@ -2908,7 +4109,7 @@ SamrAddMemberToAlias(IN SAMPR_HANDLE AliasHandle,
     TRACE("SamrAddMemberToAlias(%p %p)\n",
           AliasHandle, MemberId);
 
-    /* Validate the domain handle */
+    /* Validate the alias handle */
     Status = SampValidateDbObject(AliasHandle,
                                   SamDbAliasObject,
                                   ALIAS_ADD_MEMBER,
@@ -2924,46 +4125,172 @@ SamrAddMemberToAlias(IN SAMPR_HANDLE AliasHandle,
 
     MemberIdLength = RtlLengthSid(MemberId);
 
-    Status = SampRegCreateKey(AliasObject->KeyHandle,
-                              L"Members",
-                              KEY_WRITE,
-                              &MembersKeyHandle);
+    Status = SampRegCreateKey(AliasObject->KeyHandle,
+                              L"Members",
+                              KEY_WRITE,
+                              &MembersKeyHandle);
+    if (!NT_SUCCESS(Status))
+    {
+        TRACE("SampRegCreateKey failed with status 0x%08lx\n", Status);
+        goto done;
+    }
+
+    Status = SampRegSetValue(MembersKeyHandle,
+                             MemberIdString,
+                             REG_BINARY,
+                             MemberId,
+                             MemberIdLength);
+    if (!NT_SUCCESS(Status))
+    {
+        TRACE("SampRegSetValue failed with status 0x%08lx\n", Status);
+        goto done;
+    }
+
+    Status = SampRegCreateKey(AliasObject->MembersKeyHandle,
+                              MemberIdString,
+                              KEY_WRITE,
+                              &MemberKeyHandle);
+    if (!NT_SUCCESS(Status))
+    {
+        TRACE("SampRegCreateKey failed with status 0x%08lx\n", Status);
+        goto done;
+    }
+
+    Status = SampRegSetValue(MemberKeyHandle,
+                             AliasObject->Name,
+                             REG_BINARY,
+                             MemberId,
+                             MemberIdLength);
+    if (!NT_SUCCESS(Status))
+    {
+        TRACE("SampRegSetValue failed with status 0x%08lx\n", Status);
+        goto done;
+    }
+
+done:
+    if (MemberKeyHandle != NULL)
+        SampRegCloseKey(MemberKeyHandle);
+
+    if (MembersKeyHandle != NULL)
+        SampRegCloseKey(MembersKeyHandle);
+
+    if (MemberIdString != NULL)
+        LocalFree(MemberIdString);
+
+    return Status;
+}
+
+
+/* Function 32 */
+NTSTATUS
+NTAPI
+SamrRemoveMemberFromAlias(IN SAMPR_HANDLE AliasHandle,
+                          IN PRPC_SID MemberId)
+{
+    PSAM_DB_OBJECT AliasObject;
+    LPWSTR MemberIdString = NULL;
+    HANDLE MembersKeyHandle = NULL;
+    HANDLE MemberKeyHandle = NULL;
+    ULONG ulValueCount;
+    NTSTATUS Status;
+
+    TRACE("SamrRemoveMemberFromAlias(%p %p)\n",
+          AliasHandle, MemberId);
+
+    /* Validate the alias handle */
+    Status = SampValidateDbObject(AliasHandle,
+                                  SamDbAliasObject,
+                                  ALIAS_REMOVE_MEMBER,
+                                  &AliasObject);
+    if (!NT_SUCCESS(Status))
+    {
+        TRACE("failed with status 0x%08lx\n", Status);
+        return Status;
+    }
+
+    ConvertSidToStringSidW(MemberId, &MemberIdString);
+    TRACE("Member SID: %S\n", MemberIdString);
+
+    Status = SampRegOpenKey(AliasObject->MembersKeyHandle,
+                            MemberIdString,
+                            KEY_WRITE | KEY_QUERY_VALUE,
+                            &MemberKeyHandle);
+    if (!NT_SUCCESS(Status))
+    {
+        TRACE("SampRegOpenKey failed with status 0x%08lx\n", Status);
+        goto done;
+    }
+
+    Status = SampRegDeleteValue(MemberKeyHandle,
+                                AliasObject->Name);
+    if (!NT_SUCCESS(Status))
+    {
+        TRACE("SampRegDeleteValue failed with status 0x%08lx\n", Status);
+        goto done;
+    }
+
+    Status = SampRegQueryKeyInfo(MemberKeyHandle,
+                                 NULL,
+                                 &ulValueCount);
+    if (!NT_SUCCESS(Status))
+    {
+        TRACE("SampRegQueryKeyInfo failed with status 0x%08lx\n", Status);
+        goto done;
+    }
+
+    if (ulValueCount == 0)
+    {
+        SampRegCloseKey(MemberKeyHandle);
+        MemberKeyHandle = NULL;
+
+        Status = SampRegDeleteKey(AliasObject->MembersKeyHandle,
+                                  MemberIdString);
+        if (!NT_SUCCESS(Status))
+        {
+            TRACE("SampRegDeleteKey failed with status 0x%08lx\n", Status);
+            goto done;
+        }
+    }
+
+    Status = SampRegOpenKey(AliasObject->KeyHandle,
+                            L"Members",
+                            KEY_WRITE | KEY_QUERY_VALUE,
+                            &MembersKeyHandle);
     if (!NT_SUCCESS(Status))
     {
-        TRACE("SampRegCreateKey failed with status 0x%08lx\n", Status);
+        TRACE("SampRegOpenKey failed with status 0x%08lx\n", Status);
         goto done;
     }
 
-    Status = SampRegSetValue(MembersKeyHandle,
-                             MemberIdString,
-                             REG_BINARY,
-                             MemberId,
-                             MemberIdLength);
+    Status = SampRegDeleteValue(MembersKeyHandle,
+                                MemberIdString);
     if (!NT_SUCCESS(Status))
     {
-        TRACE("SampRegSetValue failed with status 0x%08lx\n", Status);
+        TRACE("SampRegDeleteValue failed with status 0x%08lx\n", Status);
         goto done;
     }
 
-    Status = SampRegCreateKey(AliasObject->MembersKeyHandle,
-                              MemberIdString,
-                              KEY_WRITE,
-                              &MemberKeyHandle);
+    Status = SampRegQueryKeyInfo(MembersKeyHandle,
+                                 NULL,
+                                 &ulValueCount);
     if (!NT_SUCCESS(Status))
     {
-        TRACE("SampRegCreateKey failed with status 0x%08lx\n", Status);
+        TRACE("SampRegQueryKeyInfo failed with status 0x%08lx\n", Status);
         goto done;
     }
 
-    Status = SampRegSetValue(MemberKeyHandle,
-                             AliasObject->Name,
-                             REG_BINARY,
-                             MemberId,
-                             MemberIdLength);
-    if (!NT_SUCCESS(Status))
+    if (ulValueCount == 0)
     {
-        TRACE("SampRegSetValue failed with status 0x%08lx\n", Status);
-        goto done;
+        SampRegCloseKey(MembersKeyHandle);
+        MembersKeyHandle = NULL;
+
+        Status = SampRegDeleteKey(AliasObject->KeyHandle,
+                                  L"Members");
+        if (!NT_SUCCESS(Status))
+        {
+            TRACE("SampRegDeleteKey failed with status 0x%08lx\n", Status);
+            goto done;
+        }
     }
 
 done:
@@ -2979,15 +4306,6 @@ done:
     return Status;
 }
 
-/* Function 32 */
-NTSTATUS
-NTAPI
-SamrRemoveMemberFromAlias(IN SAMPR_HANDLE AliasHandle,
-                          IN PRPC_SID MemberId)
-{
-    UNIMPLEMENTED;
-    return STATUS_NOT_IMPLEMENTED;
-}
 
 /* Function 33 */
 NTSTATUS
@@ -3120,6 +4438,7 @@ done:
     return Status;
 }
 
+
 /* Function 34 */
 NTSTATUS
 NTAPI
@@ -3143,47 +4462,350 @@ SamrOpenUser(IN SAMPR_HANDLE DomainHandle,
                                   &DomainObject);
     if (!NT_SUCCESS(Status))
     {
-        TRACE("failed with status 0x%08lx\n", Status);
-        return Status;
+        TRACE("failed with status 0x%08lx\n", Status);
+        return Status;
+    }
+
+    /* Convert the RID into a string (hex) */
+    swprintf(szRid, L"%08lX", UserId);
+
+    /* Create the user object */
+    Status = SampOpenDbObject(DomainObject,
+                              L"Users",
+                              szRid,
+                              SamDbUserObject,
+                              DesiredAccess,
+                              &UserObject);
+    if (!NT_SUCCESS(Status))
+    {
+        TRACE("failed with status 0x%08lx\n", Status);
+        return Status;
+    }
+
+    *UserHandle = (SAMPR_HANDLE)UserObject;
+
+    return STATUS_SUCCESS;
+}
+
+
+/* Function 35 */
+NTSTATUS
+NTAPI
+SamrDeleteUser(IN OUT SAMPR_HANDLE *UserHandle)
+{
+    UNIMPLEMENTED;
+    return STATUS_NOT_IMPLEMENTED;
+}
+
+
+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_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->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;
+
+//  OLD_LARGE_INTEGER PasswordCanChange;
+//  OLD_LARGE_INTEGER PasswordMustChange;
+
+    /* 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;
     }
 
-    /* Convert the RID into a string (hex) */
-    swprintf(szRid, L"%08lX", UserId);
+    /* FIXME: LogonHours */
 
-    /* Create the user object */
-    Status = SampOpenDbObject(DomainObject,
-                              L"Users",
-                              szRid,
-                              SamDbUserObject,
-                              DesiredAccess,
-                              &UserObject);
+    *Buffer = InfoBuffer;
+
+done:
     if (!NT_SUCCESS(Status))
     {
-        TRACE("failed with status 0x%08lx\n", Status);
-        return Status;
-    }
+        if (InfoBuffer != NULL)
+        {
+            if (InfoBuffer->Logon.UserName.Buffer != NULL)
+                midl_user_free(InfoBuffer->Logon.UserName.Buffer);
 
-    *UserHandle = (SAMPR_HANDLE)UserObject;
+            if (InfoBuffer->Logon.FullName.Buffer != NULL)
+                midl_user_free(InfoBuffer->Logon.FullName.Buffer);
 
-    return STATUS_SUCCESS;
-}
+            if (InfoBuffer->Logon.HomeDirectory.Buffer != NULL)
+                midl_user_free(InfoBuffer->Logon.HomeDirectory.Buffer);
 
-/* Function 35 */
-NTSTATUS
-NTAPI
-SamrDeleteUser(IN OUT SAMPR_HANDLE *UserHandle)
-{
-    UNIMPLEMENTED;
-    return STATUS_NOT_IMPLEMENTED;
+            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
-SampQueryUserName(PSAM_DB_OBJECT UserObject,
-                  PSAMPR_USER_INFO_BUFFER *Buffer)
+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;
 
@@ -3193,71 +4815,111 @@ SampQueryUserName(PSAM_DB_OBJECT UserObject,
     if (InfoBuffer == NULL)
         return STATUS_INSUFFICIENT_RESOURCES;
 
+    Length = sizeof(SAM_USER_FIXED_DATA);
     Status = SampGetObjectAttribute(UserObject,
-                                    L"Name",
-                                    NULL,
+                                    L"F",
                                     NULL,
+                                    (PVOID)&FixedData,
                                     &Length);
-    if (!NT_SUCCESS(Status) && Status != STATUS_BUFFER_OVERFLOW)
+    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;
     }
 
-    InfoBuffer->Name.UserName.Length = Length - sizeof(WCHAR);
-    InfoBuffer->Name.UserName.MaximumLength = Length;
-    InfoBuffer->Name.UserName.Buffer = midl_user_allocate(Length);
-    if (InfoBuffer->Name.UserName.Buffer == NULL)
+    /* Get the FullName string */
+    Status = SampGetObjectAttributeString(UserObject,
+                                          L"FullName",
+                                          &InfoBuffer->Account.FullName);
+    if (!NT_SUCCESS(Status))
     {
-        Status = STATUS_INSUFFICIENT_RESOURCES;
+        TRACE("Status 0x%08lx\n", Status);
         goto done;
     }
 
-    TRACE("Length: %lu\n", Length);
-    Status = SampGetObjectAttribute(UserObject,
-                                    L"Name",
-                                    NULL,
-                                    (PVOID)InfoBuffer->Name.UserName.Buffer,
-                                    &Length);
+    /* Get the HomeDirectory string */
+    Status = SampGetObjectAttributeString(UserObject,
+                                          L"HomeDirectory",
+                                          &InfoBuffer->Account.HomeDirectory);
     if (!NT_SUCCESS(Status))
     {
         TRACE("Status 0x%08lx\n", Status);
         goto done;
     }
 
-    Length = 0;
-    Status = SampGetObjectAttribute(UserObject,
-                                    L"FullName",
-                                    NULL,
-                                    NULL,
-                                    &Length);
-    if (!NT_SUCCESS(Status) && Status != STATUS_BUFFER_OVERFLOW)
+    /* Get the HomeDirectoryDrive string */
+    Status = SampGetObjectAttributeString(UserObject,
+                                          L"HomeDirectoryDrive",
+                                          &InfoBuffer->Account.HomeDirectoryDrive);
+    if (!NT_SUCCESS(Status))
     {
         TRACE("Status 0x%08lx\n", Status);
         goto done;
     }
 
-    InfoBuffer->Name.FullName.Length = Length - sizeof(WCHAR);
-    InfoBuffer->Name.FullName.MaximumLength = Length;
-    InfoBuffer->Name.FullName.Buffer = midl_user_allocate(Length);
-    if (InfoBuffer->Name.FullName.Buffer == NULL)
+    /* Get the ScriptPath string */
+    Status = SampGetObjectAttributeString(UserObject,
+                                          L"ScriptPath",
+                                          &InfoBuffer->Account.ScriptPath);
+    if (!NT_SUCCESS(Status))
     {
-        Status = STATUS_INSUFFICIENT_RESOURCES;
+        TRACE("Status 0x%08lx\n", Status);
         goto done;
     }
 
-    TRACE("Length: %lu\n", Length);
-    Status = SampGetObjectAttribute(UserObject,
-                                    L"FullName",
-                                    NULL,
-                                    (PVOID)InfoBuffer->Name.FullName.Buffer,
-                                    &Length);
+    /* 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;
     }
 
+    /* FIXME: LogonHours */
+
     *Buffer = InfoBuffer;
 
 done:
@@ -3265,11 +4927,32 @@ done:
     {
         if (InfoBuffer != NULL)
         {
-            if (InfoBuffer->Name.UserName.Buffer != NULL)
-                midl_user_free(InfoBuffer->Name.UserName.Buffer);
+            if (InfoBuffer->Account.UserName.Buffer != NULL)
+                midl_user_free(InfoBuffer->Account.UserName.Buffer);
 
-            if (InfoBuffer->Name.FullName.Buffer != NULL)
-                midl_user_free(InfoBuffer->Name.FullName.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);
         }
@@ -3278,13 +4961,14 @@ done:
     return Status;
 }
 
+/* FIXME: SampQueryUserLogonHours */
 
-static NTSTATUS
-SampQueryUserAccountName(PSAM_DB_OBJECT UserObject,
-                         PSAMPR_USER_INFO_BUFFER *Buffer)
+static
+NTSTATUS
+SampQueryUserName(PSAM_DB_OBJECT UserObject,
+                  PSAMPR_USER_INFO_BUFFER *Buffer)
 {
     PSAMPR_USER_INFO_BUFFER InfoBuffer = NULL;
-    ULONG Length = 0;
     NTSTATUS Status;
 
     *Buffer = NULL;
@@ -3293,32 +4977,20 @@ SampQueryUserAccountName(PSAM_DB_OBJECT UserObject,
     if (InfoBuffer == NULL)
         return STATUS_INSUFFICIENT_RESOURCES;
 
-    Status = SampGetObjectAttribute(UserObject,
-                                    L"Name",
-                                    NULL,
-                                    NULL,
-                                    &Length);
-    if (!NT_SUCCESS(Status) && Status != STATUS_BUFFER_OVERFLOW)
+    /* Get the Name string */
+    Status = SampGetObjectAttributeString(UserObject,
+                                          L"Name",
+                                          &InfoBuffer->Name.UserName);
+    if (!NT_SUCCESS(Status))
     {
         TRACE("Status 0x%08lx\n", Status);
         goto done;
     }
 
-    InfoBuffer->AccountName.UserName.Length = Length - sizeof(WCHAR);
-    InfoBuffer->AccountName.UserName.MaximumLength = Length;
-    InfoBuffer->AccountName.UserName.Buffer = midl_user_allocate(Length);
-    if (InfoBuffer->AccountName.UserName.Buffer == NULL)
-    {
-        Status = STATUS_INSUFFICIENT_RESOURCES;
-        goto done;
-    }
-
-    TRACE("Length: %lu\n", Length);
-    Status = SampGetObjectAttribute(UserObject,
-                                    L"Name",
-                                    NULL,
-                                    (PVOID)InfoBuffer->AccountName.UserName.Buffer,
-                                    &Length);
+    /* Get the FullName string */
+    Status = SampGetObjectAttributeString(UserObject,
+                                          L"FullName",
+                                          &InfoBuffer->Name.FullName);
     if (!NT_SUCCESS(Status))
     {
         TRACE("Status 0x%08lx\n", Status);
@@ -3332,8 +5004,11 @@ done:
     {
         if (InfoBuffer != NULL)
         {
-            if (InfoBuffer->AccountName.UserName.Buffer != NULL)
-                midl_user_free(InfoBuffer->AccountName.UserName.Buffer);
+            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);
         }
@@ -3344,11 +5019,10 @@ done:
 
 
 static NTSTATUS
-SampQueryUserFullName(PSAM_DB_OBJECT UserObject,
-                      PSAMPR_USER_INFO_BUFFER *Buffer)
+SampQueryUserAccountName(PSAM_DB_OBJECT UserObject,
+                         PSAMPR_USER_INFO_BUFFER *Buffer)
 {
     PSAMPR_USER_INFO_BUFFER InfoBuffer = NULL;
-    ULONG Length = 0;
     NTSTATUS Status;
 
     *Buffer = NULL;
@@ -3357,32 +5031,51 @@ SampQueryUserFullName(PSAM_DB_OBJECT UserObject,
     if (InfoBuffer == NULL)
         return STATUS_INSUFFICIENT_RESOURCES;
 
-    Status = SampGetObjectAttribute(UserObject,
-                                    L"FullName",
-                                    NULL,
-                                    NULL,
-                                    &Length);
-    if (!NT_SUCCESS(Status) && Status != STATUS_BUFFER_OVERFLOW)
+    /* Get the Name string */
+    Status = SampGetObjectAttributeString(UserObject,
+                                          L"Name",
+                                          &InfoBuffer->AccountName.UserName);
+    if (!NT_SUCCESS(Status))
     {
         TRACE("Status 0x%08lx\n", Status);
         goto done;
     }
 
-    InfoBuffer->FullName.FullName.Length = Length - sizeof(WCHAR);
-    InfoBuffer->FullName.FullName.MaximumLength = Length;
-    InfoBuffer->FullName.FullName.Buffer = midl_user_allocate(Length);
-    if (InfoBuffer->FullName.FullName.Buffer == NULL)
+    *Buffer = InfoBuffer;
+
+done:
+    if (!NT_SUCCESS(Status))
     {
-        Status = STATUS_INSUFFICIENT_RESOURCES;
-        goto done;
+        if (InfoBuffer != NULL)
+        {
+            if (InfoBuffer->AccountName.UserName.Buffer != NULL)
+                midl_user_free(InfoBuffer->AccountName.UserName.Buffer);
+
+            midl_user_free(InfoBuffer);
+        }
     }
 
-    TRACE("Length: %lu\n", Length);
-    Status = SampGetObjectAttribute(UserObject,
-                                    L"FullName",
-                                    NULL,
-                                    (PVOID)InfoBuffer->FullName.FullName.Buffer,
-                                    &Length);
+    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);
@@ -3454,7 +5147,6 @@ SampQueryUserHome(PSAM_DB_OBJECT UserObject,
                   PSAMPR_USER_INFO_BUFFER *Buffer)
 {
     PSAMPR_USER_INFO_BUFFER InfoBuffer = NULL;
-    ULONG Length = 0;
     NTSTATUS Status;
 
     *Buffer = NULL;
@@ -3463,65 +5155,20 @@ SampQueryUserHome(PSAM_DB_OBJECT UserObject,
     if (InfoBuffer == NULL)
         return STATUS_INSUFFICIENT_RESOURCES;
 
-    Status = SampGetObjectAttribute(UserObject,
-                                    L"HomeDirectory",
-                                    NULL,
-                                    NULL,
-                                    &Length);
-    if (!NT_SUCCESS(Status) && Status != STATUS_BUFFER_OVERFLOW)
-    {
-        TRACE("Status 0x%08lx\n", Status);
-        goto done;
-    }
-
-    InfoBuffer->Home.HomeDirectory.Length = Length - sizeof(WCHAR);
-    InfoBuffer->Home.HomeDirectory.MaximumLength = Length;
-    InfoBuffer->Home.HomeDirectory.Buffer = midl_user_allocate(Length);
-    if (InfoBuffer->Home.HomeDirectory.Buffer == NULL)
-    {
-        Status = STATUS_INSUFFICIENT_RESOURCES;
-        goto done;
-    }
-
-    TRACE("Length: %lu\n", Length);
-    Status = SampGetObjectAttribute(UserObject,
-                                    L"HomeDirectory",
-                                    NULL,
-                                    (PVOID)InfoBuffer->Home.HomeDirectory.Buffer,
-                                    &Length);
+    /* Get the HomeDirectory string */
+    Status = SampGetObjectAttributeString(UserObject,
+                                          L"HomeDirectory",
+                                          &InfoBuffer->Home.HomeDirectory);
     if (!NT_SUCCESS(Status))
     {
         TRACE("Status 0x%08lx\n", Status);
         goto done;
     }
 
-    Length = 0;
-    Status = SampGetObjectAttribute(UserObject,
-                                    L"HomeDirectoryDrive",
-                                    NULL,
-                                    NULL,
-                                    &Length);
-    if (!NT_SUCCESS(Status) && Status != STATUS_BUFFER_OVERFLOW)
-    {
-        TRACE("Status 0x%08lx\n", Status);
-        goto done;
-    }
-
-    InfoBuffer->Home.HomeDirectoryDrive.Length = Length - sizeof(WCHAR);
-    InfoBuffer->Home.HomeDirectoryDrive.MaximumLength = Length;
-    InfoBuffer->Home.HomeDirectoryDrive.Buffer = midl_user_allocate(Length);
-    if (InfoBuffer->Home.HomeDirectoryDrive.Buffer == NULL)
-    {
-        Status = STATUS_INSUFFICIENT_RESOURCES;
-        goto done;
-    }
-
-    TRACE("Length: %lu\n", Length);
-    Status = SampGetObjectAttribute(UserObject,
-                                    L"HomeDirectoryDrive",
-                                    NULL,
-                                    (PVOID)InfoBuffer->Home.HomeDirectoryDrive.Buffer,
-                                    &Length);
+    /* Get the HomeDirectoryDrive string */
+    Status = SampGetObjectAttributeString(UserObject,
+                                          L"HomeDirectoryDrive",
+                                          &InfoBuffer->Home.HomeDirectoryDrive);
     if (!NT_SUCCESS(Status))
     {
         TRACE("Status 0x%08lx\n", Status);
@@ -3554,7 +5201,6 @@ SampQueryUserScript(PSAM_DB_OBJECT UserObject,
                     PSAMPR_USER_INFO_BUFFER *Buffer)
 {
     PSAMPR_USER_INFO_BUFFER InfoBuffer = NULL;
-    ULONG Length = 0;
     NTSTATUS Status;
 
     *Buffer = NULL;
@@ -3563,32 +5209,10 @@ SampQueryUserScript(PSAM_DB_OBJECT UserObject,
     if (InfoBuffer == NULL)
         return STATUS_INSUFFICIENT_RESOURCES;
 
-    Status = SampGetObjectAttribute(UserObject,
-                                    L"ScriptPath",
-                                    NULL,
-                                    NULL,
-                                    &Length);
-    if (!NT_SUCCESS(Status) && Status != STATUS_BUFFER_OVERFLOW)
-    {
-        TRACE("Status 0x%08lx\n", Status);
-        goto done;
-    }
-
-    InfoBuffer->Script.ScriptPath.Length = Length - sizeof(WCHAR);
-    InfoBuffer->Script.ScriptPath.MaximumLength = Length;
-    InfoBuffer->Script.ScriptPath.Buffer = midl_user_allocate(Length);
-    if (InfoBuffer->Script.ScriptPath.Buffer == NULL)
-    {
-        Status = STATUS_INSUFFICIENT_RESOURCES;
-        goto done;
-    }
-
-    TRACE("Length: %lu\n", Length);
-    Status = SampGetObjectAttribute(UserObject,
-                                    L"ScriptPath",
-                                    NULL,
-                                    (PVOID)InfoBuffer->Script.ScriptPath.Buffer,
-                                    &Length);
+    /* Get the ScriptPath string */
+    Status = SampGetObjectAttributeString(UserObject,
+                                          L"ScriptPath",
+                                          &InfoBuffer->Script.ScriptPath);
     if (!NT_SUCCESS(Status))
     {
         TRACE("Status 0x%08lx\n", Status);
@@ -3618,41 +5242,18 @@ SampQueryUserProfile(PSAM_DB_OBJECT UserObject,
                      PSAMPR_USER_INFO_BUFFER *Buffer)
 {
     PSAMPR_USER_INFO_BUFFER InfoBuffer = NULL;
-    ULONG Length = 0;
     NTSTATUS Status;
 
-    *Buffer = NULL;
-
-    InfoBuffer = midl_user_allocate(sizeof(SAMPR_USER_INFO_BUFFER));
-    if (InfoBuffer == NULL)
-        return STATUS_INSUFFICIENT_RESOURCES;
-
-    Status = SampGetObjectAttribute(UserObject,
-                                    L"ProfilePath",
-                                    NULL,
-                                    NULL,
-                                    &Length);
-    if (!NT_SUCCESS(Status) && Status != STATUS_BUFFER_OVERFLOW)
-    {
-        TRACE("Status 0x%08lx\n", Status);
-        goto done;
-    }
-
-    InfoBuffer->Profile.ProfilePath.Length = Length - sizeof(WCHAR);
-    InfoBuffer->Profile.ProfilePath.MaximumLength = Length;
-    InfoBuffer->Profile.ProfilePath.Buffer = midl_user_allocate(Length);
-    if (InfoBuffer->Profile.ProfilePath.Buffer == NULL)
-    {
-        Status = STATUS_INSUFFICIENT_RESOURCES;
-        goto done;
-    }
+    *Buffer = NULL;
 
-    TRACE("Length: %lu\n", Length);
-    Status = SampGetObjectAttribute(UserObject,
-                                    L"ProfilePath",
-                                    NULL,
-                                    (PVOID)InfoBuffer->Profile.ProfilePath.Buffer,
-                                    &Length);
+    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);
@@ -3682,7 +5283,6 @@ SampQueryUserAdminComment(PSAM_DB_OBJECT UserObject,
                           PSAMPR_USER_INFO_BUFFER *Buffer)
 {
     PSAMPR_USER_INFO_BUFFER InfoBuffer = NULL;
-    ULONG Length = 0;
     NTSTATUS Status;
 
     *Buffer = NULL;
@@ -3691,32 +5291,10 @@ SampQueryUserAdminComment(PSAM_DB_OBJECT UserObject,
     if (InfoBuffer == NULL)
         return STATUS_INSUFFICIENT_RESOURCES;
 
-    Status = SampGetObjectAttribute(UserObject,
-                                    L"AdminComment",
-                                    NULL,
-                                    NULL,
-                                    &Length);
-    if (!NT_SUCCESS(Status) && Status != STATUS_BUFFER_OVERFLOW)
-    {
-        TRACE("Status 0x%08lx\n", Status);
-        goto done;
-    }
-
-    InfoBuffer->AdminComment.AdminComment.Length = Length - sizeof(WCHAR);
-    InfoBuffer->AdminComment.AdminComment.MaximumLength = Length;
-    InfoBuffer->AdminComment.AdminComment.Buffer = midl_user_allocate(Length);
-    if (InfoBuffer->AdminComment.AdminComment.Buffer == NULL)
-    {
-        Status = STATUS_INSUFFICIENT_RESOURCES;
-        goto done;
-    }
-
-    TRACE("Length: %lu\n", Length);
-    Status = SampGetObjectAttribute(UserObject,
-                                    L"AdminComment",
-                                    NULL,
-                                    (PVOID)InfoBuffer->AdminComment.AdminComment.Buffer,
-                                    &Length);
+    /* Get the AdminComment string */
+    Status = SampGetObjectAttributeString(UserObject,
+                                          L"AdminComment",
+                                          &InfoBuffer->AdminComment.AdminComment);
     if (!NT_SUCCESS(Status))
     {
         TRACE("Status 0x%08lx\n", Status);
@@ -3741,13 +5319,11 @@ done:
 }
 
 
-
 static NTSTATUS
 SampQueryUserWorkStations(PSAM_DB_OBJECT UserObject,
                           PSAMPR_USER_INFO_BUFFER *Buffer)
 {
     PSAMPR_USER_INFO_BUFFER InfoBuffer = NULL;
-    ULONG Length = 0;
     NTSTATUS Status;
 
     *Buffer = NULL;
@@ -3756,32 +5332,10 @@ SampQueryUserWorkStations(PSAM_DB_OBJECT UserObject,
     if (InfoBuffer == NULL)
         return STATUS_INSUFFICIENT_RESOURCES;
 
-    Status = SampGetObjectAttribute(UserObject,
-                                    L"WorkStations",
-                                    NULL,
-                                    NULL,
-                                    &Length);
-    if (!NT_SUCCESS(Status) && Status != STATUS_BUFFER_OVERFLOW)
-    {
-        TRACE("Status 0x%08lx\n", Status);
-        goto done;
-    }
-
-    InfoBuffer->WorkStations.WorkStations.Length = Length - sizeof(WCHAR);
-    InfoBuffer->WorkStations.WorkStations.MaximumLength = Length;
-    InfoBuffer->WorkStations.WorkStations.Buffer = midl_user_allocate(Length);
-    if (InfoBuffer->WorkStations.WorkStations.Buffer == NULL)
-    {
-        Status = STATUS_INSUFFICIENT_RESOURCES;
-        goto done;
-    }
-
-    TRACE("Length: %lu\n", Length);
-    Status = SampGetObjectAttribute(UserObject,
-                                    L"WorkStations",
-                                    NULL,
-                                    (PVOID)InfoBuffer->WorkStations.WorkStations.Buffer,
-                                    &Length);
+    /* Get the WorkStations string */
+    Status = SampGetObjectAttributeString(UserObject,
+                                          L"WorkStations",
+                                          &InfoBuffer->WorkStations.WorkStations);
     if (!NT_SUCCESS(Status))
     {
         TRACE("Status 0x%08lx\n", Status);
@@ -3959,11 +5513,30 @@ SamrQueryInformationUser(IN SAMPR_HANDLE UserHandle,
 
     switch (UserInformationClass)
     {
-//        case UserGeneralInformation:
-//        case UserPreferencesInformation:
-//        case UserLogonInformation:
+        case UserGeneralInformation:
+            Status = SampQueryUserGeneral(UserObject,
+                                          Buffer);
+            break;
+
+        case UserPreferencesInformation:
+            Status = SampQueryUserPreferences(UserObject,
+                                              Buffer);
+            break;
+
+        case UserLogonInformation:
+            Status = SampQueryUserLogon(UserObject,
+                                        Buffer);
+            break;
+
 //        case UserLogonHoursInformation:
-//        case UserAccountInformation:
+//            Status = SampQueryUserLogonHours(UserObject,
+//                                             Buffer);
+//            break;
+
+        case UserAccountInformation:
+            Status = SampQueryUserAccount(UserObject,
+                                          Buffer);
+            break;
 
         case UserNameInformation:
             Status = SampQueryUserName(UserObject,
@@ -4026,6 +5599,199 @@ SamrQueryInformationUser(IN SAMPR_HANDLE UserHandle,
     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 = SampSetObjectAttribute(UserObject,
+                                    L"Name",
+                                    REG_SZ,
+                                    Buffer->General.UserName.Buffer,
+                                    Buffer->General.UserName.MaximumLength);
+    if (!NT_SUCCESS(Status))
+        goto done;
+
+    Status = SampSetObjectAttribute(UserObject,
+                                    L"FullName",
+                                    REG_SZ,
+                                    Buffer->General.FullName.Buffer,
+                                    Buffer->General.FullName.MaximumLength);
+    if (!NT_SUCCESS(Status))
+        goto done;
+
+    Status = SampSetObjectAttribute(UserObject,
+                                    L"AdminComment",
+                                    REG_SZ,
+                                    Buffer->General.AdminComment.Buffer,
+                                    Buffer->General.AdminComment.MaximumLength);
+    if (!NT_SUCCESS(Status))
+        goto done;
+
+    Status = SampSetObjectAttribute(UserObject,
+                                    L"UserComment",
+                                    REG_SZ,
+                                    Buffer->General.UserComment.Buffer,
+                                    Buffer->General.UserComment.MaximumLength);
+
+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 = SampSetObjectAttribute(UserObject,
+                                    L"UserComment",
+                                    REG_SZ,
+                                    Buffer->Preferences.UserComment.Buffer,
+                                    Buffer->Preferences.UserComment.MaximumLength);
+
+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;
+}
+
+
 /* Function 37 */
 NTSTATUS
 NTAPI
@@ -4042,6 +5808,7 @@ SamrSetInformationUser(IN SAMPR_HANDLE UserHandle,
 
     switch (UserInformationClass)
     {
+        case UserLogonHoursInformation:
         case UserNameInformation:
         case UserAccountNameInformation:
         case UserFullNameInformation:
@@ -4056,6 +5823,15 @@ SamrSetInformationUser(IN SAMPR_HANDLE UserHandle,
             DesiredAccess = USER_WRITE_ACCOUNT;
             break;
 
+        case UserGeneralInformation:
+            DesiredAccess = USER_WRITE_ACCOUNT |
+                            USER_WRITE_PREFERENCES;
+            break;
+
+        case UserPreferencesInformation:
+            DesiredAccess = USER_WRITE_PREFERENCES;
+            break;
+
         case UserSetPasswordInformation:
             DesiredAccess = USER_FORCE_PASSWORD_CHANGE;
             break;
@@ -4077,10 +5853,21 @@ SamrSetInformationUser(IN SAMPR_HANDLE UserHandle,
 
     switch (UserInformationClass)
     {
-//        case UserGeneralInformation:
-//        case UserPreferencesInformation:
-//        case UserLogonHoursInformation:
+        case UserGeneralInformation:
+            Status = SampSetUserGeneral(UserObject,
+                                        Buffer);
+            break;
 
+        case UserPreferencesInformation:
+            Status = SampSetUserPreferences(UserObject,
+                                            Buffer);
+            break;
+/*
+        case UserLogonHoursInformation:
+            Status = SampSetUserLogonHours(UserObject,
+                                           Buffer);
+            break;
+*/
         case UserNameInformation:
             Status = SampSetObjectAttribute(UserObject,
                                             L"Name",
@@ -4113,15 +5900,10 @@ SamrSetInformationUser(IN SAMPR_HANDLE UserHandle,
                                             Buffer->FullName.FullName.MaximumLength);
             break;
 
-/*
         case UserPrimaryGroupInformation:
-            Status = SampSetObjectAttribute(UserObject,
-                                            L"PrimaryGroupId",
-                                            REG_DWORD,
-                                            &Buffer->PrimaryGroup.PrimaryGroupId,
-                                            sizeof(ULONG));
+            Status = SampSetUserPrimaryGroup(UserObject,
+                                             Buffer);
             break;
-*/
 
         case UserHomeInformation:
             Status = SampSetObjectAttribute(UserObject,
@@ -4182,24 +5964,15 @@ SamrSetInformationUser(IN SAMPR_HANDLE UserHandle,
                                             Buffer->SetPassword.Password.MaximumLength);
             break;
 
-/*
         case UserControlInformation:
-            Status = SampSetObjectAttribute(UserObject,
-                                            L"UserAccountControl",
-                                            REG_DWORD,
-                                            &Buffer->Control.UserAccountControl,
-                                            sizeof(ULONG));
+            Status = SampSetUserControl(UserObject,
+                                        Buffer);
             break;
-*/
-/*
+
         case UserExpiresInformation:
-            Status = SampSetObjectAttribute(UserObject,
-                                            L"AccountExpires",
-                                            REG_BINARY,
-                                            &Buffer->Expires.AccountExpires,
-                                            sizeof(OLD_LARGE_INTEGER));
+            Status = SampSetUserExpires(UserObject,
+                                        Buffer);
             break;
-*/
 
 //        case UserInternal1Information:
 //        case UserParametersInformation:
@@ -4361,6 +6134,7 @@ SamrGetDisplayEnumerationIndex2(IN SAMPR_HANDLE DomainHandle,
     return STATUS_NOT_IMPLEMENTED;
 }
 
+
 /* Function 50 */
 NTSTATUS
 NTAPI
@@ -4372,10 +6146,263 @@ SamrCreateUser2InDomain(IN SAMPR_HANDLE DomainHandle,
                         OUT unsigned long *GrantedAccess,
                         OUT unsigned long *RelativeId)
 {
-    UNIMPLEMENTED;
-    return STATUS_NOT_IMPLEMENTED;
+    UNICODE_STRING EmptyString = RTL_CONSTANT_STRING(L"");
+    SAM_DOMAIN_FIXED_DATA FixedDomainData;
+    SAM_USER_FIXED_DATA FixedUserData;
+    PSAM_DB_OBJECT DomainObject;
+    PSAM_DB_OBJECT UserObject;
+    ULONG ulSize;
+    ULONG ulRid;
+    WCHAR szRid[9];
+    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;
+
+    /* Validate the domain handle */
+    Status = SampValidateDbObject(DomainHandle,
+                                  SamDbDomainObject,
+                                  DOMAIN_CREATE_USER,
+                                  &DomainObject);
+    if (!NT_SUCCESS(Status))
+    {
+        TRACE("failed with status 0x%08lx\n", Status);
+        return Status;
+    }
+
+    /* 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);
+        return Status;
+    }
+
+    /* 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);
+        return Status;
+    }
+
+    /* 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);
+        return Status;
+    }
+
+    TRACE("RID: %lx\n", ulRid);
+
+    /* Convert the RID into a string (hex) */
+    swprintf(szRid, L"%08lX", ulRid);
+
+    /* Create the user object */
+    Status = SampCreateDbObject(DomainObject,
+                                L"Users",
+                                szRid,
+                                SamDbUserObject,
+                                DesiredAccess,
+                                &UserObject);
+    if (!NT_SUCCESS(Status))
+    {
+        TRACE("failed with status 0x%08lx\n", Status);
+        return Status;
+    }
+
+    /* 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);
+        return Status;
+    }
+
+    /* Initialize fixed user data */
+    memset(&FixedUserData, 0, sizeof(SAM_USER_FIXED_DATA));
+    FixedUserData.Version = 1;
+    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;
+
+    /* 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);
+        return Status;
+    }
+
+    /* Set the Name attribute */
+    Status = SampSetObjectAttribute(UserObject,
+                                    L"Name",
+                                    REG_SZ,
+                                    (LPVOID)Name->Buffer,
+                                    Name->MaximumLength);
+    if (!NT_SUCCESS(Status))
+    {
+        TRACE("failed with status 0x%08lx\n", Status);
+        return Status;
+    }
+
+    /* Set the FullName attribute */
+    Status = SampSetObjectAttribute(UserObject,
+                                    L"FullName",
+                                    REG_SZ,
+                                    EmptyString.Buffer,
+                                    EmptyString.MaximumLength);
+    if (!NT_SUCCESS(Status))
+    {
+        TRACE("failed with status 0x%08lx\n", Status);
+        return Status;
+    }
+
+    /* Set the HomeDirectory attribute */
+    Status = SampSetObjectAttribute(UserObject,
+                                    L"HomeDirectory",
+                                    REG_SZ,
+                                    EmptyString.Buffer,
+                                    EmptyString.MaximumLength);
+    if (!NT_SUCCESS(Status))
+    {
+        TRACE("failed with status 0x%08lx\n", Status);
+        return Status;
+    }
+
+    /* Set the HomeDirectoryDrive attribute */
+    Status = SampSetObjectAttribute(UserObject,
+                                    L"HomeDirectoryDrive",
+                                    REG_SZ,
+                                    EmptyString.Buffer,
+                                    EmptyString.MaximumLength);
+    if (!NT_SUCCESS(Status))
+    {
+        TRACE("failed with status 0x%08lx\n", Status);
+        return Status;
+    }
+
+    /* Set the ScriptPath attribute */
+    Status = SampSetObjectAttribute(UserObject,
+                                    L"ScriptPath",
+                                    REG_SZ,
+                                    EmptyString.Buffer,
+                                    EmptyString.MaximumLength);
+    if (!NT_SUCCESS(Status))
+    {
+        TRACE("failed with status 0x%08lx\n", Status);
+        return Status;
+    }
+
+    /* Set the ProfilePath attribute */
+    Status = SampSetObjectAttribute(UserObject,
+                                    L"ProfilePath",
+                                    REG_SZ,
+                                    EmptyString.Buffer,
+                                    EmptyString.MaximumLength);
+    if (!NT_SUCCESS(Status))
+    {
+        TRACE("failed with status 0x%08lx\n", Status);
+        return Status;
+    }
+
+    /* Set the AdminComment attribute */
+    Status = SampSetObjectAttribute(UserObject,
+                                    L"AdminComment",
+                                    REG_SZ,
+                                    EmptyString.Buffer,
+                                    EmptyString.MaximumLength);
+    if (!NT_SUCCESS(Status))
+    {
+        TRACE("failed with status 0x%08lx\n", Status);
+        return Status;
+    }
+
+    /* Set the UserComment attribute */
+    Status = SampSetObjectAttribute(UserObject,
+                                    L"UserComment",
+                                    REG_SZ,
+                                    EmptyString.Buffer,
+                                    EmptyString.MaximumLength);
+    if (!NT_SUCCESS(Status))
+    {
+        TRACE("failed with status 0x%08lx\n", Status);
+        return Status;
+    }
+
+    /* Set the WorkStations attribute */
+    Status = SampSetObjectAttribute(UserObject,
+                                    L"WorkStations",
+                                    REG_SZ,
+                                    EmptyString.Buffer,
+                                    EmptyString.MaximumLength);
+    if (!NT_SUCCESS(Status))
+    {
+        TRACE("failed with status 0x%08lx\n", Status);
+        return Status;
+    }
+
+    /* FIXME: Set default user attributes */
+
+    if (NT_SUCCESS(Status))
+    {
+        *UserHandle = (SAMPR_HANDLE)UserObject;
+        *RelativeId = ulRid;
+        *GrantedAccess = UserObject->Access;
+    }
+
+    TRACE("returns with status 0x%08lx\n", Status);
+
+    return Status;
 }
 
+
 /* Function 51 */
 NTSTATUS
 NTAPI
@@ -4392,26 +6419,63 @@ SamrQueryDisplayInformation3(IN SAMPR_HANDLE DomainHandle,
     return STATUS_NOT_IMPLEMENTED;
 }
 
+
 /* Function 52 */
 NTSTATUS
 NTAPI
 SamrAddMultipleMembersToAlias(IN SAMPR_HANDLE AliasHandle,
                               IN PSAMPR_PSID_ARRAY MembersBuffer)
 {
-    UNIMPLEMENTED;
-    return STATUS_NOT_IMPLEMENTED;
+    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)
 {
-    UNIMPLEMENTED;
-    return STATUS_NOT_IMPLEMENTED;
+    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