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 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 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 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 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 241*472b32d9SAndreas Maier static 242045cd5d4SAndreas Maier NTSTATUS 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 } 435*472b32d9SAndreas Maier 436*472b32d9SAndreas Maier 437*472b32d9SAndreas Maier static 438*472b32d9SAndreas Maier NTSTATUS 439*472b32d9SAndreas Maier GetNtAuthorityDomainSid( 440*472b32d9SAndreas Maier _In_ PRPC_SID *Sid) 441*472b32d9SAndreas Maier { 442*472b32d9SAndreas Maier SID_IDENTIFIER_AUTHORITY NtAuthority = {SECURITY_NT_AUTHORITY}; 443*472b32d9SAndreas Maier ULONG Length = 0; 444*472b32d9SAndreas Maier 445*472b32d9SAndreas Maier Length = RtlLengthRequiredSid(0); 446*472b32d9SAndreas Maier *Sid = RtlAllocateHeap(RtlGetProcessHeap(), 0, Length); 447*472b32d9SAndreas Maier if (*Sid == NULL) 448*472b32d9SAndreas Maier { 449*472b32d9SAndreas Maier ERR("Failed to allocate SID\n"); 450*472b32d9SAndreas Maier return STATUS_INSUFFICIENT_RESOURCES; 451*472b32d9SAndreas Maier } 452*472b32d9SAndreas Maier 453*472b32d9SAndreas Maier RtlInitializeSid(*Sid,&NtAuthority, 0); 454*472b32d9SAndreas Maier 455*472b32d9SAndreas Maier return STATUS_SUCCESS; 456*472b32d9SAndreas Maier } 457*472b32d9SAndreas Maier 458*472b32d9SAndreas Maier 459*472b32d9SAndreas Maier NTSTATUS 460*472b32d9SAndreas Maier SamValidateUser( 461*472b32d9SAndreas Maier _In_ SECURITY_LOGON_TYPE LogonType, 462*472b32d9SAndreas Maier _In_ PUNICODE_STRING LogonUserName, 463*472b32d9SAndreas Maier _In_ PUNICODE_STRING LogonDomain, 464*472b32d9SAndreas Maier _In_ PLSA_SAM_PWD_DATA LogonPwdData, 465*472b32d9SAndreas Maier _In_ PUNICODE_STRING ComputerName, 466*472b32d9SAndreas Maier _Out_ PBOOL SpecialAccount, 467*472b32d9SAndreas Maier _Out_ PRPC_SID* AccountDomainSidPtr, 468*472b32d9SAndreas Maier _Out_ SAMPR_HANDLE* UserHandlePtr, 469*472b32d9SAndreas Maier _Out_ PSAMPR_USER_INFO_BUFFER* UserInfoPtr, 470*472b32d9SAndreas Maier _Out_ PNTSTATUS SubStatus) 471*472b32d9SAndreas Maier { 472*472b32d9SAndreas Maier static const UNICODE_STRING NtAuthorityU = RTL_CONSTANT_STRING(L"NT AUTHORITY"); 473*472b32d9SAndreas Maier static const UNICODE_STRING LocalServiceU = RTL_CONSTANT_STRING(L"LocalService"); 474*472b32d9SAndreas Maier static const UNICODE_STRING NetworkServiceU = RTL_CONSTANT_STRING(L"NetworkService"); 475*472b32d9SAndreas Maier 476*472b32d9SAndreas Maier NTSTATUS Status = STATUS_SUCCESS; 477*472b32d9SAndreas Maier 478*472b32d9SAndreas Maier *SpecialAccount = FALSE; 479*472b32d9SAndreas Maier 480*472b32d9SAndreas Maier /* Check for special accounts */ 481*472b32d9SAndreas Maier // FIXME: Windows does not do this that way!! (msv1_0 does not contain these hardcoded values) 482*472b32d9SAndreas Maier if (RtlEqualUnicodeString(LogonDomain, &NtAuthorityU, TRUE)) 483*472b32d9SAndreas Maier { 484*472b32d9SAndreas Maier *SpecialAccount = TRUE; 485*472b32d9SAndreas Maier 486*472b32d9SAndreas Maier /* Get the authority domain SID */ 487*472b32d9SAndreas Maier Status = GetNtAuthorityDomainSid(AccountDomainSidPtr); 488*472b32d9SAndreas Maier if (!NT_SUCCESS(Status)) 489*472b32d9SAndreas Maier { 490*472b32d9SAndreas Maier ERR("GetNtAuthorityDomainSid() failed (Status 0x%08lx)\n", Status); 491*472b32d9SAndreas Maier return Status; 492*472b32d9SAndreas Maier } 493*472b32d9SAndreas Maier 494*472b32d9SAndreas Maier if (RtlEqualUnicodeString(LogonUserName, &LocalServiceU, TRUE)) 495*472b32d9SAndreas Maier { 496*472b32d9SAndreas Maier TRACE("SpecialAccount: LocalService\n"); 497*472b32d9SAndreas Maier 498*472b32d9SAndreas Maier if (LogonType != Service) 499*472b32d9SAndreas Maier return STATUS_LOGON_FAILURE; 500*472b32d9SAndreas Maier 501*472b32d9SAndreas Maier *UserInfoPtr = RtlAllocateHeap(RtlGetProcessHeap(), 502*472b32d9SAndreas Maier HEAP_ZERO_MEMORY, 503*472b32d9SAndreas Maier sizeof(SAMPR_USER_ALL_INFORMATION)); 504*472b32d9SAndreas Maier if (*UserInfoPtr == NULL) 505*472b32d9SAndreas Maier return STATUS_INSUFFICIENT_RESOURCES; 506*472b32d9SAndreas Maier 507*472b32d9SAndreas Maier (*UserInfoPtr)->All.UserId = SECURITY_LOCAL_SERVICE_RID; 508*472b32d9SAndreas Maier (*UserInfoPtr)->All.PrimaryGroupId = SECURITY_LOCAL_SERVICE_RID; 509*472b32d9SAndreas Maier } 510*472b32d9SAndreas Maier else if (RtlEqualUnicodeString(LogonUserName, &NetworkServiceU, TRUE)) 511*472b32d9SAndreas Maier { 512*472b32d9SAndreas Maier TRACE("SpecialAccount: NetworkService\n"); 513*472b32d9SAndreas Maier 514*472b32d9SAndreas Maier if (LogonType != Service) 515*472b32d9SAndreas Maier return STATUS_LOGON_FAILURE; 516*472b32d9SAndreas Maier 517*472b32d9SAndreas Maier *UserInfoPtr = RtlAllocateHeap(RtlGetProcessHeap(), 518*472b32d9SAndreas Maier HEAP_ZERO_MEMORY, 519*472b32d9SAndreas Maier sizeof(SAMPR_USER_ALL_INFORMATION)); 520*472b32d9SAndreas Maier if (*UserInfoPtr == NULL) 521*472b32d9SAndreas Maier return STATUS_INSUFFICIENT_RESOURCES; 522*472b32d9SAndreas Maier 523*472b32d9SAndreas Maier (*UserInfoPtr)->All.UserId = SECURITY_NETWORK_SERVICE_RID; 524*472b32d9SAndreas Maier (*UserInfoPtr)->All.PrimaryGroupId = SECURITY_NETWORK_SERVICE_RID; 525*472b32d9SAndreas Maier } 526*472b32d9SAndreas Maier else 527*472b32d9SAndreas Maier { 528*472b32d9SAndreas Maier return STATUS_NO_SUCH_USER; 529*472b32d9SAndreas Maier } 530*472b32d9SAndreas Maier } 531*472b32d9SAndreas Maier else 532*472b32d9SAndreas Maier { 533*472b32d9SAndreas Maier TRACE("NormalAccount\n"); 534*472b32d9SAndreas Maier Status = SamValidateNormalUser(LogonUserName, 535*472b32d9SAndreas Maier LogonPwdData, 536*472b32d9SAndreas Maier ComputerName, 537*472b32d9SAndreas Maier AccountDomainSidPtr, 538*472b32d9SAndreas Maier UserHandlePtr, 539*472b32d9SAndreas Maier UserInfoPtr, 540*472b32d9SAndreas Maier SubStatus); 541*472b32d9SAndreas Maier if (!NT_SUCCESS(Status)) 542*472b32d9SAndreas Maier { 543*472b32d9SAndreas Maier ERR("SamValidateNormalUser() failed (Status 0x%08lx)\n", Status); 544*472b32d9SAndreas Maier return Status; 545*472b32d9SAndreas Maier } 546*472b32d9SAndreas Maier } 547*472b32d9SAndreas Maier 548*472b32d9SAndreas Maier return Status; 549*472b32d9SAndreas Maier } 550