[SAMSRV]
[reactos.git] / reactos / dll / win32 / samsrv / samrpc.c
index acd34d6..385187c 100644 (file)
@@ -57,9 +57,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)
 {
@@ -191,7 +209,7 @@ NTSTATUS
 NTAPI
 SamrQuerySecurityObject(IN SAMPR_HANDLE ObjectHandle,
                         IN SECURITY_INFORMATION SecurityInformation,
-                        OUT PSAMPR_SR_SECURITY_DESCRIPTOR * SecurityDescriptor)
+                        OUT PSAMPR_SR_SECURITY_DESCRIPTOR *SecurityDescriptor)
 {
     UNIMPLEMENTED;
     return STATUS_NOT_IMPLEMENTED;
@@ -203,8 +221,23 @@ NTSTATUS
 NTAPI
 SamrShutdownSamServer(IN SAMPR_HANDLE ServerHandle)
 {
-    UNIMPLEMENTED;
-    return STATUS_NOT_IMPLEMENTED;
+    PSAM_DB_OBJECT ServerObject;
+    NTSTATUS Status;
+
+    TRACE("(%p)\n", ServerHandle);
+
+    /* Validate the server handle */
+    Status = SampValidateDbObject(ServerHandle,
+                                  SamDbServerObject,
+                                  SAM_SERVER_SHUTDOWN,
+                                  &ServerObject);
+    if (!NT_SUCCESS(Status))
+        return Status;
+
+    /* Shut the server down */
+    RpcMgmtStopServerListening(0);
+
+    return STATUS_SUCCESS;
 }
 
 
@@ -2063,6 +2096,8 @@ SamrCreateUserInDomain(IN SAMPR_HANDLE DomainHandle,
     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];
@@ -2165,6 +2200,7 @@ SamrCreateUserInDomain(IN SAMPR_HANDLE DomainHandle,
     /* Initialize fixed user data */
     memset(&FixedUserData, 0, sizeof(SAM_USER_FIXED_DATA));
     FixedUserData.Version = 1;
+    FixedUserData.Reserved = 0;
     FixedUserData.LastLogon.QuadPart = 0;
     FixedUserData.LastLogoff.QuadPart = 0;
     FixedUserData.PasswordLastSet.QuadPart = 0;
@@ -2176,6 +2212,12 @@ SamrCreateUserInDomain(IN SAMPR_HANDLE DomainHandle,
     FixedUserData.UserAccountControl = USER_ACCOUNT_DISABLED |
                                        USER_PASSWORD_NOT_REQUIRED |
                                        USER_NORMAL_ACCOUNT;
+    FixedUserData.CountryCode = 0;
+    FixedUserData.CodePage = 0;
+    FixedUserData.BadPasswordCount = 0;
+    FixedUserData.LogonCount = 0;
+    FixedUserData.AdminCount = 0;
+    FixedUserData.OperatorCount = 0;
 
     /* Set fixed user data attribute */
     Status = SampSetObjectAttribute(UserObject,
@@ -2297,7 +2339,99 @@ SamrCreateUserInDomain(IN SAMPR_HANDLE DomainHandle,
         return Status;
     }
 
-    /* FIXME: Set default user attributes */
+    /* Set the Parameters attribute */
+    Status = SampSetObjectAttribute(UserObject,
+                                    L"Parameters",
+                                    REG_SZ,
+                                    EmptyString.Buffer,
+                                    EmptyString.MaximumLength);
+    if (!NT_SUCCESS(Status))
+    {
+        TRACE("failed with status 0x%08lx\n", Status);
+        return Status;
+    }
+
+    /* Set LogonHours attribute*/
+    *((PUSHORT)LogonHours) = 168;
+    memset(&(LogonHours[2]), 0xff, 21);
+
+    Status = SampSetObjectAttribute(UserObject,
+                                    L"LogonHours",
+                                    REG_BINARY,
+                                    &LogonHours,
+                                    sizeof(LogonHours));
+    if (!NT_SUCCESS(Status))
+    {
+        TRACE("failed with status 0x%08lx\n", Status);
+        return Status;
+    }
+
+    /* 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);
+        return Status;
+    }
+
+    /* 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);
+        return Status;
+    }
+
+    /* Set NTPwd attribute*/
+    Status = SampSetObjectAttribute(UserObject,
+                                    L"NTPwd",
+                                    REG_BINARY,
+                                    &EmptyNtHash,
+                                    sizeof(ENCRYPTED_NT_OWF_PASSWORD));
+    if (!NT_SUCCESS(Status))
+    {
+        TRACE("failed with status 0x%08lx\n", Status);
+        return Status;
+    }
+
+    /* Set LMPwdHistory attribute*/
+    Status = SampSetObjectAttribute(UserObject,
+                                    L"LMPwdHistory",
+                                    REG_BINARY,
+                                    NULL,
+                                    0);
+    if (!NT_SUCCESS(Status))
+    {
+        TRACE("failed with status 0x%08lx\n", Status);
+        return Status;
+    }
+
+    /* Set NTPwdHistory attribute*/
+    Status = SampSetObjectAttribute(UserObject,
+                                    L"NTPwdHistory",
+                                    REG_BINARY,
+                                    NULL,
+                                    0);
+    if (!NT_SUCCESS(Status))
+    {
+        TRACE("failed with status 0x%08lx\n", Status);
+        return Status;
+    }
+
+    /* FIXME: Set SecDesc attribute*/
 
     if (NT_SUCCESS(Status))
     {
@@ -2877,6 +3011,7 @@ SamrGetAliasMembership(IN SAMPR_HANDLE DomainHandle,
     ULONG DataLength;
     ULONG i, j;
     NTSTATUS Status;
+    WCHAR NameBuffer[9];
 
     TRACE("SamrGetAliasMembership(%p %p %p)\n",
           DomainHandle, SidArray, Membership);
@@ -2884,7 +3019,7 @@ SamrGetAliasMembership(IN SAMPR_HANDLE DomainHandle,
     /* Validate the domain handle */
     Status = SampValidateDbObject(DomainHandle,
                                   SamDbDomainObject,
-                                  DOMAIN_LOOKUP,
+                                  DOMAIN_GET_ALIAS_MEMBERSHIP,
                                   &DomainObject);
     if (!NT_SUCCESS(Status))
         return Status;
@@ -2902,6 +3037,13 @@ SamrGetAliasMembership(IN SAMPR_HANDLE DomainHandle,
                             KEY_READ,
                             &MembersKeyHandle);
     TRACE("SampRegOpenKey returned %08lX\n", Status);
+
+    if (Status == STATUS_OBJECT_NAME_NOT_FOUND)
+    {
+        Status = STATUS_SUCCESS;
+        goto done;
+    }
+
     if (!NT_SUCCESS(Status))
         goto done;
 
@@ -2926,13 +3068,21 @@ TRACE("Open %S\n", MemberSidString);
                 MaxSidCount += ValueCount;
             }
 
-
             NtClose(MemberKeyHandle);
         }
 
+        if (Status == STATUS_OBJECT_NAME_NOT_FOUND)
+            Status = STATUS_SUCCESS;
+
         LocalFree(MemberSidString);
     }
 
+    if (MaxSidCount == 0)
+    {
+        Status = STATUS_SUCCESS;
+        goto done;
+    }
+
     TRACE("Maximum sid count: %lu\n", MaxSidCount);
     RidArray = midl_user_allocate(MaxSidCount * sizeof(ULONG));
     if (RidArray == NULL)
@@ -2962,14 +3112,18 @@ TRACE("Open %S\n", MemberSidString);
 
                 for (j = 0; j < ValueCount; j++)
                 {
-                    DataLength = sizeof(ULONG);
+                    DataLength = 9 * sizeof(WCHAR);
                     Status = SampRegEnumerateValue(MemberKeyHandle,
                                                    j,
-                      NULL,
-                      NULL,
-                      NULL,
-                      (PVOID)&RidArray[j],
-                      &DataLength);
+                                                   NameBuffer,
+                                                   &DataLength,
+                                                   NULL,
+                                                   NULL,
+                                                   NULL);
+                    if (NT_SUCCESS(Status))
+                    {
+                        RidArray[j] = wcstoul(NameBuffer, NULL, 16);
+                    }
                 }
             }
 
@@ -2979,7 +3133,6 @@ TRACE("Open %S\n", MemberSidString);
         LocalFree(MemberSidString);
     }
 
-
 done:
     if (NT_SUCCESS(Status))
     {
@@ -3562,8 +3715,8 @@ SampQueryGroupGeneral(PSAM_DB_OBJECT GroupObject,
                       PSAMPR_GROUP_INFO_BUFFER *Buffer)
 {
     PSAMPR_GROUP_INFO_BUFFER InfoBuffer = NULL;
-    HANDLE MembersKeyHandle = NULL;
     SAM_GROUP_FIXED_DATA FixedData;
+    ULONG MembersLength = 0;
     ULONG Length = 0;
     NTSTATUS Status;
 
@@ -3602,33 +3755,22 @@ SampQueryGroupGeneral(PSAM_DB_OBJECT GroupObject,
 
     InfoBuffer->General.Attributes = FixedData.Attributes;
 
-    /* Open the Members subkey */
-    Status = SampRegOpenKey(GroupObject->KeyHandle,
-                            L"Members",
-                            KEY_READ,
-                            &MembersKeyHandle);
-    if (!NT_SUCCESS(Status))
-    {
-        TRACE("Status 0x%08lx\n", Status);
+    Status = SampGetObjectAttribute(GroupObject,
+                                    L"Members",
+                                    NULL,
+                                    NULL,
+                                    &MembersLength);
+    if (!NT_SUCCESS(Status) && Status != STATUS_OBJECT_NAME_NOT_FOUND)
         goto done;
-    }
 
-    /* Retrieve the number of members of the alias */
-    Status = SampRegQueryKeyInfo(MembersKeyHandle,
-                                 NULL,
-                                 &InfoBuffer->General.MemberCount);
-    if (!NT_SUCCESS(Status))
-    {
-        TRACE("Status 0x%08lx\n", Status);
-        goto done;
-    }
+    if (Status == STATUS_OBJECT_NAME_NOT_FOUND)
+        InfoBuffer->General.MemberCount = 0;
+    else
+        InfoBuffer->General.MemberCount = MembersLength / sizeof(ULONG);
 
     *Buffer = InfoBuffer;
 
 done:
-    if (MembersKeyHandle != NULL)
-        SampRegCloseKey(MembersKeyHandle);
-
     if (!NT_SUCCESS(Status))
     {
         if (InfoBuffer != NULL)
@@ -3820,6 +3962,76 @@ SamrQueryInformationGroup(IN SAMPR_HANDLE GroupHandle,
 }
 
 
+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;
+    }
+
+    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 = SampSetObjectAttribute(GroupObject,
+                                    L"Name",
+                                    REG_SZ,
+                                    NewGroupName.Buffer,
+                                    NewGroupName.Length + sizeof(WCHAR));
+    if (!NT_SUCCESS(Status))
+    {
+        TRACE("SampSetObjectAttribute failed (Status 0x%08lx)\n", Status);
+    }
+
+done:
+    if (OldGroupName.Buffer != NULL)
+        midl_user_free(OldGroupName.Buffer);
+
+    return Status;
+}
+
+
 static NTSTATUS
 SampSetGroupAttribute(PSAM_DB_OBJECT GroupObject,
                       PSAMPR_GROUP_INFO_BUFFER Buffer)
@@ -3874,11 +4086,8 @@ SamrSetInformationGroup(IN SAMPR_HANDLE GroupHandle,
     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:
@@ -3910,38 +4119,276 @@ SamrAddMemberToGroup(IN SAMPR_HANDLE GroupHandle,
                      IN unsigned long MemberId,
                      IN unsigned long Attributes)
 {
-    UNIMPLEMENTED;
-    return STATUS_NOT_IMPLEMENTED;
+    PSAM_DB_OBJECT GroupObject;
+    PSAM_DB_OBJECT UserObject = NULL;
+    NTSTATUS Status;
+
+    TRACE("(%p %lu %lx)\n",
+          GroupHandle, MemberId, Attributes);
+
+    /* Validate the group handle */
+    Status = SampValidateDbObject(GroupHandle,
+                                  SamDbGroupObject,
+                                  GROUP_ADD_MEMBER,
+                                  &GroupObject);
+    if (!NT_SUCCESS(Status))
+        return Status;
+
+    /* Open the user object in the same domain */
+    Status = SampOpenUserObject(GroupObject->ParentObject,
+                                MemberId,
+                                0,
+                                &UserObject);
+    if (!NT_SUCCESS(Status))
+    {
+        TRACE("SampOpenUserObject() failed (Status 0x%08lx)\n", Status);
+        goto done;
+    }
+
+    /* Add group membership to the user object */
+    Status = SampAddGroupMembershipToUser(UserObject,
+                                          GroupObject->RelativeId,
+                                          Attributes);
+    if (!NT_SUCCESS(Status))
+    {
+        TRACE("SampAddGroupMembershipToUser() failed (Status 0x%08lx)\n", Status);
+        goto done;
+    }
+
+    /* Add the member to the group object */
+    Status = SampAddMemberToGroup(GroupObject,
+                                  MemberId);
+    if (!NT_SUCCESS(Status))
+    {
+        TRACE("SampAddMemberToGroup() failed (Status 0x%08lx)\n", Status);
+    }
+
+done:
+    if (UserObject)
+        SampCloseDbObject(UserObject);
+
+    return Status;
 }
 
+
 /* Function 21 */
 NTSTATUS
 NTAPI
 SamrDeleteGroup(IN OUT SAMPR_HANDLE *GroupHandle)
 {
-    UNIMPLEMENTED;
-    return STATUS_NOT_IMPLEMENTED;
+    PSAM_DB_OBJECT GroupObject;
+    ULONG Length = 0;
+    NTSTATUS Status;
+
+    TRACE("(%p)\n", GroupHandle);
+
+    /* Validate the group handle */
+    Status = SampValidateDbObject(*GroupHandle,
+                                  SamDbGroupObject,
+                                  DELETE,
+                                  &GroupObject);
+    if (!NT_SUCCESS(Status))
+    {
+        TRACE("SampValidateDbObject() failed (Status 0x%08lx)\n", Status);
+        return Status;
+    }
+
+    /* Fail, if the group is built-in */
+    if (GroupObject->RelativeId < 1000)
+    {
+        TRACE("You can not delete a special account!\n");
+        return STATUS_SPECIAL_ACCOUNT;
+    }
+
+    /* Get the length of the Members attribute */
+    SampGetObjectAttribute(GroupObject,
+                           L"Members",
+                           NULL,
+                           NULL,
+                           &Length);
+
+    /* Fail, if the group has members */
+    if (Length != 0)
+    {
+        TRACE("There are still members in the group!\n");
+        return STATUS_MEMBER_IN_GROUP;
+    }
+
+    /* FIXME: Remove the group from all aliases */
+
+    /* Delete the group from the database */
+    Status = SampDeleteAccountDbObject(GroupObject);
+    if (!NT_SUCCESS(Status))
+    {
+        TRACE("SampDeleteAccountDbObject() failed (Status 0x%08lx)\n", Status);
+        return Status;
+    }
+
+    /* Invalidate the handle */
+    *GroupHandle = NULL;
+
+    return STATUS_SUCCESS;
 }
 
+
 /* Function 24 */
 NTSTATUS
 NTAPI
 SamrRemoveMemberFromGroup(IN SAMPR_HANDLE GroupHandle,
                           IN unsigned long MemberId)
 {
-    UNIMPLEMENTED;
-    return STATUS_NOT_IMPLEMENTED;
-}
+    PSAM_DB_OBJECT GroupObject;
+    PSAM_DB_OBJECT UserObject = NULL;
+    NTSTATUS Status;
+
+    TRACE("(%p %lu)\n",
+          GroupHandle, MemberId);
+
+    /* Validate the group handle */
+    Status = SampValidateDbObject(GroupHandle,
+                                  SamDbGroupObject,
+                                  GROUP_REMOVE_MEMBER,
+                                  &GroupObject);
+    if (!NT_SUCCESS(Status))
+        return Status;
+
+    /* Open the user object in the same domain */
+    Status = SampOpenUserObject(GroupObject->ParentObject,
+                                MemberId,
+                                0,
+                                &UserObject);
+    if (!NT_SUCCESS(Status))
+    {
+        ERR("SampOpenUserObject() failed (Status 0x%08lx)\n", Status);
+        goto done;
+    }
+
+    /* Remove group membership from the user object */
+    Status = SampRemoveGroupMembershipFromUser(UserObject,
+                                               GroupObject->RelativeId);
+    if (!NT_SUCCESS(Status))
+    {
+        ERR("SampAddGroupMembershipToUser() failed (Status 0x%08lx)\n", Status);
+        goto done;
+    }
+
+    /* Remove the member from the group object */
+    Status = SampRemoveMemberFromGroup(GroupObject,
+                                       MemberId);
+    if (!NT_SUCCESS(Status))
+    {
+        ERR("SampRemoveMemberFromGroup() failed (Status 0x%08lx)\n", Status);
+    }
+
+done:
+    if (UserObject)
+        SampCloseDbObject(UserObject);
+
+    return Status;
+}
+
+
+/* Function 25 */
+NTSTATUS
+NTAPI
+SamrGetMembersInGroup(IN SAMPR_HANDLE GroupHandle,
+                      OUT PSAMPR_GET_MEMBERS_BUFFER *Members)
+{
+    PSAMPR_GET_MEMBERS_BUFFER MembersBuffer = NULL;
+    PSAM_DB_OBJECT GroupObject;
+    ULONG Length = 0;
+    ULONG i;
+    NTSTATUS Status;
+
+    /* Validate the group handle */
+    Status = SampValidateDbObject(GroupHandle,
+                                  SamDbGroupObject,
+                                  GROUP_LIST_MEMBERS,
+                                  &GroupObject);
+    if (!NT_SUCCESS(Status))
+        return Status;
+
+    MembersBuffer = midl_user_allocate(sizeof(SAMPR_GET_MEMBERS_BUFFER));
+    if (MembersBuffer == NULL)
+        return STATUS_INSUFFICIENT_RESOURCES;
+
+    SampGetObjectAttribute(GroupObject,
+                           L"Members",
+                           NULL,
+                           NULL,
+                           &Length);
+
+    if (Length == 0)
+    {
+        MembersBuffer->MemberCount = 0;
+        MembersBuffer->Members = NULL;
+        MembersBuffer->Attributes = NULL;
+
+        *Members = MembersBuffer;
+
+        return STATUS_SUCCESS;
+    }
+
+    MembersBuffer->Members = midl_user_allocate(Length);
+    if (MembersBuffer->Members == NULL)
+    {
+        Status = STATUS_INSUFFICIENT_RESOURCES;
+        goto done;
+    }
+
+    MembersBuffer->Attributes = midl_user_allocate(Length);
+    if (MembersBuffer->Attributes == NULL)
+    {
+        Status = STATUS_INSUFFICIENT_RESOURCES;
+        goto done;
+    }
+
+    Status = SampGetObjectAttribute(GroupObject,
+                                    L"Members",
+                                    NULL,
+                                    MembersBuffer->Members,
+                                    &Length);
+    if (!NT_SUCCESS(Status))
+    {
+        TRACE("SampGetObjectAttributes() failed (Status 0x%08lx)\n", Status);
+        goto done;
+    }
+
+    MembersBuffer->MemberCount = Length / sizeof(ULONG);
+
+    for (i = 0; i < MembersBuffer->MemberCount; i++)
+    {
+        Status = SampGetUserGroupAttributes(GroupObject->ParentObject,
+                                            MembersBuffer->Members[i],
+                                            GroupObject->RelativeId,
+                                            &(MembersBuffer->Attributes[i]));
+        if (!NT_SUCCESS(Status))
+        {
+            TRACE("SampGetUserGroupAttributes() failed (Status 0x%08lx)\n", Status);
+            goto done;
+        }
+    }
+
+    *Members = MembersBuffer;
+
+done:
+    if (!NT_SUCCESS(Status))
+    {
+        if (MembersBuffer != NULL)
+        {
+            if (MembersBuffer->Members != NULL)
+                midl_user_free(MembersBuffer->Members);
+
+            if (MembersBuffer->Attributes != NULL)
+                midl_user_free(MembersBuffer->Attributes);
+
+            midl_user_free(MembersBuffer);
+        }
+    }
+
+    return Status;
+}
 
-/* Function 25 */
-NTSTATUS
-NTAPI
-SamrGetMembersInGroup(IN SAMPR_HANDLE GroupHandle,
-                      OUT PSAMPR_GET_MEMBERS_BUFFER *Members)
-{
-    UNIMPLEMENTED;
-    return STATUS_NOT_IMPLEMENTED;
-}
 
 /* Function 26 */
 NTSTATUS
@@ -3950,8 +4397,30 @@ SamrSetMemberAttributesOfGroup(IN SAMPR_HANDLE GroupHandle,
                                IN unsigned long MemberId,
                                IN unsigned long Attributes)
 {
-    UNIMPLEMENTED;
-    return STATUS_NOT_IMPLEMENTED;
+    PSAM_DB_OBJECT GroupObject;
+    NTSTATUS Status;
+
+    /* Validate the group handle */
+    Status = SampValidateDbObject(GroupHandle,
+                                  SamDbGroupObject,
+                                  GROUP_ADD_MEMBER,
+                                  &GroupObject);
+    if (!NT_SUCCESS(Status))
+    {
+        TRACE("SampValidateDbObject failed with status 0x%08lx\n", Status);
+        return Status;
+    }
+
+    Status = SampSetUserGroupAttributes(GroupObject->ParentObject,
+                                        MemberId,
+                                        GroupObject->RelativeId,
+                                        Attributes);
+    if (!NT_SUCCESS(Status))
+    {
+        TRACE("SampSetUserGroupAttributes failed with status 0x%08lx\n", Status);
+    }
+
+    return Status;
 }
 
 
@@ -4220,6 +4689,76 @@ SamrQueryInformationAlias(IN SAMPR_HANDLE AliasHandle,
 }
 
 
+static NTSTATUS
+SampSetAliasName(PSAM_DB_OBJECT AliasObject,
+                 PSAMPR_ALIAS_INFO_BUFFER Buffer)
+{
+    UNICODE_STRING OldAliasName = {0, 0, NULL};
+    UNICODE_STRING NewAliasName;
+    NTSTATUS Status;
+
+    Status = SampGetObjectAttributeString(AliasObject,
+                                          L"Name",
+                                          (PRPC_UNICODE_STRING)&OldAliasName);
+    if (!NT_SUCCESS(Status))
+    {
+        TRACE("SampGetObjectAttributeString failed (Status 0x%08lx)\n", Status);
+        goto done;
+    }
+
+    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 = SampSetObjectAttribute(AliasObject,
+                                    L"Name",
+                                    REG_SZ,
+                                    NewAliasName.Buffer,
+                                    NewAliasName.Length + sizeof(WCHAR));
+    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
@@ -4244,11 +4783,8 @@ SamrSetInformationAlias(IN SAMPR_HANDLE AliasHandle,
     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:
@@ -4272,120 +4808,60 @@ SamrSetInformationAlias(IN SAMPR_HANDLE AliasHandle,
 NTSTATUS
 NTAPI
 SamrDeleteAlias(IN OUT SAMPR_HANDLE *AliasHandle)
-{
-    UNIMPLEMENTED;
-    return STATUS_NOT_IMPLEMENTED;
-}
-
-
-/* Function 31 */
-NTSTATUS
-NTAPI
-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);
-
     /* Validate the alias handle */
-    Status = SampValidateDbObject(AliasHandle,
+    Status = SampValidateDbObject(*AliasHandle,
                                   SamDbAliasObject,
-                                  ALIAS_ADD_MEMBER,
+                                  DELETE,
                                   &AliasObject);
     if (!NT_SUCCESS(Status))
     {
-        TRACE("failed with status 0x%08lx\n", Status);
+        TRACE("SampValidateDbObject failed (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))
+    /* Fail, if the alias is built-in */
+    if (AliasObject->RelativeId < 1000)
     {
-        TRACE("SampRegCreateKey failed with status 0x%08lx\n", Status);
-        goto done;
+        TRACE("You can not delete a special account!\n");
+        return STATUS_SPECIAL_ACCOUNT;
     }
 
-    Status = SampRegSetValue(MembersKeyHandle,
-                             MemberIdString,
-                             REG_BINARY,
-                             MemberId,
-                             MemberIdLength);
-    if (!NT_SUCCESS(Status))
-    {
-        TRACE("SampRegSetValue failed with status 0x%08lx\n", Status);
-        goto done;
-    }
+    /* FIXME: Remove all members from the alias */
 
-    Status = SampRegCreateKey(AliasObject->MembersKeyHandle,
-                              MemberIdString,
-                              KEY_WRITE,
-                              &MemberKeyHandle);
+    /* Delete the alias from the database */
+    Status = SampDeleteAccountDbObject(AliasObject);
     if (!NT_SUCCESS(Status))
     {
-        TRACE("SampRegCreateKey failed with status 0x%08lx\n", Status);
-        goto done;
-    }
-
-    Status = SampRegSetValue(MemberKeyHandle,
-                             AliasObject->Name,
-                             REG_BINARY,
-                             MemberId,
-                             MemberIdLength);
-    if (!NT_SUCCESS(Status))
-    {
-        TRACE("SampRegSetValue failed with status 0x%08lx\n", Status);
-        goto done;
+        TRACE("SampDeleteAccountDbObject() failed (Status 0x%08lx)\n", Status);
+        return Status;
     }
 
-done:
-    if (MemberKeyHandle != NULL)
-        SampRegCloseKey(MemberKeyHandle);
-
-    if (MembersKeyHandle != NULL)
-        SampRegCloseKey(MembersKeyHandle);
-
-    if (MemberIdString != NULL)
-        LocalFree(MemberIdString);
+    /* Invalidate the handle */
+    *AliasHandle = NULL;
 
     return Status;
 }
 
 
-/* Function 32 */
+/* Function 31 */
 NTSTATUS
 NTAPI
-SamrRemoveMemberFromAlias(IN SAMPR_HANDLE AliasHandle,
-                          IN PRPC_SID MemberId)
+SamrAddMemberToAlias(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);
 
     /* Validate the alias handle */
     Status = SampValidateDbObject(AliasHandle,
                                   SamDbAliasObject,
-                                  ALIAS_REMOVE_MEMBER,
+                                  ALIAS_ADD_MEMBER,
                                   &AliasObject);
     if (!NT_SUCCESS(Status))
     {
@@ -4393,101 +4869,46 @@ SamrRemoveMemberFromAlias(IN SAMPR_HANDLE AliasHandle,
         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);
+    Status = SampAddMemberToAlias(AliasObject,
+                                  MemberId);
     if (!NT_SUCCESS(Status))
     {
-        TRACE("SampRegDeleteValue failed with status 0x%08lx\n", Status);
-        goto done;
+        TRACE("failed with status 0x%08lx\n", Status);
     }
 
-    Status = SampRegQueryKeyInfo(MemberKeyHandle,
-                                 NULL,
-                                 &ulValueCount);
-    if (!NT_SUCCESS(Status))
-    {
-        TRACE("SampRegQueryKeyInfo failed with status 0x%08lx\n", Status);
-        goto done;
-    }
+    return Status;
+}
 
-    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;
-        }
-    }
+/* Function 32 */
+NTSTATUS
+NTAPI
+SamrRemoveMemberFromAlias(IN SAMPR_HANDLE AliasHandle,
+                          IN PRPC_SID MemberId)
+{
+    PSAM_DB_OBJECT AliasObject;
+    NTSTATUS Status;
 
-    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;
-    }
+    TRACE("(%p %p)\n", AliasHandle, MemberId);
 
-    Status = SampRegDeleteValue(MembersKeyHandle,
-                                MemberIdString);
+    /* Validate the alias handle */
+    Status = SampValidateDbObject(AliasHandle,
+                                  SamDbAliasObject,
+                                  ALIAS_REMOVE_MEMBER,
+                                  &AliasObject);
     if (!NT_SUCCESS(Status))
     {
-        TRACE("SampRegDeleteValue failed with status 0x%08lx\n", Status);
-        goto done;
+        TRACE("failed with status 0x%08lx\n", Status);
+        return Status;
     }
 
-    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);
-
     return Status;
 }
 
@@ -4683,8 +5104,45 @@ NTSTATUS
 NTAPI
 SamrDeleteUser(IN OUT SAMPR_HANDLE *UserHandle)
 {
-    UNIMPLEMENTED;
-    return STATUS_NOT_IMPLEMENTED;
+    PSAM_DB_OBJECT UserObject;
+    NTSTATUS Status;
+
+    TRACE("(%p)\n", UserHandle);
+
+    /* Validate the user handle */
+    Status = SampValidateDbObject(*UserHandle,
+                                  SamDbUserObject,
+                                  DELETE,
+                                  &UserObject);
+    if (!NT_SUCCESS(Status))
+    {
+        TRACE("SampValidateDbObject() failed (Status 0x%08lx)\n", Status);
+        return Status;
+    }
+
+    /* Fail, if the user is built-in */
+    if (UserObject->RelativeId < 1000)
+    {
+        TRACE("You can not delete a special account!\n");
+        return STATUS_SPECIAL_ACCOUNT;
+    }
+
+    /* FIXME: Remove the user from all groups */
+
+    /* FIXME: Remove the user from all aliases */
+
+    /* Delete the user from the database */
+    Status = SampDeleteAccountDbObject(UserObject);
+    if (!NT_SUCCESS(Status))
+    {
+        TRACE("SampDeleteAccountDbObject() failed (Status 0x%08lx)\n", Status);
+        return Status;
+    }
+
+    /* Invalidate the handle */
+    *UserHandle = NULL;
+
+    return STATUS_SUCCESS;
 }
 
 
@@ -4844,7 +5302,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;
 
@@ -4854,6 +5315,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",
@@ -4875,8 +5347,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,
@@ -4948,7 +5427,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;
 
@@ -5094,22 +5580,98 @@ SampQueryUserAccount(PSAM_DB_OBJECT UserObject,
                                           &InfoBuffer->Account.AdminComment);
     if (!NT_SUCCESS(Status))
     {
-        TRACE("Status 0x%08lx\n", Status);
-        goto done;
+        TRACE("Status 0x%08lx\n", Status);
+        goto done;
+    }
+
+    /* Get the WorkStations string */
+    Status = SampGetObjectAttributeString(UserObject,
+                                          L"WorkStations",
+                                          &InfoBuffer->Account.WorkStations);
+    if (!NT_SUCCESS(Status))
+    {
+        TRACE("Status 0x%08lx\n", Status);
+        goto done;
+    }
+
+    /* Get the LogonHours attribute */
+    Status = SampGetLogonHoursAttrbute(UserObject,
+                                       &InfoBuffer->Account.LogonHours);
+    if (!NT_SUCCESS(Status))
+    {
+        TRACE("Status 0x%08lx\n", Status);
+        goto done;
+    }
+
+    *Buffer = InfoBuffer;
+
+done:
+    if (!NT_SUCCESS(Status))
+    {
+        if (InfoBuffer != NULL)
+        {
+            if (InfoBuffer->Account.UserName.Buffer != NULL)
+                midl_user_free(InfoBuffer->Account.UserName.Buffer);
+
+            if (InfoBuffer->Account.FullName.Buffer != NULL)
+                midl_user_free(InfoBuffer->Account.FullName.Buffer);
+
+            if (InfoBuffer->Account.HomeDirectory.Buffer != NULL)
+                midl_user_free(InfoBuffer->Account.HomeDirectory.Buffer);
+
+            if (InfoBuffer->Account.HomeDirectoryDrive.Buffer != NULL)
+                midl_user_free(InfoBuffer->Account.HomeDirectoryDrive.Buffer);
+
+            if (InfoBuffer->Account.ScriptPath.Buffer != NULL)
+                midl_user_free(InfoBuffer->Account.ScriptPath.Buffer);
+
+            if (InfoBuffer->Account.ProfilePath.Buffer != NULL)
+                midl_user_free(InfoBuffer->Account.ProfilePath.Buffer);
+
+            if (InfoBuffer->Account.AdminComment.Buffer != NULL)
+                midl_user_free(InfoBuffer->Account.AdminComment.Buffer);
+
+            if (InfoBuffer->Account.WorkStations.Buffer != NULL)
+                midl_user_free(InfoBuffer->Account.WorkStations.Buffer);
+
+            if (InfoBuffer->Account.LogonHours.LogonHours != NULL)
+                midl_user_free(InfoBuffer->Account.LogonHours.LogonHours);
+
+            midl_user_free(InfoBuffer);
+        }
+    }
+
+    return Status;
+}
+
+
+static
+NTSTATUS
+SampQueryUserLogonHours(PSAM_DB_OBJECT UserObject,
+                        PSAMPR_USER_INFO_BUFFER *Buffer)
+{
+    PSAMPR_USER_INFO_BUFFER InfoBuffer = NULL;
+    NTSTATUS Status;
+
+    TRACE("(%p %p)\n", UserObject, Buffer);
+
+    *Buffer = NULL;
+
+    InfoBuffer = midl_user_allocate(sizeof(SAMPR_USER_INFO_BUFFER));
+    if (InfoBuffer == NULL)
+    {
+        TRACE("Failed to allocate InfoBuffer!\n");
+        return STATUS_INSUFFICIENT_RESOURCES;
     }
 
-    /* Get the WorkStations string */
-    Status = SampGetObjectAttributeString(UserObject,
-                                          L"WorkStations",
-                                          &InfoBuffer->Account.WorkStations);
+    Status = SampGetLogonHoursAttrbute(UserObject,
+                                       &InfoBuffer->LogonHours.LogonHours);
     if (!NT_SUCCESS(Status))
     {
-        TRACE("Status 0x%08lx\n", Status);
+        TRACE("SampGetLogonHoursAttrbute failed (Status 0x%08lx)\n", Status);
         goto done;
     }
 
-    /* FIXME: LogonHours */
-
     *Buffer = InfoBuffer;
 
 done:
@@ -5117,32 +5679,8 @@ done:
     {
         if (InfoBuffer != NULL)
         {
-            if (InfoBuffer->Account.UserName.Buffer != NULL)
-                midl_user_free(InfoBuffer->Account.UserName.Buffer);
-
-            if (InfoBuffer->Account.FullName.Buffer != NULL)
-                midl_user_free(InfoBuffer->Account.FullName.Buffer);
-
-            if (InfoBuffer->Account.HomeDirectory.Buffer != NULL)
-                midl_user_free(InfoBuffer->Account.HomeDirectory.Buffer);
-
-            if (InfoBuffer->Account.HomeDirectoryDrive.Buffer != NULL)
-                midl_user_free(InfoBuffer->Account.HomeDirectoryDrive.Buffer);
-
-            if (InfoBuffer->Account.ScriptPath.Buffer != NULL)
-                midl_user_free(InfoBuffer->Account.ScriptPath.Buffer);
-
-            if (InfoBuffer->Account.ProfilePath.Buffer != NULL)
-                midl_user_free(InfoBuffer->Account.ProfilePath.Buffer);
-
-            if (InfoBuffer->Account.AdminComment.Buffer != NULL)
-                midl_user_free(InfoBuffer->Account.AdminComment.Buffer);
-
-            if (InfoBuffer->Account.WorkStations.Buffer != NULL)
-                midl_user_free(InfoBuffer->Account.WorkStations.Buffer);
-
-            if (InfoBuffer->Account.LogonHours.LogonHours != NULL)
-                midl_user_free(InfoBuffer->Account.LogonHours.LogonHours);
+            if (InfoBuffer->LogonHours.LogonHours.LogonHours != NULL)
+                midl_user_free(InfoBuffer->LogonHours.LogonHours.LogonHours);
 
             midl_user_free(InfoBuffer);
         }
@@ -5151,7 +5689,6 @@ done:
     return Status;
 }
 
-/* FIXME: SampQueryUserLogonHours */
 
 static
 NTSTATUS
@@ -5617,8 +6154,373 @@ SampQueryUserExpires(PSAM_DB_OBJECT UserObject,
     if (!NT_SUCCESS(Status))
         goto done;
 
-    InfoBuffer->Expires.AccountExpires.LowPart = FixedData.AccountExpires.LowPart;
-    InfoBuffer->Expires.AccountExpires.HighPart = FixedData.AccountExpires.HighPart;
+    InfoBuffer->Expires.AccountExpires.LowPart = FixedData.AccountExpires.LowPart;
+    InfoBuffer->Expires.AccountExpires.HighPart = FixedData.AccountExpires.HighPart;
+
+    *Buffer = InfoBuffer;
+
+done:
+    if (!NT_SUCCESS(Status))
+    {
+        if (InfoBuffer != NULL)
+        {
+            midl_user_free(InfoBuffer);
+        }
+    }
+
+    return Status;
+}
+
+
+static
+NTSTATUS
+SampQueryUserInternal1(PSAM_DB_OBJECT UserObject,
+                       PSAMPR_USER_INFO_BUFFER *Buffer)
+{
+    PSAMPR_USER_INFO_BUFFER InfoBuffer = NULL;
+    ULONG Length = 0;
+    NTSTATUS Status = STATUS_SUCCESS;
+
+    /* Fail, if the caller is not a trusted caller */
+    if (UserObject->Trusted == FALSE)
+        return STATUS_INVALID_INFO_CLASS;
+
+    *Buffer = NULL;
+
+    InfoBuffer = midl_user_allocate(sizeof(SAMPR_USER_INFO_BUFFER));
+    if (InfoBuffer == NULL)
+        return STATUS_INSUFFICIENT_RESOURCES;
+
+    /* 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;
+    }
+
+    InfoBuffer->Internal1.NtPasswordPresent = (Length == sizeof(ENCRYPTED_NT_OWF_PASSWORD));
+
+    /* 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;
+    }
+
+    InfoBuffer->Internal1.LmPasswordPresent = (Length == sizeof(ENCRYPTED_LM_OWF_PASSWORD));
+
+    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)
+{
+    PSAMPR_USER_INFO_BUFFER InfoBuffer = NULL;
+    NTSTATUS Status;
+
+    *Buffer = NULL;
+
+    InfoBuffer = midl_user_allocate(sizeof(SAMPR_USER_INFO_BUFFER));
+    if (InfoBuffer == NULL)
+        return STATUS_INSUFFICIENT_RESOURCES;
+
+    /* Get the Parameters string */
+    Status = SampGetObjectAttributeString(UserObject,
+                                          L"Parameters",
+                                          &InfoBuffer->Parameters.Parameters);
+    if (!NT_SUCCESS(Status))
+    {
+        TRACE("Status 0x%08lx\n", Status);
+        goto done;
+    }
+
+    *Buffer = InfoBuffer;
+
+done:
+    if (!NT_SUCCESS(Status))
+    {
+        if (InfoBuffer != NULL)
+        {
+            if (InfoBuffer->Parameters.Parameters.Buffer != NULL)
+                midl_user_free(InfoBuffer->Parameters.Parameters.Buffer);
+
+            midl_user_free(InfoBuffer);
+        }
+    }
+
+    return Status;
+}
+
+
+static NTSTATUS
+SampQueryUserAll(PSAM_DB_OBJECT UserObject,
+                 PSAMPR_USER_INFO_BUFFER *Buffer)
+{
+    PSAMPR_USER_INFO_BUFFER InfoBuffer = NULL;
+    SAM_DOMAIN_FIXED_DATA DomainFixedData;
+    SAM_USER_FIXED_DATA FixedData;
+    LARGE_INTEGER PasswordCanChange;
+    LARGE_INTEGER PasswordMustChange;
+    ULONG Length = 0;
+    NTSTATUS Status;
+
+    *Buffer = NULL;
+
+    InfoBuffer = midl_user_allocate(sizeof(SAMPR_USER_INFO_BUFFER));
+    if (InfoBuffer == NULL)
+        return STATUS_INSUFFICIENT_RESOURCES;
+
+    /* Get the fixed size domain data */
+    Length = sizeof(SAM_DOMAIN_FIXED_DATA);
+    Status = SampGetObjectAttribute(UserObject->ParentObject,
+                                    L"F",
+                                    NULL,
+                                    (PVOID)&DomainFixedData,
+                                    &Length);
+    if (!NT_SUCCESS(Status))
+        goto done;
+
+    /* Get the fixed size user data */
+    Length = sizeof(SAM_USER_FIXED_DATA);
+    Status = SampGetObjectAttribute(UserObject,
+                                    L"F",
+                                    NULL,
+                                    (PVOID)&FixedData,
+                                    &Length);
+    if (!NT_SUCCESS(Status))
+        goto done;
+
+    if (UserObject->Access & USER_READ_GENERAL)
+    {
+        /* Get the Name string */
+        Status = SampGetObjectAttributeString(UserObject,
+                                              L"Name",
+                                              &InfoBuffer->All.UserName);
+        if (!NT_SUCCESS(Status))
+        {
+            TRACE("Status 0x%08lx\n", Status);
+            goto done;
+        }
+
+        /* Get the FullName string */
+        Status = SampGetObjectAttributeString(UserObject,
+                                              L"FullName",
+                                              &InfoBuffer->All.FullName);
+        if (!NT_SUCCESS(Status))
+        {
+            TRACE("Status 0x%08lx\n", Status);
+            goto done;
+        }
+
+        /* Get the user ID*/
+        InfoBuffer->All.UserId = FixedData.UserId;
+
+        /* Get the primary group ID */
+        InfoBuffer->All.PrimaryGroupId = FixedData.PrimaryGroupId;
+
+        /* Get the AdminComment string */
+        Status = SampGetObjectAttributeString(UserObject,
+                                              L"AdminComment",
+                                              &InfoBuffer->All.AdminComment);
+        if (!NT_SUCCESS(Status))
+        {
+            TRACE("Status 0x%08lx\n", Status);
+            goto done;
+        }
+
+        /* Get the UserComment string */
+        Status = SampGetObjectAttributeString(UserObject,
+                                              L"UserComment",
+                                              &InfoBuffer->All.UserComment);
+        if (!NT_SUCCESS(Status))
+        {
+            TRACE("Status 0x%08lx\n", Status);
+            goto done;
+        }
+
+        InfoBuffer->All.WhichFields |= USER_ALL_READ_GENERAL_MASK;
+//            USER_ALL_USERNAME |
+//            USER_ALL_FULLNAME |
+//            USER_ALL_USERID |
+//            USER_ALL_PRIMARYGROUPID |
+//            USER_ALL_ADMINCOMMENT |
+//            USER_ALL_USERCOMMENT;
+    }
+
+    if (UserObject->Access & USER_READ_LOGON)
+    {
+        /* Get the HomeDirectory string */
+        Status = SampGetObjectAttributeString(UserObject,
+                                              L"HomeDirectory",
+                                              &InfoBuffer->All.HomeDirectory);
+        if (!NT_SUCCESS(Status))
+        {
+            TRACE("Status 0x%08lx\n", Status);
+            goto done;
+        }
+
+        /* Get the HomeDirectoryDrive string */
+        Status = SampGetObjectAttributeString(UserObject,
+                                              L"HomeDirectoryDrive",
+                                              &InfoBuffer->Home.HomeDirectoryDrive);
+        if (!NT_SUCCESS(Status))
+        {
+            TRACE("Status 0x%08lx\n", Status);
+            goto done;
+        }
+
+        /* Get the ScriptPath string */
+        Status = SampGetObjectAttributeString(UserObject,
+                                              L"ScriptPath",
+                                              &InfoBuffer->All.ScriptPath);
+        if (!NT_SUCCESS(Status))
+        {
+            TRACE("Status 0x%08lx\n", Status);
+            goto done;
+        }
+
+        /* Get the ProfilePath string */
+        Status = SampGetObjectAttributeString(UserObject,
+                                              L"ProfilePath",
+                                              &InfoBuffer->All.ProfilePath);
+        if (!NT_SUCCESS(Status))
+        {
+            TRACE("Status 0x%08lx\n", Status);
+            goto done;
+        }
+
+        /* Get the WorkStations string */
+        Status = SampGetObjectAttributeString(UserObject,
+                                              L"WorkStations",
+                                              &InfoBuffer->All.WorkStations);
+        if (!NT_SUCCESS(Status))
+        {
+            TRACE("Status 0x%08lx\n", Status);
+            goto done;
+        }
+
+        /* Get the LogonHours attribute */
+        Status = SampGetLogonHoursAttrbute(UserObject,
+                                           &InfoBuffer->All.LogonHours);
+        if (!NT_SUCCESS(Status))
+        {
+            TRACE("Status 0x%08lx\n", Status);
+            goto done;
+        }
+
+        InfoBuffer->All.LastLogon.LowPart = FixedData.LastLogon.LowPart;
+        InfoBuffer->All.LastLogon.HighPart = FixedData.LastLogon.HighPart;
+
+        InfoBuffer->All.LastLogoff.LowPart = FixedData.LastLogoff.LowPart;
+        InfoBuffer->All.LastLogoff.HighPart = FixedData.LastLogoff.HighPart;
+
+        InfoBuffer->All.BadPasswordCount = FixedData.BadPasswordCount;
+
+        InfoBuffer->All.LogonCount = FixedData.LogonCount;
+
+        PasswordCanChange = SampAddRelativeTimeToTime(FixedData.PasswordLastSet,
+                                                      DomainFixedData.MinPasswordAge);
+        InfoBuffer->All.PasswordCanChange.LowPart = PasswordCanChange.LowPart;
+        InfoBuffer->All.PasswordCanChange.HighPart = PasswordCanChange.HighPart;
+
+        PasswordMustChange = SampAddRelativeTimeToTime(FixedData.PasswordLastSet,
+                                                       DomainFixedData.MaxPasswordAge);
+        InfoBuffer->All.PasswordMustChange.LowPart = PasswordMustChange.LowPart;
+        InfoBuffer->All.PasswordMustChange.HighPart = PasswordMustChange.HighPart;
+
+        InfoBuffer->All. WhichFields |= USER_ALL_READ_LOGON_MASK;
+/*
+            USER_ALL_HOMEDIRECTORY |
+            USER_ALL_HOMEDIRECTORYDRIVE |
+            USER_ALL_SCRIPTPATH |
+            USER_ALL_PROFILEPATH |
+            USER_ALL_WORKSTATIONS |
+            USER_ALL_LASTLOGON |
+            USER_ALL_LASTLOGOFF |
+            USER_ALL_LOGONHOURS |
+            USER_ALL_BADPASSWORDCOUNT |
+            USER_ALL_LOGONCOUNT;
+            USER_ALL_PASSWORDCANCHANGE |
+            USER_ALL_PASSWORDMUSTCHANGE;
+*/
+    }
+
+    if (UserObject->Access & USER_READ_ACCOUNT)
+    {
+        InfoBuffer->All.PasswordLastSet.LowPart = FixedData.PasswordLastSet.LowPart;
+        InfoBuffer->All.PasswordLastSet.HighPart = FixedData.PasswordLastSet.HighPart;
+
+        InfoBuffer->All.AccountExpires.LowPart = FixedData.AccountExpires.LowPart;
+        InfoBuffer->All.AccountExpires.HighPart = FixedData.AccountExpires.HighPart;
+
+        InfoBuffer->All.UserAccountControl = FixedData.UserAccountControl;
+
+        /* Get the Parameters string */
+        Status = SampGetObjectAttributeString(UserObject,
+                                              L"Parameters",
+                                              &InfoBuffer->All.Parameters);
+        if (!NT_SUCCESS(Status))
+        {
+            TRACE("Status 0x%08lx\n", Status);
+            goto done;
+        }
+
+        InfoBuffer->All. WhichFields |= USER_ALL_READ_ACCOUNT_MASK;
+//            USER_ALL_PASSWORDLASTSET |
+//            USER_ALL_ACCOUNTEXPIRES |
+//            USER_ALL_USERACCOUNTCONTROL |
+//            USER_ALL_PARAMETERS;
+    }
+
+    if (UserObject->Access & USER_READ_PREFERENCES)
+    {
+        InfoBuffer->All.CountryCode = FixedData.CountryCode;
+
+        InfoBuffer->All.CodePage = FixedData.CodePage;
+
+        InfoBuffer->All. WhichFields |= USER_ALL_READ_PREFERENCES_MASK;
+//            USER_ALL_COUNTRYCODE |
+//            USER_ALL_CODEPAGE;
+    }
 
     *Buffer = InfoBuffer;
 
@@ -5627,6 +6529,39 @@ done:
     {
         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);
+
             midl_user_free(InfoBuffer);
         }
     }
@@ -5670,6 +6605,7 @@ SamrQueryInformationUser(IN SAMPR_HANDLE UserHandle,
 
         case UserControlInformation:
         case UserExpiresInformation:
+        case UserParametersInformation:
             DesiredAccess = USER_READ_ACCOUNT;
             break;
 
@@ -5686,6 +6622,11 @@ SamrQueryInformationUser(IN SAMPR_HANDLE UserHandle,
                             USER_READ_ACCOUNT;
             break;
 
+        case UserInternal1Information:
+        case UserAllInformation:
+            DesiredAccess = 0;
+            break;
+
         default:
             return STATUS_INVALID_INFO_CLASS;
     }
@@ -5718,10 +6659,10 @@ SamrQueryInformationUser(IN SAMPR_HANDLE UserHandle,
                                         Buffer);
             break;
 
-//        case UserLogonHoursInformation:
-//            Status = SampQueryUserLogonHours(UserObject,
-//                                             Buffer);
-//            break;
+        case UserLogonHoursInformation:
+            Status = SampQueryUserLogonHours(UserObject,
+                                             Buffer);
+            break;
 
         case UserAccountInformation:
             Status = SampQueryUserAccount(UserObject,
@@ -5782,6 +6723,26 @@ SamrQueryInformationUser(IN SAMPR_HANDLE UserHandle,
                                           Buffer);
             break;
 
+        case UserInternal1Information:
+            Status = SampQueryUserInternal1(UserObject,
+                                            Buffer);
+            break;
+
+        case UserParametersInformation:
+            Status = SampQueryUserParameters(UserObject,
+                                             Buffer);
+            break;
+
+        case UserAllInformation:
+            Status = SampQueryUserAll(UserObject,
+                                      Buffer);
+            break;
+
+//        case UserInternal4Information:
+//        case UserInternal5Information:
+//        case UserInternal4InformationNew:
+//        case UserInternal5InformationNew:
+
         default:
             Status = STATUS_INVALID_INFO_CLASS;
     }
@@ -5790,6 +6751,71 @@ SamrQueryInformationUser(IN SAMPR_HANDLE UserHandle,
 }
 
 
+static NTSTATUS
+SampSetUserName(PSAM_DB_OBJECT UserObject,
+                PRPC_UNICODE_STRING NewUserName)
+{
+    UNICODE_STRING OldUserName = {0, 0, NULL};
+    NTSTATUS 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 = SampSetObjectAttribute(UserObject,
+                                    L"Name",
+                                    REG_SZ,
+                                    NewUserName->Buffer,
+                                    NewUserName->Length + sizeof(WCHAR));
+    if (!NT_SUCCESS(Status))
+    {
+        TRACE("SampSetObjectAttribute failed (Status 0x%08lx)\n", Status);
+    }
+
+done:
+    if (OldUserName.Buffer != NULL)
+        midl_user_free(OldUserName.Buffer);
+
+    return Status;
+}
+
+
 static NTSTATUS
 SampSetUserGeneral(PSAM_DB_OBJECT UserObject,
                    PSAMPR_USER_INFO_BUFFER Buffer)
@@ -5817,11 +6843,8 @@ 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;
 
@@ -5982,6 +7005,61 @@ done:
 }
 
 
+static NTSTATUS
+SampSetUserInternal1(PSAM_DB_OBJECT UserObject,
+                     PSAMPR_USER_INFO_BUFFER Buffer)
+{
+    SAM_USER_FIXED_DATA FixedData;
+    ULONG Length = 0;
+    NTSTATUS Status = STATUS_SUCCESS;
+
+    /* FIXME: Decrypt NT password */
+    /* FIXME: Decrypt LM password */
+
+    Status = SampSetUserPassword(UserObject,
+                                 &Buffer->Internal1.EncryptedNtOwfPassword,
+                                 Buffer->Internal1.NtPasswordPresent,
+                                 &Buffer->Internal1.EncryptedLmOwfPassword,
+                                 Buffer->Internal1.LmPasswordPresent);
+    if (!NT_SUCCESS(Status))
+        goto done;
+
+    /* Get the fixed user attributes */
+    Length = sizeof(SAM_USER_FIXED_DATA);
+    Status = SampGetObjectAttribute(UserObject,
+                                    L"F",
+                                    NULL,
+                                    (PVOID)&FixedData,
+                                    &Length);
+    if (!NT_SUCCESS(Status))
+        goto done;
+
+    if (Buffer->Internal1.PasswordExpired)
+    {
+        /* The 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;
+    }
+
+    /* Set the fixed user attributes */
+    Status = SampSetObjectAttribute(UserObject,
+                                    L"F",
+                                    REG_BINARY,
+                                    &FixedData,
+                                    Length);
+
+done:
+    return Status;
+}
+
+
 static NTSTATUS
 SampSetUserAll(PSAM_DB_OBJECT UserObject,
                PSAMPR_USER_INFO_BUFFER Buffer)
@@ -5995,11 +7073,8 @@ SampSetUserAll(PSAM_DB_OBJECT UserObject,
 
     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;
     }
@@ -6099,6 +7174,16 @@ SampSetUserAll(PSAM_DB_OBJECT UserObject,
                                         REG_SZ,
                                         Buffer->All.Parameters.Buffer,
                                         Buffer->All.Parameters.MaximumLength);
+        if (!NT_SUCCESS(Status))
+            goto done;
+    }
+
+    if (WhichFields & USER_ALL_LOGONHOURS)
+    {
+        Status = SampSetLogonHoursAttrbute(UserObject,
+                                           &Buffer->All.LogonHours);
+        if (!NT_SUCCESS(Status))
+            goto done;
     }
 
     if (WhichFields & (USER_ALL_PRIMARYGROUPID |
@@ -6145,7 +7230,6 @@ SampSetUserAll(PSAM_DB_OBJECT UserObject,
 
 /*
 FIXME:
-    USER_ALL_LOGONHOURS
     USER_ALL_NTPASSWORDPRESENT
     USER_ALL_LMPASSWORDPRESENT
     USER_ALL_PASSWORDEXPIRED
@@ -6199,6 +7283,7 @@ SamrSetInformationUser(IN SAMPR_HANDLE UserHandle,
             break;
 
         case UserSetPasswordInformation:
+        case UserInternal1Information:
             DesiredAccess = USER_FORCE_PASSWORD_CHANGE;
             break;
 
@@ -6232,18 +7317,15 @@ 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;
 
@@ -6255,11 +7337,8 @@ SamrSetInformationUser(IN SAMPR_HANDLE UserHandle,
             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:
@@ -6344,7 +7423,10 @@ SamrSetInformationUser(IN SAMPR_HANDLE UserHandle,
                                         Buffer);
             break;
 
-//        case UserInternal1Information:
+        case UserInternal1Information:
+            Status = SampSetUserInternal1(UserObject,
+                                          Buffer);
+            break;
 
         case UserParametersInformation:
             Status = SampSetObjectAttribute(UserObject,
@@ -6372,35 +7454,330 @@ SamrSetInformationUser(IN SAMPR_HANDLE UserHandle,
 }
 
 
-/* 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;
-}
-
+/* 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;
+    PENCRYPTED_LM_OWF_PASSWORD OldLmPassword;
+    PENCRYPTED_LM_OWF_PASSWORD NewLmPassword;
+    PENCRYPTED_NT_OWF_PASSWORD OldNtPassword;
+    PENCRYPTED_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);
+
+    /* 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);
+        return Status;
+    }
+
+    /* Get the current time */
+    Status = NtQuerySystemTime(&SystemTime);
+    if (!NT_SUCCESS(Status))
+    {
+        TRACE("NtQuerySystemTime failed (Status 0x%08lx)\n", Status);
+        return Status;
+    }
+
+    /* 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);
+        return Status;
+    }
+
+    /* 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);
+            return Status;
+        }
+
+        if (DomainFixedData.MinPasswordAge.QuadPart > 0)
+        {
+            if (SystemTime.QuadPart < (UserFixedData.PasswordLastSet.QuadPart + DomainFixedData.MinPasswordAge.QuadPart))
+                return STATUS_ACCOUNT_RESTRICTION;
+        }
+    }
+
+    /* FIXME: Decrypt passwords */
+    OldLmPassword = OldLmEncryptedWithNewLm;
+    NewLmPassword = NewLmEncryptedWithOldLm;
+    OldNtPassword = OldNtEncryptedWithNewNt;
+    NewNtPassword = NewNtEncryptedWithOldNt;
+
+    /* Check if the old passwords match the stored ones */
+    if (NtPresent)
+    {
+        if (LmPresent)
+        {
+            if (!RtlEqualMemory(&StoredLmPassword,
+                                OldLmPassword,
+                                sizeof(ENCRYPTED_LM_OWF_PASSWORD)))
+            {
+                TRACE("Old LM Password does not match!\n");
+                Status = STATUS_WRONG_PASSWORD;
+            }
+            else
+            {
+                if (!RtlEqualMemory(&StoredNtPassword,
+                                    OldNtPassword,
+                                    sizeof(ENCRYPTED_LM_OWF_PASSWORD)))
+                {
+                    TRACE("Old NT Password does not match!\n");
+                    Status = STATUS_WRONG_PASSWORD;
+                }
+            }
+        }
+        else
+        {
+            if (!RtlEqualMemory(&StoredNtPassword,
+                                OldNtPassword,
+                                sizeof(ENCRYPTED_LM_OWF_PASSWORD)))
+            {
+                TRACE("Old NT Password does not match!\n");
+                Status = STATUS_WRONG_PASSWORD;
+            }
+        }
+    }
+    else
+    {
+        if (LmPresent)
+        {
+            if (!RtlEqualMemory(&StoredLmPassword,
+                                OldLmPassword,
+                                sizeof(ENCRYPTED_LM_OWF_PASSWORD)))
+            {
+                TRACE("Old LM Password does not match!\n");
+                Status = STATUS_WRONG_PASSWORD;
+            }
+        }
+        else
+        {
+            Status = STATUS_INVALID_PARAMETER;
+        }
+    }
+
+    /* Store the new password hashes */
+    if (NT_SUCCESS(Status))
+    {
+        Status = SampSetUserPassword(UserObject,
+                                     NewNtPassword,
+                                     NtPresent,
+                                     NewLmPassword,
+                                     LmPresent);
+        if (NT_SUCCESS(Status))
+        {
+            /* Update PasswordLastSet */
+            UserFixedData.PasswordLastSet.QuadPart = SystemTime.QuadPart;
+
+            /* Set the fixed size user data */
+            Length = sizeof(SAM_USER_FIXED_DATA);
+            Status = SampSetObjectAttribute(UserObject,
+                                            L"F",
+                                            REG_BINARY,
+                                            &UserFixedData,
+                                            Length);
+        }
+    }
+
+    if (Status == STATUS_WRONG_PASSWORD)
+    {
+        /* Update BadPasswordCount and LastBadPasswordTime */
+        UserFixedData.BadPasswordCount++;
+        UserFixedData.LastBadPasswordTime.QuadPart = SystemTime.QuadPart;
+
+        /* Set the fixed size user data */
+        Length = sizeof(SAM_USER_FIXED_DATA);
+        Status = SampSetObjectAttribute(UserObject,
+                                        L"F",
+                                        REG_BINARY,
+                                        &UserFixedData,
+                                        Length);
+    }
+
+    return Status;
+}
+
+
 /* Function 39 */
 NTSTATUS
 NTAPI
 SamrGetGroupsForUser(IN SAMPR_HANDLE UserHandle,
                      OUT PSAMPR_GET_GROUPS_BUFFER *Groups)
 {
-    UNIMPLEMENTED;
-    return STATUS_NOT_IMPLEMENTED;
+    PSAMPR_GET_GROUPS_BUFFER GroupsBuffer = NULL;
+    PSAM_DB_OBJECT UserObject;
+    ULONG Length = 0;
+    NTSTATUS Status;
+
+    TRACE("SamrGetGroupsForUser(%p %p)\n",
+          UserHandle, Groups);
+
+    /* Validate the user handle */
+    Status = SampValidateDbObject(UserHandle,
+                                  SamDbUserObject,
+                                  USER_LIST_GROUPS,
+                                  &UserObject);
+    if (!NT_SUCCESS(Status))
+    {
+        TRACE("SampValidateDbObject failed with status 0x%08lx\n", Status);
+        return Status;
+    }
+
+    /* Allocate the groups buffer */
+    GroupsBuffer = midl_user_allocate(sizeof(SAMPR_GET_GROUPS_BUFFER));
+    if (GroupsBuffer == NULL)
+        return STATUS_INSUFFICIENT_RESOURCES;
+
+    /*
+     * Get the size of the Groups attribute.
+     * Do not check the status code because in case of an error
+     * Length will be 0. And that is all we need.
+     */
+    SampGetObjectAttribute(UserObject,
+                           L"Groups",
+                           NULL,
+                           NULL,
+                           &Length);
+
+    /* If there is no Groups attribute, return a groups buffer without an array */
+    if (Length == 0)
+    {
+        GroupsBuffer->MembershipCount = 0;
+        GroupsBuffer->Groups = NULL;
+
+        *Groups = GroupsBuffer;
+
+        return STATUS_SUCCESS;
+    }
+
+    /* Allocate a buffer for the Groups attribute */
+    GroupsBuffer->Groups = midl_user_allocate(Length);
+    if (GroupsBuffer->Groups == NULL)
+    {
+        Status = STATUS_INSUFFICIENT_RESOURCES;
+        goto done;
+    }
+
+    /* Retrieve the Grous attribute */
+    Status = SampGetObjectAttribute(UserObject,
+                                    L"Groups",
+                                    NULL,
+                                    GroupsBuffer->Groups,
+                                    &Length);
+    if (!NT_SUCCESS(Status))
+    {
+        TRACE("SampGetObjectAttribute failed with status 0x%08lx\n", Status);
+        goto done;
+    }
+
+    /* Calculate the membership count */
+    GroupsBuffer->MembershipCount = Length / sizeof(GROUP_MEMBERSHIP);
+
+    /* Return the groups buffer to the caller */
+    *Groups = GroupsBuffer;
+
+done:
+    if (!NT_SUCCESS(Status))
+    {
+        if (GroupsBuffer != NULL)
+        {
+            if (GroupsBuffer->Groups != NULL)
+                midl_user_free(GroupsBuffer->Groups);
+
+            midl_user_free(GroupsBuffer);
+        }
+    }
+
+    return Status;
 }
 
+
 /* Function 40 */
 NTSTATUS
 NTAPI
@@ -6447,26 +7824,141 @@ 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);
+
+    /* Validate the user handle */
+    Status = SampValidateDbObject(UserHandle,
+                                  SamDbUserObject,
+                                  0,
+                                  &UserObject);
+    if (!NT_SUCCESS(Status))
+    {
+        TRACE("SampValidateDbObject failed with status 0x%08lx\n", Status);
+        return Status;
+    }
+
+    /* 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);
+        return Status;
+    }
+
+    /* 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);
+        return Status;
+    }
+
+    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);
+            return Status;
+        }
+
+        PasswordInformation->MinPasswordLength = DomainFixedData.MinPasswordLength;
+        PasswordInformation->PasswordProperties = DomainFixedData.PasswordProperties;
+    }
+
+    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);
+
+    /* Validate the domain object */
+    Status = SampValidateDbObject(DomainHandle,
+                                  SamDbDomainObject,
+                                  DOMAIN_LOOKUP,
+                                  &DomainObject);
+    if (!NT_SUCCESS(Status))
+    {
+        TRACE("SampValidateDbObject failed with status 0x%08lx\n", Status);
+        return Status;
+    }
+
+    /* Retrieve the RID from the MemberSID */
+    Status = SampGetRidFromSid((PSID)MemberSid,
+                               &Rid);
+    if (!NT_SUCCESS(Status))
+    {
+        TRACE("SampGetRidFromSid failed with status 0x%08lx\n", Status);
+        return Status;
+    }
+
+    /* Fail, if the RID represents a special account */
+    if (Rid < 1000)
+    {
+        TRACE("Cannot remove a special account (RID: %lu)\n", Rid);
+        return STATUS_SPECIAL_ACCOUNT;
+    }
+
+    /* 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);
+    }
+
+    return Status;
 }
 
+
 /* Function 46 */
 NTSTATUS
 NTAPI
@@ -6474,10 +7966,14 @@ SamrQueryInformationDomain2(IN SAMPR_HANDLE DomainHandle,
                             IN DOMAIN_INFORMATION_CLASS DomainInformationClass,
                             OUT PSAMPR_DOMAIN_INFO_BUFFER *Buffer)
 {
-    UNIMPLEMENTED;
-    return STATUS_NOT_IMPLEMENTED;
+    TRACE("(%p %lu %p)\n", DomainHandle, DomainInformationClass, Buffer);
+
+    return SamrQueryInformationDomain(DomainHandle,
+                                      DomainInformationClass,
+                                      Buffer);
 }
 
+
 /* Function 47 */
 NTSTATUS
 NTAPI
@@ -6485,10 +7981,14 @@ SamrQueryInformationUser2(IN SAMPR_HANDLE UserHandle,
                           IN USER_INFORMATION_CLASS UserInformationClass,
                           OUT PSAMPR_USER_INFO_BUFFER *Buffer)
 {
-    UNIMPLEMENTED;
-    return STATUS_NOT_IMPLEMENTED;
+    TRACE("(%p %lu %p)\n", UserHandle, UserInformationClass, Buffer);
+
+    return SamrQueryInformationUser(UserHandle,
+                                    UserInformationClass,
+                                    Buffer);
 }
 
+
 /* Function 48 */
 NTSTATUS
 NTAPI
@@ -6501,10 +8001,22 @@ SamrQueryDisplayInformation2(IN SAMPR_HANDLE DomainHandle,
                              OUT unsigned long *TotalReturned,
                              OUT PSAMPR_DISPLAY_INFO_BUFFER Buffer)
 {
-    UNIMPLEMENTED;
-    return STATUS_NOT_IMPLEMENTED;
+    TRACE("%p %lu %lu %lu %lu %p %p %p\n",
+          DomainHandle, DisplayInformationClass, Index,
+          EntryCount, PreferredMaximumLength, TotalAvailable,
+          TotalReturned, Buffer);
+
+    return SamrQueryDisplayInformation(DomainHandle,
+                                       DisplayInformationClass,
+                                       Index,
+                                       EntryCount,
+                                       PreferredMaximumLength,
+                                       TotalAvailable,
+                                       TotalReturned,
+                                       Buffer);
 }
 
+
 /* Function 49 */
 NTSTATUS
 NTAPI
@@ -6513,8 +8025,13 @@ SamrGetDisplayEnumerationIndex2(IN SAMPR_HANDLE DomainHandle,
                                 IN PRPC_UNICODE_STRING Prefix,
                                 OUT unsigned long *Index)
 {
-    UNIMPLEMENTED;
-    return STATUS_NOT_IMPLEMENTED;
+    TRACE("(%p %lu %p %p)\n",
+           DomainHandle, DisplayInformationClass, Prefix, Index);
+
+    return SamrGetDisplayEnumerationIndex(DomainHandle,
+                                          DisplayInformationClass,
+                                          Prefix,
+                                          Index);
 }
 
 
@@ -6534,6 +8051,8 @@ SamrCreateUser2InDomain(IN SAMPR_HANDLE DomainHandle,
     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];
@@ -6601,10 +8120,10 @@ SamrCreateUser2InDomain(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);
@@ -6642,8 +8161,8 @@ SamrCreateUser2InDomain(IN SAMPR_HANDLE DomainHandle,
     }
 
     /* Initialize fixed user data */
-    memset(&FixedUserData, 0, sizeof(SAM_USER_FIXED_DATA));
     FixedUserData.Version = 1;
+    FixedUserData.Reserved = 0;
     FixedUserData.LastLogon.QuadPart = 0;
     FixedUserData.LastLogoff.QuadPart = 0;
     FixedUserData.PasswordLastSet.QuadPart = 0;
@@ -6655,6 +8174,12 @@ SamrCreateUser2InDomain(IN SAMPR_HANDLE DomainHandle,
     FixedUserData.UserAccountControl = USER_ACCOUNT_DISABLED |
                                        USER_PASSWORD_NOT_REQUIRED |
                                        AccountType;
+    FixedUserData.CountryCode = 0;
+    FixedUserData.CodePage = 0;
+    FixedUserData.BadPasswordCount = 0;
+    FixedUserData.LogonCount = 0;
+    FixedUserData.AdminCount = 0;
+    FixedUserData.OperatorCount = 0;
 
     /* Set fixed user data attribute */
     Status = SampSetObjectAttribute(UserObject,
@@ -6776,7 +8301,99 @@ SamrCreateUser2InDomain(IN SAMPR_HANDLE DomainHandle,
         return Status;
     }
 
-    /* FIXME: Set default user attributes */
+    /* Set the Parameters attribute */
+    Status = SampSetObjectAttribute(UserObject,
+                                    L"Parameters",
+                                    REG_SZ,
+                                    EmptyString.Buffer,
+                                    EmptyString.MaximumLength);
+    if (!NT_SUCCESS(Status))
+    {
+        TRACE("failed with status 0x%08lx\n", Status);
+        return Status;
+    }
+
+    /* Set LogonHours attribute*/
+    *((PUSHORT)LogonHours) = 168;
+    memset(&(LogonHours[2]), 0xff, 21);
+
+    Status = SampSetObjectAttribute(UserObject,
+                                    L"LogonHours",
+                                    REG_BINARY,
+                                    &LogonHours,
+                                    sizeof(LogonHours));
+    if (!NT_SUCCESS(Status))
+    {
+        TRACE("failed with status 0x%08lx\n", Status);
+        return Status;
+    }
+
+    /* 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);
+        return Status;
+    }
+
+    /* Set LMPwd attribute*/
+    Status = SampSetObjectAttribute(UserObject,
+                                    L"LMPwd",
+                                    REG_BINARY,
+                                    NULL,
+                                    0);
+    if (!NT_SUCCESS(Status))
+    {
+        TRACE("failed with status 0x%08lx\n", Status);
+        return Status;
+    }
+
+    /* Set NTPwd attribute*/
+    Status = SampSetObjectAttribute(UserObject,
+                                    L"NTPwd",
+                                    REG_BINARY,
+                                    NULL,
+                                    0);
+    if (!NT_SUCCESS(Status))
+    {
+        TRACE("failed with status 0x%08lx\n", Status);
+        return Status;
+    }
+
+    /* Set LMPwdHistory attribute*/
+    Status = SampSetObjectAttribute(UserObject,
+                                    L"LMPwdHistory",
+                                    REG_BINARY,
+                                    NULL,
+                                    0);
+    if (!NT_SUCCESS(Status))
+    {
+        TRACE("failed with status 0x%08lx\n", Status);
+        return Status;
+    }
+
+    /* Set NTPwdHistory attribute*/
+    Status = SampSetObjectAttribute(UserObject,
+                                    L"NTPwdHistory",
+                                    REG_BINARY,
+                                    NULL,
+                                    0);
+    if (!NT_SUCCESS(Status))
+    {
+        TRACE("failed with status 0x%08lx\n", Status);
+        return Status;
+    }
+
+    /* FIXME: Set SecDesc attribute*/
 
     if (NT_SUCCESS(Status))
     {
@@ -6803,8 +8420,19 @@ SamrQueryDisplayInformation3(IN SAMPR_HANDLE DomainHandle,
                              OUT unsigned long *TotalReturned,
                              OUT PSAMPR_DISPLAY_INFO_BUFFER Buffer)
 {
-    UNIMPLEMENTED;
-    return STATUS_NOT_IMPLEMENTED;
+    TRACE("%p %lu %lu %lu %lu %p %p %p\n",
+          DomainHandle, DisplayInformationClass, Index,
+          EntryCount, PreferredMaximumLength, TotalAvailable,
+          TotalReturned, Buffer);
+
+    return SamrQueryDisplayInformation(DomainHandle,
+                                       DisplayInformationClass,
+                                       Index,
+                                       EntryCount,
+                                       PreferredMaximumLength,
+                                       TotalAvailable,
+                                       TotalReturned,
+                                       Buffer);
 }
 
 
@@ -6904,6 +8532,7 @@ SamrGetDomainPasswordInformation(IN handle_t BindingHandle,
     return STATUS_NOT_IMPLEMENTED;
 }
 
+
 /* Function 57 */
 NTSTATUS
 NTAPI
@@ -6911,10 +8540,14 @@ 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);
 }
 
+
 /* Function 58 */
 NTSTATUS
 NTAPI
@@ -6922,10 +8555,14 @@ SamrSetInformationUser2(IN SAMPR_HANDLE UserHandle,
                         IN USER_INFORMATION_CLASS UserInformationClass,
                         IN PSAMPR_USER_INFO_BUFFER Buffer)
 {
-    UNIMPLEMENTED;
-    return STATUS_NOT_IMPLEMENTED;
+    TRACE("(%p %lu %p)\n", UserHandle, UserInformationClass, Buffer);
+
+    return SamrSetInformationUser(UserHandle,
+                                  UserInformationClass,
+                                  Buffer);
 }
 
+
 /* Function 59 */
 NTSTATUS
 NTAPI