xref: /reactos/dll/win32/msv1_0/sam.c (revision 99dcd6f7)
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