mirror of
https://github.com/reactos/reactos.git
synced 2025-05-07 02:41:22 +00:00
[MSV1_0] Extract SamValidateNormalUser to a separate file (2/5)
This is a part of the Partial Network Login Implementation PR. - Extract code for SamValidateNormalUser (sam.c) from LsaApLogonUserEx2. - Move dependent functions to sam.c: * GetAccountDomainSid * MsvpCheckPassword * MsvpCheckLogonHours * MsvpCheckWorkstations
This commit is contained in:
parent
5a67ed775f
commit
045cd5d42f
5 changed files with 479 additions and 400 deletions
|
@ -5,6 +5,7 @@ list(APPEND SOURCE
|
|||
lsa.c
|
||||
msv1_0.c
|
||||
ntlm/global.c
|
||||
sam.c
|
||||
user.c
|
||||
usercontext.c
|
||||
${CMAKE_CURRENT_BINARY_DIR}/msv1_0_stubs.c
|
||||
|
|
|
@ -20,54 +20,6 @@ LSA_DISPATCH_TABLE DispatchTable;
|
|||
|
||||
/* FUNCTIONS ***************************************************************/
|
||||
|
||||
static
|
||||
NTSTATUS
|
||||
GetAccountDomainSid(PRPC_SID *Sid)
|
||||
{
|
||||
LSAPR_HANDLE PolicyHandle = NULL;
|
||||
PLSAPR_POLICY_INFORMATION PolicyInfo = NULL;
|
||||
ULONG Length = 0;
|
||||
NTSTATUS Status;
|
||||
|
||||
Status = LsaIOpenPolicyTrusted(&PolicyHandle);
|
||||
if (!NT_SUCCESS(Status))
|
||||
{
|
||||
TRACE("LsaIOpenPolicyTrusted() failed (Status 0x%08lx)\n", Status);
|
||||
return Status;
|
||||
}
|
||||
|
||||
Status = LsarQueryInformationPolicy(PolicyHandle,
|
||||
PolicyAccountDomainInformation,
|
||||
&PolicyInfo);
|
||||
if (!NT_SUCCESS(Status))
|
||||
{
|
||||
TRACE("LsarQueryInformationPolicy() failed (Status 0x%08lx)\n", Status);
|
||||
goto done;
|
||||
}
|
||||
|
||||
Length = RtlLengthSid(PolicyInfo->PolicyAccountDomainInfo.Sid);
|
||||
|
||||
*Sid = RtlAllocateHeap(RtlGetProcessHeap(), 0, Length);
|
||||
if (*Sid == NULL)
|
||||
{
|
||||
ERR("Failed to allocate SID\n");
|
||||
Status = STATUS_INSUFFICIENT_RESOURCES;
|
||||
goto done;
|
||||
}
|
||||
|
||||
memcpy(*Sid, PolicyInfo->PolicyAccountDomainInfo.Sid, Length);
|
||||
|
||||
done:
|
||||
if (PolicyInfo != NULL)
|
||||
LsaIFree_LSAPR_POLICY_INFORMATION(PolicyAccountDomainInformation,
|
||||
PolicyInfo);
|
||||
|
||||
if (PolicyHandle != NULL)
|
||||
LsarClose(&PolicyHandle);
|
||||
|
||||
return Status;
|
||||
}
|
||||
|
||||
|
||||
static
|
||||
NTSTATUS
|
||||
|
@ -790,182 +742,6 @@ done:
|
|||
}
|
||||
|
||||
|
||||
static
|
||||
NTSTATUS
|
||||
MsvpCheckPassword(PUNICODE_STRING UserPassword,
|
||||
PSAMPR_USER_INFO_BUFFER UserInfo)
|
||||
{
|
||||
ENCRYPTED_NT_OWF_PASSWORD UserNtPassword;
|
||||
ENCRYPTED_LM_OWF_PASSWORD UserLmPassword;
|
||||
BOOLEAN UserLmPasswordPresent = FALSE;
|
||||
BOOLEAN UserNtPasswordPresent = FALSE;
|
||||
OEM_STRING LmPwdString;
|
||||
CHAR LmPwdBuffer[15];
|
||||
NTSTATUS Status;
|
||||
|
||||
TRACE("(%p %p)\n", UserPassword, UserInfo);
|
||||
|
||||
/* Calculate the LM password and hash for the users password */
|
||||
LmPwdString.Length = 15;
|
||||
LmPwdString.MaximumLength = 15;
|
||||
LmPwdString.Buffer = LmPwdBuffer;
|
||||
ZeroMemory(LmPwdString.Buffer, LmPwdString.MaximumLength);
|
||||
|
||||
Status = RtlUpcaseUnicodeStringToOemString(&LmPwdString,
|
||||
UserPassword,
|
||||
FALSE);
|
||||
if (NT_SUCCESS(Status))
|
||||
{
|
||||
/* Calculate the LM hash value of the users password */
|
||||
Status = SystemFunction006(LmPwdString.Buffer,
|
||||
(LPSTR)&UserLmPassword);
|
||||
if (NT_SUCCESS(Status))
|
||||
{
|
||||
UserLmPasswordPresent = TRUE;
|
||||
}
|
||||
}
|
||||
|
||||
/* Calculate the NT hash of the users password */
|
||||
Status = SystemFunction007(UserPassword,
|
||||
(LPBYTE)&UserNtPassword);
|
||||
if (NT_SUCCESS(Status))
|
||||
{
|
||||
UserNtPasswordPresent = TRUE;
|
||||
}
|
||||
|
||||
Status = STATUS_WRONG_PASSWORD;
|
||||
|
||||
/* Succeed, if no password has been set */
|
||||
if (UserInfo->All.NtPasswordPresent == FALSE &&
|
||||
UserInfo->All.LmPasswordPresent == FALSE)
|
||||
{
|
||||
TRACE("No password check!\n");
|
||||
Status = STATUS_SUCCESS;
|
||||
goto done;
|
||||
}
|
||||
|
||||
/* Succeed, if NT password matches */
|
||||
if (UserNtPasswordPresent && UserInfo->All.NtPasswordPresent)
|
||||
{
|
||||
TRACE("Check NT password hashes:\n");
|
||||
if (RtlEqualMemory(&UserNtPassword,
|
||||
UserInfo->All.NtOwfPassword.Buffer,
|
||||
sizeof(ENCRYPTED_NT_OWF_PASSWORD)))
|
||||
{
|
||||
TRACE(" success!\n");
|
||||
Status = STATUS_SUCCESS;
|
||||
goto done;
|
||||
}
|
||||
|
||||
TRACE(" failed!\n");
|
||||
}
|
||||
|
||||
/* Succeed, if LM password matches */
|
||||
if (UserLmPasswordPresent && UserInfo->All.LmPasswordPresent)
|
||||
{
|
||||
TRACE("Check LM password hashes:\n");
|
||||
if (RtlEqualMemory(&UserLmPassword,
|
||||
UserInfo->All.LmOwfPassword.Buffer,
|
||||
sizeof(ENCRYPTED_LM_OWF_PASSWORD)))
|
||||
{
|
||||
TRACE(" success!\n");
|
||||
Status = STATUS_SUCCESS;
|
||||
goto done;
|
||||
}
|
||||
TRACE(" failed!\n");
|
||||
}
|
||||
|
||||
done:
|
||||
return Status;
|
||||
}
|
||||
|
||||
|
||||
static
|
||||
BOOL
|
||||
MsvpCheckLogonHours(
|
||||
_In_ PSAMPR_LOGON_HOURS LogonHours,
|
||||
_In_ PLARGE_INTEGER LogonTime)
|
||||
{
|
||||
#if 0
|
||||
LARGE_INTEGER LocalLogonTime;
|
||||
TIME_FIELDS TimeFields;
|
||||
USHORT MinutesPerUnit, Offset;
|
||||
BOOL bFound;
|
||||
|
||||
FIXME("MsvpCheckLogonHours(%p %p)\n", LogonHours, LogonTime);
|
||||
|
||||
if (LogonHours->UnitsPerWeek == 0 || LogonHours->LogonHours == NULL)
|
||||
{
|
||||
FIXME("No logon hours!\n");
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
RtlSystemTimeToLocalTime(LogonTime, &LocalLogonTime);
|
||||
RtlTimeToTimeFields(&LocalLogonTime, &TimeFields);
|
||||
|
||||
FIXME("UnitsPerWeek: %u\n", LogonHours->UnitsPerWeek);
|
||||
MinutesPerUnit = 10080 / LogonHours->UnitsPerWeek;
|
||||
|
||||
Offset = ((TimeFields.Weekday * 24 + TimeFields.Hour) * 60 + TimeFields.Minute) / MinutesPerUnit;
|
||||
FIXME("Offset: %us\n", Offset);
|
||||
|
||||
bFound = (BOOL)(LogonHours->LogonHours[Offset / 8] & (1 << (Offset % 8)));
|
||||
FIXME("Logon permitted: %s\n", bFound ? "Yes" : "No");
|
||||
|
||||
return bFound;
|
||||
#endif
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
|
||||
static
|
||||
BOOL
|
||||
MsvpCheckWorkstations(
|
||||
_In_ PRPC_UNICODE_STRING WorkStations,
|
||||
_In_ PWSTR ComputerName)
|
||||
{
|
||||
PWSTR pStart, pEnd;
|
||||
BOOL bFound = FALSE;
|
||||
|
||||
TRACE("MsvpCheckWorkstations(%p %S)\n", WorkStations, ComputerName);
|
||||
|
||||
if (WorkStations->Length == 0 || WorkStations->Buffer == NULL)
|
||||
{
|
||||
TRACE("No workstations!\n");
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
TRACE("Workstations: %wZ\n", WorkStations);
|
||||
|
||||
pStart = WorkStations->Buffer;
|
||||
for (;;)
|
||||
{
|
||||
pEnd = wcschr(pStart, L',');
|
||||
if (pEnd != NULL)
|
||||
*pEnd = UNICODE_NULL;
|
||||
|
||||
TRACE("Comparing '%S' and '%S'\n", ComputerName, pStart);
|
||||
if (_wcsicmp(ComputerName, pStart) == 0)
|
||||
{
|
||||
bFound = TRUE;
|
||||
if (pEnd != NULL)
|
||||
*pEnd = L',';
|
||||
break;
|
||||
}
|
||||
|
||||
if (pEnd == NULL)
|
||||
break;
|
||||
|
||||
*pEnd = L',';
|
||||
pStart = pEnd + 1;
|
||||
}
|
||||
|
||||
TRACE("Found allowed workstation: %s\n", (bFound) ? "Yes" : "No");
|
||||
|
||||
return bFound;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* @unimplemented
|
||||
*/
|
||||
|
@ -1185,19 +961,11 @@ LsaApLogonUserEx2(IN PLSA_CLIENT_REQUEST ClientRequest,
|
|||
PMSV1_0_INTERACTIVE_LOGON LogonInfo;
|
||||
UNICODE_STRING ComputerName;
|
||||
WCHAR ComputerNameData[MAX_COMPUTERNAME_LENGTH + 1];
|
||||
SAMPR_HANDLE ServerHandle = NULL;
|
||||
SAMPR_HANDLE DomainHandle = NULL;
|
||||
LSA_SAM_PWD_DATA LogonPwdData = { FALSE, NULL };
|
||||
SAMPR_HANDLE UserHandle = NULL;
|
||||
PRPC_SID AccountDomainSid = NULL;
|
||||
RPC_UNICODE_STRING Names[1];
|
||||
SAMPR_ULONG_ARRAY RelativeIds = {0, NULL};
|
||||
SAMPR_ULONG_ARRAY Use = {0, NULL};
|
||||
PSAMPR_USER_INFO_BUFFER UserInfo = NULL;
|
||||
BOOLEAN SessionCreated = FALSE;
|
||||
LARGE_INTEGER LogonTime;
|
||||
LARGE_INTEGER AccountExpires;
|
||||
LARGE_INTEGER PasswordMustChange;
|
||||
LARGE_INTEGER PasswordLastSet;
|
||||
DWORD ComputerNameSize;
|
||||
BOOL SpecialAccount = FALSE;
|
||||
UCHAR LogonPassHash;
|
||||
|
@ -1324,6 +1092,9 @@ LsaApLogonUserEx2(IN PLSA_CLIENT_REQUEST ClientRequest,
|
|||
if (!NT_SUCCESS(Status))
|
||||
return STATUS_INVALID_PARAMETER;
|
||||
|
||||
LogonPwdData.IsNetwork = FALSE;
|
||||
LogonPwdData.PlainPwd = &LogonInfo->Password;
|
||||
|
||||
TRACE("Domain: %wZ\n", &LogonInfo->LogonDomainName);
|
||||
TRACE("User: %wZ\n", &LogonInfo->UserName);
|
||||
TRACE("Password: %wZ\n", &LogonInfo->Password);
|
||||
|
@ -1337,9 +1108,6 @@ LsaApLogonUserEx2(IN PLSA_CLIENT_REQUEST ClientRequest,
|
|||
}
|
||||
// TODO: Add other LogonType validity checks.
|
||||
|
||||
/* Get the logon time */
|
||||
NtQuerySystemTime(&LogonTime);
|
||||
|
||||
/* Check for special accounts */
|
||||
// FIXME: Windows does not do this that way!! (msv1_0 does not contain these hardcoded values)
|
||||
if (RtlEqualUnicodeString(&LogonInfo->LogonDomainName, &NtAuthorityU, TRUE))
|
||||
|
@ -1401,165 +1169,18 @@ LsaApLogonUserEx2(IN PLSA_CLIENT_REQUEST ClientRequest,
|
|||
else
|
||||
{
|
||||
TRACE("NormalAccount\n");
|
||||
|
||||
/* Get the account domain SID */
|
||||
Status = GetAccountDomainSid(&AccountDomainSid);
|
||||
Status = SamValidateNormalUser(&LogonInfo->UserName,
|
||||
&LogonPwdData,
|
||||
&ComputerName,
|
||||
&AccountDomainSid,
|
||||
&UserHandle,
|
||||
&UserInfo,
|
||||
SubStatus);
|
||||
if (!NT_SUCCESS(Status))
|
||||
{
|
||||
ERR("GetAccountDomainSid() failed (Status 0x%08lx)\n", Status);
|
||||
ERR("SamValidateNormalUser() failed (Status 0x%08lx)\n", Status);
|
||||
return Status;
|
||||
}
|
||||
|
||||
/* Connect to the SAM server */
|
||||
Status = SamIConnect(NULL,
|
||||
&ServerHandle,
|
||||
SAM_SERVER_CONNECT | SAM_SERVER_LOOKUP_DOMAIN,
|
||||
TRUE);
|
||||
if (!NT_SUCCESS(Status))
|
||||
{
|
||||
TRACE("SamIConnect() failed (Status 0x%08lx)\n", Status);
|
||||
goto done;
|
||||
}
|
||||
|
||||
/* Open the account domain */
|
||||
Status = SamrOpenDomain(ServerHandle,
|
||||
DOMAIN_LOOKUP,
|
||||
AccountDomainSid,
|
||||
&DomainHandle);
|
||||
if (!NT_SUCCESS(Status))
|
||||
{
|
||||
ERR("SamrOpenDomain failed (Status %08lx)\n", Status);
|
||||
goto done;
|
||||
}
|
||||
|
||||
Names[0].Length = LogonInfo->UserName.Length;
|
||||
Names[0].MaximumLength = LogonInfo->UserName.MaximumLength;
|
||||
Names[0].Buffer = LogonInfo->UserName.Buffer;
|
||||
|
||||
/* Try to get the RID for the user name */
|
||||
Status = SamrLookupNamesInDomain(DomainHandle,
|
||||
1,
|
||||
Names,
|
||||
&RelativeIds,
|
||||
&Use);
|
||||
if (!NT_SUCCESS(Status))
|
||||
{
|
||||
ERR("SamrLookupNamesInDomain failed (Status %08lx)\n", Status);
|
||||
Status = STATUS_NO_SUCH_USER;
|
||||
goto done;
|
||||
}
|
||||
|
||||
/* Fail, if it is not a user account */
|
||||
if (Use.Element[0] != SidTypeUser)
|
||||
{
|
||||
ERR("Account is not a user account!\n");
|
||||
Status = STATUS_NO_SUCH_USER;
|
||||
goto done;
|
||||
}
|
||||
|
||||
/* Open the user object */
|
||||
Status = SamrOpenUser(DomainHandle,
|
||||
USER_READ_GENERAL | USER_READ_LOGON |
|
||||
USER_READ_ACCOUNT | USER_READ_PREFERENCES, /* FIXME */
|
||||
RelativeIds.Element[0],
|
||||
&UserHandle);
|
||||
if (!NT_SUCCESS(Status))
|
||||
{
|
||||
ERR("SamrOpenUser failed (Status %08lx)\n", Status);
|
||||
goto done;
|
||||
}
|
||||
|
||||
Status = SamrQueryInformationUser(UserHandle,
|
||||
UserAllInformation,
|
||||
&UserInfo);
|
||||
if (!NT_SUCCESS(Status))
|
||||
{
|
||||
ERR("SamrQueryInformationUser failed (Status %08lx)\n", Status);
|
||||
goto done;
|
||||
}
|
||||
|
||||
TRACE("UserName: %wZ\n", &UserInfo->All.UserName);
|
||||
|
||||
/* Check the password */
|
||||
if ((UserInfo->All.UserAccountControl & USER_PASSWORD_NOT_REQUIRED) == 0)
|
||||
{
|
||||
Status = MsvpCheckPassword(&LogonInfo->Password,
|
||||
UserInfo);
|
||||
if (!NT_SUCCESS(Status))
|
||||
{
|
||||
ERR("MsvpCheckPassword failed (Status %08lx)\n", Status);
|
||||
goto done;
|
||||
}
|
||||
}
|
||||
|
||||
/* Check account restrictions for non-administrator accounts */
|
||||
if (RelativeIds.Element[0] != DOMAIN_USER_RID_ADMIN)
|
||||
{
|
||||
/* Check if the account has been disabled */
|
||||
if (UserInfo->All.UserAccountControl & USER_ACCOUNT_DISABLED)
|
||||
{
|
||||
ERR("Account disabled!\n");
|
||||
*SubStatus = STATUS_ACCOUNT_DISABLED;
|
||||
Status = STATUS_ACCOUNT_RESTRICTION;
|
||||
goto done;
|
||||
}
|
||||
|
||||
/* Check if the account has been locked */
|
||||
if (UserInfo->All.UserAccountControl & USER_ACCOUNT_AUTO_LOCKED)
|
||||
{
|
||||
ERR("Account locked!\n");
|
||||
*SubStatus = STATUS_ACCOUNT_LOCKED_OUT;
|
||||
Status = STATUS_ACCOUNT_RESTRICTION;
|
||||
goto done;
|
||||
}
|
||||
|
||||
/* Check if the account expired */
|
||||
AccountExpires.LowPart = UserInfo->All.AccountExpires.LowPart;
|
||||
AccountExpires.HighPart = UserInfo->All.AccountExpires.HighPart;
|
||||
if (LogonTime.QuadPart >= AccountExpires.QuadPart)
|
||||
{
|
||||
ERR("Account expired!\n");
|
||||
*SubStatus = STATUS_ACCOUNT_EXPIRED;
|
||||
Status = STATUS_ACCOUNT_RESTRICTION;
|
||||
goto done;
|
||||
}
|
||||
|
||||
/* Check if the password expired */
|
||||
PasswordMustChange.LowPart = UserInfo->All.PasswordMustChange.LowPart;
|
||||
PasswordMustChange.HighPart = UserInfo->All.PasswordMustChange.HighPart;
|
||||
PasswordLastSet.LowPart = UserInfo->All.PasswordLastSet.LowPart;
|
||||
PasswordLastSet.HighPart = UserInfo->All.PasswordLastSet.HighPart;
|
||||
|
||||
if (LogonTime.QuadPart >= PasswordMustChange.QuadPart)
|
||||
{
|
||||
ERR("Password expired!\n");
|
||||
if (PasswordLastSet.QuadPart == 0)
|
||||
*SubStatus = STATUS_PASSWORD_MUST_CHANGE;
|
||||
else
|
||||
*SubStatus = STATUS_PASSWORD_EXPIRED;
|
||||
|
||||
Status = STATUS_ACCOUNT_RESTRICTION;
|
||||
goto done;
|
||||
}
|
||||
|
||||
/* Check logon hours */
|
||||
if (!MsvpCheckLogonHours(&UserInfo->All.LogonHours, &LogonTime))
|
||||
{
|
||||
ERR("Invalid logon hours!\n");
|
||||
*SubStatus = STATUS_INVALID_LOGON_HOURS;
|
||||
Status = STATUS_ACCOUNT_RESTRICTION;
|
||||
goto done;
|
||||
}
|
||||
|
||||
/* Check workstations */
|
||||
if (!MsvpCheckWorkstations(&UserInfo->All.WorkStations, ComputerNameData))
|
||||
{
|
||||
ERR("Invalid workstation!\n");
|
||||
*SubStatus = STATUS_INVALID_WORKSTATION;
|
||||
Status = STATUS_ACCOUNT_RESTRICTION;
|
||||
goto done;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Return logon information */
|
||||
|
@ -1692,14 +1313,6 @@ done:
|
|||
|
||||
SamIFree_SAMPR_USER_INFO_BUFFER(UserInfo,
|
||||
UserAllInformation);
|
||||
SamIFree_SAMPR_ULONG_ARRAY(&RelativeIds);
|
||||
SamIFree_SAMPR_ULONG_ARRAY(&Use);
|
||||
|
||||
if (DomainHandle != NULL)
|
||||
SamrCloseHandle(&DomainHandle);
|
||||
|
||||
if (ServerHandle != NULL)
|
||||
SamrCloseHandle(&ServerHandle);
|
||||
|
||||
if (AccountDomainSid != NULL)
|
||||
RtlFreeHeap(RtlGetProcessHeap(), 0, AccountDomainSid);
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
#define _INC_WINDOWS
|
||||
#define COM_NO_WINDOWS_H
|
||||
#include <stdarg.h>
|
||||
#include <stdbool.h>
|
||||
#include <windef.h>
|
||||
#include <winbase.h>
|
||||
#include <winreg.h>
|
||||
|
@ -33,10 +34,10 @@
|
|||
#include "ntlm/global.h"
|
||||
#include "lsa.h"
|
||||
#include "msv1_0.h"
|
||||
#include "sam.h"
|
||||
#include "user.h"
|
||||
#include "usercontext.h"
|
||||
|
||||
|
||||
#include <wine/debug.h>
|
||||
|
||||
#endif
|
||||
|
|
433
dll/win32/msv1_0/sam.c
Normal file
433
dll/win32/msv1_0/sam.c
Normal file
|
@ -0,0 +1,433 @@
|
|||
/*
|
||||
* PROJECT: Authentication Package DLL
|
||||
* LICENSE: GPL-2.0-or-later (https://spdx.org/licenses/GPL-2.0-or-later)
|
||||
* PURPOSE: Security Account Manager (SAM) related functions
|
||||
* COPYRIGHT: Copyright 2013 Eric Kohl <eric.kohl@reactos.org>
|
||||
*/
|
||||
|
||||
#include "precomp.h"
|
||||
|
||||
#include "wine/debug.h"
|
||||
WINE_DEFAULT_DEBUG_CHANNEL(msv1_0_sam);
|
||||
|
||||
|
||||
static
|
||||
NTSTATUS
|
||||
GetAccountDomainSid(
|
||||
_In_ PRPC_SID *Sid)
|
||||
{
|
||||
LSAPR_HANDLE PolicyHandle = NULL;
|
||||
PLSAPR_POLICY_INFORMATION PolicyInfo = NULL;
|
||||
ULONG Length = 0;
|
||||
NTSTATUS Status;
|
||||
|
||||
Status = LsaIOpenPolicyTrusted(&PolicyHandle);
|
||||
if (!NT_SUCCESS(Status))
|
||||
{
|
||||
TRACE("LsaIOpenPolicyTrusted() failed (Status 0x%08lx)\n", Status);
|
||||
return Status;
|
||||
}
|
||||
|
||||
Status = LsarQueryInformationPolicy(PolicyHandle,
|
||||
PolicyAccountDomainInformation,
|
||||
&PolicyInfo);
|
||||
if (!NT_SUCCESS(Status))
|
||||
{
|
||||
TRACE("LsarQueryInformationPolicy() failed (Status 0x%08lx)\n", Status);
|
||||
goto done;
|
||||
}
|
||||
|
||||
Length = RtlLengthSid(PolicyInfo->PolicyAccountDomainInfo.Sid);
|
||||
|
||||
*Sid = RtlAllocateHeap(RtlGetProcessHeap(), 0, Length);
|
||||
if (*Sid == NULL)
|
||||
{
|
||||
ERR("Failed to allocate SID\n");
|
||||
Status = STATUS_INSUFFICIENT_RESOURCES;
|
||||
goto done;
|
||||
}
|
||||
|
||||
memcpy(*Sid, PolicyInfo->PolicyAccountDomainInfo.Sid, Length);
|
||||
|
||||
done:
|
||||
if (PolicyInfo != NULL)
|
||||
LsaIFree_LSAPR_POLICY_INFORMATION(PolicyAccountDomainInformation,
|
||||
PolicyInfo);
|
||||
|
||||
if (PolicyHandle != NULL)
|
||||
LsarClose(&PolicyHandle);
|
||||
|
||||
return Status;
|
||||
}
|
||||
|
||||
|
||||
static
|
||||
NTSTATUS
|
||||
MsvpCheckPassword(
|
||||
_In_ PLSA_SAM_PWD_DATA UserPwdData,
|
||||
_In_ PSAMPR_USER_INFO_BUFFER UserInfo)
|
||||
{
|
||||
ENCRYPTED_NT_OWF_PASSWORD UserNtPassword;
|
||||
ENCRYPTED_LM_OWF_PASSWORD UserLmPassword;
|
||||
BOOLEAN UserLmPasswordPresent = FALSE;
|
||||
BOOLEAN UserNtPasswordPresent = FALSE;
|
||||
OEM_STRING LmPwdString;
|
||||
CHAR LmPwdBuffer[15];
|
||||
NTSTATUS Status;
|
||||
|
||||
TRACE("(%p %p)\n", UserPwdData, UserInfo);
|
||||
|
||||
/* Calculate the LM password and hash for the users password */
|
||||
LmPwdString.Length = 15;
|
||||
LmPwdString.MaximumLength = 15;
|
||||
LmPwdString.Buffer = LmPwdBuffer;
|
||||
ZeroMemory(LmPwdString.Buffer, LmPwdString.MaximumLength);
|
||||
|
||||
Status = RtlUpcaseUnicodeStringToOemString(&LmPwdString,
|
||||
UserPwdData->PlainPwd,
|
||||
FALSE);
|
||||
if (NT_SUCCESS(Status))
|
||||
{
|
||||
/* Calculate the LM hash value of the users password */
|
||||
Status = SystemFunction006(LmPwdString.Buffer,
|
||||
(LPSTR)&UserLmPassword);
|
||||
if (NT_SUCCESS(Status))
|
||||
{
|
||||
UserLmPasswordPresent = TRUE;
|
||||
}
|
||||
}
|
||||
|
||||
/* Calculate the NT hash of the users password */
|
||||
Status = SystemFunction007(UserPwdData->PlainPwd,
|
||||
(LPBYTE)&UserNtPassword);
|
||||
if (NT_SUCCESS(Status))
|
||||
{
|
||||
UserNtPasswordPresent = TRUE;
|
||||
}
|
||||
|
||||
Status = STATUS_WRONG_PASSWORD;
|
||||
|
||||
/* Succeed, if no password has been set */
|
||||
if (UserInfo->All.NtPasswordPresent == FALSE &&
|
||||
UserInfo->All.LmPasswordPresent == FALSE)
|
||||
{
|
||||
TRACE("No password check!\n");
|
||||
Status = STATUS_SUCCESS;
|
||||
goto done;
|
||||
}
|
||||
|
||||
/* Succeed, if NT password matches */
|
||||
if (UserNtPasswordPresent && UserInfo->All.NtPasswordPresent)
|
||||
{
|
||||
TRACE("Check NT password hashes:\n");
|
||||
if (RtlEqualMemory(&UserNtPassword,
|
||||
UserInfo->All.NtOwfPassword.Buffer,
|
||||
sizeof(ENCRYPTED_NT_OWF_PASSWORD)))
|
||||
{
|
||||
TRACE(" success!\n");
|
||||
Status = STATUS_SUCCESS;
|
||||
goto done;
|
||||
}
|
||||
|
||||
TRACE(" failed!\n");
|
||||
}
|
||||
|
||||
/* Succeed, if LM password matches */
|
||||
if (UserLmPasswordPresent && UserInfo->All.LmPasswordPresent)
|
||||
{
|
||||
TRACE("Check LM password hashes:\n");
|
||||
if (RtlEqualMemory(&UserLmPassword,
|
||||
UserInfo->All.LmOwfPassword.Buffer,
|
||||
sizeof(ENCRYPTED_LM_OWF_PASSWORD)))
|
||||
{
|
||||
TRACE(" success!\n");
|
||||
Status = STATUS_SUCCESS;
|
||||
goto done;
|
||||
}
|
||||
TRACE(" failed!\n");
|
||||
}
|
||||
|
||||
done:
|
||||
return Status;
|
||||
}
|
||||
|
||||
|
||||
static
|
||||
bool
|
||||
MsvpCheckLogonHours(
|
||||
_In_ PSAMPR_LOGON_HOURS LogonHours,
|
||||
_In_ LARGE_INTEGER LogonTime)
|
||||
{
|
||||
#if 0
|
||||
LARGE_INTEGER LocalLogonTime;
|
||||
TIME_FIELDS TimeFields;
|
||||
USHORT MinutesPerUnit, Offset;
|
||||
bool bFound;
|
||||
|
||||
FIXME("MsvpCheckLogonHours(%p %llx)\n", LogonHours, LogonTime);
|
||||
|
||||
if (LogonHours->UnitsPerWeek == 0 || LogonHours->LogonHours == NULL)
|
||||
{
|
||||
FIXME("No logon hours!\n");
|
||||
return true;
|
||||
}
|
||||
|
||||
RtlSystemTimeToLocalTime(&LogonTime, &LocalLogonTime);
|
||||
RtlTimeToTimeFields(&LocalLogonTime, &TimeFields);
|
||||
|
||||
FIXME("UnitsPerWeek: %u\n", LogonHours->UnitsPerWeek);
|
||||
MinutesPerUnit = 10080 / LogonHours->UnitsPerWeek;
|
||||
|
||||
Offset = ((TimeFields.Weekday * 24 + TimeFields.Hour) * 60 + TimeFields.Minute) / MinutesPerUnit;
|
||||
FIXME("Offset: %us\n", Offset);
|
||||
|
||||
bFound = (bool)(LogonHours->LogonHours[Offset / 8] & (1 << (Offset % 8)));
|
||||
FIXME("Logon permitted: %s\n", bFound ? "Yes" : "No");
|
||||
|
||||
return bFound;
|
||||
#endif
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
static
|
||||
bool
|
||||
MsvpCheckWorkstations(
|
||||
_In_ PRPC_UNICODE_STRING WorkStations,
|
||||
_In_ PWSTR ComputerName)
|
||||
{
|
||||
PWSTR pStart, pEnd;
|
||||
bool bFound = false;
|
||||
|
||||
TRACE("MsvpCheckWorkstations(%p %S)\n", WorkStations, ComputerName);
|
||||
|
||||
if (WorkStations->Length == 0 || WorkStations->Buffer == NULL)
|
||||
{
|
||||
TRACE("No workstations!\n");
|
||||
return true;
|
||||
}
|
||||
|
||||
TRACE("Workstations: %wZ\n", WorkStations);
|
||||
|
||||
pStart = WorkStations->Buffer;
|
||||
for (;;)
|
||||
{
|
||||
pEnd = wcschr(pStart, L',');
|
||||
if (pEnd != NULL)
|
||||
*pEnd = UNICODE_NULL;
|
||||
|
||||
TRACE("Comparing '%S' and '%S'\n", ComputerName, pStart);
|
||||
if (_wcsicmp(ComputerName, pStart) == 0)
|
||||
{
|
||||
bFound = true;
|
||||
if (pEnd != NULL)
|
||||
*pEnd = L',';
|
||||
break;
|
||||
}
|
||||
|
||||
if (pEnd == NULL)
|
||||
break;
|
||||
|
||||
*pEnd = L',';
|
||||
pStart = pEnd + 1;
|
||||
}
|
||||
|
||||
TRACE("Found allowed workstation: %s\n", (bFound) ? "Yes" : "No");
|
||||
|
||||
return bFound;
|
||||
}
|
||||
|
||||
|
||||
NTSTATUS
|
||||
SamValidateNormalUser(
|
||||
_In_ PUNICODE_STRING UserName,
|
||||
_In_ PLSA_SAM_PWD_DATA PwdData,
|
||||
_In_ PUNICODE_STRING ComputerName,
|
||||
_Out_ PRPC_SID* AccountDomainSidPtr,
|
||||
_Out_ SAMPR_HANDLE* UserHandlePtr,
|
||||
_Out_ PSAMPR_USER_INFO_BUFFER* UserInfoPtr,
|
||||
_Out_ PNTSTATUS SubStatus)
|
||||
{
|
||||
NTSTATUS Status;
|
||||
SAMPR_HANDLE ServerHandle = NULL;
|
||||
SAMPR_HANDLE DomainHandle = NULL;
|
||||
PRPC_SID AccountDomainSid;
|
||||
RPC_UNICODE_STRING Names[1];
|
||||
SAMPR_HANDLE UserHandle = NULL;
|
||||
SAMPR_ULONG_ARRAY RelativeIds = {0, NULL};
|
||||
SAMPR_ULONG_ARRAY Use = {0, NULL};
|
||||
PSAMPR_USER_INFO_BUFFER UserInfo = NULL;
|
||||
LARGE_INTEGER LogonTime;
|
||||
|
||||
/* Get the logon time */
|
||||
NtQuerySystemTime(&LogonTime);
|
||||
|
||||
/* Get the account domain SID */
|
||||
Status = GetAccountDomainSid(&AccountDomainSid);
|
||||
if (!NT_SUCCESS(Status))
|
||||
{
|
||||
ERR("GetAccountDomainSid() failed (Status 0x%08lx)\n", Status);
|
||||
return Status;
|
||||
}
|
||||
|
||||
/* Connect to the SAM server */
|
||||
Status = SamIConnect(NULL, &ServerHandle, SAM_SERVER_CONNECT | SAM_SERVER_LOOKUP_DOMAIN, TRUE);
|
||||
if (!NT_SUCCESS(Status))
|
||||
{
|
||||
TRACE("SamIConnect() failed (Status 0x%08lx)\n", Status);
|
||||
goto done;
|
||||
}
|
||||
|
||||
/* Open the account domain */
|
||||
Status = SamrOpenDomain(ServerHandle, DOMAIN_LOOKUP, AccountDomainSid, &DomainHandle);
|
||||
if (!NT_SUCCESS(Status))
|
||||
{
|
||||
ERR("SamrOpenDomain failed (Status %08lx)\n", Status);
|
||||
goto done;
|
||||
}
|
||||
|
||||
Names[0].Length = UserName->Length;
|
||||
Names[0].MaximumLength = UserName->MaximumLength;
|
||||
Names[0].Buffer = UserName->Buffer;
|
||||
|
||||
/* Try to get the RID for the user name */
|
||||
Status = SamrLookupNamesInDomain(DomainHandle, 1, Names, &RelativeIds, &Use);
|
||||
if (!NT_SUCCESS(Status))
|
||||
{
|
||||
ERR("SamrLookupNamesInDomain failed (Status %08lx)\n", Status);
|
||||
Status = STATUS_NO_SUCH_USER;
|
||||
// FIXME: Try without domain?
|
||||
goto done;
|
||||
}
|
||||
|
||||
/* Fail, if it is not a user account */
|
||||
if (Use.Element[0] != SidTypeUser)
|
||||
{
|
||||
ERR("Account is not a user account!\n");
|
||||
Status = STATUS_NO_SUCH_USER;
|
||||
goto done;
|
||||
}
|
||||
|
||||
/* Open the user object */
|
||||
Status = SamrOpenUser(DomainHandle,
|
||||
USER_READ_GENERAL | USER_READ_LOGON |
|
||||
USER_READ_ACCOUNT | USER_READ_PREFERENCES, /* FIXME */
|
||||
RelativeIds.Element[0],
|
||||
&UserHandle);
|
||||
if (!NT_SUCCESS(Status))
|
||||
{
|
||||
ERR("SamrOpenUser failed (Status %08lx)\n", Status);
|
||||
goto done;
|
||||
}
|
||||
|
||||
Status = SamrQueryInformationUser(UserHandle, UserAllInformation, &UserInfo);
|
||||
if (!NT_SUCCESS(Status))
|
||||
{
|
||||
ERR("SamrQueryInformationUser failed (Status %08lx)\n", Status);
|
||||
goto done;
|
||||
}
|
||||
|
||||
TRACE("UserName: %wZ\n", &UserInfo->All.UserName);
|
||||
|
||||
/* Check the password */
|
||||
if ((UserInfo->All.UserAccountControl & USER_PASSWORD_NOT_REQUIRED) == 0)
|
||||
{
|
||||
Status = MsvpCheckPassword(PwdData, UserInfo);
|
||||
if (!NT_SUCCESS(Status))
|
||||
{
|
||||
ERR("MsvpCheckPassword failed (Status %08lx)\n", Status);
|
||||
goto done;
|
||||
}
|
||||
}
|
||||
|
||||
/* Check account restrictions for non-administrator accounts */
|
||||
if (RelativeIds.Element[0] != DOMAIN_USER_RID_ADMIN)
|
||||
{
|
||||
/* Check if the account has been disabled */
|
||||
if (UserInfo->All.UserAccountControl & USER_ACCOUNT_DISABLED)
|
||||
{
|
||||
ERR("Account disabled!\n");
|
||||
*SubStatus = STATUS_ACCOUNT_DISABLED;
|
||||
Status = STATUS_ACCOUNT_RESTRICTION;
|
||||
goto done;
|
||||
}
|
||||
|
||||
/* Check if the account has been locked */
|
||||
if (UserInfo->All.UserAccountControl & USER_ACCOUNT_AUTO_LOCKED)
|
||||
{
|
||||
ERR("Account locked!\n");
|
||||
*SubStatus = STATUS_ACCOUNT_LOCKED_OUT;
|
||||
Status = STATUS_ACCOUNT_RESTRICTION;
|
||||
goto done;
|
||||
}
|
||||
|
||||
/* Check if the account expired */
|
||||
if (LogonTime.QuadPart >= *(UINT64*)&UserInfo->All.AccountExpires)
|
||||
{
|
||||
ERR("Account expired!\n");
|
||||
*SubStatus = STATUS_ACCOUNT_EXPIRED;
|
||||
Status = STATUS_ACCOUNT_RESTRICTION;
|
||||
goto done;
|
||||
}
|
||||
|
||||
/* Check if the password expired */
|
||||
if (LogonTime.QuadPart >= *(UINT64*)&UserInfo->All.PasswordMustChange)
|
||||
{
|
||||
ERR("Password expired!\n");
|
||||
if (*(UINT64*)&UserInfo->All.PasswordLastSet == 0)
|
||||
*SubStatus = STATUS_PASSWORD_MUST_CHANGE;
|
||||
else
|
||||
*SubStatus = STATUS_PASSWORD_EXPIRED;
|
||||
|
||||
Status = STATUS_ACCOUNT_RESTRICTION;
|
||||
goto done;
|
||||
}
|
||||
|
||||
/* Check logon hours */
|
||||
if (!MsvpCheckLogonHours(&UserInfo->All.LogonHours, LogonTime))
|
||||
{
|
||||
ERR("Invalid logon hours!\n");
|
||||
*SubStatus = STATUS_INVALID_LOGON_HOURS;
|
||||
Status = STATUS_ACCOUNT_RESTRICTION;
|
||||
goto done;
|
||||
}
|
||||
|
||||
/* Check workstations */
|
||||
if (!MsvpCheckWorkstations(&UserInfo->All.WorkStations, ComputerName->Buffer))
|
||||
{
|
||||
ERR("Invalid workstation!\n");
|
||||
*SubStatus = STATUS_INVALID_WORKSTATION;
|
||||
Status = STATUS_ACCOUNT_RESTRICTION;
|
||||
goto done;
|
||||
}
|
||||
}
|
||||
done:
|
||||
if (NT_SUCCESS(Status))
|
||||
{
|
||||
*UserHandlePtr = UserHandle;
|
||||
*AccountDomainSidPtr = AccountDomainSid;
|
||||
*UserInfoPtr = UserInfo;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (AccountDomainSid != NULL)
|
||||
RtlFreeHeap(RtlGetProcessHeap(), 0, AccountDomainSid);
|
||||
|
||||
if (UserHandle != NULL)
|
||||
SamrCloseHandle(&UserHandle);
|
||||
|
||||
SamIFree_SAMPR_USER_INFO_BUFFER(UserInfo,
|
||||
UserAllInformation);
|
||||
}
|
||||
|
||||
SamIFree_SAMPR_ULONG_ARRAY(&RelativeIds);
|
||||
SamIFree_SAMPR_ULONG_ARRAY(&Use);
|
||||
|
||||
if (DomainHandle != NULL)
|
||||
SamrCloseHandle(&DomainHandle);
|
||||
|
||||
if (ServerHandle != NULL)
|
||||
SamrCloseHandle(&ServerHandle);
|
||||
|
||||
return Status;
|
||||
}
|
31
dll/win32/msv1_0/sam.h
Normal file
31
dll/win32/msv1_0/sam.h
Normal file
|
@ -0,0 +1,31 @@
|
|||
/*
|
||||
* PROJECT: Authentication Package DLL
|
||||
* LICENSE: GPL-2.0-or-later (https://spdx.org/licenses/GPL-2.0-or-later)
|
||||
* PURPOSE: Security Account Manager (SAM) related functions - header
|
||||
* COPYRIGHT: Copyright 2013 Eric Kohl <eric.kohl@reactos.org>
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
typedef struct _LSA_SAM_PWD_DATA
|
||||
{
|
||||
/* TRUE: PlainPwd is filled,
|
||||
FALSE: LmPwd and NtPwd is filled */
|
||||
BOOL IsNetwork;
|
||||
PUNICODE_STRING PlainPwd;
|
||||
|
||||
} LSA_SAM_PWD_DATA, *PLSA_SAM_PWD_DATA;
|
||||
|
||||
/**
|
||||
* @brief Validates a normal user by checking if it exists in the sam database.
|
||||
* Further some other checks are done.
|
||||
*/
|
||||
NTSTATUS
|
||||
SamValidateNormalUser(
|
||||
_In_ PUNICODE_STRING UserName,
|
||||
_In_ PLSA_SAM_PWD_DATA PwdData,
|
||||
_In_ PUNICODE_STRING ComputerName,
|
||||
_Out_ PRPC_SID* AccountDomainSidPtr,
|
||||
_Out_ SAMPR_HANDLE* UserHandlePtr,
|
||||
_Out_ PSAMPR_USER_INFO_BUFFER* UserInfoPtr,
|
||||
_Out_ PNTSTATUS SubStatus);
|
Loading…
Reference in a new issue