1045cd5d4SAndreas Maier /*
2045cd5d4SAndreas Maier * PROJECT: Authentication Package DLL
3045cd5d4SAndreas Maier * LICENSE: GPL-2.0-or-later (https://spdx.org/licenses/GPL-2.0-or-later)
4045cd5d4SAndreas Maier * PURPOSE: Security Account Manager (SAM) related functions
5045cd5d4SAndreas Maier * COPYRIGHT: Copyright 2013 Eric Kohl <eric.kohl@reactos.org>
6045cd5d4SAndreas Maier */
7045cd5d4SAndreas Maier
8045cd5d4SAndreas Maier #include "precomp.h"
9045cd5d4SAndreas Maier
10045cd5d4SAndreas Maier #include "wine/debug.h"
11045cd5d4SAndreas Maier WINE_DEFAULT_DEBUG_CHANNEL(msv1_0_sam);
12045cd5d4SAndreas Maier
13045cd5d4SAndreas Maier
14045cd5d4SAndreas Maier static
15045cd5d4SAndreas Maier NTSTATUS
GetAccountDomainSid(_In_ PRPC_SID * Sid)16045cd5d4SAndreas Maier GetAccountDomainSid(
17045cd5d4SAndreas Maier _In_ PRPC_SID *Sid)
18045cd5d4SAndreas Maier {
19045cd5d4SAndreas Maier LSAPR_HANDLE PolicyHandle = NULL;
20045cd5d4SAndreas Maier PLSAPR_POLICY_INFORMATION PolicyInfo = NULL;
21045cd5d4SAndreas Maier ULONG Length = 0;
22045cd5d4SAndreas Maier NTSTATUS Status;
23045cd5d4SAndreas Maier
24045cd5d4SAndreas Maier Status = LsaIOpenPolicyTrusted(&PolicyHandle);
25045cd5d4SAndreas Maier if (!NT_SUCCESS(Status))
26045cd5d4SAndreas Maier {
27045cd5d4SAndreas Maier TRACE("LsaIOpenPolicyTrusted() failed (Status 0x%08lx)\n", Status);
28045cd5d4SAndreas Maier return Status;
29045cd5d4SAndreas Maier }
30045cd5d4SAndreas Maier
31045cd5d4SAndreas Maier Status = LsarQueryInformationPolicy(PolicyHandle,
32045cd5d4SAndreas Maier PolicyAccountDomainInformation,
33045cd5d4SAndreas Maier &PolicyInfo);
34045cd5d4SAndreas Maier if (!NT_SUCCESS(Status))
35045cd5d4SAndreas Maier {
36045cd5d4SAndreas Maier TRACE("LsarQueryInformationPolicy() failed (Status 0x%08lx)\n", Status);
37045cd5d4SAndreas Maier goto done;
38045cd5d4SAndreas Maier }
39045cd5d4SAndreas Maier
40045cd5d4SAndreas Maier Length = RtlLengthSid(PolicyInfo->PolicyAccountDomainInfo.Sid);
41045cd5d4SAndreas Maier
42045cd5d4SAndreas Maier *Sid = RtlAllocateHeap(RtlGetProcessHeap(), 0, Length);
43045cd5d4SAndreas Maier if (*Sid == NULL)
44045cd5d4SAndreas Maier {
45045cd5d4SAndreas Maier ERR("Failed to allocate SID\n");
46045cd5d4SAndreas Maier Status = STATUS_INSUFFICIENT_RESOURCES;
47045cd5d4SAndreas Maier goto done;
48045cd5d4SAndreas Maier }
49045cd5d4SAndreas Maier
50045cd5d4SAndreas Maier memcpy(*Sid, PolicyInfo->PolicyAccountDomainInfo.Sid, Length);
51045cd5d4SAndreas Maier
52045cd5d4SAndreas Maier done:
53045cd5d4SAndreas Maier if (PolicyInfo != NULL)
54045cd5d4SAndreas Maier LsaIFree_LSAPR_POLICY_INFORMATION(PolicyAccountDomainInformation,
55045cd5d4SAndreas Maier PolicyInfo);
56045cd5d4SAndreas Maier
57045cd5d4SAndreas Maier if (PolicyHandle != NULL)
58045cd5d4SAndreas Maier LsarClose(&PolicyHandle);
59045cd5d4SAndreas Maier
60045cd5d4SAndreas Maier return Status;
61045cd5d4SAndreas Maier }
62045cd5d4SAndreas Maier
63045cd5d4SAndreas Maier
64045cd5d4SAndreas Maier static
65045cd5d4SAndreas Maier NTSTATUS
MsvpCheckPassword(_In_ PLSA_SAM_PWD_DATA UserPwdData,_In_ PSAMPR_USER_INFO_BUFFER UserInfo)66045cd5d4SAndreas Maier MsvpCheckPassword(
67045cd5d4SAndreas Maier _In_ PLSA_SAM_PWD_DATA UserPwdData,
68045cd5d4SAndreas Maier _In_ PSAMPR_USER_INFO_BUFFER UserInfo)
69045cd5d4SAndreas Maier {
70045cd5d4SAndreas Maier ENCRYPTED_NT_OWF_PASSWORD UserNtPassword;
71045cd5d4SAndreas Maier ENCRYPTED_LM_OWF_PASSWORD UserLmPassword;
72045cd5d4SAndreas Maier BOOLEAN UserLmPasswordPresent = FALSE;
73045cd5d4SAndreas Maier BOOLEAN UserNtPasswordPresent = FALSE;
74045cd5d4SAndreas Maier OEM_STRING LmPwdString;
75045cd5d4SAndreas Maier CHAR LmPwdBuffer[15];
76045cd5d4SAndreas Maier NTSTATUS Status;
77045cd5d4SAndreas Maier
78045cd5d4SAndreas Maier TRACE("(%p %p)\n", UserPwdData, UserInfo);
79045cd5d4SAndreas Maier
80045cd5d4SAndreas Maier /* Calculate the LM password and hash for the users password */
81045cd5d4SAndreas Maier LmPwdString.Length = 15;
82045cd5d4SAndreas Maier LmPwdString.MaximumLength = 15;
83045cd5d4SAndreas Maier LmPwdString.Buffer = LmPwdBuffer;
84045cd5d4SAndreas Maier ZeroMemory(LmPwdString.Buffer, LmPwdString.MaximumLength);
85045cd5d4SAndreas Maier
86045cd5d4SAndreas Maier Status = RtlUpcaseUnicodeStringToOemString(&LmPwdString,
87045cd5d4SAndreas Maier UserPwdData->PlainPwd,
88045cd5d4SAndreas Maier FALSE);
89045cd5d4SAndreas Maier if (NT_SUCCESS(Status))
90045cd5d4SAndreas Maier {
91045cd5d4SAndreas Maier /* Calculate the LM hash value of the users password */
92045cd5d4SAndreas Maier Status = SystemFunction006(LmPwdString.Buffer,
93045cd5d4SAndreas Maier (LPSTR)&UserLmPassword);
94045cd5d4SAndreas Maier if (NT_SUCCESS(Status))
95045cd5d4SAndreas Maier {
96045cd5d4SAndreas Maier UserLmPasswordPresent = TRUE;
97045cd5d4SAndreas Maier }
98045cd5d4SAndreas Maier }
99045cd5d4SAndreas Maier
100045cd5d4SAndreas Maier /* Calculate the NT hash of the users password */
101045cd5d4SAndreas Maier Status = SystemFunction007(UserPwdData->PlainPwd,
102045cd5d4SAndreas Maier (LPBYTE)&UserNtPassword);
103045cd5d4SAndreas Maier if (NT_SUCCESS(Status))
104045cd5d4SAndreas Maier {
105045cd5d4SAndreas Maier UserNtPasswordPresent = TRUE;
106045cd5d4SAndreas Maier }
107045cd5d4SAndreas Maier
108045cd5d4SAndreas Maier Status = STATUS_WRONG_PASSWORD;
109045cd5d4SAndreas Maier
110045cd5d4SAndreas Maier /* Succeed, if no password has been set */
111045cd5d4SAndreas Maier if (UserInfo->All.NtPasswordPresent == FALSE &&
112045cd5d4SAndreas Maier UserInfo->All.LmPasswordPresent == FALSE)
113045cd5d4SAndreas Maier {
114045cd5d4SAndreas Maier TRACE("No password check!\n");
115045cd5d4SAndreas Maier Status = STATUS_SUCCESS;
116045cd5d4SAndreas Maier goto done;
117045cd5d4SAndreas Maier }
118045cd5d4SAndreas Maier
119045cd5d4SAndreas Maier /* Succeed, if NT password matches */
120045cd5d4SAndreas Maier if (UserNtPasswordPresent && UserInfo->All.NtPasswordPresent)
121045cd5d4SAndreas Maier {
122045cd5d4SAndreas Maier TRACE("Check NT password hashes:\n");
123045cd5d4SAndreas Maier if (RtlEqualMemory(&UserNtPassword,
124045cd5d4SAndreas Maier UserInfo->All.NtOwfPassword.Buffer,
125045cd5d4SAndreas Maier sizeof(ENCRYPTED_NT_OWF_PASSWORD)))
126045cd5d4SAndreas Maier {
127045cd5d4SAndreas Maier TRACE(" success!\n");
128045cd5d4SAndreas Maier Status = STATUS_SUCCESS;
129045cd5d4SAndreas Maier goto done;
130045cd5d4SAndreas Maier }
131045cd5d4SAndreas Maier
132045cd5d4SAndreas Maier TRACE(" failed!\n");
133045cd5d4SAndreas Maier }
134045cd5d4SAndreas Maier
135045cd5d4SAndreas Maier /* Succeed, if LM password matches */
136045cd5d4SAndreas Maier if (UserLmPasswordPresent && UserInfo->All.LmPasswordPresent)
137045cd5d4SAndreas Maier {
138045cd5d4SAndreas Maier TRACE("Check LM password hashes:\n");
139045cd5d4SAndreas Maier if (RtlEqualMemory(&UserLmPassword,
140045cd5d4SAndreas Maier UserInfo->All.LmOwfPassword.Buffer,
141045cd5d4SAndreas Maier sizeof(ENCRYPTED_LM_OWF_PASSWORD)))
142045cd5d4SAndreas Maier {
143045cd5d4SAndreas Maier TRACE(" success!\n");
144045cd5d4SAndreas Maier Status = STATUS_SUCCESS;
145045cd5d4SAndreas Maier goto done;
146045cd5d4SAndreas Maier }
147045cd5d4SAndreas Maier TRACE(" failed!\n");
148045cd5d4SAndreas Maier }
149045cd5d4SAndreas Maier
150045cd5d4SAndreas Maier done:
151045cd5d4SAndreas Maier return Status;
152045cd5d4SAndreas Maier }
153045cd5d4SAndreas Maier
154045cd5d4SAndreas Maier
155045cd5d4SAndreas Maier static
156045cd5d4SAndreas Maier bool
MsvpCheckLogonHours(_In_ PSAMPR_LOGON_HOURS LogonHours,_In_ LARGE_INTEGER LogonTime)157045cd5d4SAndreas Maier MsvpCheckLogonHours(
158045cd5d4SAndreas Maier _In_ PSAMPR_LOGON_HOURS LogonHours,
159045cd5d4SAndreas Maier _In_ LARGE_INTEGER LogonTime)
160045cd5d4SAndreas Maier {
161045cd5d4SAndreas Maier #if 0
162045cd5d4SAndreas Maier LARGE_INTEGER LocalLogonTime;
163045cd5d4SAndreas Maier TIME_FIELDS TimeFields;
164045cd5d4SAndreas Maier USHORT MinutesPerUnit, Offset;
165045cd5d4SAndreas Maier bool bFound;
166045cd5d4SAndreas Maier
167045cd5d4SAndreas Maier FIXME("MsvpCheckLogonHours(%p %llx)\n", LogonHours, LogonTime);
168045cd5d4SAndreas Maier
169045cd5d4SAndreas Maier if (LogonHours->UnitsPerWeek == 0 || LogonHours->LogonHours == NULL)
170045cd5d4SAndreas Maier {
171045cd5d4SAndreas Maier FIXME("No logon hours!\n");
172045cd5d4SAndreas Maier return true;
173045cd5d4SAndreas Maier }
174045cd5d4SAndreas Maier
175045cd5d4SAndreas Maier RtlSystemTimeToLocalTime(&LogonTime, &LocalLogonTime);
176045cd5d4SAndreas Maier RtlTimeToTimeFields(&LocalLogonTime, &TimeFields);
177045cd5d4SAndreas Maier
178045cd5d4SAndreas Maier FIXME("UnitsPerWeek: %u\n", LogonHours->UnitsPerWeek);
179045cd5d4SAndreas Maier MinutesPerUnit = 10080 / LogonHours->UnitsPerWeek;
180045cd5d4SAndreas Maier
181045cd5d4SAndreas Maier Offset = ((TimeFields.Weekday * 24 + TimeFields.Hour) * 60 + TimeFields.Minute) / MinutesPerUnit;
182045cd5d4SAndreas Maier FIXME("Offset: %us\n", Offset);
183045cd5d4SAndreas Maier
184045cd5d4SAndreas Maier bFound = (bool)(LogonHours->LogonHours[Offset / 8] & (1 << (Offset % 8)));
185045cd5d4SAndreas Maier FIXME("Logon permitted: %s\n", bFound ? "Yes" : "No");
186045cd5d4SAndreas Maier
187045cd5d4SAndreas Maier return bFound;
188045cd5d4SAndreas Maier #endif
189045cd5d4SAndreas Maier return true;
190045cd5d4SAndreas Maier }
191045cd5d4SAndreas Maier
192045cd5d4SAndreas Maier
193045cd5d4SAndreas Maier static
194045cd5d4SAndreas Maier bool
MsvpCheckWorkstations(_In_ PRPC_UNICODE_STRING WorkStations,_In_ PWSTR ComputerName)195045cd5d4SAndreas Maier MsvpCheckWorkstations(
196045cd5d4SAndreas Maier _In_ PRPC_UNICODE_STRING WorkStations,
197045cd5d4SAndreas Maier _In_ PWSTR ComputerName)
198045cd5d4SAndreas Maier {
199045cd5d4SAndreas Maier PWSTR pStart, pEnd;
200045cd5d4SAndreas Maier bool bFound = false;
201045cd5d4SAndreas Maier
202045cd5d4SAndreas Maier TRACE("MsvpCheckWorkstations(%p %S)\n", WorkStations, ComputerName);
203045cd5d4SAndreas Maier
204045cd5d4SAndreas Maier if (WorkStations->Length == 0 || WorkStations->Buffer == NULL)
205045cd5d4SAndreas Maier {
206045cd5d4SAndreas Maier TRACE("No workstations!\n");
207045cd5d4SAndreas Maier return true;
208045cd5d4SAndreas Maier }
209045cd5d4SAndreas Maier
210045cd5d4SAndreas Maier TRACE("Workstations: %wZ\n", WorkStations);
211045cd5d4SAndreas Maier
212045cd5d4SAndreas Maier pStart = WorkStations->Buffer;
213045cd5d4SAndreas Maier for (;;)
214045cd5d4SAndreas Maier {
215045cd5d4SAndreas Maier pEnd = wcschr(pStart, L',');
216045cd5d4SAndreas Maier if (pEnd != NULL)
217045cd5d4SAndreas Maier *pEnd = UNICODE_NULL;
218045cd5d4SAndreas Maier
219045cd5d4SAndreas Maier TRACE("Comparing '%S' and '%S'\n", ComputerName, pStart);
220045cd5d4SAndreas Maier if (_wcsicmp(ComputerName, pStart) == 0)
221045cd5d4SAndreas Maier {
222045cd5d4SAndreas Maier bFound = true;
223045cd5d4SAndreas Maier if (pEnd != NULL)
224045cd5d4SAndreas Maier *pEnd = L',';
225045cd5d4SAndreas Maier break;
226045cd5d4SAndreas Maier }
227045cd5d4SAndreas Maier
228045cd5d4SAndreas Maier if (pEnd == NULL)
229045cd5d4SAndreas Maier break;
230045cd5d4SAndreas Maier
231045cd5d4SAndreas Maier *pEnd = L',';
232045cd5d4SAndreas Maier pStart = pEnd + 1;
233045cd5d4SAndreas Maier }
234045cd5d4SAndreas Maier
235045cd5d4SAndreas Maier TRACE("Found allowed workstation: %s\n", (bFound) ? "Yes" : "No");
236045cd5d4SAndreas Maier
237045cd5d4SAndreas Maier return bFound;
238045cd5d4SAndreas Maier }
239045cd5d4SAndreas Maier
240045cd5d4SAndreas Maier
241472b32d9SAndreas Maier static
242045cd5d4SAndreas Maier 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)243045cd5d4SAndreas Maier SamValidateNormalUser(
244045cd5d4SAndreas Maier _In_ PUNICODE_STRING UserName,
245045cd5d4SAndreas Maier _In_ PLSA_SAM_PWD_DATA PwdData,
246045cd5d4SAndreas Maier _In_ PUNICODE_STRING ComputerName,
247045cd5d4SAndreas Maier _Out_ PRPC_SID* AccountDomainSidPtr,
248045cd5d4SAndreas Maier _Out_ SAMPR_HANDLE* UserHandlePtr,
249045cd5d4SAndreas Maier _Out_ PSAMPR_USER_INFO_BUFFER* UserInfoPtr,
250045cd5d4SAndreas Maier _Out_ PNTSTATUS SubStatus)
251045cd5d4SAndreas Maier {
252045cd5d4SAndreas Maier NTSTATUS Status;
253045cd5d4SAndreas Maier SAMPR_HANDLE ServerHandle = NULL;
254045cd5d4SAndreas Maier SAMPR_HANDLE DomainHandle = NULL;
255045cd5d4SAndreas Maier PRPC_SID AccountDomainSid;
256045cd5d4SAndreas Maier RPC_UNICODE_STRING Names[1];
257045cd5d4SAndreas Maier SAMPR_HANDLE UserHandle = NULL;
258045cd5d4SAndreas Maier SAMPR_ULONG_ARRAY RelativeIds = {0, NULL};
259045cd5d4SAndreas Maier SAMPR_ULONG_ARRAY Use = {0, NULL};
260045cd5d4SAndreas Maier PSAMPR_USER_INFO_BUFFER UserInfo = NULL;
261045cd5d4SAndreas Maier LARGE_INTEGER LogonTime;
262045cd5d4SAndreas Maier
263045cd5d4SAndreas Maier /* Get the logon time */
264045cd5d4SAndreas Maier NtQuerySystemTime(&LogonTime);
265045cd5d4SAndreas Maier
266045cd5d4SAndreas Maier /* Get the account domain SID */
267045cd5d4SAndreas Maier Status = GetAccountDomainSid(&AccountDomainSid);
268045cd5d4SAndreas Maier if (!NT_SUCCESS(Status))
269045cd5d4SAndreas Maier {
270045cd5d4SAndreas Maier ERR("GetAccountDomainSid() failed (Status 0x%08lx)\n", Status);
271045cd5d4SAndreas Maier return Status;
272045cd5d4SAndreas Maier }
273045cd5d4SAndreas Maier
274045cd5d4SAndreas Maier /* Connect to the SAM server */
275045cd5d4SAndreas Maier Status = SamIConnect(NULL, &ServerHandle, SAM_SERVER_CONNECT | SAM_SERVER_LOOKUP_DOMAIN, TRUE);
276045cd5d4SAndreas Maier if (!NT_SUCCESS(Status))
277045cd5d4SAndreas Maier {
278045cd5d4SAndreas Maier TRACE("SamIConnect() failed (Status 0x%08lx)\n", Status);
279045cd5d4SAndreas Maier goto done;
280045cd5d4SAndreas Maier }
281045cd5d4SAndreas Maier
282045cd5d4SAndreas Maier /* Open the account domain */
283045cd5d4SAndreas Maier Status = SamrOpenDomain(ServerHandle, DOMAIN_LOOKUP, AccountDomainSid, &DomainHandle);
284045cd5d4SAndreas Maier if (!NT_SUCCESS(Status))
285045cd5d4SAndreas Maier {
286045cd5d4SAndreas Maier ERR("SamrOpenDomain failed (Status %08lx)\n", Status);
287045cd5d4SAndreas Maier goto done;
288045cd5d4SAndreas Maier }
289045cd5d4SAndreas Maier
290045cd5d4SAndreas Maier Names[0].Length = UserName->Length;
291045cd5d4SAndreas Maier Names[0].MaximumLength = UserName->MaximumLength;
292045cd5d4SAndreas Maier Names[0].Buffer = UserName->Buffer;
293045cd5d4SAndreas Maier
294045cd5d4SAndreas Maier /* Try to get the RID for the user name */
295045cd5d4SAndreas Maier Status = SamrLookupNamesInDomain(DomainHandle, 1, Names, &RelativeIds, &Use);
296045cd5d4SAndreas Maier if (!NT_SUCCESS(Status))
297045cd5d4SAndreas Maier {
298045cd5d4SAndreas Maier ERR("SamrLookupNamesInDomain failed (Status %08lx)\n", Status);
299045cd5d4SAndreas Maier Status = STATUS_NO_SUCH_USER;
300045cd5d4SAndreas Maier // FIXME: Try without domain?
301045cd5d4SAndreas Maier goto done;
302045cd5d4SAndreas Maier }
303045cd5d4SAndreas Maier
304045cd5d4SAndreas Maier /* Fail, if it is not a user account */
305045cd5d4SAndreas Maier if (Use.Element[0] != SidTypeUser)
306045cd5d4SAndreas Maier {
307045cd5d4SAndreas Maier ERR("Account is not a user account!\n");
308045cd5d4SAndreas Maier Status = STATUS_NO_SUCH_USER;
309045cd5d4SAndreas Maier goto done;
310045cd5d4SAndreas Maier }
311045cd5d4SAndreas Maier
312045cd5d4SAndreas Maier /* Open the user object */
313045cd5d4SAndreas Maier Status = SamrOpenUser(DomainHandle,
314045cd5d4SAndreas Maier USER_READ_GENERAL | USER_READ_LOGON |
315045cd5d4SAndreas Maier USER_READ_ACCOUNT | USER_READ_PREFERENCES, /* FIXME */
316045cd5d4SAndreas Maier RelativeIds.Element[0],
317045cd5d4SAndreas Maier &UserHandle);
318045cd5d4SAndreas Maier if (!NT_SUCCESS(Status))
319045cd5d4SAndreas Maier {
320045cd5d4SAndreas Maier ERR("SamrOpenUser failed (Status %08lx)\n", Status);
321045cd5d4SAndreas Maier goto done;
322045cd5d4SAndreas Maier }
323045cd5d4SAndreas Maier
324045cd5d4SAndreas Maier Status = SamrQueryInformationUser(UserHandle, UserAllInformation, &UserInfo);
325045cd5d4SAndreas Maier if (!NT_SUCCESS(Status))
326045cd5d4SAndreas Maier {
327045cd5d4SAndreas Maier ERR("SamrQueryInformationUser failed (Status %08lx)\n", Status);
328045cd5d4SAndreas Maier goto done;
329045cd5d4SAndreas Maier }
330045cd5d4SAndreas Maier
331045cd5d4SAndreas Maier TRACE("UserName: %wZ\n", &UserInfo->All.UserName);
332045cd5d4SAndreas Maier
333045cd5d4SAndreas Maier /* Check the password */
334045cd5d4SAndreas Maier if ((UserInfo->All.UserAccountControl & USER_PASSWORD_NOT_REQUIRED) == 0)
335045cd5d4SAndreas Maier {
336045cd5d4SAndreas Maier Status = MsvpCheckPassword(PwdData, UserInfo);
337045cd5d4SAndreas Maier if (!NT_SUCCESS(Status))
338045cd5d4SAndreas Maier {
339045cd5d4SAndreas Maier ERR("MsvpCheckPassword failed (Status %08lx)\n", Status);
340045cd5d4SAndreas Maier goto done;
341045cd5d4SAndreas Maier }
342045cd5d4SAndreas Maier }
343045cd5d4SAndreas Maier
344045cd5d4SAndreas Maier /* Check account restrictions for non-administrator accounts */
345045cd5d4SAndreas Maier if (RelativeIds.Element[0] != DOMAIN_USER_RID_ADMIN)
346045cd5d4SAndreas Maier {
347045cd5d4SAndreas Maier /* Check if the account has been disabled */
348045cd5d4SAndreas Maier if (UserInfo->All.UserAccountControl & USER_ACCOUNT_DISABLED)
349045cd5d4SAndreas Maier {
350045cd5d4SAndreas Maier ERR("Account disabled!\n");
351045cd5d4SAndreas Maier *SubStatus = STATUS_ACCOUNT_DISABLED;
352045cd5d4SAndreas Maier Status = STATUS_ACCOUNT_RESTRICTION;
353045cd5d4SAndreas Maier goto done;
354045cd5d4SAndreas Maier }
355045cd5d4SAndreas Maier
356045cd5d4SAndreas Maier /* Check if the account has been locked */
357045cd5d4SAndreas Maier if (UserInfo->All.UserAccountControl & USER_ACCOUNT_AUTO_LOCKED)
358045cd5d4SAndreas Maier {
359045cd5d4SAndreas Maier ERR("Account locked!\n");
360045cd5d4SAndreas Maier *SubStatus = STATUS_ACCOUNT_LOCKED_OUT;
361045cd5d4SAndreas Maier Status = STATUS_ACCOUNT_RESTRICTION;
362045cd5d4SAndreas Maier goto done;
363045cd5d4SAndreas Maier }
364045cd5d4SAndreas Maier
365045cd5d4SAndreas Maier /* Check if the account expired */
366045cd5d4SAndreas Maier if (LogonTime.QuadPart >= *(UINT64*)&UserInfo->All.AccountExpires)
367045cd5d4SAndreas Maier {
368045cd5d4SAndreas Maier ERR("Account expired!\n");
369045cd5d4SAndreas Maier *SubStatus = STATUS_ACCOUNT_EXPIRED;
370045cd5d4SAndreas Maier Status = STATUS_ACCOUNT_RESTRICTION;
371045cd5d4SAndreas Maier goto done;
372045cd5d4SAndreas Maier }
373045cd5d4SAndreas Maier
374045cd5d4SAndreas Maier /* Check if the password expired */
375045cd5d4SAndreas Maier if (LogonTime.QuadPart >= *(UINT64*)&UserInfo->All.PasswordMustChange)
376045cd5d4SAndreas Maier {
377045cd5d4SAndreas Maier ERR("Password expired!\n");
378045cd5d4SAndreas Maier if (*(UINT64*)&UserInfo->All.PasswordLastSet == 0)
379045cd5d4SAndreas Maier *SubStatus = STATUS_PASSWORD_MUST_CHANGE;
380045cd5d4SAndreas Maier else
381045cd5d4SAndreas Maier *SubStatus = STATUS_PASSWORD_EXPIRED;
382045cd5d4SAndreas Maier
383045cd5d4SAndreas Maier Status = STATUS_ACCOUNT_RESTRICTION;
384045cd5d4SAndreas Maier goto done;
385045cd5d4SAndreas Maier }
386045cd5d4SAndreas Maier
387045cd5d4SAndreas Maier /* Check logon hours */
388045cd5d4SAndreas Maier if (!MsvpCheckLogonHours(&UserInfo->All.LogonHours, LogonTime))
389045cd5d4SAndreas Maier {
390045cd5d4SAndreas Maier ERR("Invalid logon hours!\n");
391045cd5d4SAndreas Maier *SubStatus = STATUS_INVALID_LOGON_HOURS;
392045cd5d4SAndreas Maier Status = STATUS_ACCOUNT_RESTRICTION;
393045cd5d4SAndreas Maier goto done;
394045cd5d4SAndreas Maier }
395045cd5d4SAndreas Maier
396045cd5d4SAndreas Maier /* Check workstations */
397045cd5d4SAndreas Maier if (!MsvpCheckWorkstations(&UserInfo->All.WorkStations, ComputerName->Buffer))
398045cd5d4SAndreas Maier {
399045cd5d4SAndreas Maier ERR("Invalid workstation!\n");
400045cd5d4SAndreas Maier *SubStatus = STATUS_INVALID_WORKSTATION;
401045cd5d4SAndreas Maier Status = STATUS_ACCOUNT_RESTRICTION;
402045cd5d4SAndreas Maier goto done;
403045cd5d4SAndreas Maier }
404045cd5d4SAndreas Maier }
405045cd5d4SAndreas Maier done:
406045cd5d4SAndreas Maier if (NT_SUCCESS(Status))
407045cd5d4SAndreas Maier {
408045cd5d4SAndreas Maier *UserHandlePtr = UserHandle;
409045cd5d4SAndreas Maier *AccountDomainSidPtr = AccountDomainSid;
410045cd5d4SAndreas Maier *UserInfoPtr = UserInfo;
411045cd5d4SAndreas Maier }
412045cd5d4SAndreas Maier else
413045cd5d4SAndreas Maier {
414045cd5d4SAndreas Maier if (AccountDomainSid != NULL)
415045cd5d4SAndreas Maier RtlFreeHeap(RtlGetProcessHeap(), 0, AccountDomainSid);
416045cd5d4SAndreas Maier
417045cd5d4SAndreas Maier if (UserHandle != NULL)
418045cd5d4SAndreas Maier SamrCloseHandle(&UserHandle);
419045cd5d4SAndreas Maier
420045cd5d4SAndreas Maier SamIFree_SAMPR_USER_INFO_BUFFER(UserInfo,
421045cd5d4SAndreas Maier UserAllInformation);
422045cd5d4SAndreas Maier }
423045cd5d4SAndreas Maier
424045cd5d4SAndreas Maier SamIFree_SAMPR_ULONG_ARRAY(&RelativeIds);
425045cd5d4SAndreas Maier SamIFree_SAMPR_ULONG_ARRAY(&Use);
426045cd5d4SAndreas Maier
427045cd5d4SAndreas Maier if (DomainHandle != NULL)
428045cd5d4SAndreas Maier SamrCloseHandle(&DomainHandle);
429045cd5d4SAndreas Maier
430045cd5d4SAndreas Maier if (ServerHandle != NULL)
431045cd5d4SAndreas Maier SamrCloseHandle(&ServerHandle);
432045cd5d4SAndreas Maier
433045cd5d4SAndreas Maier return Status;
434045cd5d4SAndreas Maier }
435472b32d9SAndreas Maier
436472b32d9SAndreas Maier
437472b32d9SAndreas Maier static
438472b32d9SAndreas Maier NTSTATUS
GetNtAuthorityDomainSid(_In_ PRPC_SID * Sid)439472b32d9SAndreas Maier GetNtAuthorityDomainSid(
440472b32d9SAndreas Maier _In_ PRPC_SID *Sid)
441472b32d9SAndreas Maier {
442472b32d9SAndreas Maier SID_IDENTIFIER_AUTHORITY NtAuthority = {SECURITY_NT_AUTHORITY};
443472b32d9SAndreas Maier ULONG Length = 0;
444472b32d9SAndreas Maier
445472b32d9SAndreas Maier Length = RtlLengthRequiredSid(0);
446472b32d9SAndreas Maier *Sid = RtlAllocateHeap(RtlGetProcessHeap(), 0, Length);
447472b32d9SAndreas Maier if (*Sid == NULL)
448472b32d9SAndreas Maier {
449472b32d9SAndreas Maier ERR("Failed to allocate SID\n");
450472b32d9SAndreas Maier return STATUS_INSUFFICIENT_RESOURCES;
451472b32d9SAndreas Maier }
452472b32d9SAndreas Maier
453472b32d9SAndreas Maier RtlInitializeSid(*Sid,&NtAuthority, 0);
454472b32d9SAndreas Maier
455472b32d9SAndreas Maier return STATUS_SUCCESS;
456472b32d9SAndreas Maier }
457472b32d9SAndreas Maier
458472b32d9SAndreas Maier
459472b32d9SAndreas Maier NTSTATUS
SamValidateUser(_In_ SECURITY_LOGON_TYPE LogonType,_In_ PUNICODE_STRING LogonUserName,_In_ PUNICODE_STRING LogonDomain,_In_ PLSA_SAM_PWD_DATA LogonPwdData,_In_ PUNICODE_STRING ComputerName,_Out_ PBOOL SpecialAccount,_Out_ PRPC_SID * AccountDomainSidPtr,_Out_ SAMPR_HANDLE * UserHandlePtr,_Out_ PSAMPR_USER_INFO_BUFFER * UserInfoPtr,_Out_ PNTSTATUS SubStatus)460472b32d9SAndreas Maier SamValidateUser(
461472b32d9SAndreas Maier _In_ SECURITY_LOGON_TYPE LogonType,
462472b32d9SAndreas Maier _In_ PUNICODE_STRING LogonUserName,
463472b32d9SAndreas Maier _In_ PUNICODE_STRING LogonDomain,
464472b32d9SAndreas Maier _In_ PLSA_SAM_PWD_DATA LogonPwdData,
465472b32d9SAndreas Maier _In_ PUNICODE_STRING ComputerName,
466472b32d9SAndreas Maier _Out_ PBOOL SpecialAccount,
467472b32d9SAndreas Maier _Out_ PRPC_SID* AccountDomainSidPtr,
468472b32d9SAndreas Maier _Out_ SAMPR_HANDLE* UserHandlePtr,
469472b32d9SAndreas Maier _Out_ PSAMPR_USER_INFO_BUFFER* UserInfoPtr,
470472b32d9SAndreas Maier _Out_ PNTSTATUS SubStatus)
471472b32d9SAndreas Maier {
472472b32d9SAndreas Maier static const UNICODE_STRING NtAuthorityU = RTL_CONSTANT_STRING(L"NT AUTHORITY");
473472b32d9SAndreas Maier static const UNICODE_STRING LocalServiceU = RTL_CONSTANT_STRING(L"LocalService");
474472b32d9SAndreas Maier static const UNICODE_STRING NetworkServiceU = RTL_CONSTANT_STRING(L"NetworkService");
475472b32d9SAndreas Maier
476472b32d9SAndreas Maier NTSTATUS Status = STATUS_SUCCESS;
477472b32d9SAndreas Maier
478472b32d9SAndreas Maier *SpecialAccount = FALSE;
479*99dcd6f7SAndreas Maier *UserInfoPtr = NULL;
480*99dcd6f7SAndreas Maier *SubStatus = STATUS_SUCCESS;
481472b32d9SAndreas Maier
482472b32d9SAndreas Maier /* Check for special accounts */
483472b32d9SAndreas Maier // FIXME: Windows does not do this that way!! (msv1_0 does not contain these hardcoded values)
484472b32d9SAndreas Maier if (RtlEqualUnicodeString(LogonDomain, &NtAuthorityU, TRUE))
485472b32d9SAndreas Maier {
486472b32d9SAndreas Maier *SpecialAccount = TRUE;
487472b32d9SAndreas Maier
488472b32d9SAndreas Maier /* Get the authority domain SID */
489472b32d9SAndreas Maier Status = GetNtAuthorityDomainSid(AccountDomainSidPtr);
490472b32d9SAndreas Maier if (!NT_SUCCESS(Status))
491472b32d9SAndreas Maier {
492472b32d9SAndreas Maier ERR("GetNtAuthorityDomainSid() failed (Status 0x%08lx)\n", Status);
493472b32d9SAndreas Maier return Status;
494472b32d9SAndreas Maier }
495472b32d9SAndreas Maier
496472b32d9SAndreas Maier if (RtlEqualUnicodeString(LogonUserName, &LocalServiceU, TRUE))
497472b32d9SAndreas Maier {
498472b32d9SAndreas Maier TRACE("SpecialAccount: LocalService\n");
499472b32d9SAndreas Maier
500472b32d9SAndreas Maier if (LogonType != Service)
501472b32d9SAndreas Maier return STATUS_LOGON_FAILURE;
502472b32d9SAndreas Maier
503472b32d9SAndreas Maier *UserInfoPtr = RtlAllocateHeap(RtlGetProcessHeap(),
504472b32d9SAndreas Maier HEAP_ZERO_MEMORY,
505472b32d9SAndreas Maier sizeof(SAMPR_USER_ALL_INFORMATION));
506472b32d9SAndreas Maier if (*UserInfoPtr == NULL)
507472b32d9SAndreas Maier return STATUS_INSUFFICIENT_RESOURCES;
508472b32d9SAndreas Maier
509472b32d9SAndreas Maier (*UserInfoPtr)->All.UserId = SECURITY_LOCAL_SERVICE_RID;
510472b32d9SAndreas Maier (*UserInfoPtr)->All.PrimaryGroupId = SECURITY_LOCAL_SERVICE_RID;
511472b32d9SAndreas Maier }
512472b32d9SAndreas Maier else if (RtlEqualUnicodeString(LogonUserName, &NetworkServiceU, TRUE))
513472b32d9SAndreas Maier {
514472b32d9SAndreas Maier TRACE("SpecialAccount: NetworkService\n");
515472b32d9SAndreas Maier
516472b32d9SAndreas Maier if (LogonType != Service)
517472b32d9SAndreas Maier return STATUS_LOGON_FAILURE;
518472b32d9SAndreas Maier
519472b32d9SAndreas Maier *UserInfoPtr = RtlAllocateHeap(RtlGetProcessHeap(),
520472b32d9SAndreas Maier HEAP_ZERO_MEMORY,
521472b32d9SAndreas Maier sizeof(SAMPR_USER_ALL_INFORMATION));
522472b32d9SAndreas Maier if (*UserInfoPtr == NULL)
523472b32d9SAndreas Maier return STATUS_INSUFFICIENT_RESOURCES;
524472b32d9SAndreas Maier
525472b32d9SAndreas Maier (*UserInfoPtr)->All.UserId = SECURITY_NETWORK_SERVICE_RID;
526472b32d9SAndreas Maier (*UserInfoPtr)->All.PrimaryGroupId = SECURITY_NETWORK_SERVICE_RID;
527472b32d9SAndreas Maier }
528472b32d9SAndreas Maier else
529472b32d9SAndreas Maier {
530472b32d9SAndreas Maier return STATUS_NO_SUCH_USER;
531472b32d9SAndreas Maier }
532472b32d9SAndreas Maier }
533472b32d9SAndreas Maier else
534472b32d9SAndreas Maier {
535472b32d9SAndreas Maier TRACE("NormalAccount\n");
536472b32d9SAndreas Maier Status = SamValidateNormalUser(LogonUserName,
537472b32d9SAndreas Maier LogonPwdData,
538472b32d9SAndreas Maier ComputerName,
539472b32d9SAndreas Maier AccountDomainSidPtr,
540472b32d9SAndreas Maier UserHandlePtr,
541472b32d9SAndreas Maier UserInfoPtr,
542472b32d9SAndreas Maier SubStatus);
543472b32d9SAndreas Maier if (!NT_SUCCESS(Status))
544472b32d9SAndreas Maier {
545472b32d9SAndreas Maier ERR("SamValidateNormalUser() failed (Status 0x%08lx)\n", Status);
546472b32d9SAndreas Maier return Status;
547472b32d9SAndreas Maier }
548472b32d9SAndreas Maier }
549472b32d9SAndreas Maier
550472b32d9SAndreas Maier return Status;
551472b32d9SAndreas Maier }
552