[SAMLIB]
[reactos.git] / reactos / dll / win32 / samsrv / samrpc.c
index f40d9b4..9bf3075 100644 (file)
@@ -7,12 +7,8 @@
  * PROGRAMMERS:     Eric Kohl
  */
 
-/* INCLUDES ******************************************************************/
-
 #include "samsrv.h"
 
-WINE_DEFAULT_DEBUG_CHANNEL(samsrv);
-
 /* GLOBALS *******************************************************************/
 
 static SID_IDENTIFIER_AUTHORITY NtSidAuthority = {SECURITY_NT_AUTHORITY};
@@ -57,9 +53,27 @@ static GENERIC_MAPPING UserMapping =
     USER_ALL_ACCESS
 };
 
+PGENERIC_MAPPING pServerMapping = &ServerMapping;
+
 
 /* FUNCTIONS *****************************************************************/
 
+static
+LARGE_INTEGER
+SampAddRelativeTimeToTime(IN LARGE_INTEGER AbsoluteTime,
+                          IN LARGE_INTEGER RelativeTime)
+{
+    LARGE_INTEGER NewTime;
+
+    NewTime.QuadPart = AbsoluteTime.QuadPart - RelativeTime.QuadPart;
+
+    if (NewTime.QuadPart < 0)
+        NewTime.QuadPart = 0;
+
+    return NewTime;
+}
+
+
 VOID
 SampStartRpcServer(VOID)
 {
@@ -127,6 +141,9 @@ SamrConnect(IN PSAMPR_SERVER_NAME ServerName,
     TRACE("SamrConnect(%p %p %lx)\n",
           ServerName, ServerHandle, DesiredAccess);
 
+    RtlAcquireResourceShared(&SampResource,
+                             TRUE);
+
     /* Map generic access rights */
     RtlMapGenericMask(&DesiredAccess,
                       &ServerMapping);
@@ -142,6 +159,8 @@ SamrConnect(IN PSAMPR_SERVER_NAME ServerName,
     if (NT_SUCCESS(Status))
         *ServerHandle = (SAMPR_HANDLE)ServerObject;
 
+    RtlReleaseResource(&SampResource);
+
     TRACE("SamrConnect done (Status 0x%08lx)\n", Status);
 
     return Status;
@@ -158,6 +177,9 @@ SamrCloseHandle(IN OUT SAMPR_HANDLE *SamHandle)
 
     TRACE("SamrCloseHandle(%p)\n", SamHandle);
 
+    RtlAcquireResourceShared(&SampResource,
+                             TRUE);
+
     Status = SampValidateDbObject(*SamHandle,
                                   SamDbIgnoreObject,
                                   0,
@@ -168,6 +190,8 @@ SamrCloseHandle(IN OUT SAMPR_HANDLE *SamHandle)
         *SamHandle = NULL;
     }
 
+    RtlReleaseResource(&SampResource);
+
     TRACE("SamrCloseHandle done (Status 0x%08lx)\n", Status);
 
     return Status;
@@ -193,8 +217,97 @@ SamrQuerySecurityObject(IN SAMPR_HANDLE ObjectHandle,
                         IN SECURITY_INFORMATION SecurityInformation,
                         OUT PSAMPR_SR_SECURITY_DESCRIPTOR *SecurityDescriptor)
 {
-    UNIMPLEMENTED;
-    return STATUS_NOT_IMPLEMENTED;
+    PSAM_DB_OBJECT SamObject;
+    PSAMPR_SR_SECURITY_DESCRIPTOR SamSD = NULL;
+    PSECURITY_DESCRIPTOR SdBuffer = NULL;
+    ACCESS_MASK DesiredAccess = 0;
+    ULONG Length = 0;
+    NTSTATUS Status;
+
+    TRACE("(%p %lx %p)\n",
+          ObjectHandle, SecurityInformation, SecurityDescriptor);
+
+    *SecurityDescriptor = NULL;
+
+    RtlAcquireResourceShared(&SampResource,
+                             TRUE);
+
+    if (SecurityInformation & (DACL_SECURITY_INFORMATION |
+                               OWNER_SECURITY_INFORMATION |
+                               GROUP_SECURITY_INFORMATION))
+        DesiredAccess |= READ_CONTROL;
+
+    if (SecurityInformation & SACL_SECURITY_INFORMATION)
+        DesiredAccess |= ACCESS_SYSTEM_SECURITY;
+
+    /* Validate the server handle */
+    Status = SampValidateDbObject(ObjectHandle,
+                                  SamDbIgnoreObject,
+                                  DesiredAccess,
+                                  &SamObject);
+    if (!NT_SUCCESS(Status))
+        goto done;
+
+    SamSD = midl_user_allocate(sizeof(SAMPR_SR_SECURITY_DESCRIPTOR));
+    if (SamSD == NULL)
+    {
+        Status = STATUS_INSUFFICIENT_RESOURCES;
+        goto done;
+    }
+
+    Status = SampGetObjectAttribute(SamObject,
+                                    L"SecDesc",
+                                    NULL,
+                                    NULL,
+                                    &Length);
+    if (!NT_SUCCESS(Status) && Status != STATUS_BUFFER_OVERFLOW)
+    {
+        TRACE("Status 0x%08lx\n", Status);
+        goto done;
+    }
+
+    TRACE("SD Length: %lu\n", Length);
+
+    SdBuffer = midl_user_allocate(Length);
+    if (SdBuffer == NULL)
+    {
+        Status = STATUS_INSUFFICIENT_RESOURCES;
+        goto done;
+    }
+
+    Status = SampGetObjectAttribute(SamObject,
+                                    L"SecDesc",
+                                    NULL,
+                                    SdBuffer,
+                                    &Length);
+    if (!NT_SUCCESS(Status))
+    {
+        TRACE("Status 0x%08lx\n", Status);
+        goto done;
+    }
+
+    /* FIXME: Use SecurityInformation to return only the requested information */
+
+    SamSD->Length = Length;
+    SamSD->SecurityDescriptor = SdBuffer;
+
+done:
+    RtlReleaseResource(&SampResource);
+
+    if (NT_SUCCESS(Status))
+    {
+        *SecurityDescriptor = SamSD;
+    }
+    else
+    {
+        if (SdBuffer != NULL)
+            midl_user_free(SdBuffer);
+
+        if (SamSD != NULL)
+            midl_user_free(SamSD);
+    }
+
+    return Status;
 }
 
 
@@ -203,8 +316,29 @@ NTSTATUS
 NTAPI
 SamrShutdownSamServer(IN SAMPR_HANDLE ServerHandle)
 {
-    UNIMPLEMENTED;
-    return STATUS_NOT_IMPLEMENTED;
+    PSAM_DB_OBJECT ServerObject;
+    NTSTATUS Status;
+
+    TRACE("(%p)\n", ServerHandle);
+
+    RtlAcquireResourceShared(&SampResource,
+                             TRUE);
+
+    /* Validate the server handle */
+    Status = SampValidateDbObject(ServerHandle,
+                                  SamDbServerObject,
+                                  SAM_SERVER_SHUTDOWN,
+                                  &ServerObject);
+
+    RtlReleaseResource(&SampResource);
+
+    if (!NT_SUCCESS(Status))
+        return Status;
+
+    /* Shut the server down */
+    RpcMgmtStopServerListening(0);
+
+    return STATUS_SUCCESS;
 }
 
 
@@ -229,13 +363,16 @@ SamrLookupDomainInSamServer(IN SAMPR_HANDLE ServerHandle,
     TRACE("SamrLookupDomainInSamServer(%p %p %p)\n",
           ServerHandle, Name, DomainId);
 
+    RtlAcquireResourceShared(&SampResource,
+                             TRUE);
+
     /* Validate the server handle */
     Status = SampValidateDbObject(ServerHandle,
                                   SamDbServerObject,
                                   SAM_SERVER_LOOKUP_DOMAIN,
                                   &ServerObject);
     if (!NT_SUCCESS(Status))
-        return Status;
+        goto done;
 
     *DomainId = NULL;
 
@@ -244,7 +381,7 @@ SamrLookupDomainInSamServer(IN SAMPR_HANDLE ServerHandle,
                             KEY_READ,
                             &DomainsKeyHandle);
     if (!NT_SUCCESS(Status))
-        return Status;
+        goto done;
 
     Index = 0;
     while (Found == FALSE)
@@ -306,13 +443,17 @@ SamrLookupDomainInSamServer(IN SAMPR_HANDLE ServerHandle,
                 }
             }
 
-            NtClose(DomainKeyHandle);
+            SampRegCloseKey(&DomainKeyHandle);
         }
 
         Index++;
     }
 
-    NtClose(DomainsKeyHandle);
+done:
+    SampRegCloseKey(&DomainKeyHandle);
+    SampRegCloseKey(&DomainsKeyHandle);
+
+    RtlReleaseResource(&SampResource);
 
     return Status;
 }
@@ -329,8 +470,8 @@ SamrEnumerateDomainsInSamServer(IN SAMPR_HANDLE ServerHandle,
 {
     PSAM_DB_OBJECT ServerObject;
     WCHAR DomainKeyName[64];
-    HANDLE DomainsKeyHandle;
-    HANDLE DomainKeyHandle;
+    HANDLE DomainsKeyHandle = NULL;
+    HANDLE DomainKeyHandle = NULL;
     ULONG EnumIndex;
     ULONG EnumCount;
     ULONG RequiredLength;
@@ -343,20 +484,23 @@ SamrEnumerateDomainsInSamServer(IN SAMPR_HANDLE ServerHandle,
           ServerHandle, EnumerationContext, Buffer, PreferedMaximumLength,
           CountReturned);
 
+    RtlAcquireResourceShared(&SampResource,
+                             TRUE);
+
     /* Validate the server handle */
     Status = SampValidateDbObject(ServerHandle,
                                   SamDbServerObject,
                                   SAM_SERVER_ENUMERATE_DOMAINS,
                                   &ServerObject);
     if (!NT_SUCCESS(Status))
-        return Status;
+        goto done;
 
     Status = SampRegOpenKey(ServerObject->KeyHandle,
                             L"Domains",
                             KEY_READ,
                             &DomainsKeyHandle);
     if (!NT_SUCCESS(Status))
-        return Status;
+        goto done;
 
     EnumIndex = *EnumerationContext;
     EnumCount = 0;
@@ -399,7 +543,7 @@ SamrEnumerateDomainsInSamServer(IN SAMPR_HANDLE ServerHandle,
                 EnumCount++;
             }
 
-            NtClose(DomainKeyHandle);
+            SampRegCloseKey(&DomainKeyHandle);
         }
 
         EnumIndex++;
@@ -458,7 +602,7 @@ SamrEnumerateDomainsInSamServer(IN SAMPR_HANDLE ServerHandle,
                 EnumBuffer->Buffer[i].Name.Buffer = midl_user_allocate(DataLength);
                 if (EnumBuffer->Buffer[i].Name.Buffer == NULL)
                 {
-                    NtClose(DomainKeyHandle);
+                    SampRegCloseKey(&DomainKeyHandle);
                     Status = STATUS_INSUFFICIENT_RESOURCES;
                     goto done;
                 }
@@ -475,7 +619,7 @@ SamrEnumerateDomainsInSamServer(IN SAMPR_HANDLE ServerHandle,
                 }
             }
 
-            NtClose(DomainKeyHandle);
+            SampRegCloseKey(&DomainKeyHandle);
 
             if (!NT_SUCCESS(Status))
                 goto done;
@@ -490,6 +634,9 @@ SamrEnumerateDomainsInSamServer(IN SAMPR_HANDLE ServerHandle,
     }
 
 done:
+    SampRegCloseKey(&DomainKeyHandle);
+    SampRegCloseKey(&DomainsKeyHandle);
+
     if (!NT_SUCCESS(Status))
     {
         *EnumerationContext = 0;
@@ -516,7 +663,7 @@ done:
         }
     }
 
-    NtClose(DomainsKeyHandle);
+    RtlReleaseResource(&SampResource);
 
     return Status;
 }
@@ -541,6 +688,9 @@ SamrOpenDomain(IN SAMPR_HANDLE ServerHandle,
     RtlMapGenericMask(&DesiredAccess,
                       &DomainMapping);
 
+    RtlAcquireResourceShared(&SampResource,
+                             TRUE);
+
     /* Validate the server handle */
     Status = SampValidateDbObject(ServerHandle,
                                   SamDbServerObject,
@@ -595,6 +745,8 @@ SamrOpenDomain(IN SAMPR_HANDLE ServerHandle,
     if (NT_SUCCESS(Status))
         *DomainHandle = (SAMPR_HANDLE)DomainObject;
 
+    RtlReleaseResource(&SampResource);
+
     TRACE("SamrOpenDomain done (Status 0x%08lx)\n", Status);
 
     return Status;
@@ -676,12 +828,10 @@ SampGetNumberOfAccounts(PSAM_DB_OBJECT DomainObject,
     Status = SampRegQueryKeyInfo(NamesKeyHandle,
                                  NULL,
                                  Count);
-done:
-    if (NamesKeyHandle != NULL)
-        SampRegCloseKey(NamesKeyHandle);
 
-    if (AccountKeyHandle != NULL)
-        SampRegCloseKey(AccountKeyHandle);
+done:
+    SampRegCloseKey(&NamesKeyHandle);
+    SampRegCloseKey(&AccountKeyHandle);
 
     return Status;
 }
@@ -1345,13 +1495,16 @@ SamrQueryInformationDomain(IN SAMPR_HANDLE DomainHandle,
             return STATUS_INVALID_INFO_CLASS;
     }
 
+    RtlAcquireResourceShared(&SampResource,
+                             TRUE);
+
     /* Validate the server handle */
     Status = SampValidateDbObject(DomainHandle,
                                   SamDbDomainObject,
                                   DesiredAccess,
                                   &DomainObject);
     if (!NT_SUCCESS(Status))
-        return Status;
+        goto done;
 
     switch (DomainInformationClass)
     {
@@ -1419,6 +1572,9 @@ SamrQueryInformationDomain(IN SAMPR_HANDLE DomainHandle,
             Status = STATUS_NOT_IMPLEMENTED;
     }
 
+done:
+    RtlReleaseResource(&SampResource);
+
     return Status;
 }
 
@@ -1619,13 +1775,16 @@ SamrSetInformationDomain(IN SAMPR_HANDLE DomainHandle,
             return STATUS_INVALID_INFO_CLASS;
     }
 
+    RtlAcquireResourceExclusive(&SampResource,
+                                TRUE);
+
     /* Validate the server handle */
     Status = SampValidateDbObject(DomainHandle,
                                   SamDbDomainObject,
                                   DesiredAccess,
                                   &DomainObject);
     if (!NT_SUCCESS(Status))
-        return Status;
+        goto done;
 
     switch (DomainInformationClass)
     {
@@ -1640,27 +1799,21 @@ SamrSetInformationDomain(IN SAMPR_HANDLE DomainHandle,
             break;
 
         case DomainOemInformation:
-            Status = SampSetObjectAttribute(DomainObject,
-                                            L"OemInformation",
-                                            REG_SZ,
-                                            DomainInformation->Oem.OemInformation.Buffer,
-                                            DomainInformation->Oem.OemInformation.Length + sizeof(WCHAR));
+            Status = SampSetObjectAttributeString(DomainObject,
+                                                  L"OemInformation",
+                                                  &DomainInformation->Oem.OemInformation);
             break;
 
         case DomainNameInformation:
-            Status = SampSetObjectAttribute(DomainObject,
-                                            L"Name",
-                                            REG_SZ,
-                                            DomainInformation->Name.DomainName.Buffer,
-                                            DomainInformation->Name.DomainName.Length + sizeof(WCHAR));
+            Status = SampSetObjectAttributeString(DomainObject,
+                                                  L"Name",
+                                                  &DomainInformation->Name.DomainName);
             break;
 
         case DomainReplicationInformation:
-            Status = SampSetObjectAttribute(DomainObject,
-                                            L"ReplicaSourceNodeName",
-                                            REG_SZ,
-                                            DomainInformation->Replication.ReplicaSourceNodeName.Buffer,
-                                            DomainInformation->Replication.ReplicaSourceNodeName.Length + sizeof(WCHAR));
+            Status = SampSetObjectAttributeString(DomainObject,
+                                                  L"ReplicaSourceNodeName",
+                                                  &DomainInformation->Replication.ReplicaSourceNodeName);
             break;
 
         case DomainServerRoleInformation:
@@ -1682,6 +1835,9 @@ SamrSetInformationDomain(IN SAMPR_HANDLE DomainHandle,
             Status = STATUS_NOT_IMPLEMENTED;
     }
 
+done:
+    RtlReleaseResource(&SampResource);
+
     return Status;
 }
 
@@ -1695,11 +1851,12 @@ SamrCreateGroupInDomain(IN SAMPR_HANDLE DomainHandle,
                         OUT SAMPR_HANDLE *GroupHandle,
                         OUT unsigned long *RelativeId)
 {
-    UNICODE_STRING EmptyString = RTL_CONSTANT_STRING(L"");
     SAM_DOMAIN_FIXED_DATA FixedDomainData;
     SAM_GROUP_FIXED_DATA FixedGroupData;
     PSAM_DB_OBJECT DomainObject;
     PSAM_DB_OBJECT GroupObject;
+    PSECURITY_DESCRIPTOR Sd = NULL;
+    ULONG SdSize = 0;
     ULONG ulSize;
     ULONG ulRid;
     WCHAR szRid[9];
@@ -1712,6 +1869,9 @@ SamrCreateGroupInDomain(IN SAMPR_HANDLE DomainHandle,
     RtlMapGenericMask(&DesiredAccess,
                       &GroupMapping);
 
+    RtlAcquireResourceExclusive(&SampResource,
+                                TRUE);
+
     /* Validate the domain handle */
     Status = SampValidateDbObject(DomainHandle,
                                   SamDbDomainObject,
@@ -1720,7 +1880,15 @@ SamrCreateGroupInDomain(IN SAMPR_HANDLE DomainHandle,
     if (!NT_SUCCESS(Status))
     {
         TRACE("failed with status 0x%08lx\n", Status);
-        return Status;
+        goto done;
+    }
+
+    /* Check the group account name */
+    Status = SampCheckAccountName(Name, 256);
+    if (!NT_SUCCESS(Status))
+    {
+        TRACE("SampCheckAccountName failed (Status 0x%08lx)\n", Status);
+        goto done;
     }
 
     /* Check if the group name already exists in the domain */
@@ -1730,7 +1898,16 @@ SamrCreateGroupInDomain(IN SAMPR_HANDLE DomainHandle,
     {
         TRACE("Group name \'%S\' already exists in domain (Status 0x%08lx)\n",
               Name->Buffer, Status);
-        return Status;
+        goto done;
+    }
+
+    /* Create the security descriptor */
+    Status = SampCreateGroupSD(&Sd,
+                               &SdSize);
+    if (!NT_SUCCESS(Status))
+    {
+        TRACE("SampCreateGroupSD failed (Status 0x%08lx)\n", Status);
+        goto done;
     }
 
     /* Get the fixed domain attributes */
@@ -1743,7 +1920,7 @@ SamrCreateGroupInDomain(IN SAMPR_HANDLE DomainHandle,
     if (!NT_SUCCESS(Status))
     {
         TRACE("failed with status 0x%08lx\n", Status);
-        return Status;
+        goto done;
     }
 
     /* Increment the NextRid attribute */
@@ -1752,14 +1929,14 @@ SamrCreateGroupInDomain(IN SAMPR_HANDLE DomainHandle,
 
     /* Store the fixed domain attributes */
     Status = SampSetObjectAttribute(DomainObject,
-                           L"F",
-                           REG_BINARY,
-                           &FixedDomainData,
-                           ulSize);
+                                    L"F",
+                                    REG_BINARY,
+                                    &FixedDomainData,
+                                    ulSize);
     if (!NT_SUCCESS(Status))
     {
         TRACE("failed with status 0x%08lx\n", Status);
-        return Status;
+        goto done;
     }
 
     TRACE("RID: %lx\n", ulRid);
@@ -1778,7 +1955,7 @@ SamrCreateGroupInDomain(IN SAMPR_HANDLE DomainHandle,
     if (!NT_SUCCESS(Status))
     {
         TRACE("failed with status 0x%08lx\n", Status);
-        return Status;
+        goto done;
     }
 
     /* Add the account name of the user object */
@@ -1789,13 +1966,12 @@ SamrCreateGroupInDomain(IN SAMPR_HANDLE DomainHandle,
     if (!NT_SUCCESS(Status))
     {
         TRACE("failed with status 0x%08lx\n", Status);
-        return Status;
+        goto done;
     }
 
     /* Initialize fixed user data */
     memset(&FixedGroupData, 0, sizeof(SAM_GROUP_FIXED_DATA));
     FixedGroupData.Version = 1;
-
     FixedGroupData.GroupId = ulRid;
 
     /* Set fixed user data attribute */
@@ -1807,31 +1983,39 @@ SamrCreateGroupInDomain(IN SAMPR_HANDLE DomainHandle,
     if (!NT_SUCCESS(Status))
     {
         TRACE("failed with status 0x%08lx\n", Status);
-        return Status;
+        goto done;
     }
 
     /* Set the Name attribute */
-    Status = SampSetObjectAttribute(GroupObject,
-                                    L"Name",
-                                    REG_SZ,
-                                    (LPVOID)Name->Buffer,
-                                    Name->MaximumLength);
+    Status = SampSetObjectAttributeString(GroupObject,
+                                          L"Name",
+                                          Name);
     if (!NT_SUCCESS(Status))
     {
         TRACE("failed with status 0x%08lx\n", Status);
-        return Status;
+        goto done;
     }
 
     /* Set the AdminComment attribute */
+    Status = SampSetObjectAttributeString(GroupObject,
+                                          L"AdminComment",
+                                          NULL);
+    if (!NT_SUCCESS(Status))
+    {
+        TRACE("failed with status 0x%08lx\n", Status);
+        goto done;
+    }
+
+    /* Set the SecDesc attribute*/
     Status = SampSetObjectAttribute(GroupObject,
-                                    L"AdminComment",
-                                    REG_SZ,
-                                    EmptyString.Buffer,
-                                    EmptyString.MaximumLength);
+                                    L"SecDesc",
+                                    REG_BINARY,
+                                    Sd,
+                                    SdSize);
     if (!NT_SUCCESS(Status))
     {
         TRACE("failed with status 0x%08lx\n", Status);
-        return Status;
+        goto done;
     }
 
     if (NT_SUCCESS(Status))
@@ -1840,6 +2024,12 @@ SamrCreateGroupInDomain(IN SAMPR_HANDLE DomainHandle,
         *RelativeId = ulRid;
     }
 
+done:
+    if (Sd != NULL)
+        RtlFreeHeap(RtlGetProcessHeap(), 0, Sd);
+
+    RtlReleaseResource(&SampResource);
+
     TRACE("returns with status 0x%08lx\n", Status);
 
     return Status;
@@ -1874,20 +2064,23 @@ SamrEnumerateGroupsInDomain(IN SAMPR_HANDLE DomainHandle,
           DomainHandle, EnumerationContext, Buffer,
           PreferedMaximumLength, CountReturned);
 
+    RtlAcquireResourceShared(&SampResource,
+                             TRUE);
+
     /* Validate the domain handle */
     Status = SampValidateDbObject(DomainHandle,
                                   SamDbDomainObject,
                                   DOMAIN_LIST_ACCOUNTS,
                                   &DomainObject);
     if (!NT_SUCCESS(Status))
-        return Status;
+        goto done;
 
     Status = SampRegOpenKey(DomainObject->KeyHandle,
                             L"Groups",
                             KEY_READ,
                             &GroupsKeyHandle);
     if (!NT_SUCCESS(Status))
-        return Status;
+        goto done;
 
     Status = SampRegOpenKey(GroupsKeyHandle,
                             L"Names",
@@ -2036,15 +2229,14 @@ done:
         }
     }
 
-    if (NamesKeyHandle != NULL)
-        SampRegCloseKey(NamesKeyHandle);
-
-    if (GroupsKeyHandle != NULL)
-        SampRegCloseKey(GroupsKeyHandle);
+    SampRegCloseKey(&NamesKeyHandle);
+    SampRegCloseKey(&GroupsKeyHandle);
 
     if ((Status == STATUS_SUCCESS) && (MoreEntries == TRUE))
         Status = STATUS_MORE_ENTRIES;
 
+    RtlReleaseResource(&SampResource);
+
     return Status;
 }
 
@@ -2058,14 +2250,18 @@ SamrCreateUserInDomain(IN SAMPR_HANDLE DomainHandle,
                        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;
     PSAM_DB_OBJECT UserObject;
+    GROUP_MEMBERSHIP GroupMembership;
+    UCHAR LogonHours[23];
     ULONG ulSize;
     ULONG ulRid;
     WCHAR szRid[9];
+    PSECURITY_DESCRIPTOR Sd = NULL;
+    ULONG SdSize = 0;
+    PSID UserSid = NULL;
     NTSTATUS Status;
 
     TRACE("SamrCreateUserInDomain(%p %p %lx %p %p)\n",
@@ -2082,6 +2278,9 @@ SamrCreateUserInDomain(IN SAMPR_HANDLE DomainHandle,
     RtlMapGenericMask(&DesiredAccess,
                       &UserMapping);
 
+    RtlAcquireResourceExclusive(&SampResource,
+                                TRUE);
+
     /* Validate the domain handle */
     Status = SampValidateDbObject(DomainHandle,
                                   SamDbDomainObject,
@@ -2090,7 +2289,15 @@ SamrCreateUserInDomain(IN SAMPR_HANDLE DomainHandle,
     if (!NT_SUCCESS(Status))
     {
         TRACE("failed with status 0x%08lx\n", Status);
-        return Status;
+        goto done;
+    }
+
+    /* Check the user account name */
+    Status = SampCheckAccountName(Name, 20);
+    if (!NT_SUCCESS(Status))
+    {
+        TRACE("SampCheckAccountName failed (Status 0x%08lx)\n", Status);
+        goto done;
     }
 
     /* Check if the user name already exists in the domain */
@@ -2100,7 +2307,7 @@ SamrCreateUserInDomain(IN SAMPR_HANDLE DomainHandle,
     {
         TRACE("User name \'%S\' already exists in domain (Status 0x%08lx)\n",
               Name->Buffer, Status);
-        return Status;
+        goto done;
     }
 
     /* Get the fixed domain attributes */
@@ -2113,13 +2320,35 @@ SamrCreateUserInDomain(IN SAMPR_HANDLE DomainHandle,
     if (!NT_SUCCESS(Status))
     {
         TRACE("failed with status 0x%08lx\n", Status);
-        return Status;
+        goto done;
     }
 
     /* Increment the NextRid attribute */
     ulRid = FixedDomainData.NextRid;
     FixedDomainData.NextRid++;
 
+    TRACE("RID: %lx\n", ulRid);
+
+    /* Create the user SID */
+    Status = SampCreateAccountSid(DomainObject,
+                                  ulRid,
+                                  &UserSid);
+    if (!NT_SUCCESS(Status))
+    {
+        TRACE("SampCreateAccountSid failed (Status 0x%08lx)\n", Status);
+        goto done;
+    }
+
+    /* Create the security descriptor */
+    Status = SampCreateUserSD(UserSid,
+                              &Sd,
+                              &SdSize);
+    if (!NT_SUCCESS(Status))
+    {
+        TRACE("SampCreateUserSD failed (Status 0x%08lx)\n", Status);
+        goto done;
+    }
+
     /* Store the fixed domain attributes */
     Status = SampSetObjectAttribute(DomainObject,
                            L"F",
@@ -2129,11 +2358,9 @@ SamrCreateUserInDomain(IN SAMPR_HANDLE DomainHandle,
     if (!NT_SUCCESS(Status))
     {
         TRACE("failed with status 0x%08lx\n", Status);
-        return Status;
+        goto done;
     }
 
-    TRACE("RID: %lx\n", ulRid);
-
     /* Convert the RID into a string (hex) */
     swprintf(szRid, L"%08lX", ulRid);
 
@@ -2148,7 +2375,7 @@ SamrCreateUserInDomain(IN SAMPR_HANDLE DomainHandle,
     if (!NT_SUCCESS(Status))
     {
         TRACE("failed with status 0x%08lx\n", Status);
-        return Status;
+        goto done;
     }
 
     /* Add the account name for the user object */
@@ -2159,7 +2386,7 @@ SamrCreateUserInDomain(IN SAMPR_HANDLE DomainHandle,
     if (!NT_SUCCESS(Status))
     {
         TRACE("failed with status 0x%08lx\n", Status);
-        return Status;
+        goto done;
     }
 
     /* Initialize fixed user data */
@@ -2193,154 +2420,163 @@ SamrCreateUserInDomain(IN SAMPR_HANDLE DomainHandle,
     if (!NT_SUCCESS(Status))
     {
         TRACE("failed with status 0x%08lx\n", Status);
-        return Status;
+        goto done;
     }
 
     /* Set the Name attribute */
-    Status = SampSetObjectAttribute(UserObject,
-                                    L"Name",
-                                    REG_SZ,
-                                    (LPVOID)Name->Buffer,
-                                    Name->MaximumLength);
+    Status = SampSetObjectAttributeString(UserObject,
+                                          L"Name",
+                                          Name);
     if (!NT_SUCCESS(Status))
     {
         TRACE("failed with status 0x%08lx\n", Status);
-        return Status;
+        goto done;
     }
 
     /* Set the FullName attribute */
-    Status = SampSetObjectAttribute(UserObject,
-                                    L"FullName",
-                                    REG_SZ,
-                                    EmptyString.Buffer,
-                                    EmptyString.MaximumLength);
+    Status = SampSetObjectAttributeString(UserObject,
+                                          L"FullName",
+                                          NULL);
     if (!NT_SUCCESS(Status))
     {
         TRACE("failed with status 0x%08lx\n", Status);
-        return Status;
+        goto done;
     }
 
     /* Set the HomeDirectory attribute */
-    Status = SampSetObjectAttribute(UserObject,
-                                    L"HomeDirectory",
-                                    REG_SZ,
-                                    EmptyString.Buffer,
-                                    EmptyString.MaximumLength);
+    Status = SampSetObjectAttributeString(UserObject,
+                                          L"HomeDirectory",
+                                          NULL);
     if (!NT_SUCCESS(Status))
     {
         TRACE("failed with status 0x%08lx\n", Status);
-        return Status;
+        goto done;
     }
 
     /* Set the HomeDirectoryDrive attribute */
-    Status = SampSetObjectAttribute(UserObject,
-                                    L"HomeDirectoryDrive",
-                                    REG_SZ,
-                                    EmptyString.Buffer,
-                                    EmptyString.MaximumLength);
+    Status = SampSetObjectAttributeString(UserObject,
+                                          L"HomeDirectoryDrive",
+                                          NULL);
     if (!NT_SUCCESS(Status))
     {
         TRACE("failed with status 0x%08lx\n", Status);
-        return Status;
+        goto done;
     }
 
     /* Set the ScriptPath attribute */
-    Status = SampSetObjectAttribute(UserObject,
-                                    L"ScriptPath",
-                                    REG_SZ,
-                                    EmptyString.Buffer,
-                                    EmptyString.MaximumLength);
+    Status = SampSetObjectAttributeString(UserObject,
+                                          L"ScriptPath",
+                                          NULL);
     if (!NT_SUCCESS(Status))
     {
         TRACE("failed with status 0x%08lx\n", Status);
-        return Status;
+        goto done;
     }
 
     /* Set the ProfilePath attribute */
-    Status = SampSetObjectAttribute(UserObject,
-                                    L"ProfilePath",
-                                    REG_SZ,
-                                    EmptyString.Buffer,
-                                    EmptyString.MaximumLength);
+    Status = SampSetObjectAttributeString(UserObject,
+                                          L"ProfilePath",
+                                          NULL);
     if (!NT_SUCCESS(Status))
     {
         TRACE("failed with status 0x%08lx\n", Status);
-        return Status;
+        goto done;
     }
 
     /* Set the AdminComment attribute */
-    Status = SampSetObjectAttribute(UserObject,
-                                    L"AdminComment",
-                                    REG_SZ,
-                                    EmptyString.Buffer,
-                                    EmptyString.MaximumLength);
+    Status = SampSetObjectAttributeString(UserObject,
+                                          L"AdminComment",
+                                          NULL);
     if (!NT_SUCCESS(Status))
     {
         TRACE("failed with status 0x%08lx\n", Status);
-        return Status;
+        goto done;
     }
 
     /* Set the UserComment attribute */
-    Status = SampSetObjectAttribute(UserObject,
-                                    L"UserComment",
-                                    REG_SZ,
-                                    EmptyString.Buffer,
-                                    EmptyString.MaximumLength);
+    Status = SampSetObjectAttributeString(UserObject,
+                                          L"UserComment",
+                                          NULL);
     if (!NT_SUCCESS(Status))
     {
         TRACE("failed with status 0x%08lx\n", Status);
-        return Status;
+        goto done;
     }
 
     /* Set the WorkStations attribute */
-    Status = SampSetObjectAttribute(UserObject,
-                                    L"WorkStations",
-                                    REG_SZ,
-                                    EmptyString.Buffer,
-                                    EmptyString.MaximumLength);
+    Status = SampSetObjectAttributeString(UserObject,
+                                          L"WorkStations",
+                                          NULL);
     if (!NT_SUCCESS(Status))
     {
         TRACE("failed with status 0x%08lx\n", Status);
-        return Status;
+        goto done;
     }
 
     /* Set the Parameters attribute */
-    Status = SampSetObjectAttribute(UserObject,
-                                    L"Parameters",
-                                    REG_SZ,
-                                    EmptyString.Buffer,
-                                    EmptyString.MaximumLength);
+    Status = SampSetObjectAttributeString(UserObject,
+                                          L"Parameters",
+                                          NULL);
     if (!NT_SUCCESS(Status))
     {
         TRACE("failed with status 0x%08lx\n", Status);
-        return Status;
+        goto done;
     }
 
-    /* FIXME: Set LogonHours attribute*/
-    /* FIXME: Set Groups attribute*/
+    /* Set LogonHours attribute*/
+    *((PUSHORT)LogonHours) = 168;
+    memset(&(LogonHours[2]), 0xff, 21);
 
-    /* Set LMPwd attribute*/
     Status = SampSetObjectAttribute(UserObject,
-                                    L"LMPwd",
+                                    L"LogonHours",
                                     REG_BINARY,
-                                    NULL,
-                                    0);
+                                    &LogonHours,
+                                    sizeof(LogonHours));
     if (!NT_SUCCESS(Status))
     {
         TRACE("failed with status 0x%08lx\n", Status);
-        return Status;
+        goto done;
+    }
+
+    /* Set Groups attribute*/
+    GroupMembership.RelativeId = DOMAIN_GROUP_RID_USERS;
+    GroupMembership.Attributes = SE_GROUP_MANDATORY |
+                                 SE_GROUP_ENABLED |
+                                 SE_GROUP_ENABLED_BY_DEFAULT;
+
+    Status = SampSetObjectAttribute(UserObject,
+                                    L"Groups",
+                                    REG_BINARY,
+                                    &GroupMembership,
+                                    sizeof(GROUP_MEMBERSHIP));
+    if (!NT_SUCCESS(Status))
+    {
+        TRACE("failed with status 0x%08lx\n", Status);
+        goto done;
+    }
+
+    /* Set LMPwd attribute*/
+    Status = SampSetObjectAttribute(UserObject,
+                                    L"LMPwd",
+                                    REG_BINARY,
+                                    &EmptyLmHash,
+                                    sizeof(ENCRYPTED_LM_OWF_PASSWORD));
+    if (!NT_SUCCESS(Status))
+    {
+        TRACE("failed with status 0x%08lx\n", Status);
+        goto done;
     }
 
     /* Set NTPwd attribute*/
     Status = SampSetObjectAttribute(UserObject,
                                     L"NTPwd",
                                     REG_BINARY,
-                                    NULL,
-                                    0);
+                                    &EmptyNtHash,
+                                    sizeof(ENCRYPTED_NT_OWF_PASSWORD));
     if (!NT_SUCCESS(Status))
     {
         TRACE("failed with status 0x%08lx\n", Status);
-        return Status;
+        goto done;
     }
 
     /* Set LMPwdHistory attribute*/
@@ -2352,7 +2588,7 @@ SamrCreateUserInDomain(IN SAMPR_HANDLE DomainHandle,
     if (!NT_SUCCESS(Status))
     {
         TRACE("failed with status 0x%08lx\n", Status);
-        return Status;
+        goto done;
     }
 
     /* Set NTPwdHistory attribute*/
@@ -2364,10 +2600,30 @@ SamrCreateUserInDomain(IN SAMPR_HANDLE DomainHandle,
     if (!NT_SUCCESS(Status))
     {
         TRACE("failed with status 0x%08lx\n", Status);
-        return Status;
+        goto done;
+    }
+
+    /* Set the PrivateData attribute */
+    Status = SampSetObjectAttributeString(UserObject,
+                                          L"PrivateData",
+                                          NULL);
+    if (!NT_SUCCESS(Status))
+    {
+        TRACE("failed with status 0x%08lx\n", Status);
+        goto done;
     }
 
-    /* FIXME: Set SecDesc attribute*/
+    /* Set the SecDesc attribute*/
+    Status = SampSetObjectAttribute(UserObject,
+                                    L"SecDesc",
+                                    REG_BINARY,
+                                    Sd,
+                                    SdSize);
+    if (!NT_SUCCESS(Status))
+    {
+        TRACE("failed with status 0x%08lx\n", Status);
+        goto done;
+    }
 
     if (NT_SUCCESS(Status))
     {
@@ -2375,6 +2631,15 @@ SamrCreateUserInDomain(IN SAMPR_HANDLE DomainHandle,
         *RelativeId = ulRid;
     }
 
+done:
+    if (Sd != NULL)
+        RtlFreeHeap(RtlGetProcessHeap(), 0, Sd);
+
+    if (UserSid != NULL)
+        RtlFreeHeap(RtlGetProcessHeap(), 0, UserSid);
+
+    RtlReleaseResource(&SampResource);
+
     TRACE("returns with status 0x%08lx\n", Status);
 
     return Status;
@@ -2410,20 +2675,23 @@ SamrEnumerateUsersInDomain(IN SAMPR_HANDLE DomainHandle,
           DomainHandle, EnumerationContext, UserAccountControl, Buffer,
           PreferedMaximumLength, CountReturned);
 
+    RtlAcquireResourceShared(&SampResource,
+                             TRUE);
+
     /* Validate the domain handle */
     Status = SampValidateDbObject(DomainHandle,
                                   SamDbDomainObject,
                                   DOMAIN_LIST_ACCOUNTS,
                                   &DomainObject);
     if (!NT_SUCCESS(Status))
-        return Status;
+        goto done;
 
     Status = SampRegOpenKey(DomainObject->KeyHandle,
                             L"Users",
                             KEY_READ,
                             &UsersKeyHandle);
     if (!NT_SUCCESS(Status))
-        return Status;
+        goto done;
 
     Status = SampRegOpenKey(UsersKeyHandle,
                             L"Names",
@@ -2572,15 +2840,14 @@ done:
         }
     }
 
-    if (NamesKeyHandle != NULL)
-        SampRegCloseKey(NamesKeyHandle);
-
-    if (UsersKeyHandle != NULL)
-        SampRegCloseKey(UsersKeyHandle);
+    SampRegCloseKey(&NamesKeyHandle);
+    SampRegCloseKey(&UsersKeyHandle);
 
     if ((Status == STATUS_SUCCESS) && (MoreEntries == TRUE))
         Status = STATUS_MORE_ENTRIES;
 
+    RtlReleaseResource(&SampResource);
+
     return Status;
 }
 
@@ -2597,7 +2864,8 @@ SamrCreateAliasInDomain(IN SAMPR_HANDLE DomainHandle,
     SAM_DOMAIN_FIXED_DATA FixedDomainData;
     PSAM_DB_OBJECT DomainObject;
     PSAM_DB_OBJECT AliasObject;
-    UNICODE_STRING EmptyString = RTL_CONSTANT_STRING(L"");
+    PSECURITY_DESCRIPTOR Sd = NULL;
+    ULONG SdSize = 0;
     ULONG ulSize;
     ULONG ulRid;
     WCHAR szRid[9];
@@ -2610,6 +2878,9 @@ SamrCreateAliasInDomain(IN SAMPR_HANDLE DomainHandle,
     RtlMapGenericMask(&DesiredAccess,
                       &AliasMapping);
 
+    RtlAcquireResourceExclusive(&SampResource,
+                                TRUE);
+
     /* Validate the domain handle */
     Status = SampValidateDbObject(DomainHandle,
                                   SamDbDomainObject,
@@ -2618,7 +2889,15 @@ SamrCreateAliasInDomain(IN SAMPR_HANDLE DomainHandle,
     if (!NT_SUCCESS(Status))
     {
         TRACE("failed with status 0x%08lx\n", Status);
-        return Status;
+        goto done;
+    }
+
+    /* Check the alias acoount name */
+    Status = SampCheckAccountName(AccountName, 256);
+    if (!NT_SUCCESS(Status))
+    {
+        TRACE("SampCheckAccountName failed (Status 0x%08lx)\n", Status);
+        goto done;
     }
 
     /* Check if the alias name already exists in the domain */
@@ -2628,7 +2907,16 @@ SamrCreateAliasInDomain(IN SAMPR_HANDLE DomainHandle,
     {
         TRACE("Alias name \'%S\' already exists in domain (Status 0x%08lx)\n",
               AccountName->Buffer, Status);
-        return Status;
+        goto done;
+    }
+
+    /* Create the security descriptor */
+    Status = SampCreateAliasSD(&Sd,
+                               &SdSize);
+    if (!NT_SUCCESS(Status))
+    {
+        TRACE("SampCreateAliasSD failed (Status 0x%08lx)\n", Status);
+        goto done;
     }
 
     /* Get the fixed domain attributes */
@@ -2641,7 +2929,7 @@ SamrCreateAliasInDomain(IN SAMPR_HANDLE DomainHandle,
     if (!NT_SUCCESS(Status))
     {
         TRACE("failed with status 0x%08lx\n", Status);
-        return Status;
+        goto done;
     }
 
     /* Increment the NextRid attribute */
@@ -2657,7 +2945,7 @@ SamrCreateAliasInDomain(IN SAMPR_HANDLE DomainHandle,
     if (!NT_SUCCESS(Status))
     {
         TRACE("failed with status 0x%08lx\n", Status);
-        return Status;
+        goto done;
     }
 
     TRACE("RID: %lx\n", ulRid);
@@ -2676,7 +2964,7 @@ SamrCreateAliasInDomain(IN SAMPR_HANDLE DomainHandle,
     if (!NT_SUCCESS(Status))
     {
         TRACE("failed with status 0x%08lx\n", Status);
-        return Status;
+        goto done;
     }
 
     /* Add the account name for the alias object */
@@ -2687,31 +2975,39 @@ SamrCreateAliasInDomain(IN SAMPR_HANDLE DomainHandle,
     if (!NT_SUCCESS(Status))
     {
         TRACE("failed with status 0x%08lx\n", Status);
-        return Status;
+        goto done;
     }
 
     /* Set the Name attribute */
-    Status = SampSetObjectAttribute(AliasObject,
-                                    L"Name",
-                                    REG_SZ,
-                                    (LPVOID)AccountName->Buffer,
-                                    AccountName->MaximumLength);
+    Status = SampSetObjectAttributeString(AliasObject,
+                                          L"Name",
+                                          AccountName);
     if (!NT_SUCCESS(Status))
     {
         TRACE("failed with status 0x%08lx\n", Status);
-        return Status;
+        goto done;
     }
 
     /* Set the Description attribute */
+    Status = SampSetObjectAttributeString(AliasObject,
+                                          L"Description",
+                                          NULL);
+    if (!NT_SUCCESS(Status))
+    {
+        TRACE("failed with status 0x%08lx\n", Status);
+        goto done;
+    }
+
+    /* Set the SecDesc attribute*/
     Status = SampSetObjectAttribute(AliasObject,
-                                    L"Description",
-                                    REG_SZ,
-                                    EmptyString.Buffer,
-                                    EmptyString.MaximumLength);
+                                    L"SecDesc",
+                                    REG_BINARY,
+                                    Sd,
+                                    SdSize);
     if (!NT_SUCCESS(Status))
     {
         TRACE("failed with status 0x%08lx\n", Status);
-        return Status;
+        goto done;
     }
 
     if (NT_SUCCESS(Status))
@@ -2720,6 +3016,12 @@ SamrCreateAliasInDomain(IN SAMPR_HANDLE DomainHandle,
         *RelativeId = ulRid;
     }
 
+done:
+    if (Sd != NULL)
+        RtlFreeHeap(RtlGetProcessHeap(), 0, Sd);
+
+    RtlReleaseResource(&SampResource);
+
     TRACE("returns with status 0x%08lx\n", Status);
 
     return Status;
@@ -2754,20 +3056,23 @@ SamrEnumerateAliasesInDomain(IN SAMPR_HANDLE DomainHandle,
           DomainHandle, EnumerationContext, Buffer,
           PreferedMaximumLength, CountReturned);
 
+    RtlAcquireResourceShared(&SampResource,
+                             TRUE);
+
     /* Validate the domain handle */
     Status = SampValidateDbObject(DomainHandle,
                                   SamDbDomainObject,
                                   DOMAIN_LIST_ACCOUNTS,
                                   &DomainObject);
     if (!NT_SUCCESS(Status))
-        return Status;
+        goto done;
 
     Status = SampRegOpenKey(DomainObject->KeyHandle,
                             L"Aliases",
                             KEY_READ,
                             &AliasesKeyHandle);
     if (!NT_SUCCESS(Status))
-        return Status;
+        goto done;
 
     Status = SampRegOpenKey(AliasesKeyHandle,
                             L"Names",
@@ -2916,15 +3221,14 @@ done:
         }
     }
 
-    if (NamesKeyHandle != NULL)
-        SampRegCloseKey(NamesKeyHandle);
-
-    if (AliasesKeyHandle != NULL)
-        SampRegCloseKey(AliasesKeyHandle);
+    SampRegCloseKey(&NamesKeyHandle);
+    SampRegCloseKey(&AliasesKeyHandle);
 
     if ((Status == STATUS_SUCCESS) && (MoreEntries == TRUE))
         Status = STATUS_MORE_ENTRIES;
 
+    RtlReleaseResource(&SampResource);
+
     return Status;
 }
 
@@ -2946,19 +3250,23 @@ SamrGetAliasMembership(IN SAMPR_HANDLE DomainHandle,
     ULONG ValueCount;
     ULONG DataLength;
     ULONG i, j;
+    ULONG RidIndex;
     NTSTATUS Status;
     WCHAR NameBuffer[9];
 
     TRACE("SamrGetAliasMembership(%p %p %p)\n",
           DomainHandle, SidArray, Membership);
 
+    RtlAcquireResourceShared(&SampResource,
+                             TRUE);
+
     /* Validate the domain handle */
     Status = SampValidateDbObject(DomainHandle,
                                   SamDbDomainObject,
                                   DOMAIN_GET_ALIAS_MEMBERSHIP,
                                   &DomainObject);
     if (!NT_SUCCESS(Status))
-        return Status;
+        goto done;
 
     Status = SampRegOpenKey(DomainObject->KeyHandle,
                             L"Aliases",
@@ -3004,7 +3312,7 @@ TRACE("Open %S\n", MemberSidString);
                 MaxSidCount += ValueCount;
             }
 
-            NtClose(MemberKeyHandle);
+            SampRegCloseKey(&MemberKeyHandle);
         }
 
         if (Status == STATUS_OBJECT_NAME_NOT_FOUND)
@@ -3027,6 +3335,7 @@ TRACE("Open %S\n", MemberSidString);
         goto done;
     }
 
+    RidIndex = 0;
     for (i = 0; i < SidArray->Count; i++)
     {
         ConvertSidToStringSid(SidArray->Sids[i].SidPointer, &MemberSidString);
@@ -3058,18 +3367,23 @@ TRACE("Open %S\n", MemberSidString);
                                                    NULL);
                     if (NT_SUCCESS(Status))
                     {
-                        RidArray[j] = wcstoul(NameBuffer, NULL, 16);
+                        /* FIXME: Do not return each RID more than once. */
+                        RidArray[RidIndex] = wcstoul(NameBuffer, NULL, 16);
+                        RidIndex++;
                     }
                 }
             }
 
-            NtClose(MemberKeyHandle);
+            SampRegCloseKey(&MemberKeyHandle);
         }
 
         LocalFree(MemberSidString);
     }
 
 done:
+    SampRegCloseKey(&MembersKeyHandle);
+    SampRegCloseKey(&AliasesKeyHandle);
+
     if (NT_SUCCESS(Status))
     {
         Membership->Count = MaxSidCount;
@@ -3081,14 +3395,7 @@ done:
             midl_user_free(RidArray);
     }
 
-    if (MembersKeyHandle != NULL)
-        NtClose(MembersKeyHandle);
-
-    if (MembersKeyHandle != NULL)
-        NtClose(MembersKeyHandle);
-
-    if (AliasesKeyHandle != NULL)
-        NtClose(AliasesKeyHandle);
+    RtlReleaseResource(&SampResource);
 
     return Status;
 }
@@ -3104,8 +3411,8 @@ SamrLookupNamesInDomain(IN SAMPR_HANDLE DomainHandle,
                         OUT PSAMPR_ULONG_ARRAY Use)
 {
     PSAM_DB_OBJECT DomainObject;
-    HANDLE AccountsKeyHandle;
-    HANDLE NamesKeyHandle;
+    HANDLE AccountsKeyHandle = NULL;
+    HANDLE NamesKeyHandle = NULL;
     ULONG MappedCount = 0;
     ULONG DataLength;
     ULONG i;
@@ -3115,6 +3422,9 @@ SamrLookupNamesInDomain(IN SAMPR_HANDLE DomainHandle,
     TRACE("SamrLookupNamesInDomain(%p %lu %p %p %p)\n",
           DomainHandle, Count, Names, RelativeIds, Use);
 
+    RtlAcquireResourceShared(&SampResource,
+                             TRUE);
+
     /* Validate the domain handle */
     Status = SampValidateDbObject(DomainHandle,
                                   SamDbDomainObject,
@@ -3123,14 +3433,17 @@ SamrLookupNamesInDomain(IN SAMPR_HANDLE DomainHandle,
     if (!NT_SUCCESS(Status))
     {
         TRACE("failed with status 0x%08lx\n", Status);
-        return Status;
+        goto done;
     }
 
     RelativeIds->Count = 0;
     Use->Count = 0;
 
     if (Count == 0)
-        return STATUS_SUCCESS;
+    {
+        Status = STATUS_SUCCESS;
+        goto done;
+    }
 
     /* Allocate the relative IDs array */
     RelativeIds->Element = midl_user_allocate(Count * sizeof(ULONG));
@@ -3177,10 +3490,10 @@ SamrLookupNamesInDomain(IN SAMPR_HANDLE DomainHandle,
                                            &RelativeId,
                                            &DataLength);
 
-                SampRegCloseKey(NamesKeyHandle);
+                SampRegCloseKey(&NamesKeyHandle);
             }
 
-            SampRegCloseKey(AccountsKeyHandle);
+            SampRegCloseKey(&AccountsKeyHandle);
         }
 
         if (!NT_SUCCESS(Status) && Status != STATUS_OBJECT_NAME_NOT_FOUND)
@@ -3216,10 +3529,10 @@ SamrLookupNamesInDomain(IN SAMPR_HANDLE DomainHandle,
                                            &RelativeId,
                                            &DataLength);
 
-                SampRegCloseKey(NamesKeyHandle);
+                SampRegCloseKey(&NamesKeyHandle);
             }
 
-            SampRegCloseKey(AccountsKeyHandle);
+            SampRegCloseKey(&AccountsKeyHandle);
         }
 
         if (!NT_SUCCESS(Status) && Status != STATUS_OBJECT_NAME_NOT_FOUND)
@@ -3255,10 +3568,10 @@ SamrLookupNamesInDomain(IN SAMPR_HANDLE DomainHandle,
                                            &RelativeId,
                                            &DataLength);
 
-                SampRegCloseKey(NamesKeyHandle);
+                SampRegCloseKey(&NamesKeyHandle);
             }
 
-            SampRegCloseKey(AccountsKeyHandle);
+            SampRegCloseKey(&AccountsKeyHandle);
         }
 
         if (!NT_SUCCESS(Status) && Status != STATUS_OBJECT_NAME_NOT_FOUND)
@@ -3309,6 +3622,8 @@ done:
         Use->Count = 0;
     }
 
+    RtlReleaseResource(&SampResource);
+
     TRACE("Returned Status %lx\n", Status);
 
     return Status;
@@ -3326,8 +3641,8 @@ SamrLookupIdsInDomain(IN SAMPR_HANDLE DomainHandle,
 {
     PSAM_DB_OBJECT DomainObject;
     WCHAR RidString[9];
-    HANDLE AccountsKeyHandle;
-    HANDLE AccountKeyHandle;
+    HANDLE AccountsKeyHandle = NULL;
+    HANDLE AccountKeyHandle = NULL;
     ULONG MappedCount = 0;
     ULONG DataLength;
     ULONG i;
@@ -3336,6 +3651,9 @@ SamrLookupIdsInDomain(IN SAMPR_HANDLE DomainHandle,
     TRACE("SamrLookupIdsInDomain(%p %lu %p %p %p)\n",
           DomainHandle, Count, RelativeIds, Names, Use);
 
+    RtlAcquireResourceShared(&SampResource,
+                             TRUE);
+
     /* Validate the domain handle */
     Status = SampValidateDbObject(DomainHandle,
                                   SamDbDomainObject,
@@ -3344,17 +3662,20 @@ SamrLookupIdsInDomain(IN SAMPR_HANDLE DomainHandle,
     if (!NT_SUCCESS(Status))
     {
         TRACE("failed with status 0x%08lx\n", Status);
-        return Status;
+        goto done;
     }
 
     Names->Count = 0;
     Use->Count = 0;
 
     if (Count == 0)
-        return STATUS_SUCCESS;
+    {
+        Status = STATUS_SUCCESS;
+        goto done;
+    }
 
     /* Allocate the names array */
-    Names->Element = midl_user_allocate(Count * sizeof(ULONG));
+    Names->Element = midl_user_allocate(Count * sizeof(*Names->Element));
     if (Names->Element == NULL)
     {
         Status = STATUS_INSUFFICIENT_RESOURCES;
@@ -3362,7 +3683,7 @@ SamrLookupIdsInDomain(IN SAMPR_HANDLE DomainHandle,
     }
 
     /* Allocate the use array */
-    Use->Element = midl_user_allocate(Count * sizeof(ULONG));
+    Use->Element = midl_user_allocate(Count * sizeof(*Use->Element));
     if (Use->Element == NULL)
     {
         Status = STATUS_INSUFFICIENT_RESOURCES;
@@ -3416,10 +3737,10 @@ SamrLookupIdsInDomain(IN SAMPR_HANDLE DomainHandle,
                     }
                 }
 
-                SampRegCloseKey(AccountKeyHandle);
+                SampRegCloseKey(&AccountKeyHandle);
             }
 
-            SampRegCloseKey(AccountsKeyHandle);
+            SampRegCloseKey(&AccountsKeyHandle);
         }
 
         if (!NT_SUCCESS(Status) && Status != STATUS_OBJECT_NAME_NOT_FOUND)
@@ -3472,10 +3793,10 @@ SamrLookupIdsInDomain(IN SAMPR_HANDLE DomainHandle,
                     }
                 }
 
-                SampRegCloseKey(AccountKeyHandle);
+                SampRegCloseKey(&AccountKeyHandle);
             }
 
-            SampRegCloseKey(AccountsKeyHandle);
+            SampRegCloseKey(&AccountsKeyHandle);
         }
 
         if (!NT_SUCCESS(Status) && Status != STATUS_OBJECT_NAME_NOT_FOUND)
@@ -3530,10 +3851,10 @@ SamrLookupIdsInDomain(IN SAMPR_HANDLE DomainHandle,
                     }
                 }
 
-                SampRegCloseKey(AccountKeyHandle);
+                SampRegCloseKey(&AccountKeyHandle);
             }
 
-            SampRegCloseKey(AccountsKeyHandle);
+            SampRegCloseKey(&AccountsKeyHandle);
         }
 
         if (!NT_SUCCESS(Status) && Status != STATUS_OBJECT_NAME_NOT_FOUND)
@@ -3588,6 +3909,8 @@ done:
         Use->Count = 0;
     }
 
+    RtlReleaseResource(&SampResource);
+
     return Status;
 }
 
@@ -3612,6 +3935,9 @@ SamrOpenGroup(IN SAMPR_HANDLE DomainHandle,
     RtlMapGenericMask(&DesiredAccess,
                       &GroupMapping);
 
+    RtlAcquireResourceShared(&SampResource,
+                             TRUE);
+
     /* Validate the domain handle */
     Status = SampValidateDbObject(DomainHandle,
                                   SamDbDomainObject,
@@ -3620,7 +3946,7 @@ SamrOpenGroup(IN SAMPR_HANDLE DomainHandle,
     if (!NT_SUCCESS(Status))
     {
         TRACE("failed with status 0x%08lx\n", Status);
-        return Status;
+        goto done;
     }
 
     /* Convert the RID into a string (hex) */
@@ -3637,12 +3963,15 @@ SamrOpenGroup(IN SAMPR_HANDLE DomainHandle,
     if (!NT_SUCCESS(Status))
     {
         TRACE("failed with status 0x%08lx\n", Status);
-        return Status;
+        goto done;
     }
 
     *GroupHandle = (SAMPR_HANDLE)GroupObject;
 
-    return STATUS_SUCCESS;
+done:
+    RtlReleaseResource(&SampResource);
+
+    return Status;
 }
 
 
@@ -3859,13 +4188,16 @@ SamrQueryInformationGroup(IN SAMPR_HANDLE GroupHandle,
     TRACE("SamrQueryInformationGroup(%p %lu %p)\n",
           GroupHandle, GroupInformationClass, Buffer);
 
+    RtlAcquireResourceShared(&SampResource,
+                             TRUE);
+
     /* Validate the group handle */
     Status = SampValidateDbObject(GroupHandle,
                                   SamDbGroupObject,
                                   GROUP_READ_INFORMATION,
                                   &GroupObject);
     if (!NT_SUCCESS(Status))
-        return Status;
+        goto done;
 
     switch (GroupInformationClass)
     {
@@ -3894,6 +4226,85 @@ SamrQueryInformationGroup(IN SAMPR_HANDLE GroupHandle,
             break;
     }
 
+done:
+    RtlReleaseResource(&SampResource);
+
+    return Status;
+}
+
+
+static NTSTATUS
+SampSetGroupName(PSAM_DB_OBJECT GroupObject,
+                 PSAMPR_GROUP_INFO_BUFFER Buffer)
+{
+    UNICODE_STRING OldGroupName = {0, 0, NULL};
+    UNICODE_STRING NewGroupName;
+    NTSTATUS Status;
+
+    Status = SampGetObjectAttributeString(GroupObject,
+                                          L"Name",
+                                          (PRPC_UNICODE_STRING)&OldGroupName);
+    if (!NT_SUCCESS(Status))
+    {
+        TRACE("SampGetObjectAttributeString failed (Status 0x%08lx)\n", Status);
+        goto done;
+    }
+
+    /* Check the new account name */
+    Status = SampCheckAccountName(&Buffer->Name.Name, 256);
+    if (!NT_SUCCESS(Status))
+    {
+        TRACE("SampCheckAccountName failed (Status 0x%08lx)\n", Status);
+        return Status;
+    }
+
+    NewGroupName.Length = Buffer->Name.Name.Length;
+    NewGroupName.MaximumLength = Buffer->Name.Name.MaximumLength;
+    NewGroupName.Buffer = Buffer->Name.Name.Buffer;
+
+    if (!RtlEqualUnicodeString(&OldGroupName, &NewGroupName, TRUE))
+    {
+        Status = SampCheckAccountNameInDomain(GroupObject->ParentObject,
+                                              NewGroupName.Buffer);
+        if (!NT_SUCCESS(Status))
+        {
+            TRACE("Group name \'%S\' already exists in domain (Status 0x%08lx)\n",
+                  NewGroupName.Buffer, Status);
+            goto done;
+        }
+    }
+
+    Status = SampSetAccountNameInDomain(GroupObject->ParentObject,
+                                        L"Groups",
+                                        NewGroupName.Buffer,
+                                        GroupObject->RelativeId);
+    if (!NT_SUCCESS(Status))
+    {
+        TRACE("SampSetAccountNameInDomain failed (Status 0x%08lx)\n", Status);
+        goto done;
+    }
+
+    Status = SampRemoveAccountNameFromDomain(GroupObject->ParentObject,
+                                             L"Groups",
+                                             OldGroupName.Buffer);
+    if (!NT_SUCCESS(Status))
+    {
+        TRACE("SampRemoveAccountNameFromDomain failed (Status 0x%08lx)\n", Status);
+        goto done;
+    }
+
+    Status = SampSetObjectAttributeString(GroupObject,
+                                          L"Name",
+                                          (PRPC_UNICODE_STRING)&NewGroupName);
+    if (!NT_SUCCESS(Status))
+    {
+        TRACE("SampSetObjectAttribute failed (Status 0x%08lx)\n", Status);
+    }
+
+done:
+    if (OldGroupName.Buffer != NULL)
+        midl_user_free(OldGroupName.Buffer);
+
     return Status;
 }
 
@@ -3941,22 +4352,22 @@ SamrSetInformationGroup(IN SAMPR_HANDLE GroupHandle,
     TRACE("SamrSetInformationGroup(%p %lu %p)\n",
           GroupHandle, GroupInformationClass, Buffer);
 
+    RtlAcquireResourceExclusive(&SampResource,
+                                TRUE);
+
     /* Validate the group handle */
     Status = SampValidateDbObject(GroupHandle,
                                   SamDbGroupObject,
                                   GROUP_WRITE_ACCOUNT,
                                   &GroupObject);
     if (!NT_SUCCESS(Status))
-        return Status;
+        goto done;
 
     switch (GroupInformationClass)
     {
         case GroupNameInformation:
-            Status = SampSetObjectAttribute(GroupObject,
-                                            L"Name",
-                                            REG_SZ,
-                                            Buffer->Name.Name.Buffer,
-                                            Buffer->Name.Name.Length + sizeof(WCHAR));
+            Status = SampSetGroupName(GroupObject,
+                                      Buffer);
             break;
 
         case GroupAttributeInformation:
@@ -3965,11 +4376,9 @@ SamrSetInformationGroup(IN SAMPR_HANDLE GroupHandle,
             break;
 
         case GroupAdminCommentInformation:
-            Status = SampSetObjectAttribute(GroupObject,
-                                            L"Description",
-                                            REG_SZ,
-                                            Buffer->AdminComment.AdminComment.Buffer,
-                                            Buffer->AdminComment.AdminComment.Length + sizeof(WCHAR));
+            Status = SampSetObjectAttributeString(GroupObject,
+                                                  L"Description",
+                                                  &Buffer->AdminComment.AdminComment);
             break;
 
         default:
@@ -3977,6 +4386,9 @@ SamrSetInformationGroup(IN SAMPR_HANDLE GroupHandle,
             break;
     }
 
+done:
+    RtlReleaseResource(&SampResource);
+
     return Status;
 }
 
@@ -3995,13 +4407,16 @@ SamrAddMemberToGroup(IN SAMPR_HANDLE GroupHandle,
     TRACE("(%p %lu %lx)\n",
           GroupHandle, MemberId, Attributes);
 
+    RtlAcquireResourceExclusive(&SampResource,
+                                TRUE);
+
     /* Validate the group handle */
     Status = SampValidateDbObject(GroupHandle,
                                   SamDbGroupObject,
                                   GROUP_ADD_MEMBER,
                                   &GroupObject);
     if (!NT_SUCCESS(Status))
-        return Status;
+        goto done;
 
     /* Open the user object in the same domain */
     Status = SampOpenUserObject(GroupObject->ParentObject,
@@ -4036,11 +4451,13 @@ done:
     if (UserObject)
         SampCloseDbObject(UserObject);
 
+    RtlReleaseResource(&SampResource);
+
     return Status;
 }
 
 
-/* Function 21 */
+/* Function 23 */
 NTSTATUS
 NTAPI
 SamrDeleteGroup(IN OUT SAMPR_HANDLE *GroupHandle)
@@ -4051,6 +4468,9 @@ SamrDeleteGroup(IN OUT SAMPR_HANDLE *GroupHandle)
 
     TRACE("(%p)\n", GroupHandle);
 
+    RtlAcquireResourceExclusive(&SampResource,
+                                TRUE);
+
     /* Validate the group handle */
     Status = SampValidateDbObject(*GroupHandle,
                                   SamDbGroupObject,
@@ -4059,14 +4479,15 @@ SamrDeleteGroup(IN OUT SAMPR_HANDLE *GroupHandle)
     if (!NT_SUCCESS(Status))
     {
         TRACE("SampValidateDbObject() failed (Status 0x%08lx)\n", Status);
-        return Status;
+        goto done;
     }
 
     /* Fail, if the group is built-in */
     if (GroupObject->RelativeId < 1000)
     {
         TRACE("You can not delete a special account!\n");
-        return STATUS_SPECIAL_ACCOUNT;
+        Status = STATUS_SPECIAL_ACCOUNT;
+        goto done;
     }
 
     /* Get the length of the Members attribute */
@@ -4080,7 +4501,8 @@ SamrDeleteGroup(IN OUT SAMPR_HANDLE *GroupHandle)
     if (Length != 0)
     {
         TRACE("There are still members in the group!\n");
-        return STATUS_MEMBER_IN_GROUP;
+        Status = STATUS_MEMBER_IN_GROUP;
+        goto done;
     }
 
     /* FIXME: Remove the group from all aliases */
@@ -4090,13 +4512,16 @@ SamrDeleteGroup(IN OUT SAMPR_HANDLE *GroupHandle)
     if (!NT_SUCCESS(Status))
     {
         TRACE("SampDeleteAccountDbObject() failed (Status 0x%08lx)\n", Status);
-        return Status;
+        goto done;
     }
 
     /* Invalidate the handle */
     *GroupHandle = NULL;
 
-    return STATUS_SUCCESS;
+done:
+    RtlReleaseResource(&SampResource);
+
+    return Status;
 }
 
 
@@ -4113,13 +4538,16 @@ SamrRemoveMemberFromGroup(IN SAMPR_HANDLE GroupHandle,
     TRACE("(%p %lu)\n",
           GroupHandle, MemberId);
 
+    RtlAcquireResourceExclusive(&SampResource,
+                                TRUE);
+
     /* Validate the group handle */
     Status = SampValidateDbObject(GroupHandle,
                                   SamDbGroupObject,
                                   GROUP_REMOVE_MEMBER,
                                   &GroupObject);
     if (!NT_SUCCESS(Status))
-        return Status;
+        goto done;
 
     /* Open the user object in the same domain */
     Status = SampOpenUserObject(GroupObject->ParentObject,
@@ -4153,6 +4581,8 @@ done:
     if (UserObject)
         SampCloseDbObject(UserObject);
 
+    RtlReleaseResource(&SampResource);
+
     return Status;
 }
 
@@ -4169,17 +4599,23 @@ SamrGetMembersInGroup(IN SAMPR_HANDLE GroupHandle,
     ULONG i;
     NTSTATUS Status;
 
+    RtlAcquireResourceShared(&SampResource,
+                             TRUE);
+
     /* Validate the group handle */
     Status = SampValidateDbObject(GroupHandle,
                                   SamDbGroupObject,
                                   GROUP_LIST_MEMBERS,
                                   &GroupObject);
     if (!NT_SUCCESS(Status))
-        return Status;
+        goto done;
 
     MembersBuffer = midl_user_allocate(sizeof(SAMPR_GET_MEMBERS_BUFFER));
     if (MembersBuffer == NULL)
-        return STATUS_INSUFFICIENT_RESOURCES;
+    {
+        Status = STATUS_INSUFFICIENT_RESOURCES;
+        goto done;
+    }
 
     SampGetObjectAttribute(GroupObject,
                            L"Members",
@@ -4195,7 +4631,8 @@ SamrGetMembersInGroup(IN SAMPR_HANDLE GroupHandle,
 
         *Members = MembersBuffer;
 
-        return STATUS_SUCCESS;
+        Status = STATUS_SUCCESS;
+        goto done;
     }
 
     MembersBuffer->Members = midl_user_allocate(Length);
@@ -4255,6 +4692,8 @@ done:
         }
     }
 
+    RtlReleaseResource(&SampResource);
+
     return Status;
 }
 
@@ -4269,6 +4708,9 @@ SamrSetMemberAttributesOfGroup(IN SAMPR_HANDLE GroupHandle,
     PSAM_DB_OBJECT GroupObject;
     NTSTATUS Status;
 
+    RtlAcquireResourceExclusive(&SampResource,
+                                TRUE);
+
     /* Validate the group handle */
     Status = SampValidateDbObject(GroupHandle,
                                   SamDbGroupObject,
@@ -4277,7 +4719,7 @@ SamrSetMemberAttributesOfGroup(IN SAMPR_HANDLE GroupHandle,
     if (!NT_SUCCESS(Status))
     {
         TRACE("SampValidateDbObject failed with status 0x%08lx\n", Status);
-        return Status;
+        goto done;
     }
 
     Status = SampSetUserGroupAttributes(GroupObject->ParentObject,
@@ -4289,6 +4731,9 @@ SamrSetMemberAttributesOfGroup(IN SAMPR_HANDLE GroupHandle,
         TRACE("SampSetUserGroupAttributes failed with status 0x%08lx\n", Status);
     }
 
+done:
+    RtlReleaseResource(&SampResource);
+
     return Status;
 }
 
@@ -4313,6 +4758,9 @@ SamrOpenAlias(IN SAMPR_HANDLE DomainHandle,
     RtlMapGenericMask(&DesiredAccess,
                       &AliasMapping);
 
+    RtlAcquireResourceShared(&SampResource,
+                             TRUE);
+
     /* Validate the domain handle */
     Status = SampValidateDbObject(DomainHandle,
                                   SamDbDomainObject,
@@ -4321,7 +4769,7 @@ SamrOpenAlias(IN SAMPR_HANDLE DomainHandle,
     if (!NT_SUCCESS(Status))
     {
         TRACE("failed with status 0x%08lx\n", Status);
-        return Status;
+        goto done;
     }
 
     /* Convert the RID into a string (hex) */
@@ -4338,12 +4786,15 @@ SamrOpenAlias(IN SAMPR_HANDLE DomainHandle,
     if (!NT_SUCCESS(Status))
     {
         TRACE("failed with status 0x%08lx\n", Status);
-        return Status;
+        goto done;
     }
 
     *AliasHandle = (SAMPR_HANDLE)AliasObject;
 
-    return STATUS_SUCCESS;
+done:
+    RtlReleaseResource(&SampResource);
+
+    return Status;
 }
 
 
@@ -4410,8 +4861,7 @@ SampQueryAliasGeneral(PSAM_DB_OBJECT AliasObject,
     *Buffer = InfoBuffer;
 
 done:
-    if (MembersKeyHandle != NULL)
-        SampRegCloseKey(MembersKeyHandle);
+    SampRegCloseKey(&MembersKeyHandle);
 
     if (!NT_SUCCESS(Status))
     {
@@ -4524,13 +4974,16 @@ SamrQueryInformationAlias(IN SAMPR_HANDLE AliasHandle,
     TRACE("SamrQueryInformationAlias(%p %lu %p)\n",
           AliasHandle, AliasInformationClass, Buffer);
 
+    RtlAcquireResourceShared(&SampResource,
+                             TRUE);
+
     /* Validate the alias handle */
     Status = SampValidateDbObject(AliasHandle,
                                   SamDbAliasObject,
                                   ALIAS_READ_INFORMATION,
                                   &AliasObject);
     if (!NT_SUCCESS(Status))
-        return Status;
+        goto done;
 
     switch (AliasInformationClass)
     {
@@ -4554,22 +5007,104 @@ SamrQueryInformationAlias(IN SAMPR_HANDLE AliasHandle,
             break;
     }
 
+done:
+    RtlReleaseResource(&SampResource);
+
     return Status;
 }
 
 
-/* Function 29 */
-NTSTATUS
-NTAPI
-SamrSetInformationAlias(IN SAMPR_HANDLE AliasHandle,
-                        IN ALIAS_INFORMATION_CLASS AliasInformationClass,
-                        IN PSAMPR_ALIAS_INFO_BUFFER Buffer)
+static NTSTATUS
+SampSetAliasName(PSAM_DB_OBJECT AliasObject,
+                 PSAMPR_ALIAS_INFO_BUFFER Buffer)
 {
-    PSAM_DB_OBJECT AliasObject;
+    UNICODE_STRING OldAliasName = {0, 0, NULL};
+    UNICODE_STRING NewAliasName;
     NTSTATUS Status;
 
-    TRACE("SamrSetInformationAlias(%p %lu %p)\n",
-          AliasHandle, AliasInformationClass, Buffer);
+    Status = SampGetObjectAttributeString(AliasObject,
+                                          L"Name",
+                                          (PRPC_UNICODE_STRING)&OldAliasName);
+    if (!NT_SUCCESS(Status))
+    {
+        TRACE("SampGetObjectAttributeString failed (Status 0x%08lx)\n", Status);
+        goto done;
+    }
+
+    /* Check the new account name */
+    Status = SampCheckAccountName(&Buffer->Name.Name, 256);
+    if (!NT_SUCCESS(Status))
+    {
+        TRACE("SampCheckAccountName failed (Status 0x%08lx)\n", Status);
+        return Status;
+    }
+
+    NewAliasName.Length = Buffer->Name.Name.Length;
+    NewAliasName.MaximumLength = Buffer->Name.Name.MaximumLength;
+    NewAliasName.Buffer = Buffer->Name.Name.Buffer;
+
+    if (!RtlEqualUnicodeString(&OldAliasName, &NewAliasName, TRUE))
+    {
+        Status = SampCheckAccountNameInDomain(AliasObject->ParentObject,
+                                              NewAliasName.Buffer);
+        if (!NT_SUCCESS(Status))
+        {
+            TRACE("Alias name \'%S\' already exists in domain (Status 0x%08lx)\n",
+                  NewAliasName.Buffer, Status);
+            goto done;
+        }
+    }
+
+    Status = SampSetAccountNameInDomain(AliasObject->ParentObject,
+                                        L"Aliases",
+                                        NewAliasName.Buffer,
+                                        AliasObject->RelativeId);
+    if (!NT_SUCCESS(Status))
+    {
+        TRACE("SampSetAccountNameInDomain failed (Status 0x%08lx)\n", Status);
+        goto done;
+    }
+
+    Status = SampRemoveAccountNameFromDomain(AliasObject->ParentObject,
+                                             L"Aliases",
+                                             OldAliasName.Buffer);
+    if (!NT_SUCCESS(Status))
+    {
+        TRACE("SampRemoveAccountNameFromDomain failed (Status 0x%08lx)\n", Status);
+        goto done;
+    }
+
+    Status = SampSetObjectAttributeString(AliasObject,
+                                          L"Name",
+                                          (PRPC_UNICODE_STRING)&NewAliasName);
+    if (!NT_SUCCESS(Status))
+    {
+        TRACE("SampSetObjectAttribute failed (Status 0x%08lx)\n", Status);
+    }
+
+done:
+    if (OldAliasName.Buffer != NULL)
+        midl_user_free(OldAliasName.Buffer);
+
+    return Status;
+}
+
+
+/* Function 29 */
+NTSTATUS
+NTAPI
+SamrSetInformationAlias(IN SAMPR_HANDLE AliasHandle,
+                        IN ALIAS_INFORMATION_CLASS AliasInformationClass,
+                        IN PSAMPR_ALIAS_INFO_BUFFER Buffer)
+{
+    PSAM_DB_OBJECT AliasObject;
+    NTSTATUS Status;
+
+    TRACE("SamrSetInformationAlias(%p %lu %p)\n",
+          AliasHandle, AliasInformationClass, Buffer);
+
+    RtlAcquireResourceExclusive(&SampResource,
+                                TRUE);
 
     /* Validate the alias handle */
     Status = SampValidateDbObject(AliasHandle,
@@ -4577,24 +5112,19 @@ SamrSetInformationAlias(IN SAMPR_HANDLE AliasHandle,
                                   ALIAS_WRITE_ACCOUNT,
                                   &AliasObject);
     if (!NT_SUCCESS(Status))
-        return Status;
+        goto done;
 
     switch (AliasInformationClass)
     {
         case AliasNameInformation:
-            Status = SampSetObjectAttribute(AliasObject,
-                                            L"Name",
-                                            REG_SZ,
-                                            Buffer->Name.Name.Buffer,
-                                            Buffer->Name.Name.Length + sizeof(WCHAR));
+            Status = SampSetAliasName(AliasObject,
+                                      Buffer);
             break;
 
         case AliasAdminCommentInformation:
-            Status = SampSetObjectAttribute(AliasObject,
-                                            L"Description",
-                                            REG_SZ,
-                                            Buffer->AdminComment.AdminComment.Buffer,
-                                            Buffer->AdminComment.AdminComment.Length + sizeof(WCHAR));
+            Status = SampSetObjectAttributeString(AliasObject,
+                                                  L"Description",
+                                                  &Buffer->AdminComment.AdminComment);
             break;
 
         default:
@@ -4602,6 +5132,9 @@ SamrSetInformationAlias(IN SAMPR_HANDLE AliasHandle,
             break;
     }
 
+done:
+    RtlReleaseResource(&SampResource);
+
     return Status;
 }
 
@@ -4614,37 +5147,50 @@ SamrDeleteAlias(IN OUT SAMPR_HANDLE *AliasHandle)
     PSAM_DB_OBJECT AliasObject;
     NTSTATUS Status;
 
+    RtlAcquireResourceExclusive(&SampResource,
+                                TRUE);
+
     /* Validate the alias handle */
-    Status = SampValidateDbObject(AliasHandle,
+    Status = SampValidateDbObject(*AliasHandle,
                                   SamDbAliasObject,
                                   DELETE,
                                   &AliasObject);
     if (!NT_SUCCESS(Status))
     {
-        TRACE("failed with status 0x%08lx\n", Status);
-        return Status;
+        TRACE("SampValidateDbObject failed (Status 0x%08lx)\n", Status);
+        goto done;
     }
 
     /* Fail, if the alias is built-in */
     if (AliasObject->RelativeId < 1000)
     {
         TRACE("You can not delete a special account!\n");
-        return STATUS_SPECIAL_ACCOUNT;
+        Status = STATUS_SPECIAL_ACCOUNT;
+        goto done;
     }
 
-    /* FIXME: Remove all members from the alias */
+    /* Remove all members from the alias */
+    Status = SampRemoveAllMembersFromAlias(AliasObject);
+    if (!NT_SUCCESS(Status))
+    {
+        TRACE("SampRemoveAllMembersFromAlias() failed (Status 0x%08lx)\n", Status);
+        goto done;
+    }
 
     /* Delete the alias from the database */
     Status = SampDeleteAccountDbObject(AliasObject);
     if (!NT_SUCCESS(Status))
     {
         TRACE("SampDeleteAccountDbObject() failed (Status 0x%08lx)\n", Status);
-        return Status;
+        goto done;
     }
 
     /* Invalidate the handle */
     *AliasHandle = NULL;
 
+done:
+    RtlReleaseResource(&SampResource);
+
     return Status;
 }
 
@@ -4656,14 +5202,12 @@ SamrAddMemberToAlias(IN SAMPR_HANDLE AliasHandle,
                      IN PRPC_SID MemberId)
 {
     PSAM_DB_OBJECT AliasObject;
-    LPWSTR MemberIdString = NULL;
-    HANDLE MembersKeyHandle = NULL;
-    HANDLE MemberKeyHandle = NULL;
-    ULONG MemberIdLength;
     NTSTATUS Status;
 
-    TRACE("SamrAddMemberToAlias(%p %p)\n",
-          AliasHandle, MemberId);
+    TRACE("(%p %p)\n", AliasHandle, MemberId);
+
+    RtlAcquireResourceExclusive(&SampResource,
+                                TRUE);
 
     /* Validate the alias handle */
     Status = SampValidateDbObject(AliasHandle,
@@ -4673,65 +5217,18 @@ SamrAddMemberToAlias(IN SAMPR_HANDLE AliasHandle,
     if (!NT_SUCCESS(Status))
     {
         TRACE("failed with status 0x%08lx\n", Status);
-        return Status;
-    }
-
-    ConvertSidToStringSidW(MemberId, &MemberIdString);
-    TRACE("Member SID: %S\n", MemberIdString);
-
-    MemberIdLength = RtlLengthSid(MemberId);
-
-    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);
+    Status = SampAddMemberToAlias(AliasObject,
+                                  MemberId);
     if (!NT_SUCCESS(Status))
     {
-        TRACE("SampRegSetValue failed with status 0x%08lx\n", Status);
-        goto done;
+        TRACE("failed with status 0x%08lx\n", Status);
     }
 
 done:
-    if (MemberKeyHandle != NULL)
-        SampRegCloseKey(MemberKeyHandle);
-
-    if (MembersKeyHandle != NULL)
-        SampRegCloseKey(MembersKeyHandle);
-
-    if (MemberIdString != NULL)
-        LocalFree(MemberIdString);
+    RtlReleaseResource(&SampResource);
 
     return Status;
 }
@@ -4744,14 +5241,12 @@ 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);
+    TRACE("(%p %p)\n", AliasHandle, MemberId);
+
+    RtlAcquireResourceExclusive(&SampResource,
+                                TRUE);
 
     /* Validate the alias handle */
     Status = SampValidateDbObject(AliasHandle,
@@ -4761,103 +5256,18 @@ SamrRemoveMemberFromAlias(IN SAMPR_HANDLE AliasHandle,
     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("SampRegOpenKey failed with status 0x%08lx\n", Status);
-        goto done;
-    }
-
-    Status = SampRegDeleteValue(MembersKeyHandle,
-                                MemberIdString);
-    if (!NT_SUCCESS(Status))
-    {
-        TRACE("SampRegDeleteValue failed with status 0x%08lx\n", Status);
         goto done;
     }
 
-    Status = SampRegQueryKeyInfo(MembersKeyHandle,
-                                 NULL,
-                                 &ulValueCount);
+    Status = SampRemoveMemberFromAlias(AliasObject,
+                                       MemberId);
     if (!NT_SUCCESS(Status))
     {
-        TRACE("SampRegQueryKeyInfo failed with status 0x%08lx\n", Status);
-        goto done;
-    }
-
-    if (ulValueCount == 0)
-    {
-        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;
-        }
+        TRACE("failed with status 0x%08lx\n", Status);
     }
 
 done:
-    if (MemberKeyHandle != NULL)
-        SampRegCloseKey(MemberKeyHandle);
-
-    if (MembersKeyHandle != NULL)
-        SampRegCloseKey(MembersKeyHandle);
-
-    if (MemberIdString != NULL)
-        LocalFree(MemberIdString);
+    RtlReleaseResource(&SampResource);
 
     return Status;
 }
@@ -4870,16 +5280,17 @@ SamrGetMembersInAlias(IN SAMPR_HANDLE AliasHandle,
                       OUT PSAMPR_PSID_ARRAY_OUT Members)
 {
     PSAM_DB_OBJECT AliasObject;
-    HANDLE MembersKeyHandle = NULL;
     PSAMPR_SID_INFORMATION MemberArray = NULL;
-    ULONG ValueCount = 0;
-    ULONG DataLength;
+    ULONG MemberCount = 0;
     ULONG Index;
     NTSTATUS Status;
 
     TRACE("SamrGetMembersInAlias(%p %p %p)\n",
           AliasHandle, Members);
 
+    RtlAcquireResourceShared(&SampResource,
+                             TRUE);
+
     /* Validate the alias handle */
     Status = SampValidateDbObject(AliasHandle,
                                   SamDbAliasObject,
@@ -4888,86 +5299,17 @@ SamrGetMembersInAlias(IN SAMPR_HANDLE AliasHandle,
     if (!NT_SUCCESS(Status))
     {
         ERR("failed with status 0x%08lx\n", Status);
-        return Status;
-    }
-
-    /* Open the members key of the alias objct */
-    Status = SampRegOpenKey(AliasObject->KeyHandle,
-                            L"Members",
-                            KEY_READ,
-                            &MembersKeyHandle);
-    if (!NT_SUCCESS(Status))
-    {
-        ERR("SampRegOpenKey failed with status 0x%08lx\n", Status);
-        return Status;
-    }
-
-    /* Get the number of members */
-    Status = SampRegQueryKeyInfo(MembersKeyHandle,
-                                 NULL,
-                                 &ValueCount);
-    if (!NT_SUCCESS(Status))
-    {
-        ERR("SampRegQueryKeyInfo failed with status 0x%08lx\n", Status);
-        goto done;
-    }
-
-    /* Allocate the member array */
-    MemberArray = midl_user_allocate(ValueCount * sizeof(SAMPR_SID_INFORMATION));
-    if (MemberArray == NULL)
-    {
-        Status = STATUS_INSUFFICIENT_RESOURCES;
         goto done;
     }
 
-    /* Enumerate the members */
-    Index = 0;
-    while (TRUE)
-    {
-        /* Get the size of the next SID */
-        DataLength = 0;
-        Status = SampRegEnumerateValue(MembersKeyHandle,
-                                       Index,
-                                       NULL,
-                                       NULL,
-                                       NULL,
-                                       NULL,
-                                       &DataLength);
-        if (!NT_SUCCESS(Status))
-        {
-            if (Status == STATUS_NO_MORE_ENTRIES)
-                Status = STATUS_SUCCESS;
-            break;
-        }
-
-        /* Allocate a buffer for the SID */
-        MemberArray[Index].SidPointer = midl_user_allocate(DataLength);
-        if (MemberArray[Index].SidPointer == NULL)
-        {
-            Status = STATUS_INSUFFICIENT_RESOURCES;
-            goto done;
-        }
-
-        /* Read the SID into the buffer */
-        Status = SampRegEnumerateValue(MembersKeyHandle,
-                                       Index,
-                                       NULL,
-                                       NULL,
-                                       NULL,
-                                       (PVOID)MemberArray[Index].SidPointer,
-                                       &DataLength);
-        if (!NT_SUCCESS(Status))
-        {
-            goto done;
-        }
-
-        Index++;
-    }
+    Status = SampGetMembersInAlias(AliasObject,
+                                   &MemberCount,
+                                   &MemberArray);
 
     /* Return the number of members and the member array */
     if (NT_SUCCESS(Status))
     {
-        Members->Count = ValueCount;
+        Members->Count = MemberCount;
         Members->Sids = MemberArray;
     }
 
@@ -4977,7 +5319,7 @@ done:
     {
         if (MemberArray != NULL)
         {
-            for (Index = 0; Index < ValueCount; Index++)
+            for (Index = 0; Index < MemberCount; Index++)
             {
                 if (MemberArray[Index].SidPointer != NULL)
                     midl_user_free(MemberArray[Index].SidPointer);
@@ -4987,9 +5329,7 @@ done:
         }
     }
 
-    /* Close the members key */
-    if (MembersKeyHandle != NULL)
-        SampRegCloseKey(MembersKeyHandle);
+    RtlReleaseResource(&SampResource);
 
     return Status;
 }
@@ -5015,6 +5355,9 @@ SamrOpenUser(IN SAMPR_HANDLE DomainHandle,
     RtlMapGenericMask(&DesiredAccess,
                       &UserMapping);
 
+    RtlAcquireResourceShared(&SampResource,
+                             TRUE);
+
     /* Validate the domain handle */
     Status = SampValidateDbObject(DomainHandle,
                                   SamDbDomainObject,
@@ -5023,7 +5366,7 @@ SamrOpenUser(IN SAMPR_HANDLE DomainHandle,
     if (!NT_SUCCESS(Status))
     {
         TRACE("failed with status 0x%08lx\n", Status);
-        return Status;
+        goto done;
     }
 
     /* Convert the RID into a string (hex) */
@@ -5040,12 +5383,15 @@ SamrOpenUser(IN SAMPR_HANDLE DomainHandle,
     if (!NT_SUCCESS(Status))
     {
         TRACE("failed with status 0x%08lx\n", Status);
-        return Status;
+        goto done;
     }
 
     *UserHandle = (SAMPR_HANDLE)UserObject;
 
-    return STATUS_SUCCESS;
+done:
+    RtlReleaseResource(&SampResource);
+
+    return Status;
 }
 
 
@@ -5059,6 +5405,9 @@ SamrDeleteUser(IN OUT SAMPR_HANDLE *UserHandle)
 
     TRACE("(%p)\n", UserHandle);
 
+    RtlAcquireResourceExclusive(&SampResource,
+                                TRUE);
+
     /* Validate the user handle */
     Status = SampValidateDbObject(*UserHandle,
                                   SamDbUserObject,
@@ -5067,32 +5416,48 @@ SamrDeleteUser(IN OUT SAMPR_HANDLE *UserHandle)
     if (!NT_SUCCESS(Status))
     {
         TRACE("SampValidateDbObject() failed (Status 0x%08lx)\n", Status);
-        return Status;
+        goto done;
     }
 
     /* Fail, if the user is built-in */
     if (UserObject->RelativeId < 1000)
     {
         TRACE("You can not delete a special account!\n");
-        return STATUS_SPECIAL_ACCOUNT;
+        Status = STATUS_SPECIAL_ACCOUNT;
+        goto done;
     }
 
-    /* FIXME: Remove the user from all groups */
+    /* Remove the user from all groups */
+    Status = SampRemoveUserFromAllGroups(UserObject);
+    if (!NT_SUCCESS(Status))
+    {
+        TRACE("SampRemoveUserFromAllGroups() failed (Status 0x%08lx)\n", Status);
+        goto done;
+    }
 
-    /* FIXME: Remove the user from all aliases */
+    /* Remove the user from all aliases */
+    Status = SampRemoveUserFromAllAliases(UserObject);
+    if (!NT_SUCCESS(Status))
+    {
+        TRACE("SampRemoveUserFromAllAliases() failed (Status 0x%08lx)\n", Status);
+        goto done;
+    }
 
     /* Delete the user from the database */
     Status = SampDeleteAccountDbObject(UserObject);
     if (!NT_SUCCESS(Status))
     {
         TRACE("SampDeleteAccountDbObject() failed (Status 0x%08lx)\n", Status);
-        return Status;
+        goto done;
     }
 
     /* Invalidate the handle */
     *UserHandle = NULL;
 
-    return STATUS_SUCCESS;
+done:
+    RtlReleaseResource(&SampResource);
+
+    return Status;
 }
 
 
@@ -5252,7 +5617,10 @@ SampQueryUserLogon(PSAM_DB_OBJECT UserObject,
                    PSAMPR_USER_INFO_BUFFER *Buffer)
 {
     PSAMPR_USER_INFO_BUFFER InfoBuffer = NULL;
+    SAM_DOMAIN_FIXED_DATA DomainFixedData;
     SAM_USER_FIXED_DATA FixedData;
+    LARGE_INTEGER PasswordCanChange;
+    LARGE_INTEGER PasswordMustChange;
     ULONG Length = 0;
     NTSTATUS Status;
 
@@ -5262,6 +5630,17 @@ SampQueryUserLogon(PSAM_DB_OBJECT UserObject,
     if (InfoBuffer == NULL)
         return STATUS_INSUFFICIENT_RESOURCES;
 
+    /* Get the fixed size domain data */
+    Length = sizeof(SAM_DOMAIN_FIXED_DATA);
+    Status = SampGetObjectAttribute(UserObject->ParentObject,
+                                    L"F",
+                                    NULL,
+                                    (PVOID)&DomainFixedData,
+                                    &Length);
+    if (!NT_SUCCESS(Status))
+        goto done;
+
+    /* Get the fixed size user data */
     Length = sizeof(SAM_USER_FIXED_DATA);
     Status = SampGetObjectAttribute(UserObject,
                                     L"F",
@@ -5283,8 +5662,15 @@ SampQueryUserLogon(PSAM_DB_OBJECT UserObject,
     InfoBuffer->Logon.LogonCount = FixedData.LogonCount;
     InfoBuffer->Logon.UserAccountControl = FixedData.UserAccountControl;
 
-//  OLD_LARGE_INTEGER PasswordCanChange;
-//  OLD_LARGE_INTEGER PasswordMustChange;
+    PasswordCanChange = SampAddRelativeTimeToTime(FixedData.PasswordLastSet,
+                                                  DomainFixedData.MinPasswordAge);
+    InfoBuffer->Logon.PasswordCanChange.LowPart = PasswordCanChange.LowPart;
+    InfoBuffer->Logon.PasswordCanChange.HighPart = PasswordCanChange.HighPart;
+
+    PasswordMustChange = SampAddRelativeTimeToTime(FixedData.PasswordLastSet,
+                                                   DomainFixedData.MaxPasswordAge);
+    InfoBuffer->Logon.PasswordMustChange.LowPart = PasswordMustChange.LowPart;
+    InfoBuffer->Logon.PasswordMustChange.HighPart = PasswordMustChange.HighPart;
 
     /* Get the Name string */
     Status = SampGetObjectAttributeString(UserObject,
@@ -5356,7 +5742,14 @@ SampQueryUserLogon(PSAM_DB_OBJECT UserObject,
         goto done;
     }
 
-    /* FIXME: LogonHours */
+    /* Get the LogonHours attribute */
+    Status = SampGetLogonHoursAttrbute(UserObject,
+                                       &InfoBuffer->Logon.LogonHours);
+    if (!NT_SUCCESS(Status))
+    {
+        TRACE("Status 0x%08lx\n", Status);
+        goto done;
+    }
 
     *Buffer = InfoBuffer;
 
@@ -5516,7 +5909,14 @@ SampQueryUserAccount(PSAM_DB_OBJECT UserObject,
         goto done;
     }
 
-    /* FIXME: LogonHours */
+    /* Get the LogonHours attribute */
+    Status = SampGetLogonHoursAttrbute(UserObject,
+                                       &InfoBuffer->Account.LogonHours);
+    if (!NT_SUCCESS(Status))
+    {
+        TRACE("Status 0x%08lx\n", Status);
+        goto done;
+    }
 
     *Buffer = InfoBuffer;
 
@@ -5559,23 +5959,67 @@ done:
     return Status;
 }
 
-/* FIXME: SampQueryUserLogonHours */
 
 static
 NTSTATUS
-SampQueryUserName(PSAM_DB_OBJECT UserObject,
-                  PSAMPR_USER_INFO_BUFFER *Buffer)
+SampQueryUserLogonHours(PSAM_DB_OBJECT UserObject,
+                        PSAMPR_USER_INFO_BUFFER *Buffer)
 {
     PSAMPR_USER_INFO_BUFFER InfoBuffer = NULL;
     NTSTATUS Status;
 
+    TRACE("(%p %p)\n", UserObject, Buffer);
+
     *Buffer = NULL;
 
     InfoBuffer = midl_user_allocate(sizeof(SAMPR_USER_INFO_BUFFER));
     if (InfoBuffer == NULL)
+    {
+        TRACE("Failed to allocate InfoBuffer!\n");
         return STATUS_INSUFFICIENT_RESOURCES;
+    }
 
-    /* Get the Name string */
+    Status = SampGetLogonHoursAttrbute(UserObject,
+                                       &InfoBuffer->LogonHours.LogonHours);
+    if (!NT_SUCCESS(Status))
+    {
+        TRACE("SampGetLogonHoursAttrbute failed (Status 0x%08lx)\n", Status);
+        goto done;
+    }
+
+    *Buffer = InfoBuffer;
+
+done:
+    if (!NT_SUCCESS(Status))
+    {
+        if (InfoBuffer != NULL)
+        {
+            if (InfoBuffer->LogonHours.LogonHours.LogonHours != NULL)
+                midl_user_free(InfoBuffer->LogonHours.LogonHours.LogonHours);
+
+            midl_user_free(InfoBuffer);
+        }
+    }
+
+    return Status;
+}
+
+
+static
+NTSTATUS
+SampQueryUserName(PSAM_DB_OBJECT UserObject,
+                  PSAMPR_USER_INFO_BUFFER *Buffer)
+{
+    PSAMPR_USER_INFO_BUFFER InfoBuffer = NULL;
+    NTSTATUS Status;
+
+    *Buffer = NULL;
+
+    InfoBuffer = midl_user_allocate(sizeof(SAMPR_USER_INFO_BUFFER));
+    if (InfoBuffer == NULL)
+        return STATUS_INSUFFICIENT_RESOURCES;
+
+    /* Get the Name string */
     Status = SampGetObjectAttributeString(UserObject,
                                           L"Name",
                                           &InfoBuffer->Name.UserName);
@@ -6043,6 +6487,94 @@ done:
 }
 
 
+static
+NTSTATUS
+SampQueryUserInternal1(PSAM_DB_OBJECT UserObject,
+                       PSAMPR_USER_INFO_BUFFER *Buffer)
+{
+    PSAMPR_USER_INFO_BUFFER InfoBuffer = NULL;
+    ULONG Length = 0;
+    NTSTATUS Status = STATUS_SUCCESS;
+
+    /* Fail, if the caller is not a trusted caller */
+    if (UserObject->Trusted == FALSE)
+        return STATUS_INVALID_INFO_CLASS;
+
+    *Buffer = NULL;
+
+    InfoBuffer = midl_user_allocate(sizeof(SAMPR_USER_INFO_BUFFER));
+    if (InfoBuffer == NULL)
+        return STATUS_INSUFFICIENT_RESOURCES;
+
+    InfoBuffer->Internal1.LmPasswordPresent = FALSE;
+    InfoBuffer->Internal1.NtPasswordPresent = FALSE;
+
+    /* Get the NT password */
+    Length = 0;
+    SampGetObjectAttribute(UserObject,
+                           L"NTPwd",
+                           NULL,
+                           NULL,
+                           &Length);
+
+    if (Length == sizeof(ENCRYPTED_NT_OWF_PASSWORD))
+    {
+        Status = SampGetObjectAttribute(UserObject,
+                                        L"NTPwd",
+                                        NULL,
+                                        (PVOID)&InfoBuffer->Internal1.EncryptedNtOwfPassword,
+                                        &Length);
+        if (!NT_SUCCESS(Status))
+            goto done;
+
+        if (memcmp(&InfoBuffer->Internal1.EncryptedNtOwfPassword,
+                   &EmptyNtHash,
+                   sizeof(ENCRYPTED_NT_OWF_PASSWORD)))
+            InfoBuffer->Internal1.NtPasswordPresent = TRUE;
+    }
+
+
+    /* Get the LM password */
+    Length = 0;
+    SampGetObjectAttribute(UserObject,
+                           L"LMPwd",
+                           NULL,
+                           NULL,
+                           &Length);
+
+    if (Length == sizeof(ENCRYPTED_LM_OWF_PASSWORD))
+    {
+        Status = SampGetObjectAttribute(UserObject,
+                                        L"LMPwd",
+                                        NULL,
+                                        (PVOID)&InfoBuffer->Internal1.EncryptedLmOwfPassword,
+                                        &Length);
+        if (!NT_SUCCESS(Status))
+            goto done;
+
+        if (memcmp(&InfoBuffer->Internal1.EncryptedLmOwfPassword,
+                   &EmptyLmHash,
+                   sizeof(ENCRYPTED_LM_OWF_PASSWORD)))
+            InfoBuffer->Internal1.LmPasswordPresent = TRUE;
+    }
+
+    InfoBuffer->Internal1.PasswordExpired = FALSE;
+
+    *Buffer = InfoBuffer;
+
+done:
+    if (!NT_SUCCESS(Status))
+    {
+        if (InfoBuffer != NULL)
+        {
+            midl_user_free(InfoBuffer);
+        }
+    }
+
+    return Status;
+}
+
+
 static NTSTATUS
 SampQueryUserParameters(PSAM_DB_OBJECT UserObject,
                         PSAMPR_USER_INFO_BUFFER *Buffer)
@@ -6084,119 +6616,610 @@ done:
 }
 
 
-/* Function 36 */
-NTSTATUS
-NTAPI
-SamrQueryInformationUser(IN SAMPR_HANDLE UserHandle,
-                         IN USER_INFORMATION_CLASS UserInformationClass,
-                         OUT PSAMPR_USER_INFO_BUFFER *Buffer)
+static NTSTATUS
+SampQueryUserAll(PSAM_DB_OBJECT UserObject,
+                 PSAMPR_USER_INFO_BUFFER *Buffer)
 {
-    PSAM_DB_OBJECT UserObject;
-    ACCESS_MASK DesiredAccess;
+    PSAMPR_USER_INFO_BUFFER InfoBuffer = NULL;
+    SAM_DOMAIN_FIXED_DATA DomainFixedData;
+    SAM_USER_FIXED_DATA FixedData;
+    LARGE_INTEGER PasswordCanChange;
+    LARGE_INTEGER PasswordMustChange;
+    ULONG Length = 0;
     NTSTATUS Status;
 
-    TRACE("SamrQueryInformationUser(%p %lu %p)\n",
-          UserHandle, UserInformationClass, Buffer);
+    *Buffer = NULL;
 
-    switch (UserInformationClass)
-    {
-        case UserGeneralInformation:
-        case UserNameInformation:
-        case UserAccountNameInformation:
-        case UserFullNameInformation:
-        case UserPrimaryGroupInformation:
-        case UserAdminCommentInformation:
-            DesiredAccess = USER_READ_GENERAL;
-            break;
+    InfoBuffer = midl_user_allocate(sizeof(SAMPR_USER_INFO_BUFFER));
+    if (InfoBuffer == NULL)
+        return STATUS_INSUFFICIENT_RESOURCES;
 
-        case UserLogonHoursInformation:
-        case UserHomeInformation:
-        case UserScriptInformation:
-        case UserProfileInformation:
-        case UserWorkStationsInformation:
-            DesiredAccess = USER_READ_LOGON;
-            break;
+    /* Get the fixed size domain data */
+    Length = sizeof(SAM_DOMAIN_FIXED_DATA);
+    Status = SampGetObjectAttribute(UserObject->ParentObject,
+                                    L"F",
+                                    NULL,
+                                    (PVOID)&DomainFixedData,
+                                    &Length);
+    if (!NT_SUCCESS(Status))
+        goto done;
 
-        case UserControlInformation:
-        case UserExpiresInformation:
-        case UserParametersInformation:
-            DesiredAccess = USER_READ_ACCOUNT;
-            break;
+    /* Get the fixed size user data */
+    Length = sizeof(SAM_USER_FIXED_DATA);
+    Status = SampGetObjectAttribute(UserObject,
+                                    L"F",
+                                    NULL,
+                                    (PVOID)&FixedData,
+                                    &Length);
+    if (!NT_SUCCESS(Status))
+        goto done;
 
-        case UserPreferencesInformation:
-            DesiredAccess = USER_READ_GENERAL |
-                            USER_READ_PREFERENCES;
-            break;
+    /* Set the fields to be returned */
+    if (UserObject->Trusted)
+    {
+        InfoBuffer->All.WhichFields = USER_ALL_READ_GENERAL_MASK |
+                                      USER_ALL_READ_LOGON_MASK |
+                                      USER_ALL_READ_ACCOUNT_MASK |
+                                      USER_ALL_READ_PREFERENCES_MASK |
+                                      USER_ALL_READ_TRUSTED_MASK;
+    }
+    else
+    {
+        InfoBuffer->All.WhichFields = 0;
 
-        case UserLogonInformation:
-        case UserAccountInformation:
-            DesiredAccess = USER_READ_GENERAL |
-                            USER_READ_PREFERENCES |
-                            USER_READ_LOGON |
-                            USER_READ_ACCOUNT;
-            break;
+        if (UserObject->Access & USER_READ_GENERAL)
+            InfoBuffer->All.WhichFields |= USER_ALL_READ_GENERAL_MASK;
 
-        default:
-            return STATUS_INVALID_INFO_CLASS;
+        if (UserObject->Access & USER_READ_LOGON)
+            InfoBuffer->All.WhichFields |= USER_ALL_READ_LOGON_MASK;
+
+        if (UserObject->Access & USER_READ_ACCOUNT)
+            InfoBuffer->All.WhichFields |= USER_ALL_READ_ACCOUNT_MASK;
+
+        if (UserObject->Access & USER_READ_PREFERENCES)
+            InfoBuffer->All.WhichFields |= USER_ALL_READ_PREFERENCES_MASK;
     }
 
-    /* Validate the domain handle */
-    Status = SampValidateDbObject(UserHandle,
-                                  SamDbUserObject,
-                                  DesiredAccess,
-                                  &UserObject);
-    if (!NT_SUCCESS(Status))
+    /* Fail, if no fields are to be returned */
+    if (InfoBuffer->All.WhichFields == 0)
     {
-        TRACE("failed with status 0x%08lx\n", Status);
-        return Status;
+        Status = STATUS_ACCESS_DENIED;
+        goto done;
     }
 
-    switch (UserInformationClass)
+    /* Get the UserName attribute */
+    if (InfoBuffer->All.WhichFields & USER_ALL_USERNAME)
     {
-        case UserGeneralInformation:
-            Status = SampQueryUserGeneral(UserObject,
-                                          Buffer);
-            break;
+        Status = SampGetObjectAttributeString(UserObject,
+                                              L"Name",
+                                              &InfoBuffer->All.UserName);
+        if (!NT_SUCCESS(Status))
+        {
+            TRACE("Status 0x%08lx\n", Status);
+            goto done;
+        }
+    }
 
-        case UserPreferencesInformation:
-            Status = SampQueryUserPreferences(UserObject,
-                                              Buffer);
-            break;
+    /* Get the FullName attribute */
+    if (InfoBuffer->All.WhichFields & USER_ALL_FULLNAME)
+    {
+        Status = SampGetObjectAttributeString(UserObject,
+                                              L"FullName",
+                                              &InfoBuffer->All.FullName);
+        if (!NT_SUCCESS(Status))
+        {
+            TRACE("Status 0x%08lx\n", Status);
+            goto done;
+        }
+    }
 
-        case UserLogonInformation:
-            Status = SampQueryUserLogon(UserObject,
-                                        Buffer);
-            break;
+    /* Get the UserId attribute */
+    if (InfoBuffer->All.WhichFields & USER_ALL_USERID)
+    {
+        InfoBuffer->All.UserId = FixedData.UserId;
+    }
 
-//        case UserLogonHoursInformation:
-//            Status = SampQueryUserLogonHours(UserObject,
-//                                             Buffer);
-//            break;
+    /* Get the PrimaryGroupId attribute */
+    if (InfoBuffer->All.WhichFields & USER_ALL_PRIMARYGROUPID)
+    {
+        InfoBuffer->All.PrimaryGroupId = FixedData.PrimaryGroupId;
+    }
 
-        case UserAccountInformation:
-            Status = SampQueryUserAccount(UserObject,
-                                          Buffer);
-            break;
+    /* Get the AdminComment attribute */
+    if (InfoBuffer->All.WhichFields & USER_ALL_ADMINCOMMENT)
+    {
+        Status = SampGetObjectAttributeString(UserObject,
+                                              L"AdminComment",
+                                              &InfoBuffer->All.AdminComment);
+        if (!NT_SUCCESS(Status))
+        {
+            TRACE("Status 0x%08lx\n", Status);
+            goto done;
+        }
+    }
 
-        case UserNameInformation:
-            Status = SampQueryUserName(UserObject,
-                                       Buffer);
-            break;
+    /* Get the UserComment attribute */
+    if (InfoBuffer->All.WhichFields & USER_ALL_USERCOMMENT)
+    {
+        Status = SampGetObjectAttributeString(UserObject,
+                                              L"UserComment",
+                                              &InfoBuffer->All.UserComment);
+        if (!NT_SUCCESS(Status))
+        {
+            TRACE("Status 0x%08lx\n", Status);
+            goto done;
+        }
+    }
 
-        case UserAccountNameInformation:
-            Status = SampQueryUserAccountName(UserObject,
-                                              Buffer);
-            break;
+    /* Get the HomeDirectory attribute */
+    if (InfoBuffer->All.WhichFields & USER_ALL_HOMEDIRECTORY)
+    {
+        Status = SampGetObjectAttributeString(UserObject,
+                                              L"HomeDirectory",
+                                              &InfoBuffer->All.HomeDirectory);
+        if (!NT_SUCCESS(Status))
+        {
+            TRACE("Status 0x%08lx\n", Status);
+            goto done;
+        }
+    }
 
-        case UserFullNameInformation:
-            Status = SampQueryUserFullName(UserObject,
-                                           Buffer);
-            break;
+    /* Get the HomeDirectoryDrive attribute */
+    if (InfoBuffer->All.WhichFields & USER_ALL_HOMEDIRECTORYDRIVE)
+    {
+        Status = SampGetObjectAttributeString(UserObject,
+                                              L"HomeDirectoryDrive",
+                                              &InfoBuffer->Home.HomeDirectoryDrive);
+        if (!NT_SUCCESS(Status))
+        {
+            TRACE("Status 0x%08lx\n", Status);
+            goto done;
+        }
+    }
 
-        case UserPrimaryGroupInformation:
-            Status = SampQueryUserPrimaryGroup(UserObject,
-                                               Buffer);
-            break;
+    /* Get the ScriptPath attribute */
+    if (InfoBuffer->All.WhichFields & USER_ALL_SCRIPTPATH)
+    {
+        Status = SampGetObjectAttributeString(UserObject,
+                                              L"ScriptPath",
+                                              &InfoBuffer->All.ScriptPath);
+        if (!NT_SUCCESS(Status))
+        {
+            TRACE("Status 0x%08lx\n", Status);
+            goto done;
+        }
+    }
+
+    /* Get the ProfilePath attribute */
+    if (InfoBuffer->All.WhichFields & USER_ALL_PROFILEPATH)
+    {
+        Status = SampGetObjectAttributeString(UserObject,
+                                              L"ProfilePath",
+                                              &InfoBuffer->All.ProfilePath);
+        if (!NT_SUCCESS(Status))
+        {
+            TRACE("Status 0x%08lx\n", Status);
+            goto done;
+        }
+    }
+
+    /* Get the WorkStations attribute */
+    if (InfoBuffer->All.WhichFields & USER_ALL_WORKSTATIONS)
+    {
+        Status = SampGetObjectAttributeString(UserObject,
+                                              L"WorkStations",
+                                              &InfoBuffer->All.WorkStations);
+        if (!NT_SUCCESS(Status))
+        {
+            TRACE("Status 0x%08lx\n", Status);
+            goto done;
+        }
+    }
+
+    /* Get the LastLogon attribute */
+    if (InfoBuffer->All.WhichFields & USER_ALL_LASTLOGON)
+    {
+        InfoBuffer->All.LastLogon.LowPart = FixedData.LastLogon.LowPart;
+        InfoBuffer->All.LastLogon.HighPart = FixedData.LastLogon.HighPart;
+    }
+
+    /* Get the LastLogoff attribute */
+    if (InfoBuffer->All.WhichFields & USER_ALL_LASTLOGOFF)
+    {
+        InfoBuffer->All.LastLogoff.LowPart = FixedData.LastLogoff.LowPart;
+        InfoBuffer->All.LastLogoff.HighPart = FixedData.LastLogoff.HighPart;
+    }
+
+    /* Get the LogonHours attribute */
+    if (InfoBuffer->All.WhichFields & USER_ALL_LOGONHOURS)
+    {
+        Status = SampGetLogonHoursAttrbute(UserObject,
+                                           &InfoBuffer->All.LogonHours);
+        if (!NT_SUCCESS(Status))
+        {
+            TRACE("Status 0x%08lx\n", Status);
+            goto done;
+        }
+    }
+
+    /* Get the BadPasswordCount attribute */
+    if (InfoBuffer->All.WhichFields & USER_ALL_BADPASSWORDCOUNT)
+    {
+        InfoBuffer->All.BadPasswordCount = FixedData.BadPasswordCount;
+    }
+
+    /* Get the LogonCount attribute */
+    if (InfoBuffer->All.WhichFields & USER_ALL_LOGONCOUNT)
+    {
+        InfoBuffer->All.LogonCount = FixedData.LogonCount;
+    }
+
+    /* Get the PasswordCanChange attribute */
+    if (InfoBuffer->All.WhichFields & USER_ALL_PASSWORDCANCHANGE)
+    {
+        PasswordCanChange = SampAddRelativeTimeToTime(FixedData.PasswordLastSet,
+                                                      DomainFixedData.MinPasswordAge);
+        InfoBuffer->All.PasswordCanChange.LowPart = PasswordCanChange.LowPart;
+        InfoBuffer->All.PasswordCanChange.HighPart = PasswordCanChange.HighPart;
+    }
+
+    /* Get the PasswordMustChange attribute */
+    if (InfoBuffer->All.WhichFields & USER_ALL_PASSWORDMUSTCHANGE)
+    {
+        PasswordMustChange = SampAddRelativeTimeToTime(FixedData.PasswordLastSet,
+                                                       DomainFixedData.MaxPasswordAge);
+        InfoBuffer->All.PasswordMustChange.LowPart = PasswordMustChange.LowPart;
+        InfoBuffer->All.PasswordMustChange.HighPart = PasswordMustChange.HighPart;
+    }
+
+    /* Get the PasswordLastSet attribute */
+    if (InfoBuffer->All.WhichFields & USER_ALL_PASSWORDLASTSET)
+    {
+        InfoBuffer->All.PasswordLastSet.LowPart = FixedData.PasswordLastSet.LowPart;
+        InfoBuffer->All.PasswordLastSet.HighPart = FixedData.PasswordLastSet.HighPart;
+    }
+
+    /* Get the AccountExpires attribute */
+    if (InfoBuffer->All.WhichFields & USER_ALL_ACCOUNTEXPIRES)
+    {
+        InfoBuffer->All.AccountExpires.LowPart = FixedData.AccountExpires.LowPart;
+        InfoBuffer->All.AccountExpires.HighPart = FixedData.AccountExpires.HighPart;
+    }
+
+    /* Get the UserAccountControl attribute */
+    if (InfoBuffer->All.WhichFields & USER_ALL_USERACCOUNTCONTROL)
+    {
+        InfoBuffer->All.UserAccountControl = FixedData.UserAccountControl;
+    }
+
+    /* Get the Parameters attribute */
+    if (InfoBuffer->All.WhichFields & USER_ALL_PARAMETERS)
+    {
+        Status = SampGetObjectAttributeString(UserObject,
+                                              L"Parameters",
+                                              &InfoBuffer->All.Parameters);
+        if (!NT_SUCCESS(Status))
+        {
+            TRACE("Status 0x%08lx\n", Status);
+            goto done;
+        }
+    }
+
+    /* Get the CountryCode attribute */
+    if (InfoBuffer->All.WhichFields & USER_ALL_COUNTRYCODE)
+    {
+        InfoBuffer->All.CountryCode = FixedData.CountryCode;
+    }
+
+    /* Get the CodePage attribute */
+    if (InfoBuffer->All.WhichFields & USER_ALL_CODEPAGE)
+    {
+        InfoBuffer->All.CodePage = FixedData.CodePage;
+    }
+
+    /* Get the LmPassword and NtPassword attributes */
+    if (InfoBuffer->All.WhichFields & (USER_ALL_NTPASSWORDPRESENT | USER_ALL_LMPASSWORDPRESENT))
+    {
+        InfoBuffer->All.LmPasswordPresent = FALSE;
+        InfoBuffer->All.NtPasswordPresent = FALSE;
+
+        /* Get the NT password */
+        Length = 0;
+        SampGetObjectAttribute(UserObject,
+                               L"NTPwd",
+                               NULL,
+                               NULL,
+                               &Length);
+
+        if (Length == sizeof(ENCRYPTED_NT_OWF_PASSWORD))
+        {
+            InfoBuffer->All.NtOwfPassword.Buffer = midl_user_allocate(sizeof(ENCRYPTED_NT_OWF_PASSWORD));
+            if (InfoBuffer->All.NtOwfPassword.Buffer == NULL)
+            {
+                Status = STATUS_INSUFFICIENT_RESOURCES;
+                goto done;
+            }
+
+            InfoBuffer->All.NtOwfPassword.Length = sizeof(ENCRYPTED_NT_OWF_PASSWORD);
+            InfoBuffer->All.NtOwfPassword.MaximumLength = sizeof(ENCRYPTED_NT_OWF_PASSWORD);
+
+            Status = SampGetObjectAttribute(UserObject,
+                                            L"NTPwd",
+                                            NULL,
+                                            (PVOID)InfoBuffer->All.NtOwfPassword.Buffer,
+                                            &Length);
+            if (!NT_SUCCESS(Status))
+                goto done;
+
+            if (memcmp(InfoBuffer->All.NtOwfPassword.Buffer,
+                       &EmptyNtHash,
+                       sizeof(ENCRYPTED_NT_OWF_PASSWORD)))
+                InfoBuffer->All.NtPasswordPresent = TRUE;
+        }
+
+        /* Get the LM password */
+        Length = 0;
+        SampGetObjectAttribute(UserObject,
+                               L"LMPwd",
+                               NULL,
+                               NULL,
+                               &Length);
+
+        if (Length == sizeof(ENCRYPTED_LM_OWF_PASSWORD))
+        {
+            InfoBuffer->All.LmOwfPassword.Buffer = midl_user_allocate(sizeof(ENCRYPTED_LM_OWF_PASSWORD));
+            if (InfoBuffer->All.LmOwfPassword.Buffer == NULL)
+            {
+                Status = STATUS_INSUFFICIENT_RESOURCES;
+                goto done;
+            }
+
+            InfoBuffer->All.LmOwfPassword.Length = sizeof(ENCRYPTED_LM_OWF_PASSWORD);
+            InfoBuffer->All.LmOwfPassword.MaximumLength = sizeof(ENCRYPTED_LM_OWF_PASSWORD);
+
+            Status = SampGetObjectAttribute(UserObject,
+                                            L"LMPwd",
+                                            NULL,
+                                            (PVOID)InfoBuffer->All.LmOwfPassword.Buffer,
+                                            &Length);
+            if (!NT_SUCCESS(Status))
+                goto done;
+
+            if (memcmp(InfoBuffer->All.LmOwfPassword.Buffer,
+                       &EmptyLmHash,
+                       sizeof(ENCRYPTED_LM_OWF_PASSWORD)))
+                InfoBuffer->All.LmPasswordPresent = TRUE;
+        }
+    }
+
+    if (InfoBuffer->All.WhichFields & USER_ALL_PRIVATEDATA)
+    {
+        Status = SampGetObjectAttributeString(UserObject,
+                                              L"PrivateData",
+                                              &InfoBuffer->All.PrivateData);
+        if (!NT_SUCCESS(Status))
+        {
+            TRACE("Status 0x%08lx\n", Status);
+            goto done;
+        }
+    }
+
+    if (InfoBuffer->All.WhichFields & USER_ALL_PASSWORDEXPIRED)
+    {
+        /* FIXME */
+    }
+
+    if (InfoBuffer->All.WhichFields & USER_ALL_SECURITYDESCRIPTOR)
+    {
+        Length = 0;
+        SampGetObjectAttribute(UserObject,
+                               L"SecDesc",
+                               NULL,
+                               NULL,
+                               &Length);
+
+        if (Length > 0)
+        {
+            InfoBuffer->All.SecurityDescriptor.SecurityDescriptor = midl_user_allocate(Length);
+            if (InfoBuffer->All.SecurityDescriptor.SecurityDescriptor == NULL)
+            {
+                Status = STATUS_INSUFFICIENT_RESOURCES;
+                goto done;
+            }
+
+            InfoBuffer->All.SecurityDescriptor.Length = Length;
+
+            Status = SampGetObjectAttribute(UserObject,
+                                            L"SecDesc",
+                                            NULL,
+                                            (PVOID)InfoBuffer->All.SecurityDescriptor.SecurityDescriptor,
+                                            &Length);
+            if (!NT_SUCCESS(Status))
+                goto done;
+        }
+    }
+
+    *Buffer = InfoBuffer;
+
+done:
+    if (!NT_SUCCESS(Status))
+    {
+        if (InfoBuffer != NULL)
+        {
+            if (InfoBuffer->All.UserName.Buffer != NULL)
+                midl_user_free(InfoBuffer->All.UserName.Buffer);
+
+            if (InfoBuffer->All.FullName.Buffer != NULL)
+                midl_user_free(InfoBuffer->All.FullName.Buffer);
+
+            if (InfoBuffer->All.AdminComment.Buffer != NULL)
+                midl_user_free(InfoBuffer->All.AdminComment.Buffer);
+
+            if (InfoBuffer->All.UserComment.Buffer != NULL)
+                midl_user_free(InfoBuffer->All.UserComment.Buffer);
+
+            if (InfoBuffer->All.HomeDirectory.Buffer != NULL)
+                midl_user_free(InfoBuffer->All.HomeDirectory.Buffer);
+
+            if (InfoBuffer->All.HomeDirectoryDrive.Buffer != NULL)
+                midl_user_free(InfoBuffer->All.HomeDirectoryDrive.Buffer);
+
+            if (InfoBuffer->All.ScriptPath.Buffer != NULL)
+                midl_user_free(InfoBuffer->All.ScriptPath.Buffer);
+
+            if (InfoBuffer->All.ProfilePath.Buffer != NULL)
+                midl_user_free(InfoBuffer->All.ProfilePath.Buffer);
+
+            if (InfoBuffer->All.WorkStations.Buffer != NULL)
+                midl_user_free(InfoBuffer->All.WorkStations.Buffer);
+
+            if (InfoBuffer->All.LogonHours.LogonHours != NULL)
+                midl_user_free(InfoBuffer->All.LogonHours.LogonHours);
+
+            if (InfoBuffer->All.Parameters.Buffer != NULL)
+                midl_user_free(InfoBuffer->All.Parameters.Buffer);
+
+            if (InfoBuffer->All.LmOwfPassword.Buffer != NULL)
+                midl_user_free(InfoBuffer->All.LmOwfPassword.Buffer);
+
+            if (InfoBuffer->All.NtOwfPassword.Buffer != NULL)
+                midl_user_free(InfoBuffer->All.NtOwfPassword.Buffer);
+
+            if (InfoBuffer->All.PrivateData.Buffer != NULL)
+                midl_user_free(InfoBuffer->All.PrivateData.Buffer);
+
+            if (InfoBuffer->All.SecurityDescriptor.SecurityDescriptor != NULL)
+                midl_user_free(InfoBuffer->All.SecurityDescriptor.SecurityDescriptor);
+
+            midl_user_free(InfoBuffer);
+        }
+    }
+
+    return Status;
+}
+
+
+/* Function 36 */
+NTSTATUS
+NTAPI
+SamrQueryInformationUser(IN SAMPR_HANDLE UserHandle,
+                         IN USER_INFORMATION_CLASS UserInformationClass,
+                         OUT PSAMPR_USER_INFO_BUFFER *Buffer)
+{
+    PSAM_DB_OBJECT UserObject;
+    ACCESS_MASK DesiredAccess;
+    NTSTATUS Status;
+
+    TRACE("SamrQueryInformationUser(%p %lu %p)\n",
+          UserHandle, UserInformationClass, Buffer);
+
+    switch (UserInformationClass)
+    {
+        case UserGeneralInformation:
+        case UserNameInformation:
+        case UserAccountNameInformation:
+        case UserFullNameInformation:
+        case UserPrimaryGroupInformation:
+        case UserAdminCommentInformation:
+            DesiredAccess = USER_READ_GENERAL;
+            break;
+
+        case UserLogonHoursInformation:
+        case UserHomeInformation:
+        case UserScriptInformation:
+        case UserProfileInformation:
+        case UserWorkStationsInformation:
+            DesiredAccess = USER_READ_LOGON;
+            break;
+
+        case UserControlInformation:
+        case UserExpiresInformation:
+        case UserParametersInformation:
+            DesiredAccess = USER_READ_ACCOUNT;
+            break;
+
+        case UserPreferencesInformation:
+            DesiredAccess = USER_READ_GENERAL |
+                            USER_READ_PREFERENCES;
+            break;
+
+        case UserLogonInformation:
+        case UserAccountInformation:
+            DesiredAccess = USER_READ_GENERAL |
+                            USER_READ_PREFERENCES |
+                            USER_READ_LOGON |
+                            USER_READ_ACCOUNT;
+            break;
+
+        case UserInternal1Information:
+        case UserAllInformation:
+            DesiredAccess = 0;
+            break;
+
+        default:
+            return STATUS_INVALID_INFO_CLASS;
+    }
+
+    RtlAcquireResourceShared(&SampResource,
+                             TRUE);
+
+    /* Validate the domain handle */
+    Status = SampValidateDbObject(UserHandle,
+                                  SamDbUserObject,
+                                  DesiredAccess,
+                                  &UserObject);
+    if (!NT_SUCCESS(Status))
+    {
+        TRACE("failed with status 0x%08lx\n", Status);
+        goto done;
+    }
+
+    switch (UserInformationClass)
+    {
+        case UserGeneralInformation:
+            Status = SampQueryUserGeneral(UserObject,
+                                          Buffer);
+            break;
+
+        case UserPreferencesInformation:
+            Status = SampQueryUserPreferences(UserObject,
+                                              Buffer);
+            break;
+
+        case UserLogonInformation:
+            Status = SampQueryUserLogon(UserObject,
+                                        Buffer);
+            break;
+
+        case UserLogonHoursInformation:
+            Status = SampQueryUserLogonHours(UserObject,
+                                             Buffer);
+            break;
+
+        case UserAccountInformation:
+            Status = SampQueryUserAccount(UserObject,
+                                          Buffer);
+            break;
+
+        case UserNameInformation:
+            Status = SampQueryUserName(UserObject,
+                                       Buffer);
+            break;
+
+        case UserAccountNameInformation:
+            Status = SampQueryUserAccountName(UserObject,
+                                              Buffer);
+            break;
+
+        case UserFullNameInformation:
+            Status = SampQueryUserFullName(UserObject,
+                                           Buffer);
+            break;
+
+        case UserPrimaryGroupInformation:
+            Status = SampQueryUserPrimaryGroup(UserObject,
+                                               Buffer);
+            break;
 
         case UserHomeInformation:
             Status = SampQueryUserHome(UserObject,
@@ -6232,14 +7255,21 @@ SamrQueryInformationUser(IN SAMPR_HANDLE UserHandle,
                                           Buffer);
             break;
 
-//        case UserInternal1Information:
+        case UserInternal1Information:
+            Status = SampQueryUserInternal1(UserObject,
+                                            Buffer);
+            break;
 
         case UserParametersInformation:
             Status = SampQueryUserParameters(UserObject,
                                              Buffer);
             break;
 
-//        case UserAllInformation:
+        case UserAllInformation:
+            Status = SampQueryUserAll(UserObject,
+                                      Buffer);
+            break;
+
 //        case UserInternal4Information:
 //        case UserInternal5Information:
 //        case UserInternal4InformationNew:
@@ -6249,6 +7279,80 @@ SamrQueryInformationUser(IN SAMPR_HANDLE UserHandle,
             Status = STATUS_INVALID_INFO_CLASS;
     }
 
+done:
+    RtlReleaseResource(&SampResource);
+
+    return Status;
+}
+
+
+static NTSTATUS
+SampSetUserName(PSAM_DB_OBJECT UserObject,
+                PRPC_UNICODE_STRING NewUserName)
+{
+    UNICODE_STRING OldUserName = {0, 0, NULL};
+    NTSTATUS Status;
+
+    /* Check the account name */
+    Status = SampCheckAccountName(NewUserName, 20);
+    if (!NT_SUCCESS(Status))
+    {
+        TRACE("SampCheckAccountName failed (Status 0x%08lx)\n", Status);
+        return Status;
+    }
+
+    Status = SampGetObjectAttributeString(UserObject,
+                                          L"Name",
+                                          (PRPC_UNICODE_STRING)&OldUserName);
+    if (!NT_SUCCESS(Status))
+    {
+        TRACE("SampGetObjectAttributeString failed (Status 0x%08lx)\n", Status);
+        goto done;
+    }
+
+    if (!RtlEqualUnicodeString(&OldUserName, (PCUNICODE_STRING)NewUserName, TRUE))
+    {
+        Status = SampCheckAccountNameInDomain(UserObject->ParentObject,
+                                              NewUserName->Buffer);
+        if (!NT_SUCCESS(Status))
+        {
+            TRACE("User name \'%S\' already exists in domain (Status 0x%08lx)\n",
+                  NewUserName->Buffer, Status);
+            goto done;
+        }
+    }
+
+    Status = SampSetAccountNameInDomain(UserObject->ParentObject,
+                                        L"Users",
+                                        NewUserName->Buffer,
+                                        UserObject->RelativeId);
+    if (!NT_SUCCESS(Status))
+    {
+        TRACE("SampSetAccountNameInDomain failed (Status 0x%08lx)\n", Status);
+        goto done;
+    }
+
+    Status = SampRemoveAccountNameFromDomain(UserObject->ParentObject,
+                                             L"Users",
+                                             OldUserName.Buffer);
+    if (!NT_SUCCESS(Status))
+    {
+        TRACE("SampRemoveAccountNameFromDomain failed (Status 0x%08lx)\n", Status);
+        goto done;
+    }
+
+    Status = SampSetObjectAttributeString(UserObject,
+                                          L"Name",
+                                          NewUserName);
+    if (!NT_SUCCESS(Status))
+    {
+        TRACE("SampSetObjectAttribute failed (Status 0x%08lx)\n", Status);
+    }
+
+done:
+    if (OldUserName.Buffer != NULL)
+        midl_user_free(OldUserName.Buffer);
+
     return Status;
 }
 
@@ -6280,35 +7384,26 @@ SampSetUserGeneral(PSAM_DB_OBJECT UserObject,
     if (!NT_SUCCESS(Status))
         goto done;
 
-    Status = SampSetObjectAttribute(UserObject,
-                                    L"Name",
-                                    REG_SZ,
-                                    Buffer->General.UserName.Buffer,
-                                    Buffer->General.UserName.MaximumLength);
+    Status = SampSetUserName(UserObject,
+                             &Buffer->General.UserName);
     if (!NT_SUCCESS(Status))
         goto done;
 
-    Status = SampSetObjectAttribute(UserObject,
-                                    L"FullName",
-                                    REG_SZ,
-                                    Buffer->General.FullName.Buffer,
-                                    Buffer->General.FullName.MaximumLength);
+    Status = SampSetObjectAttributeString(UserObject,
+                                          L"FullName",
+                                          &Buffer->General.FullName);
     if (!NT_SUCCESS(Status))
         goto done;
 
-    Status = SampSetObjectAttribute(UserObject,
-                                    L"AdminComment",
-                                    REG_SZ,
-                                    Buffer->General.AdminComment.Buffer,
-                                    Buffer->General.AdminComment.MaximumLength);
+    Status = SampSetObjectAttributeString(UserObject,
+                                          L"AdminComment",
+                                          &Buffer->General.AdminComment);
     if (!NT_SUCCESS(Status))
         goto done;
 
-    Status = SampSetObjectAttribute(UserObject,
-                                    L"UserComment",
-                                    REG_SZ,
-                                    Buffer->General.UserComment.Buffer,
-                                    Buffer->General.UserComment.MaximumLength);
+    Status = SampSetObjectAttributeString(UserObject,
+                                          L"UserComment",
+                                          &Buffer->General.UserComment);
 
 done:
     return Status;
@@ -6343,11 +7438,9 @@ SampSetUserPreferences(PSAM_DB_OBJECT UserObject,
     if (!NT_SUCCESS(Status))
         goto done;
 
-    Status = SampSetObjectAttribute(UserObject,
-                                    L"UserComment",
-                                    REG_SZ,
-                                    Buffer->Preferences.UserComment.Buffer,
-                                    Buffer->Preferences.UserComment.MaximumLength);
+    Status = SampSetObjectAttributeString(UserObject,
+                                          L"UserComment",
+                                          &Buffer->Preferences.UserComment);
 
 done:
     return Status;
@@ -6415,13 +7508,56 @@ done:
 
 
 static NTSTATUS
-SampSetUserExpires(PSAM_DB_OBJECT UserObject,
-                   PSAMPR_USER_INFO_BUFFER Buffer)
+SampSetUserExpires(PSAM_DB_OBJECT UserObject,
+                   PSAMPR_USER_INFO_BUFFER Buffer)
+{
+    SAM_USER_FIXED_DATA FixedData;
+    ULONG Length = 0;
+    NTSTATUS Status;
+
+    Length = sizeof(SAM_USER_FIXED_DATA);
+    Status = SampGetObjectAttribute(UserObject,
+                                    L"F",
+                                    NULL,
+                                    (PVOID)&FixedData,
+                                    &Length);
+    if (!NT_SUCCESS(Status))
+        goto done;
+
+    FixedData.AccountExpires.LowPart = Buffer->Expires.AccountExpires.LowPart;
+    FixedData.AccountExpires.HighPart = Buffer->Expires.AccountExpires.HighPart;
+
+    Status = SampSetObjectAttribute(UserObject,
+                                    L"F",
+                                    REG_BINARY,
+                                    &FixedData,
+                                    Length);
+
+done:
+    return Status;
+}
+
+
+static NTSTATUS
+SampSetUserInternal1(PSAM_DB_OBJECT UserObject,
+                     PSAMPR_USER_INFO_BUFFER Buffer)
 {
     SAM_USER_FIXED_DATA FixedData;
     ULONG Length = 0;
-    NTSTATUS Status;
+    NTSTATUS Status = STATUS_SUCCESS;
+
+    /* FIXME: Decrypt NT password */
+    /* FIXME: Decrypt LM password */
+
+    Status = SampSetUserPassword(UserObject,
+                                 &Buffer->Internal1.EncryptedNtOwfPassword,
+                                 Buffer->Internal1.NtPasswordPresent,
+                                 &Buffer->Internal1.EncryptedLmOwfPassword,
+                                 Buffer->Internal1.LmPasswordPresent);
+    if (!NT_SUCCESS(Status))
+        goto done;
 
+    /* Get the fixed user attributes */
     Length = sizeof(SAM_USER_FIXED_DATA);
     Status = SampGetObjectAttribute(UserObject,
                                     L"F",
@@ -6431,9 +7567,21 @@ SampSetUserExpires(PSAM_DB_OBJECT UserObject,
     if (!NT_SUCCESS(Status))
         goto done;
 
-    FixedData.AccountExpires.LowPart = Buffer->Expires.AccountExpires.LowPart;
-    FixedData.AccountExpires.HighPart = Buffer->Expires.AccountExpires.HighPart;
+    if (Buffer->Internal1.PasswordExpired)
+    {
+        /* The password was last set ages ago */
+        FixedData.PasswordLastSet.LowPart = 0;
+        FixedData.PasswordLastSet.HighPart = 0;
+    }
+    else
+    {
+        /* The password was last set right now */
+        Status = NtQuerySystemTime(&FixedData.PasswordLastSet);
+        if (!NT_SUCCESS(Status))
+            goto done;
+    }
 
+    /* Set the fixed user attributes */
     Status = SampSetObjectAttribute(UserObject,
                                     L"F",
                                     REG_BINARY,
@@ -6452,151 +7600,223 @@ SampSetUserAll(PSAM_DB_OBJECT UserObject,
     SAM_USER_FIXED_DATA FixedData;
     ULONG Length = 0;
     ULONG WhichFields;
+    PENCRYPTED_NT_OWF_PASSWORD NtPassword = NULL;
+    PENCRYPTED_LM_OWF_PASSWORD LmPassword = NULL;
+    BOOLEAN NtPasswordPresent = FALSE;
+    BOOLEAN LmPasswordPresent = FALSE;
+    BOOLEAN WriteFixedData = FALSE;
     NTSTATUS Status = STATUS_SUCCESS;
 
     WhichFields = Buffer->All.WhichFields;
 
+    /* Get the fixed size attributes */
+    Length = sizeof(SAM_USER_FIXED_DATA);
+    Status = SampGetObjectAttribute(UserObject,
+                                    L"F",
+                                    NULL,
+                                    (PVOID)&FixedData,
+                                    &Length);
+    if (!NT_SUCCESS(Status))
+        goto done;
+
     if (WhichFields & USER_ALL_USERNAME)
     {
-        Status = SampSetObjectAttribute(UserObject,
-                                        L"Name",
-                                        REG_SZ,
-                                        Buffer->All.UserName.Buffer,
-                                        Buffer->All.UserName.MaximumLength);
+        Status = SampSetUserName(UserObject,
+                                 &Buffer->All.UserName);
         if (!NT_SUCCESS(Status))
             goto done;
     }
 
     if (WhichFields & USER_ALL_FULLNAME)
     {
-        Status = SampSetObjectAttribute(UserObject,
-                                        L"FullName",
-                                        REG_SZ,
-                                        Buffer->All.FullName.Buffer,
-                                        Buffer->All.FullName.MaximumLength);
+        Status = SampSetObjectAttributeString(UserObject,
+                                              L"FullName",
+                                              &Buffer->All.FullName);
         if (!NT_SUCCESS(Status))
             goto done;
     }
 
     if (WhichFields & USER_ALL_ADMINCOMMENT)
     {
-        Status = SampSetObjectAttribute(UserObject,
-                                        L"AdminComment",
-                                        REG_SZ,
-                                        Buffer->All.AdminComment.Buffer,
-                                        Buffer->All.AdminComment.MaximumLength);
+        Status = SampSetObjectAttributeString(UserObject,
+                                              L"AdminComment",
+                                              &Buffer->All.AdminComment);
         if (!NT_SUCCESS(Status))
             goto done;
     }
 
     if (WhichFields & USER_ALL_USERCOMMENT)
     {
-        Status = SampSetObjectAttribute(UserObject,
-                                        L"UserComment",
-                                        REG_SZ,
-                                        Buffer->All.UserComment.Buffer,
-                                        Buffer->All.UserComment.MaximumLength);
+        Status = SampSetObjectAttributeString(UserObject,
+                                              L"UserComment",
+                                              &Buffer->All.UserComment);
         if (!NT_SUCCESS(Status))
             goto done;
     }
 
     if (WhichFields & USER_ALL_HOMEDIRECTORY)
     {
-        Status = SampSetObjectAttribute(UserObject,
-                                        L"HomeDirectory",
-                                        REG_SZ,
-                                        Buffer->All.HomeDirectory.Buffer,
-                                        Buffer->All.HomeDirectory.MaximumLength);
+        Status = SampSetObjectAttributeString(UserObject,
+                                              L"HomeDirectory",
+                                              &Buffer->All.HomeDirectory);
         if (!NT_SUCCESS(Status))
             goto done;
     }
 
     if (WhichFields & USER_ALL_HOMEDIRECTORYDRIVE)
     {
-        Status = SampSetObjectAttribute(UserObject,
-                                        L"HomeDirectoryDrive",
-                                        REG_SZ,
-                                        Buffer->All.HomeDirectoryDrive.Buffer,
-                                        Buffer->All.HomeDirectoryDrive.MaximumLength);
+        Status = SampSetObjectAttributeString(UserObject,
+                                              L"HomeDirectoryDrive",
+                                              &Buffer->All.HomeDirectoryDrive);
         if (!NT_SUCCESS(Status))
             goto done;
     }
 
     if (WhichFields & USER_ALL_SCRIPTPATH)
     {
-        Status = SampSetObjectAttribute(UserObject,
-                                        L"ScriptPath",
-                                        REG_SZ,
-                                        Buffer->All.ScriptPath.Buffer,
-                                        Buffer->All.ScriptPath.MaximumLength);
+        Status = SampSetObjectAttributeString(UserObject,
+                                              L"ScriptPath",
+                                              &Buffer->All.ScriptPath);
         if (!NT_SUCCESS(Status))
             goto done;
     }
 
     if (WhichFields & USER_ALL_PROFILEPATH)
     {
-        Status = SampSetObjectAttribute(UserObject,
-                                        L"ProfilePath",
-                                        REG_SZ,
-                                        Buffer->All.ProfilePath.Buffer,
-                                        Buffer->All.ProfilePath.MaximumLength);
+        Status = SampSetObjectAttributeString(UserObject,
+                                              L"ProfilePath",
+                                              &Buffer->All.ProfilePath);
         if (!NT_SUCCESS(Status))
             goto done;
     }
 
     if (WhichFields & USER_ALL_WORKSTATIONS)
     {
-        Status = SampSetObjectAttribute(UserObject,
-                                        L"WorkStations",
-                                        REG_SZ,
-                                        Buffer->All.WorkStations.Buffer,
-                                        Buffer->All.WorkStations.MaximumLength);
+        Status = SampSetObjectAttributeString(UserObject,
+                                              L"WorkStations",
+                                              &Buffer->All.WorkStations);
         if (!NT_SUCCESS(Status))
             goto done;
     }
 
     if (WhichFields & USER_ALL_PARAMETERS)
     {
-        Status = SampSetObjectAttribute(UserObject,
-                                        L"Parameters",
-                                        REG_SZ,
-                                        Buffer->All.Parameters.Buffer,
-                                        Buffer->All.Parameters.MaximumLength);
+        Status = SampSetObjectAttributeString(UserObject,
+                                              L"Parameters",
+                                              &Buffer->All.Parameters);
+        if (!NT_SUCCESS(Status))
+            goto done;
     }
 
-    if (WhichFields & (USER_ALL_PRIMARYGROUPID |
-                       USER_ALL_ACCOUNTEXPIRES |
-                       USER_ALL_USERACCOUNTCONTROL |
-                       USER_ALL_COUNTRYCODE |
-                       USER_ALL_CODEPAGE))
+    if (WhichFields & USER_ALL_LOGONHOURS)
     {
-        Length = sizeof(SAM_USER_FIXED_DATA);
-        Status = SampGetObjectAttribute(UserObject,
-                                        L"F",
-                                        NULL,
-                                        (PVOID)&FixedData,
-                                        &Length);
+        Status = SampSetLogonHoursAttrbute(UserObject,
+                                           &Buffer->All.LogonHours);
         if (!NT_SUCCESS(Status))
             goto done;
+    }
+
+    if (WhichFields & USER_ALL_PRIMARYGROUPID)
+    {
+        FixedData.PrimaryGroupId = Buffer->All.PrimaryGroupId;
+        WriteFixedData = TRUE;
+    }
 
-        if (WhichFields & USER_ALL_PRIMARYGROUPID)
-            FixedData.PrimaryGroupId = Buffer->All.PrimaryGroupId;
+    if (WhichFields & USER_ALL_ACCOUNTEXPIRES)
+    {
+        FixedData.AccountExpires.LowPart = Buffer->All.AccountExpires.LowPart;
+        FixedData.AccountExpires.HighPart = Buffer->All.AccountExpires.HighPart;
+        WriteFixedData = TRUE;
+    }
+
+    if (WhichFields & USER_ALL_USERACCOUNTCONTROL)
+    {
+        FixedData.UserAccountControl = Buffer->All.UserAccountControl;
+        WriteFixedData = TRUE;
+    }
+
+    if (WhichFields & USER_ALL_COUNTRYCODE)
+    {
+        FixedData.CountryCode = Buffer->All.CountryCode;
+        WriteFixedData = TRUE;
+    }
 
-        if (WhichFields & USER_ALL_ACCOUNTEXPIRES)
+    if (WhichFields & USER_ALL_CODEPAGE)
+    {
+        FixedData.CodePage = Buffer->All.CodePage;
+        WriteFixedData = TRUE;
+    }
+
+    if (WhichFields & (USER_ALL_NTPASSWORDPRESENT |
+                       USER_ALL_LMPASSWORDPRESENT))
+    {
+        if (WhichFields & USER_ALL_NTPASSWORDPRESENT)
         {
-            FixedData.AccountExpires.LowPart = Buffer->All.AccountExpires.LowPart;
-            FixedData.AccountExpires.HighPart = Buffer->All.AccountExpires.HighPart;
+            NtPassword = (PENCRYPTED_NT_OWF_PASSWORD)Buffer->All.NtOwfPassword.Buffer;
+            NtPasswordPresent = Buffer->All.NtPasswordPresent;
         }
 
-        if (WhichFields & USER_ALL_USERACCOUNTCONTROL)
-            FixedData.UserAccountControl = Buffer->All.UserAccountControl;
+        if (WhichFields & USER_ALL_LMPASSWORDPRESENT)
+        {
+            LmPassword = (PENCRYPTED_LM_OWF_PASSWORD)Buffer->All.LmOwfPassword.Buffer;
+            LmPasswordPresent = Buffer->All.LmPasswordPresent;
+        }
+
+        Status = SampSetUserPassword(UserObject,
+                                     NtPassword,
+                                     NtPasswordPresent,
+                                     LmPassword,
+                                     LmPasswordPresent);
+        if (!NT_SUCCESS(Status))
+            goto done;
+
+        /* The password has just been set */
+        Status = NtQuerySystemTime(&FixedData.PasswordLastSet);
+        if (!NT_SUCCESS(Status))
+            goto done;
+
+        WriteFixedData = TRUE;
+    }
+
+    if (WhichFields & USER_ALL_PRIVATEDATA)
+    {
+        Status = SampSetObjectAttributeString(UserObject,
+                                              L"PrivateData",
+                                              &Buffer->All.PrivateData);
+        if (!NT_SUCCESS(Status))
+            goto done;
+    }
+
+    if (WhichFields & USER_ALL_PASSWORDEXPIRED)
+    {
+        if (Buffer->All.PasswordExpired)
+        {
+            /* The pasword was last set ages ago */
+            FixedData.PasswordLastSet.LowPart = 0;
+            FixedData.PasswordLastSet.HighPart = 0;
+        }
+        else
+        {
+            /* The pasword was last set right now */
+            Status = NtQuerySystemTime(&FixedData.PasswordLastSet);
+            if (!NT_SUCCESS(Status))
+                goto done;
+        }
 
-        if (WhichFields & USER_ALL_COUNTRYCODE)
-            FixedData.CountryCode = Buffer->Preferences.CountryCode;
+        WriteFixedData = TRUE;
+    }
 
-        if (WhichFields & USER_ALL_CODEPAGE)
-            FixedData.CodePage = Buffer->Preferences.CodePage;
+    if (WhichFields & USER_ALL_SECURITYDESCRIPTOR)
+    {
+        Status = SampSetObjectAttribute(UserObject,
+                                        L"SecDesc",
+                                        REG_BINARY,
+                                        Buffer->All.SecurityDescriptor.SecurityDescriptor,
+                                        Buffer->All.SecurityDescriptor.Length);
+    }
 
+    if (WriteFixedData == TRUE)
+    {
         Status = SampSetObjectAttribute(UserObject,
                                         L"F",
                                         REG_BINARY,
@@ -6606,16 +7826,7 @@ SampSetUserAll(PSAM_DB_OBJECT UserObject,
             goto done;
     }
 
-/*
-FIXME:
-    USER_ALL_LOGONHOURS
-    USER_ALL_NTPASSWORDPRESENT
-    USER_ALL_LMPASSWORDPRESENT
-    USER_ALL_PASSWORDEXPIRED
-*/
-
 done:
-
     return Status;
 }
 
@@ -6662,6 +7873,7 @@ SamrSetInformationUser(IN SAMPR_HANDLE UserHandle,
             break;
 
         case UserSetPasswordInformation:
+        case UserInternal1Information:
             DesiredAccess = USER_FORCE_PASSWORD_CHANGE;
             break;
 
@@ -6673,6 +7885,9 @@ SamrSetInformationUser(IN SAMPR_HANDLE UserHandle,
             return STATUS_INVALID_INFO_CLASS;
     }
 
+    RtlAcquireResourceExclusive(&SampResource,
+                                TRUE);
+
     /* Validate the domain handle */
     Status = SampValidateDbObject(UserHandle,
                                   SamDbUserObject,
@@ -6681,7 +7896,7 @@ SamrSetInformationUser(IN SAMPR_HANDLE UserHandle,
     if (!NT_SUCCESS(Status))
     {
         TRACE("failed with status 0x%08lx\n", Status);
-        return Status;
+        goto done;
     }
 
     switch (UserInformationClass)
@@ -6695,42 +7910,32 @@ SamrSetInformationUser(IN SAMPR_HANDLE UserHandle,
             Status = SampSetUserPreferences(UserObject,
                                             Buffer);
             break;
-/*
+
         case UserLogonHoursInformation:
-            Status = SampSetUserLogonHours(UserObject,
-                                           Buffer);
+            Status = SampSetLogonHoursAttrbute(UserObject,
+                                               &Buffer->LogonHours.LogonHours);
             break;
-*/
+
         case UserNameInformation:
-            Status = SampSetObjectAttribute(UserObject,
-                                            L"Name",
-                                            REG_SZ,
-                                            Buffer->Name.UserName.Buffer,
-                                            Buffer->Name.UserName.MaximumLength);
+            Status = SampSetUserName(UserObject,
+                                     &Buffer->Name.UserName);
             if (!NT_SUCCESS(Status))
                 break;
 
-            Status = SampSetObjectAttribute(UserObject,
-                                            L"FullName",
-                                            REG_SZ,
-                                            Buffer->Name.FullName.Buffer,
-                                            Buffer->Name.FullName.MaximumLength);
+            Status = SampSetObjectAttributeString(UserObject,
+                                                  L"FullName",
+                                                  &Buffer->Name.FullName);
             break;
 
         case UserAccountNameInformation:
-            Status = SampSetObjectAttribute(UserObject,
-                                            L"Name",
-                                            REG_SZ,
-                                            Buffer->AccountName.UserName.Buffer,
-                                            Buffer->AccountName.UserName.MaximumLength);
+            Status = SampSetUserName(UserObject,
+                                     &Buffer->AccountName.UserName);
             break;
 
         case UserFullNameInformation:
-            Status = SampSetObjectAttribute(UserObject,
-                                            L"FullName",
-                                            REG_SZ,
-                                            Buffer->FullName.FullName.Buffer,
-                                            Buffer->FullName.FullName.MaximumLength);
+            Status = SampSetObjectAttributeString(UserObject,
+                                                  L"FullName",
+                                                  &Buffer->FullName.FullName);
             break;
 
         case UserPrimaryGroupInformation:
@@ -6739,62 +7944,48 @@ SamrSetInformationUser(IN SAMPR_HANDLE UserHandle,
             break;
 
         case UserHomeInformation:
-            Status = SampSetObjectAttribute(UserObject,
-                                            L"HomeDirectory",
-                                            REG_SZ,
-                                            Buffer->Home.HomeDirectory.Buffer,
-                                            Buffer->Home.HomeDirectory.MaximumLength);
+            Status = SampSetObjectAttributeString(UserObject,
+                                                  L"HomeDirectory",
+                                                  &Buffer->Home.HomeDirectory);
             if (!NT_SUCCESS(Status))
                 break;
 
-            Status = SampSetObjectAttribute(UserObject,
-                                            L"HomeDirectoryDrive",
-                                            REG_SZ,
-                                            Buffer->Home.HomeDirectoryDrive.Buffer,
-                                            Buffer->Home.HomeDirectoryDrive.MaximumLength);
+            Status = SampSetObjectAttributeString(UserObject,
+                                                  L"HomeDirectoryDrive",
+                                                  &Buffer->Home.HomeDirectoryDrive);
             break;
 
         case UserScriptInformation:
-            Status = SampSetObjectAttribute(UserObject,
-                                            L"ScriptPath",
-                                            REG_SZ,
-                                            Buffer->Script.ScriptPath.Buffer,
-                                            Buffer->Script.ScriptPath.MaximumLength);
+            Status = SampSetObjectAttributeString(UserObject,
+                                                  L"ScriptPath",
+                                                  &Buffer->Script.ScriptPath);
             break;
 
         case UserProfileInformation:
-            Status = SampSetObjectAttribute(UserObject,
-                                            L"ProfilePath",
-                                            REG_SZ,
-                                            Buffer->Profile.ProfilePath.Buffer,
-                                            Buffer->Profile.ProfilePath.MaximumLength);
+            Status = SampSetObjectAttributeString(UserObject,
+                                                  L"ProfilePath",
+                                                  &Buffer->Profile.ProfilePath);
             break;
 
         case UserAdminCommentInformation:
-            Status = SampSetObjectAttribute(UserObject,
-                                            L"AdminComment",
-                                            REG_SZ,
-                                            Buffer->AdminComment.AdminComment.Buffer,
-                                            Buffer->AdminComment.AdminComment.MaximumLength);
+            Status = SampSetObjectAttributeString(UserObject,
+                                                  L"AdminComment",
+                                                  &Buffer->AdminComment.AdminComment);
             break;
 
         case UserWorkStationsInformation:
-            Status = SampSetObjectAttribute(UserObject,
-                                            L"WorkStations",
-                                            REG_SZ,
-                                            Buffer->WorkStations.WorkStations.Buffer,
-                                            Buffer->WorkStations.WorkStations.MaximumLength);
+            Status = SampSetObjectAttributeString(UserObject,
+                                                  L"WorkStations",
+                                                  &Buffer->WorkStations.WorkStations);
             break;
 
         case UserSetPasswordInformation:
             TRACE("Password: %S\n", Buffer->SetPassword.Password.Buffer);
             TRACE("PasswordExpired: %d\n", Buffer->SetPassword.PasswordExpired);
 
-            Status = SampSetObjectAttribute(UserObject,
-                                            L"Password",
-                                            REG_SZ,
-                                            Buffer->SetPassword.Password.Buffer,
-                                            Buffer->SetPassword.Password.MaximumLength);
+            Status = SampSetObjectAttributeString(UserObject,
+                                                  L"Password",
+                                                  &Buffer->SetPassword.Password);
             break;
 
         case UserControlInformation:
@@ -6807,14 +7998,15 @@ SamrSetInformationUser(IN SAMPR_HANDLE UserHandle,
                                         Buffer);
             break;
 
-//        case UserInternal1Information:
+        case UserInternal1Information:
+            Status = SampSetUserInternal1(UserObject,
+                                          Buffer);
+            break;
 
         case UserParametersInformation:
-            Status = SampSetObjectAttribute(UserObject,
-                                            L"Parameters",
-                                            REG_SZ,
-                                            Buffer->Parameters.Parameters.Buffer,
-                                            Buffer->Parameters.Parameters.MaximumLength);
+            Status = SampSetObjectAttributeString(UserObject,
+                                                  L"Parameters",
+                                                  &Buffer->Parameters.Parameters);
             break;
 
         case UserAllInformation:
@@ -6827,31 +8019,291 @@ SamrSetInformationUser(IN SAMPR_HANDLE UserHandle,
 //        case UserInternal4InformationNew:
 //        case UserInternal5InformationNew:
 
-        default:
-            Status = STATUS_INVALID_INFO_CLASS;
+        default:
+            Status = STATUS_INVALID_INFO_CLASS;
+    }
+
+done:
+    RtlReleaseResource(&SampResource);
+
+    return Status;
+}
+
+
+/* Function 38 */
+NTSTATUS
+NTAPI
+SamrChangePasswordUser(IN SAMPR_HANDLE UserHandle,
+                       IN unsigned char LmPresent,
+                       IN PENCRYPTED_LM_OWF_PASSWORD OldLmEncryptedWithNewLm,
+                       IN PENCRYPTED_LM_OWF_PASSWORD NewLmEncryptedWithOldLm,
+                       IN unsigned char NtPresent,
+                       IN PENCRYPTED_NT_OWF_PASSWORD OldNtEncryptedWithNewNt,
+                       IN PENCRYPTED_NT_OWF_PASSWORD NewNtEncryptedWithOldNt,
+                       IN unsigned char NtCrossEncryptionPresent,
+                       IN PENCRYPTED_NT_OWF_PASSWORD NewNtEncryptedWithNewLm,
+                       IN unsigned char LmCrossEncryptionPresent,
+                       IN PENCRYPTED_LM_OWF_PASSWORD NewLmEncryptedWithNewNt)
+{
+    ENCRYPTED_LM_OWF_PASSWORD StoredLmPassword;
+    ENCRYPTED_NT_OWF_PASSWORD StoredNtPassword;
+    ENCRYPTED_LM_OWF_PASSWORD OldLmPassword;
+    ENCRYPTED_LM_OWF_PASSWORD NewLmPassword;
+    ENCRYPTED_NT_OWF_PASSWORD OldNtPassword;
+    ENCRYPTED_NT_OWF_PASSWORD NewNtPassword;
+    BOOLEAN StoredLmPresent = FALSE;
+    BOOLEAN StoredNtPresent = FALSE;
+    BOOLEAN StoredLmEmpty = TRUE;
+    BOOLEAN StoredNtEmpty = TRUE;
+    PSAM_DB_OBJECT UserObject;
+    ULONG Length;
+    SAM_USER_FIXED_DATA UserFixedData;
+    SAM_DOMAIN_FIXED_DATA DomainFixedData;
+    LARGE_INTEGER SystemTime;
+    NTSTATUS Status;
+
+    TRACE("(%p %u %p %p %u %p %p %u %p %u %p)\n",
+          UserHandle, LmPresent, OldLmEncryptedWithNewLm, NewLmEncryptedWithOldLm,
+          NtPresent, OldNtEncryptedWithNewNt, NewNtEncryptedWithOldNt, NtCrossEncryptionPresent,
+          NewNtEncryptedWithNewLm, LmCrossEncryptionPresent, NewLmEncryptedWithNewNt);
+
+    RtlAcquireResourceExclusive(&SampResource,
+                                TRUE);
+
+    /* Validate the user handle */
+    Status = SampValidateDbObject(UserHandle,
+                                  SamDbUserObject,
+                                  USER_CHANGE_PASSWORD,
+                                  &UserObject);
+    if (!NT_SUCCESS(Status))
+    {
+        TRACE("SampValidateDbObject failed with status 0x%08lx\n", Status);
+        goto done;
+    }
+
+    /* Get the current time */
+    Status = NtQuerySystemTime(&SystemTime);
+    if (!NT_SUCCESS(Status))
+    {
+        TRACE("NtQuerySystemTime failed (Status 0x%08lx)\n", Status);
+        goto done;
+    }
+
+    /* Retrieve the LM password */
+    Length = sizeof(ENCRYPTED_LM_OWF_PASSWORD);
+    Status = SampGetObjectAttribute(UserObject,
+                                    L"LMPwd",
+                                    NULL,
+                                    &StoredLmPassword,
+                                    &Length);
+    if (NT_SUCCESS(Status))
+    {
+        if (Length == sizeof(ENCRYPTED_LM_OWF_PASSWORD))
+        {
+            StoredLmPresent = TRUE;
+            if (!RtlEqualMemory(&StoredLmPassword,
+                                &EmptyLmHash,
+                                sizeof(ENCRYPTED_LM_OWF_PASSWORD)))
+                StoredLmEmpty = FALSE;
+        }
+    }
+
+    /* Retrieve the NT password */
+    Length = sizeof(ENCRYPTED_NT_OWF_PASSWORD);
+    Status = SampGetObjectAttribute(UserObject,
+                                    L"NTPwd",
+                                    NULL,
+                                    &StoredNtPassword,
+                                    &Length);
+    if (NT_SUCCESS(Status))
+    {
+        if (Length == sizeof(ENCRYPTED_NT_OWF_PASSWORD))
+        {
+            StoredNtPresent = TRUE;
+            if (!RtlEqualMemory(&StoredNtPassword,
+                                &EmptyNtHash,
+                                sizeof(ENCRYPTED_NT_OWF_PASSWORD)))
+                StoredNtEmpty = FALSE;
+        }
+    }
+
+    /* Retrieve the fixed size user data */
+    Length = sizeof(SAM_USER_FIXED_DATA);
+    Status = SampGetObjectAttribute(UserObject,
+                                    L"F",
+                                    NULL,
+                                    &UserFixedData,
+                                    &Length);
+    if (!NT_SUCCESS(Status))
+    {
+        TRACE("SampGetObjectAttribute failed to retrieve the fixed user data (Status 0x%08lx)\n", Status);
+        goto done;
+    }
+
+    /* Check if we can change the password at this time */
+    if ((StoredNtEmpty == FALSE) || (StoredNtEmpty == FALSE))
+    {
+        /* Get fixed domain data */
+        Length = sizeof(SAM_DOMAIN_FIXED_DATA);
+        Status = SampGetObjectAttribute(UserObject->ParentObject,
+                                        L"F",
+                                        NULL,
+                                        &DomainFixedData,
+                                        &Length);
+        if (!NT_SUCCESS(Status))
+        {
+            TRACE("SampGetObjectAttribute failed to retrieve the fixed domain data (Status 0x%08lx)\n", Status);
+            goto done;
+        }
+
+        if (DomainFixedData.MinPasswordAge.QuadPart > 0)
+        {
+            if (SystemTime.QuadPart < (UserFixedData.PasswordLastSet.QuadPart + DomainFixedData.MinPasswordAge.QuadPart))
+            {
+                Status = STATUS_ACCOUNT_RESTRICTION;
+                goto done;
+            }
+        }
+    }
+
+    /* Decrypt the LM passwords, if present */
+    if (LmPresent)
+    {
+        Status = SystemFunction013((const BYTE *)NewLmEncryptedWithOldLm,
+                                   (const BYTE *)&StoredLmPassword,
+                                   (LPBYTE)&NewLmPassword);
+        if (!NT_SUCCESS(Status))
+        {
+            TRACE("SystemFunction013 failed (Status 0x%08lx)\n", Status);
+            goto done;
+        }
+
+        Status = SystemFunction013((const BYTE *)OldLmEncryptedWithNewLm,
+                                   (const BYTE *)&NewLmPassword,
+                                   (LPBYTE)&OldLmPassword);
+        if (!NT_SUCCESS(Status))
+        {
+            TRACE("SystemFunction013 failed (Status 0x%08lx)\n", Status);
+            goto done;
+        }
+    }
+
+    /* Decrypt the NT passwords, if present */
+    if (NtPresent)
+    {
+        Status = SystemFunction013((const BYTE *)NewNtEncryptedWithOldNt,
+                                   (const BYTE *)&StoredNtPassword,
+                                   (LPBYTE)&NewNtPassword);
+        if (!NT_SUCCESS(Status))
+        {
+            TRACE("SystemFunction013 failed (Status 0x%08lx)\n", Status);
+            goto done;
+        }
+
+        Status = SystemFunction013((const BYTE *)OldNtEncryptedWithNewNt,
+                                   (const BYTE *)&NewNtPassword,
+                                   (LPBYTE)&OldNtPassword);
+        if (!NT_SUCCESS(Status))
+        {
+            TRACE("SystemFunction013 failed (Status 0x%08lx)\n", Status);
+            goto done;
+        }
+    }
+
+    /* Check if the old passwords match the stored ones */
+    if (NtPresent)
+    {
+        if (LmPresent)
+        {
+            if (!RtlEqualMemory(&StoredLmPassword,
+                                &OldLmPassword,
+                                sizeof(ENCRYPTED_LM_OWF_PASSWORD)))
+            {
+                TRACE("Old LM Password does not match!\n");
+                Status = STATUS_WRONG_PASSWORD;
+            }
+            else
+            {
+                if (!RtlEqualMemory(&StoredNtPassword,
+                                    &OldNtPassword,
+                                    sizeof(ENCRYPTED_LM_OWF_PASSWORD)))
+                {
+                    TRACE("Old NT Password does not match!\n");
+                    Status = STATUS_WRONG_PASSWORD;
+                }
+            }
+        }
+        else
+        {
+            if (!RtlEqualMemory(&StoredNtPassword,
+                                &OldNtPassword,
+                                sizeof(ENCRYPTED_LM_OWF_PASSWORD)))
+            {
+                TRACE("Old NT Password does not match!\n");
+                Status = STATUS_WRONG_PASSWORD;
+            }
+        }
+    }
+    else
+    {
+        if (LmPresent)
+        {
+            if (!RtlEqualMemory(&StoredLmPassword,
+                                &OldLmPassword,
+                                sizeof(ENCRYPTED_LM_OWF_PASSWORD)))
+            {
+                TRACE("Old LM Password does not match!\n");
+                Status = STATUS_WRONG_PASSWORD;
+            }
+        }
+        else
+        {
+            Status = STATUS_INVALID_PARAMETER;
+        }
+    }
+
+    /* Store the new password hashes */
+    if (NT_SUCCESS(Status))
+    {
+        Status = SampSetUserPassword(UserObject,
+                                     &NewNtPassword,
+                                     NtPresent,
+                                     &NewLmPassword,
+                                     LmPresent);
+        if (NT_SUCCESS(Status))
+        {
+            /* Update PasswordLastSet */
+            UserFixedData.PasswordLastSet.QuadPart = SystemTime.QuadPart;
+
+            /* Set the fixed size user data */
+            Length = sizeof(SAM_USER_FIXED_DATA);
+            Status = SampSetObjectAttribute(UserObject,
+                                            L"F",
+                                            REG_BINARY,
+                                            &UserFixedData,
+                                            Length);
+        }
     }
 
-    return Status;
-}
+    if (Status == STATUS_WRONG_PASSWORD)
+    {
+        /* Update BadPasswordCount and LastBadPasswordTime */
+        UserFixedData.BadPasswordCount++;
+        UserFixedData.LastBadPasswordTime.QuadPart = SystemTime.QuadPart;
 
+        /* Set the fixed size user data */
+        Length = sizeof(SAM_USER_FIXED_DATA);
+        Status = SampSetObjectAttribute(UserObject,
+                                        L"F",
+                                        REG_BINARY,
+                                        &UserFixedData,
+                                        Length);
+    }
 
-/* Function 38 */
-NTSTATUS
-NTAPI
-SamrChangePasswordUser(IN SAMPR_HANDLE UserHandle,
-                       IN unsigned char LmPresent,
-                       IN PENCRYPTED_LM_OWF_PASSWORD OldLmEncryptedWithNewLm,
-                       IN PENCRYPTED_LM_OWF_PASSWORD NewLmEncryptedWithOldLm,
-                       IN unsigned char NtPresent,
-                       IN PENCRYPTED_NT_OWF_PASSWORD OldNtEncryptedWithNewNt,
-                       IN PENCRYPTED_NT_OWF_PASSWORD NewNtEncryptedWithOldNt,
-                       IN unsigned char NtCrossEncryptionPresent,
-                       IN PENCRYPTED_NT_OWF_PASSWORD NewNtEncryptedWithNewLm,
-                       IN unsigned char LmCrossEncryptionPresent,
-                       IN PENCRYPTED_LM_OWF_PASSWORD NewLmEncryptedWithNewNt)
-{
-    UNIMPLEMENTED;
-    return STATUS_NOT_IMPLEMENTED;
+done:
+    RtlReleaseResource(&SampResource);
+
+    return Status;
 }
 
 
@@ -6869,6 +8321,9 @@ SamrGetGroupsForUser(IN SAMPR_HANDLE UserHandle,
     TRACE("SamrGetGroupsForUser(%p %p)\n",
           UserHandle, Groups);
 
+    RtlAcquireResourceShared(&SampResource,
+                             TRUE);
+
     /* Validate the user handle */
     Status = SampValidateDbObject(UserHandle,
                                   SamDbUserObject,
@@ -6877,13 +8332,16 @@ SamrGetGroupsForUser(IN SAMPR_HANDLE UserHandle,
     if (!NT_SUCCESS(Status))
     {
         TRACE("SampValidateDbObject failed with status 0x%08lx\n", Status);
-        return Status;
+        goto done;
     }
 
     /* Allocate the groups buffer */
     GroupsBuffer = midl_user_allocate(sizeof(SAMPR_GET_GROUPS_BUFFER));
     if (GroupsBuffer == NULL)
-        return STATUS_INSUFFICIENT_RESOURCES;
+    {
+        Status = STATUS_INSUFFICIENT_RESOURCES;
+        goto done;
+    }
 
     /*
      * Get the size of the Groups attribute.
@@ -6904,7 +8362,8 @@ SamrGetGroupsForUser(IN SAMPR_HANDLE UserHandle,
 
         *Groups = GroupsBuffer;
 
-        return STATUS_SUCCESS;
+        Status = STATUS_SUCCESS;
+        goto done;
     }
 
     /* Allocate a buffer for the Groups attribute */
@@ -6945,6 +8404,8 @@ done:
         }
     }
 
+    RtlReleaseResource(&SampResource);
+
     return Status;
 }
 
@@ -6995,24 +8456,151 @@ SamrTestPrivateFunctionsUser(IN SAMPR_HANDLE UserHandle)
     return STATUS_NOT_IMPLEMENTED;
 }
 
+
 /* Function 44 */
 NTSTATUS
 NTAPI
 SamrGetUserDomainPasswordInformation(IN SAMPR_HANDLE UserHandle,
                                      OUT PUSER_DOMAIN_PASSWORD_INFORMATION PasswordInformation)
 {
-    UNIMPLEMENTED;
-    return STATUS_NOT_IMPLEMENTED;
+    SAM_DOMAIN_FIXED_DATA DomainFixedData;
+    SAM_USER_FIXED_DATA UserFixedData;
+    PSAM_DB_OBJECT DomainObject;
+    PSAM_DB_OBJECT UserObject;
+    ULONG Length = 0;
+    NTSTATUS Status;
+
+    TRACE("(%p %p)\n",
+          UserHandle, PasswordInformation);
+
+    RtlAcquireResourceShared(&SampResource,
+                             TRUE);
+
+    /* Validate the user handle */
+    Status = SampValidateDbObject(UserHandle,
+                                  SamDbUserObject,
+                                  0,
+                                  &UserObject);
+    if (!NT_SUCCESS(Status))
+    {
+        TRACE("SampValidateDbObject failed with status 0x%08lx\n", Status);
+        goto done;
+    }
+
+    /* Validate the domain object */
+    Status = SampValidateDbObject((SAMPR_HANDLE)UserObject->ParentObject,
+                                  SamDbDomainObject,
+                                  DOMAIN_READ_PASSWORD_PARAMETERS,
+                                  &DomainObject);
+    if (!NT_SUCCESS(Status))
+    {
+        TRACE("SampValidateDbObject failed with status 0x%08lx\n", Status);
+        goto done;
+    }
+
+    /* Get fixed user data */
+    Length = sizeof(SAM_USER_FIXED_DATA);
+    Status = SampGetObjectAttribute(UserObject,
+                                    L"F",
+                                    NULL,
+                                    (PVOID)&UserFixedData,
+                                    &Length);
+    if (!NT_SUCCESS(Status))
+    {
+        TRACE("SampGetObjectAttribute failed with status 0x%08lx\n", Status);
+        goto done;
+    }
+
+    if ((UserObject->RelativeId == DOMAIN_USER_RID_KRBTGT) ||
+        (UserFixedData.UserAccountControl & (USER_INTERDOMAIN_TRUST_ACCOUNT |
+                                             USER_WORKSTATION_TRUST_ACCOUNT |
+                                             USER_SERVER_TRUST_ACCOUNT)))
+    {
+        PasswordInformation->MinPasswordLength = 0;
+        PasswordInformation->PasswordProperties = 0;
+    }
+    else
+    {
+        /* Get fixed domain data */
+        Length = sizeof(SAM_DOMAIN_FIXED_DATA);
+        Status = SampGetObjectAttribute(DomainObject,
+                                        L"F",
+                                        NULL,
+                                        (PVOID)&DomainFixedData,
+                                        &Length);
+        if (!NT_SUCCESS(Status))
+        {
+            TRACE("SampGetObjectAttribute failed with status 0x%08lx\n", Status);
+            goto done;
+        }
+
+        PasswordInformation->MinPasswordLength = DomainFixedData.MinPasswordLength;
+        PasswordInformation->PasswordProperties = DomainFixedData.PasswordProperties;
+    }
+
+done:
+    RtlReleaseResource(&SampResource);
+
+    return STATUS_SUCCESS;
 }
 
+
 /* Function 45 */
 NTSTATUS
 NTAPI
 SamrRemoveMemberFromForeignDomain(IN SAMPR_HANDLE DomainHandle,
                                   IN PRPC_SID MemberSid)
 {
-    UNIMPLEMENTED;
-    return STATUS_NOT_IMPLEMENTED;
+    PSAM_DB_OBJECT DomainObject;
+    ULONG Rid = 0;
+    NTSTATUS Status;
+
+    TRACE("(%p %p)\n",
+          DomainHandle, MemberSid);
+
+    RtlAcquireResourceExclusive(&SampResource,
+                                TRUE);
+
+    /* Validate the domain object */
+    Status = SampValidateDbObject(DomainHandle,
+                                  SamDbDomainObject,
+                                  DOMAIN_LOOKUP,
+                                  &DomainObject);
+    if (!NT_SUCCESS(Status))
+    {
+        TRACE("SampValidateDbObject failed with status 0x%08lx\n", Status);
+        goto done;
+    }
+
+    /* Retrieve the RID from the MemberSID */
+    Status = SampGetRidFromSid((PSID)MemberSid,
+                               &Rid);
+    if (!NT_SUCCESS(Status))
+    {
+        TRACE("SampGetRidFromSid failed with status 0x%08lx\n", Status);
+        goto done;
+    }
+
+    /* Fail, if the RID represents a special account */
+    if (Rid < 1000)
+    {
+        TRACE("Cannot remove a special account (RID: %lu)\n", Rid);
+        Status = STATUS_SPECIAL_ACCOUNT;
+        goto done;
+    }
+
+    /* Remove the member from all aliases in the domain */
+    Status = SampRemoveMemberFromAllAliases(DomainObject,
+                                            MemberSid);
+    if (!NT_SUCCESS(Status))
+    {
+        TRACE("SampRemoveMemberFromAllAliases failed with status 0x%08lx\n", Status);
+    }
+
+done:
+    RtlReleaseResource(&SampResource);
+
+    return Status;
 }
 
 
@@ -7103,14 +8691,18 @@ SamrCreateUser2InDomain(IN SAMPR_HANDLE DomainHandle,
                         OUT unsigned long *GrantedAccess,
                         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;
     PSAM_DB_OBJECT UserObject;
+    GROUP_MEMBERSHIP GroupMembership;
+    UCHAR LogonHours[23];
     ULONG ulSize;
     ULONG ulRid;
     WCHAR szRid[9];
+    PSECURITY_DESCRIPTOR Sd = NULL;
+    ULONG SdSize = 0;
+    PSID UserSid = NULL;
     NTSTATUS Status;
 
     TRACE("SamrCreateUserInDomain(%p %p %lx %p %p)\n",
@@ -7135,6 +8727,9 @@ SamrCreateUser2InDomain(IN SAMPR_HANDLE DomainHandle,
     RtlMapGenericMask(&DesiredAccess,
                       &UserMapping);
 
+    RtlAcquireResourceExclusive(&SampResource,
+                                TRUE);
+
     /* Validate the domain handle */
     Status = SampValidateDbObject(DomainHandle,
                                   SamDbDomainObject,
@@ -7143,7 +8738,15 @@ SamrCreateUser2InDomain(IN SAMPR_HANDLE DomainHandle,
     if (!NT_SUCCESS(Status))
     {
         TRACE("failed with status 0x%08lx\n", Status);
-        return Status;
+        goto done;
+    }
+
+    /* Check the user account name */
+    Status = SampCheckAccountName(Name, 20);
+    if (!NT_SUCCESS(Status))
+    {
+        TRACE("SampCheckAccountName failed (Status 0x%08lx)\n", Status);
+        goto done;
     }
 
     /* Check if the user name already exists in the domain */
@@ -7153,7 +8756,7 @@ SamrCreateUser2InDomain(IN SAMPR_HANDLE DomainHandle,
     {
         TRACE("User name \'%S\' already exists in domain (Status 0x%08lx)\n",
               Name->Buffer, Status);
-        return Status;
+        goto done;
     }
 
     /* Get the fixed domain attributes */
@@ -7166,13 +8769,35 @@ SamrCreateUser2InDomain(IN SAMPR_HANDLE DomainHandle,
     if (!NT_SUCCESS(Status))
     {
         TRACE("failed with status 0x%08lx\n", Status);
-        return Status;
+        goto done;
     }
 
     /* Increment the NextRid attribute */
     ulRid = FixedDomainData.NextRid;
     FixedDomainData.NextRid++;
 
+    TRACE("RID: %lx\n", ulRid);
+
+    /* Create the user SID */
+    Status = SampCreateAccountSid(DomainObject,
+                                  ulRid,
+                                  &UserSid);
+    if (!NT_SUCCESS(Status))
+    {
+        TRACE("SampCreateAccountSid failed (Status 0x%08lx)\n", Status);
+        goto done;
+    }
+
+    /* Create the security descriptor */
+    Status = SampCreateUserSD(UserSid,
+                              &Sd,
+                              &SdSize);
+    if (!NT_SUCCESS(Status))
+    {
+        TRACE("SampCreateUserSD failed (Status 0x%08lx)\n", Status);
+        goto done;
+    }
+
     /* Store the fixed domain attributes */
     Status = SampSetObjectAttribute(DomainObject,
                                     L"F",
@@ -7182,11 +8807,9 @@ SamrCreateUser2InDomain(IN SAMPR_HANDLE DomainHandle,
     if (!NT_SUCCESS(Status))
     {
         TRACE("failed with status 0x%08lx\n", Status);
-        return Status;
+        goto done;
     }
 
-    TRACE("RID: %lx\n", ulRid);
-
     /* Convert the RID into a string (hex) */
     swprintf(szRid, L"%08lX", ulRid);
 
@@ -7201,7 +8824,7 @@ SamrCreateUser2InDomain(IN SAMPR_HANDLE DomainHandle,
     if (!NT_SUCCESS(Status))
     {
         TRACE("failed with status 0x%08lx\n", Status);
-        return Status;
+        goto done;
     }
 
     /* Add the account name for the user object */
@@ -7212,7 +8835,7 @@ SamrCreateUser2InDomain(IN SAMPR_HANDLE DomainHandle,
     if (!NT_SUCCESS(Status))
     {
         TRACE("failed with status 0x%08lx\n", Status);
-        return Status;
+        goto done;
     }
 
     /* Initialize fixed user data */
@@ -7245,131 +8868,140 @@ SamrCreateUser2InDomain(IN SAMPR_HANDLE DomainHandle,
     if (!NT_SUCCESS(Status))
     {
         TRACE("failed with status 0x%08lx\n", Status);
-        return Status;
+        goto done;
     }
 
     /* Set the Name attribute */
-    Status = SampSetObjectAttribute(UserObject,
-                                    L"Name",
-                                    REG_SZ,
-                                    (LPVOID)Name->Buffer,
-                                    Name->MaximumLength);
+    Status = SampSetObjectAttributeString(UserObject,
+                                          L"Name",
+                                          Name);
     if (!NT_SUCCESS(Status))
     {
         TRACE("failed with status 0x%08lx\n", Status);
-        return Status;
+        goto done;
     }
 
     /* Set the FullName attribute */
-    Status = SampSetObjectAttribute(UserObject,
-                                    L"FullName",
-                                    REG_SZ,
-                                    EmptyString.Buffer,
-                                    EmptyString.MaximumLength);
+    Status = SampSetObjectAttributeString(UserObject,
+                                          L"FullName",
+                                          NULL);
     if (!NT_SUCCESS(Status))
     {
         TRACE("failed with status 0x%08lx\n", Status);
-        return Status;
+        goto done;
     }
 
     /* Set the HomeDirectory attribute */
-    Status = SampSetObjectAttribute(UserObject,
-                                    L"HomeDirectory",
-                                    REG_SZ,
-                                    EmptyString.Buffer,
-                                    EmptyString.MaximumLength);
+    Status = SampSetObjectAttributeString(UserObject,
+                                          L"HomeDirectory",
+                                          NULL);
     if (!NT_SUCCESS(Status))
     {
         TRACE("failed with status 0x%08lx\n", Status);
-        return Status;
+        goto done;
     }
 
     /* Set the HomeDirectoryDrive attribute */
-    Status = SampSetObjectAttribute(UserObject,
-                                    L"HomeDirectoryDrive",
-                                    REG_SZ,
-                                    EmptyString.Buffer,
-                                    EmptyString.MaximumLength);
+    Status = SampSetObjectAttributeString(UserObject,
+                                          L"HomeDirectoryDrive",
+                                          NULL);
     if (!NT_SUCCESS(Status))
     {
         TRACE("failed with status 0x%08lx\n", Status);
-        return Status;
+        goto done;
     }
 
     /* Set the ScriptPath attribute */
-    Status = SampSetObjectAttribute(UserObject,
-                                    L"ScriptPath",
-                                    REG_SZ,
-                                    EmptyString.Buffer,
-                                    EmptyString.MaximumLength);
+    Status = SampSetObjectAttributeString(UserObject,
+                                          L"ScriptPath",
+                                          NULL);
     if (!NT_SUCCESS(Status))
     {
         TRACE("failed with status 0x%08lx\n", Status);
-        return Status;
+        goto done;
     }
 
     /* Set the ProfilePath attribute */
-    Status = SampSetObjectAttribute(UserObject,
-                                    L"ProfilePath",
-                                    REG_SZ,
-                                    EmptyString.Buffer,
-                                    EmptyString.MaximumLength);
+    Status = SampSetObjectAttributeString(UserObject,
+                                          L"ProfilePath",
+                                          NULL);
     if (!NT_SUCCESS(Status))
     {
         TRACE("failed with status 0x%08lx\n", Status);
-        return Status;
+        goto done;
     }
 
     /* Set the AdminComment attribute */
-    Status = SampSetObjectAttribute(UserObject,
-                                    L"AdminComment",
-                                    REG_SZ,
-                                    EmptyString.Buffer,
-                                    EmptyString.MaximumLength);
+    Status = SampSetObjectAttributeString(UserObject,
+                                          L"AdminComment",
+                                          NULL);
     if (!NT_SUCCESS(Status))
     {
         TRACE("failed with status 0x%08lx\n", Status);
-        return Status;
+        goto done;
     }
 
     /* Set the UserComment attribute */
-    Status = SampSetObjectAttribute(UserObject,
-                                    L"UserComment",
-                                    REG_SZ,
-                                    EmptyString.Buffer,
-                                    EmptyString.MaximumLength);
+    Status = SampSetObjectAttributeString(UserObject,
+                                          L"UserComment",
+                                          NULL);
     if (!NT_SUCCESS(Status))
     {
         TRACE("failed with status 0x%08lx\n", Status);
-        return Status;
+        goto done;
     }
 
     /* Set the WorkStations attribute */
-    Status = SampSetObjectAttribute(UserObject,
-                                    L"WorkStations",
-                                    REG_SZ,
-                                    EmptyString.Buffer,
-                                    EmptyString.MaximumLength);
+    Status = SampSetObjectAttributeString(UserObject,
+                                          L"WorkStations",
+                                          NULL);
     if (!NT_SUCCESS(Status))
     {
         TRACE("failed with status 0x%08lx\n", Status);
-        return Status;
+        goto done;
     }
 
     /* Set the Parameters attribute */
+    Status = SampSetObjectAttributeString(UserObject,
+                                          L"Parameters",
+                                          NULL);
+    if (!NT_SUCCESS(Status))
+    {
+        TRACE("failed with status 0x%08lx\n", Status);
+        goto done;
+    }
+
+    /* Set LogonHours attribute*/
+    *((PUSHORT)LogonHours) = 168;
+    memset(&(LogonHours[2]), 0xff, 21);
+
     Status = SampSetObjectAttribute(UserObject,
-                                    L"Parameters",
-                                    REG_SZ,
-                                    EmptyString.Buffer,
-                                    EmptyString.MaximumLength);
+                                    L"LogonHours",
+                                    REG_BINARY,
+                                    &LogonHours,
+                                    sizeof(LogonHours));
     if (!NT_SUCCESS(Status))
     {
         TRACE("failed with status 0x%08lx\n", Status);
-        return Status;
+        goto done;
     }
 
-    /* FIXME: Set LogonHours attribute*/
-    /* FIXME: Set Groups attribute*/
+    /* Set Groups attribute*/
+    GroupMembership.RelativeId = DOMAIN_GROUP_RID_USERS;
+    GroupMembership.Attributes = SE_GROUP_MANDATORY |
+                                 SE_GROUP_ENABLED |
+                                 SE_GROUP_ENABLED_BY_DEFAULT;
+
+    Status = SampSetObjectAttribute(UserObject,
+                                    L"Groups",
+                                    REG_BINARY,
+                                    &GroupMembership,
+                                    sizeof(GROUP_MEMBERSHIP));
+    if (!NT_SUCCESS(Status))
+    {
+        TRACE("failed with status 0x%08lx\n", Status);
+        goto done;
+    }
 
     /* Set LMPwd attribute*/
     Status = SampSetObjectAttribute(UserObject,
@@ -7380,7 +9012,7 @@ SamrCreateUser2InDomain(IN SAMPR_HANDLE DomainHandle,
     if (!NT_SUCCESS(Status))
     {
         TRACE("failed with status 0x%08lx\n", Status);
-        return Status;
+        goto done;
     }
 
     /* Set NTPwd attribute*/
@@ -7392,7 +9024,7 @@ SamrCreateUser2InDomain(IN SAMPR_HANDLE DomainHandle,
     if (!NT_SUCCESS(Status))
     {
         TRACE("failed with status 0x%08lx\n", Status);
-        return Status;
+        goto done;
     }
 
     /* Set LMPwdHistory attribute*/
@@ -7404,7 +9036,7 @@ SamrCreateUser2InDomain(IN SAMPR_HANDLE DomainHandle,
     if (!NT_SUCCESS(Status))
     {
         TRACE("failed with status 0x%08lx\n", Status);
-        return Status;
+        goto done;
     }
 
     /* Set NTPwdHistory attribute*/
@@ -7416,10 +9048,30 @@ SamrCreateUser2InDomain(IN SAMPR_HANDLE DomainHandle,
     if (!NT_SUCCESS(Status))
     {
         TRACE("failed with status 0x%08lx\n", Status);
-        return Status;
+        goto done;
+    }
+
+    /* Set the PrivateData attribute */
+    Status = SampSetObjectAttributeString(UserObject,
+                                          L"PrivateData",
+                                          NULL);
+    if (!NT_SUCCESS(Status))
+    {
+        TRACE("failed with status 0x%08lx\n", Status);
+        goto done;
     }
 
-    /* FIXME: Set SecDesc attribute*/
+    /* Set the SecDesc attribute*/
+    Status = SampSetObjectAttribute(UserObject,
+                                    L"SecDesc",
+                                    REG_BINARY,
+                                    Sd,
+                                    SdSize);
+    if (!NT_SUCCESS(Status))
+    {
+        TRACE("failed with status 0x%08lx\n", Status);
+        goto done;
+    }
 
     if (NT_SUCCESS(Status))
     {
@@ -7428,6 +9080,15 @@ SamrCreateUser2InDomain(IN SAMPR_HANDLE DomainHandle,
         *GrantedAccess = UserObject->Access;
     }
 
+done:
+    if (Sd != NULL)
+        RtlFreeHeap(RtlGetProcessHeap(), 0, Sd);
+
+    if (UserSid != NULL)
+        RtlFreeHeap(RtlGetProcessHeap(), 0, UserSid);
+
+    RtlReleaseResource(&SampResource);
+
     TRACE("returns with status 0x%08lx\n", Status);
 
     return Status;
@@ -7558,6 +9219,7 @@ SamrGetDomainPasswordInformation(IN handle_t BindingHandle,
     return STATUS_NOT_IMPLEMENTED;
 }
 
+
 /* Function 57 */
 NTSTATUS
 NTAPI
@@ -7565,8 +9227,11 @@ SamrConnect2(IN PSAMPR_SERVER_NAME ServerName,
              OUT SAMPR_HANDLE *ServerHandle,
              IN ACCESS_MASK DesiredAccess)
 {
-    UNIMPLEMENTED;
-    return STATUS_NOT_IMPLEMENTED;
+    TRACE("(%p %p %lx)\n", ServerName, ServerHandle, DesiredAccess);
+
+    return SamrConnect(ServerName,
+                       ServerHandle,
+                       DesiredAccess);
 }