reactos/dll/win32/samsrv/user.c

731 lines
20 KiB
C

/*
* PROJECT: Local Security Authority Server DLL
* LICENSE: GPL - See COPYING in the top level directory
* FILE: dll/win32/samsrv/user.c
* PURPOSE: User specific helper functions
* COPYRIGHT: Copyright 2013 Eric Kohl
*/
#include "samsrv.h"
/* FUNCTIONS ***************************************************************/
NTSTATUS
SampOpenUserObject(IN PSAM_DB_OBJECT DomainObject,
IN ULONG UserId,
IN ACCESS_MASK DesiredAccess,
OUT PSAM_DB_OBJECT *UserObject)
{
WCHAR szRid[9];
TRACE("(%p %lu %lx %p)\n",
DomainObject, UserId, DesiredAccess, UserObject);
/* Convert the RID into a string (hex) */
swprintf(szRid, L"%08lX", UserId);
/* Create the user object */
return SampOpenDbObject(DomainObject,
L"Users",
szRid,
UserId,
SamDbUserObject,
DesiredAccess,
UserObject);
}
NTSTATUS
SampAddGroupMembershipToUser(IN PSAM_DB_OBJECT UserObject,
IN ULONG GroupId,
IN ULONG Attributes)
{
PGROUP_MEMBERSHIP GroupsBuffer = NULL;
ULONG GroupsCount = 0;
ULONG Length = 0;
ULONG i;
NTSTATUS Status;
TRACE("(%p %lu %lx)\n",
UserObject, GroupId, Attributes);
Status = SampGetObjectAttribute(UserObject,
L"Groups",
NULL,
NULL,
&Length);
if (!NT_SUCCESS(Status) && Status != STATUS_OBJECT_NAME_NOT_FOUND)
goto done;
GroupsBuffer = midl_user_allocate(Length + sizeof(GROUP_MEMBERSHIP));
if (GroupsBuffer == NULL)
{
Status = STATUS_INSUFFICIENT_RESOURCES;
goto done;
}
if (Status != STATUS_OBJECT_NAME_NOT_FOUND)
{
Status = SampGetObjectAttribute(UserObject,
L"Groups",
NULL,
GroupsBuffer,
&Length);
if (!NT_SUCCESS(Status))
goto done;
GroupsCount = Length / sizeof(GROUP_MEMBERSHIP);
}
for (i = 0; i < GroupsCount; i++)
{
if (GroupsBuffer[i].RelativeId == GroupId)
{
Status = STATUS_MEMBER_IN_GROUP;
goto done;
}
}
GroupsBuffer[GroupsCount].RelativeId = GroupId;
GroupsBuffer[GroupsCount].Attributes = Attributes;
Length += sizeof(GROUP_MEMBERSHIP);
Status = SampSetObjectAttribute(UserObject,
L"Groups",
REG_BINARY,
GroupsBuffer,
Length);
done:
if (GroupsBuffer != NULL)
midl_user_free(GroupsBuffer);
return Status;
}
NTSTATUS
SampRemoveGroupMembershipFromUser(IN PSAM_DB_OBJECT UserObject,
IN ULONG GroupId)
{
PGROUP_MEMBERSHIP GroupsBuffer = NULL;
ULONG GroupsCount = 0;
ULONG Length = 0;
ULONG i;
NTSTATUS Status = STATUS_SUCCESS;
TRACE("(%p %lu)\n",
UserObject, GroupId);
SampGetObjectAttribute(UserObject,
L"Groups",
NULL,
NULL,
&Length);
if (Length == 0)
return STATUS_MEMBER_NOT_IN_GROUP;
GroupsBuffer = midl_user_allocate(Length);
if (GroupsBuffer == NULL)
{
Status = STATUS_INSUFFICIENT_RESOURCES;
goto done;
}
Status = SampGetObjectAttribute(UserObject,
L"Groups",
NULL,
GroupsBuffer,
&Length);
if (!NT_SUCCESS(Status))
goto done;
Status = STATUS_MEMBER_NOT_IN_GROUP;
GroupsCount = Length / sizeof(GROUP_MEMBERSHIP);
for (i = 0; i < GroupsCount; i++)
{
if (GroupsBuffer[i].RelativeId == GroupId)
{
Length -= sizeof(GROUP_MEMBERSHIP);
Status = STATUS_SUCCESS;
if (GroupsCount - i - 1 > 0)
{
CopyMemory(&GroupsBuffer[i],
&GroupsBuffer[i + 1],
(GroupsCount - i - 1) * sizeof(GROUP_MEMBERSHIP));
}
break;
}
}
if (!NT_SUCCESS(Status))
goto done;
Status = SampSetObjectAttribute(UserObject,
L"Groups",
REG_BINARY,
GroupsBuffer,
Length);
done:
if (GroupsBuffer != NULL)
midl_user_free(GroupsBuffer);
return Status;
}
NTSTATUS
SampGetUserGroupAttributes(IN PSAM_DB_OBJECT DomainObject,
IN ULONG UserId,
IN ULONG GroupId,
OUT PULONG GroupAttributes)
{
PSAM_DB_OBJECT UserObject = NULL;
PGROUP_MEMBERSHIP GroupsBuffer = NULL;
ULONG Length = 0;
ULONG i;
NTSTATUS Status;
Status = SampOpenUserObject(DomainObject,
UserId,
0,
&UserObject);
if (!NT_SUCCESS(Status))
{
return Status;
}
SampGetObjectAttribute(UserObject,
L"Groups",
NULL,
NULL,
&Length);
if (Length == 0)
return STATUS_UNSUCCESSFUL; /* FIXME */
GroupsBuffer = midl_user_allocate(Length);
if (GroupsBuffer == NULL)
{
Status = STATUS_INSUFFICIENT_RESOURCES;
goto done;
}
Status = SampGetObjectAttribute(UserObject,
L"Groups",
NULL,
GroupsBuffer,
&Length);
if (!NT_SUCCESS(Status))
goto done;
for (i = 0; i < (Length / sizeof(GROUP_MEMBERSHIP)); i++)
{
if (GroupsBuffer[i].RelativeId == GroupId)
{
*GroupAttributes = GroupsBuffer[i].Attributes;
goto done;
}
}
done:
if (GroupsBuffer != NULL)
midl_user_free(GroupsBuffer);
if (UserObject != NULL)
SampCloseDbObject(UserObject);
return Status;
}
NTSTATUS
SampSetUserGroupAttributes(IN PSAM_DB_OBJECT DomainObject,
IN ULONG UserId,
IN ULONG GroupId,
IN ULONG GroupAttributes)
{
PSAM_DB_OBJECT UserObject = NULL;
PGROUP_MEMBERSHIP GroupsBuffer = NULL;
ULONG Length = 0;
ULONG i;
NTSTATUS Status;
Status = SampOpenUserObject(DomainObject,
UserId,
0,
&UserObject);
if (!NT_SUCCESS(Status))
{
return Status;
}
SampGetObjectAttribute(UserObject,
L"Groups",
NULL,
NULL,
&Length);
if (Length == 0)
return STATUS_UNSUCCESSFUL; /* FIXME */
GroupsBuffer = midl_user_allocate(Length);
if (GroupsBuffer == NULL)
{
Status = STATUS_INSUFFICIENT_RESOURCES;
goto done;
}
Status = SampGetObjectAttribute(UserObject,
L"Groups",
NULL,
GroupsBuffer,
&Length);
if (!NT_SUCCESS(Status))
goto done;
for (i = 0; i < (Length / sizeof(GROUP_MEMBERSHIP)); i++)
{
if (GroupsBuffer[i].RelativeId == GroupId)
{
GroupsBuffer[i].Attributes = GroupAttributes;
break;
}
}
Status = SampSetObjectAttribute(UserObject,
L"Groups",
REG_BINARY,
GroupsBuffer,
Length);
done:
if (GroupsBuffer != NULL)
midl_user_free(GroupsBuffer);
if (UserObject != NULL)
SampCloseDbObject(UserObject);
return Status;
}
NTSTATUS
SampRemoveUserFromAllGroups(IN PSAM_DB_OBJECT UserObject)
{
PGROUP_MEMBERSHIP GroupsBuffer = NULL;
PSAM_DB_OBJECT GroupObject;
ULONG Length = 0;
ULONG i;
NTSTATUS Status;
SampGetObjectAttribute(UserObject,
L"Groups",
NULL,
NULL,
&Length);
if (Length == 0)
return STATUS_SUCCESS;
GroupsBuffer = midl_user_allocate(Length);
if (GroupsBuffer == NULL)
{
Status = STATUS_INSUFFICIENT_RESOURCES;
goto done;
}
Status = SampGetObjectAttribute(UserObject,
L"Groups",
NULL,
GroupsBuffer,
&Length);
if (!NT_SUCCESS(Status))
goto done;
for (i = 0; i < (Length / sizeof(GROUP_MEMBERSHIP)); i++)
{
Status = SampOpenGroupObject(UserObject->ParentObject,
GroupsBuffer[i].RelativeId,
0,
&GroupObject);
if (!NT_SUCCESS(Status))
{
goto done;
}
Status = SampRemoveMemberFromGroup(GroupObject,
UserObject->RelativeId);
if (Status == STATUS_MEMBER_NOT_IN_GROUP)
Status = STATUS_SUCCESS;
SampCloseDbObject(GroupObject);
if (!NT_SUCCESS(Status))
{
goto done;
}
}
/* Remove all groups from the Groups attribute */
Status = SampSetObjectAttribute(UserObject,
L"Groups",
REG_BINARY,
NULL,
0);
done:
if (GroupsBuffer != NULL)
midl_user_free(GroupsBuffer);
return Status;
}
NTSTATUS
SampRemoveUserFromAllAliases(IN PSAM_DB_OBJECT UserObject)
{
FIXME("(%p)\n", UserObject);
return STATUS_SUCCESS;
}
NTSTATUS
SampSetUserPassword(IN PSAM_DB_OBJECT UserObject,
IN PENCRYPTED_NT_OWF_PASSWORD NtPassword,
IN BOOLEAN NtPasswordPresent,
IN PENCRYPTED_LM_OWF_PASSWORD LmPassword,
IN BOOLEAN LmPasswordPresent)
{
PENCRYPTED_NT_OWF_PASSWORD NtHistory = NULL;
PENCRYPTED_LM_OWF_PASSWORD LmHistory = NULL;
ULONG NtHistoryLength = 0;
ULONG LmHistoryLength = 0;
ULONG CurrentHistoryLength;
ULONG MaxHistoryLength = 3;
ULONG Length = 0;
BOOLEAN UseNtPassword;
BOOLEAN UseLmPassword;
NTSTATUS Status;
UseNtPassword =
((NtPasswordPresent != FALSE) &&
(NtPassword != NULL) &&
(memcmp(NtPassword, &EmptyNtHash, sizeof(ENCRYPTED_NT_OWF_PASSWORD)) != 0));
UseLmPassword =
((LmPasswordPresent != FALSE) &&
(LmPassword != NULL) &&
(memcmp(LmPassword, &EmptyLmHash, sizeof(ENCRYPTED_LM_OWF_PASSWORD)) != 0));
/* Update the NT password history only if we have a new non-empty NT password */
if (UseNtPassword)
{
/* Get the size of the NT history */
SampGetObjectAttribute(UserObject,
L"NTPwdHistory",
NULL,
NULL,
&Length);
CurrentHistoryLength = Length / sizeof(ENCRYPTED_NT_OWF_PASSWORD);
if (CurrentHistoryLength < MaxHistoryLength)
{
NtHistoryLength = (CurrentHistoryLength + 1) * sizeof(ENCRYPTED_NT_OWF_PASSWORD);
}
else
{
NtHistoryLength = MaxHistoryLength * sizeof(ENCRYPTED_NT_OWF_PASSWORD);
}
/* Allocate the history buffer */
NtHistory = midl_user_allocate(NtHistoryLength);
if (NtHistory == NULL)
return STATUS_INSUFFICIENT_RESOURCES;
if (Length > 0)
{
/* Get the history */
Status = SampGetObjectAttribute(UserObject,
L"NTPwdHistory",
NULL,
NtHistory,
&Length);
if (!NT_SUCCESS(Status))
goto done;
}
/* Move the old passwords down by one entry */
if (NtHistoryLength > sizeof(ENCRYPTED_NT_OWF_PASSWORD))
{
MoveMemory(&(NtHistory[1]),
&(NtHistory[0]),
NtHistoryLength - sizeof(ENCRYPTED_NT_OWF_PASSWORD));
}
/* Add the new password to the top of the history */
if (NtPasswordPresent)
{
CopyMemory(&(NtHistory[0]),
NtPassword,
sizeof(ENCRYPTED_NT_OWF_PASSWORD));
}
else
{
ZeroMemory(&(NtHistory[0]),
sizeof(ENCRYPTED_NT_OWF_PASSWORD));
}
/* Set the history */
Status = SampSetObjectAttribute(UserObject,
L"NTPwdHistory",
REG_BINARY,
(PVOID)NtHistory,
NtHistoryLength);
if (!NT_SUCCESS(Status))
goto done;
}
/* Update the LM password history only if we have a new non-empty LM password */
if (UseLmPassword)
{
/* Get the size of the LM history */
Length = 0;
SampGetObjectAttribute(UserObject,
L"LMPwdHistory",
NULL,
NULL,
&Length);
CurrentHistoryLength = Length / sizeof(ENCRYPTED_LM_OWF_PASSWORD);
if (CurrentHistoryLength < MaxHistoryLength)
{
LmHistoryLength = (CurrentHistoryLength + 1) * sizeof(ENCRYPTED_LM_OWF_PASSWORD);
}
else
{
LmHistoryLength = MaxHistoryLength * sizeof(ENCRYPTED_LM_OWF_PASSWORD);
}
/* Allocate the history buffer */
LmHistory = midl_user_allocate(LmHistoryLength);
if (LmHistory == NULL)
return STATUS_INSUFFICIENT_RESOURCES;
if (Length > 0)
{
/* Get the history */
Status = SampGetObjectAttribute(UserObject,
L"LMPwdHistory",
NULL,
LmHistory,
&Length);
if (!NT_SUCCESS(Status))
goto done;
}
/* Move the old passwords down by one entry */
if (LmHistoryLength > sizeof(ENCRYPTED_LM_OWF_PASSWORD))
{
MoveMemory(&(LmHistory[1]),
&(LmHistory[0]),
LmHistoryLength - sizeof(ENCRYPTED_LM_OWF_PASSWORD));
}
/* Add the new password to the top of the history */
if (LmPasswordPresent)
{
CopyMemory(&(LmHistory[0]),
LmPassword,
sizeof(ENCRYPTED_LM_OWF_PASSWORD));
}
else
{
ZeroMemory(&(LmHistory[0]),
sizeof(ENCRYPTED_LM_OWF_PASSWORD));
}
/* Set the LM password history */
Status = SampSetObjectAttribute(UserObject,
L"LMPwdHistory",
REG_BINARY,
(PVOID)LmHistory,
LmHistoryLength);
if (!NT_SUCCESS(Status))
goto done;
}
/* Set the new NT password */
if (UseNtPassword)
{
Status = SampSetObjectAttribute(UserObject,
L"NTPwd",
REG_BINARY,
(PVOID)NtPassword,
sizeof(ENCRYPTED_NT_OWF_PASSWORD));
if (!NT_SUCCESS(Status))
goto done;
}
else
{
Status = SampSetObjectAttribute(UserObject,
L"NTPwd",
REG_BINARY,
&EmptyNtHash,
sizeof(ENCRYPTED_NT_OWF_PASSWORD));
if (!NT_SUCCESS(Status))
goto done;
}
/* Set the new LM password */
if (UseLmPassword)
{
Status = SampSetObjectAttribute(UserObject,
L"LMPwd",
REG_BINARY,
(PVOID)LmPassword,
sizeof(ENCRYPTED_LM_OWF_PASSWORD));
if (!NT_SUCCESS(Status))
goto done;
}
else
{
Status = SampSetObjectAttribute(UserObject,
L"LMPwd",
REG_BINARY,
&EmptyLmHash,
sizeof(ENCRYPTED_LM_OWF_PASSWORD));
if (!NT_SUCCESS(Status))
goto done;
}
done:
if (NtHistory != NULL)
midl_user_free(NtHistory);
if (LmHistory != NULL)
midl_user_free(LmHistory);
return Status;
}
NTSTATUS
SampGetLogonHoursAttribute(IN PSAM_DB_OBJECT UserObject,
IN OUT PSAMPR_LOGON_HOURS LogonHours)
{
PUCHAR RawBuffer = NULL;
ULONG Length = 0;
ULONG BufferLength = 0;
NTSTATUS Status;
Status = SampGetObjectAttribute(UserObject,
L"LogonHours",
NULL,
NULL,
&Length);
if (Status != STATUS_BUFFER_OVERFLOW)
{
TRACE("SampGetObjectAttribute failed (Status 0x%08lx)\n", Status);
return Status;
}
Status = STATUS_SUCCESS;
if (Length == 0)
{
LogonHours->UnitsPerWeek = 0;
LogonHours->LogonHours = NULL;
}
else
{
RawBuffer = midl_user_allocate(Length);
if (RawBuffer == NULL)
{
Status = STATUS_INSUFFICIENT_RESOURCES;
goto done;
}
Status = SampGetObjectAttribute(UserObject,
L"LogonHours",
NULL,
(PVOID)RawBuffer,
&Length);
if (!NT_SUCCESS(Status))
goto done;
LogonHours->UnitsPerWeek = *((PUSHORT)RawBuffer);
BufferLength = (((ULONG)LogonHours->UnitsPerWeek) + 7) / 8;
LogonHours->LogonHours = midl_user_allocate(BufferLength);
if (LogonHours->LogonHours == NULL)
{
TRACE("Failed to allocate LogonHours buffer!\n");
Status = STATUS_INSUFFICIENT_RESOURCES;
goto done;
}
memcpy(LogonHours->LogonHours,
&(RawBuffer[2]),
BufferLength);
}
done:
if (RawBuffer != NULL)
midl_user_free(RawBuffer);
return Status;
}
NTSTATUS
SampSetLogonHoursAttribute(IN PSAM_DB_OBJECT UserObject,
IN PSAMPR_LOGON_HOURS LogonHours)
{
PUCHAR RawBuffer = NULL;
ULONG BufferLength;
ULONG Length = 0;
NTSTATUS Status;
if (LogonHours->UnitsPerWeek > 0)
{
BufferLength = (((ULONG)LogonHours->UnitsPerWeek) + 7) / 8;
Length = BufferLength + sizeof(USHORT);
RawBuffer = midl_user_allocate(Length);
if (RawBuffer == NULL)
{
Status = STATUS_INSUFFICIENT_RESOURCES;
goto done;
}
*((PUSHORT)RawBuffer) = LogonHours->UnitsPerWeek;
memcpy(&(RawBuffer[2]),
LogonHours->LogonHours,
BufferLength);
}
Status = SampSetObjectAttribute(UserObject,
L"LogonHours",
REG_BINARY,
RawBuffer,
Length);
done:
if (RawBuffer != NULL)
midl_user_free(RawBuffer);
return Status;
}
/* EOF */