1 /* 2 * PROJECT: Authentication Package DLL 3 * LICENSE: GPL-2.0-or-later (https://spdx.org/licenses/GPL-2.0-or-later) 4 * PURPOSE: Security Account Manager (SAM) related functions 5 * COPYRIGHT: Copyright 2013 Eric Kohl <eric.kohl@reactos.org> 6 */ 7 8 #include "precomp.h" 9 10 #include "wine/debug.h" 11 WINE_DEFAULT_DEBUG_CHANNEL(msv1_0_sam); 12 13 14 static 15 NTSTATUS 16 GetAccountDomainSid( 17 _In_ PRPC_SID *Sid) 18 { 19 LSAPR_HANDLE PolicyHandle = NULL; 20 PLSAPR_POLICY_INFORMATION PolicyInfo = NULL; 21 ULONG Length = 0; 22 NTSTATUS Status; 23 24 Status = LsaIOpenPolicyTrusted(&PolicyHandle); 25 if (!NT_SUCCESS(Status)) 26 { 27 TRACE("LsaIOpenPolicyTrusted() failed (Status 0x%08lx)\n", Status); 28 return Status; 29 } 30 31 Status = LsarQueryInformationPolicy(PolicyHandle, 32 PolicyAccountDomainInformation, 33 &PolicyInfo); 34 if (!NT_SUCCESS(Status)) 35 { 36 TRACE("LsarQueryInformationPolicy() failed (Status 0x%08lx)\n", Status); 37 goto done; 38 } 39 40 Length = RtlLengthSid(PolicyInfo->PolicyAccountDomainInfo.Sid); 41 42 *Sid = RtlAllocateHeap(RtlGetProcessHeap(), 0, Length); 43 if (*Sid == NULL) 44 { 45 ERR("Failed to allocate SID\n"); 46 Status = STATUS_INSUFFICIENT_RESOURCES; 47 goto done; 48 } 49 50 memcpy(*Sid, PolicyInfo->PolicyAccountDomainInfo.Sid, Length); 51 52 done: 53 if (PolicyInfo != NULL) 54 LsaIFree_LSAPR_POLICY_INFORMATION(PolicyAccountDomainInformation, 55 PolicyInfo); 56 57 if (PolicyHandle != NULL) 58 LsarClose(&PolicyHandle); 59 60 return Status; 61 } 62 63 64 static 65 NTSTATUS 66 MsvpCheckPassword( 67 _In_ PLSA_SAM_PWD_DATA UserPwdData, 68 _In_ PSAMPR_USER_INFO_BUFFER UserInfo) 69 { 70 ENCRYPTED_NT_OWF_PASSWORD UserNtPassword; 71 ENCRYPTED_LM_OWF_PASSWORD UserLmPassword; 72 BOOLEAN UserLmPasswordPresent = FALSE; 73 BOOLEAN UserNtPasswordPresent = FALSE; 74 OEM_STRING LmPwdString; 75 CHAR LmPwdBuffer[15]; 76 NTSTATUS Status; 77 78 TRACE("(%p %p)\n", UserPwdData, UserInfo); 79 80 /* Calculate the LM password and hash for the users password */ 81 LmPwdString.Length = 15; 82 LmPwdString.MaximumLength = 15; 83 LmPwdString.Buffer = LmPwdBuffer; 84 ZeroMemory(LmPwdString.Buffer, LmPwdString.MaximumLength); 85 86 Status = RtlUpcaseUnicodeStringToOemString(&LmPwdString, 87 UserPwdData->PlainPwd, 88 FALSE); 89 if (NT_SUCCESS(Status)) 90 { 91 /* Calculate the LM hash value of the users password */ 92 Status = SystemFunction006(LmPwdString.Buffer, 93 (LPSTR)&UserLmPassword); 94 if (NT_SUCCESS(Status)) 95 { 96 UserLmPasswordPresent = TRUE; 97 } 98 } 99 100 /* Calculate the NT hash of the users password */ 101 Status = SystemFunction007(UserPwdData->PlainPwd, 102 (LPBYTE)&UserNtPassword); 103 if (NT_SUCCESS(Status)) 104 { 105 UserNtPasswordPresent = TRUE; 106 } 107 108 Status = STATUS_WRONG_PASSWORD; 109 110 /* Succeed, if no password has been set */ 111 if (UserInfo->All.NtPasswordPresent == FALSE && 112 UserInfo->All.LmPasswordPresent == FALSE) 113 { 114 TRACE("No password check!\n"); 115 Status = STATUS_SUCCESS; 116 goto done; 117 } 118 119 /* Succeed, if NT password matches */ 120 if (UserNtPasswordPresent && UserInfo->All.NtPasswordPresent) 121 { 122 TRACE("Check NT password hashes:\n"); 123 if (RtlEqualMemory(&UserNtPassword, 124 UserInfo->All.NtOwfPassword.Buffer, 125 sizeof(ENCRYPTED_NT_OWF_PASSWORD))) 126 { 127 TRACE(" success!\n"); 128 Status = STATUS_SUCCESS; 129 goto done; 130 } 131 132 TRACE(" failed!\n"); 133 } 134 135 /* Succeed, if LM password matches */ 136 if (UserLmPasswordPresent && UserInfo->All.LmPasswordPresent) 137 { 138 TRACE("Check LM password hashes:\n"); 139 if (RtlEqualMemory(&UserLmPassword, 140 UserInfo->All.LmOwfPassword.Buffer, 141 sizeof(ENCRYPTED_LM_OWF_PASSWORD))) 142 { 143 TRACE(" success!\n"); 144 Status = STATUS_SUCCESS; 145 goto done; 146 } 147 TRACE(" failed!\n"); 148 } 149 150 done: 151 return Status; 152 } 153 154 155 static 156 bool 157 MsvpCheckLogonHours( 158 _In_ PSAMPR_LOGON_HOURS LogonHours, 159 _In_ LARGE_INTEGER LogonTime) 160 { 161 #if 0 162 LARGE_INTEGER LocalLogonTime; 163 TIME_FIELDS TimeFields; 164 USHORT MinutesPerUnit, Offset; 165 bool bFound; 166 167 FIXME("MsvpCheckLogonHours(%p %llx)\n", LogonHours, LogonTime); 168 169 if (LogonHours->UnitsPerWeek == 0 || LogonHours->LogonHours == NULL) 170 { 171 FIXME("No logon hours!\n"); 172 return true; 173 } 174 175 RtlSystemTimeToLocalTime(&LogonTime, &LocalLogonTime); 176 RtlTimeToTimeFields(&LocalLogonTime, &TimeFields); 177 178 FIXME("UnitsPerWeek: %u\n", LogonHours->UnitsPerWeek); 179 MinutesPerUnit = 10080 / LogonHours->UnitsPerWeek; 180 181 Offset = ((TimeFields.Weekday * 24 + TimeFields.Hour) * 60 + TimeFields.Minute) / MinutesPerUnit; 182 FIXME("Offset: %us\n", Offset); 183 184 bFound = (bool)(LogonHours->LogonHours[Offset / 8] & (1 << (Offset % 8))); 185 FIXME("Logon permitted: %s\n", bFound ? "Yes" : "No"); 186 187 return bFound; 188 #endif 189 return true; 190 } 191 192 193 static 194 bool 195 MsvpCheckWorkstations( 196 _In_ PRPC_UNICODE_STRING WorkStations, 197 _In_ PWSTR ComputerName) 198 { 199 PWSTR pStart, pEnd; 200 bool bFound = false; 201 202 TRACE("MsvpCheckWorkstations(%p %S)\n", WorkStations, ComputerName); 203 204 if (WorkStations->Length == 0 || WorkStations->Buffer == NULL) 205 { 206 TRACE("No workstations!\n"); 207 return true; 208 } 209 210 TRACE("Workstations: %wZ\n", WorkStations); 211 212 pStart = WorkStations->Buffer; 213 for (;;) 214 { 215 pEnd = wcschr(pStart, L','); 216 if (pEnd != NULL) 217 *pEnd = UNICODE_NULL; 218 219 TRACE("Comparing '%S' and '%S'\n", ComputerName, pStart); 220 if (_wcsicmp(ComputerName, pStart) == 0) 221 { 222 bFound = true; 223 if (pEnd != NULL) 224 *pEnd = L','; 225 break; 226 } 227 228 if (pEnd == NULL) 229 break; 230 231 *pEnd = L','; 232 pStart = pEnd + 1; 233 } 234 235 TRACE("Found allowed workstation: %s\n", (bFound) ? "Yes" : "No"); 236 237 return bFound; 238 } 239 240 241 static 242 NTSTATUS 243 SamValidateNormalUser( 244 _In_ PUNICODE_STRING UserName, 245 _In_ PLSA_SAM_PWD_DATA PwdData, 246 _In_ PUNICODE_STRING ComputerName, 247 _Out_ PRPC_SID* AccountDomainSidPtr, 248 _Out_ SAMPR_HANDLE* UserHandlePtr, 249 _Out_ PSAMPR_USER_INFO_BUFFER* UserInfoPtr, 250 _Out_ PNTSTATUS SubStatus) 251 { 252 NTSTATUS Status; 253 SAMPR_HANDLE ServerHandle = NULL; 254 SAMPR_HANDLE DomainHandle = NULL; 255 PRPC_SID AccountDomainSid; 256 RPC_UNICODE_STRING Names[1]; 257 SAMPR_HANDLE UserHandle = NULL; 258 SAMPR_ULONG_ARRAY RelativeIds = {0, NULL}; 259 SAMPR_ULONG_ARRAY Use = {0, NULL}; 260 PSAMPR_USER_INFO_BUFFER UserInfo = NULL; 261 LARGE_INTEGER LogonTime; 262 263 /* Get the logon time */ 264 NtQuerySystemTime(&LogonTime); 265 266 /* Get the account domain SID */ 267 Status = GetAccountDomainSid(&AccountDomainSid); 268 if (!NT_SUCCESS(Status)) 269 { 270 ERR("GetAccountDomainSid() failed (Status 0x%08lx)\n", Status); 271 return Status; 272 } 273 274 /* Connect to the SAM server */ 275 Status = SamIConnect(NULL, &ServerHandle, SAM_SERVER_CONNECT | SAM_SERVER_LOOKUP_DOMAIN, TRUE); 276 if (!NT_SUCCESS(Status)) 277 { 278 TRACE("SamIConnect() failed (Status 0x%08lx)\n", Status); 279 goto done; 280 } 281 282 /* Open the account domain */ 283 Status = SamrOpenDomain(ServerHandle, DOMAIN_LOOKUP, AccountDomainSid, &DomainHandle); 284 if (!NT_SUCCESS(Status)) 285 { 286 ERR("SamrOpenDomain failed (Status %08lx)\n", Status); 287 goto done; 288 } 289 290 Names[0].Length = UserName->Length; 291 Names[0].MaximumLength = UserName->MaximumLength; 292 Names[0].Buffer = UserName->Buffer; 293 294 /* Try to get the RID for the user name */ 295 Status = SamrLookupNamesInDomain(DomainHandle, 1, Names, &RelativeIds, &Use); 296 if (!NT_SUCCESS(Status)) 297 { 298 ERR("SamrLookupNamesInDomain failed (Status %08lx)\n", Status); 299 Status = STATUS_NO_SUCH_USER; 300 // FIXME: Try without domain? 301 goto done; 302 } 303 304 /* Fail, if it is not a user account */ 305 if (Use.Element[0] != SidTypeUser) 306 { 307 ERR("Account is not a user account!\n"); 308 Status = STATUS_NO_SUCH_USER; 309 goto done; 310 } 311 312 /* Open the user object */ 313 Status = SamrOpenUser(DomainHandle, 314 USER_READ_GENERAL | USER_READ_LOGON | 315 USER_READ_ACCOUNT | USER_READ_PREFERENCES, /* FIXME */ 316 RelativeIds.Element[0], 317 &UserHandle); 318 if (!NT_SUCCESS(Status)) 319 { 320 ERR("SamrOpenUser failed (Status %08lx)\n", Status); 321 goto done; 322 } 323 324 Status = SamrQueryInformationUser(UserHandle, UserAllInformation, &UserInfo); 325 if (!NT_SUCCESS(Status)) 326 { 327 ERR("SamrQueryInformationUser failed (Status %08lx)\n", Status); 328 goto done; 329 } 330 331 TRACE("UserName: %wZ\n", &UserInfo->All.UserName); 332 333 /* Check the password */ 334 if ((UserInfo->All.UserAccountControl & USER_PASSWORD_NOT_REQUIRED) == 0) 335 { 336 Status = MsvpCheckPassword(PwdData, UserInfo); 337 if (!NT_SUCCESS(Status)) 338 { 339 ERR("MsvpCheckPassword failed (Status %08lx)\n", Status); 340 goto done; 341 } 342 } 343 344 /* Check account restrictions for non-administrator accounts */ 345 if (RelativeIds.Element[0] != DOMAIN_USER_RID_ADMIN) 346 { 347 /* Check if the account has been disabled */ 348 if (UserInfo->All.UserAccountControl & USER_ACCOUNT_DISABLED) 349 { 350 ERR("Account disabled!\n"); 351 *SubStatus = STATUS_ACCOUNT_DISABLED; 352 Status = STATUS_ACCOUNT_RESTRICTION; 353 goto done; 354 } 355 356 /* Check if the account has been locked */ 357 if (UserInfo->All.UserAccountControl & USER_ACCOUNT_AUTO_LOCKED) 358 { 359 ERR("Account locked!\n"); 360 *SubStatus = STATUS_ACCOUNT_LOCKED_OUT; 361 Status = STATUS_ACCOUNT_RESTRICTION; 362 goto done; 363 } 364 365 /* Check if the account expired */ 366 if (LogonTime.QuadPart >= *(UINT64*)&UserInfo->All.AccountExpires) 367 { 368 ERR("Account expired!\n"); 369 *SubStatus = STATUS_ACCOUNT_EXPIRED; 370 Status = STATUS_ACCOUNT_RESTRICTION; 371 goto done; 372 } 373 374 /* Check if the password expired */ 375 if (LogonTime.QuadPart >= *(UINT64*)&UserInfo->All.PasswordMustChange) 376 { 377 ERR("Password expired!\n"); 378 if (*(UINT64*)&UserInfo->All.PasswordLastSet == 0) 379 *SubStatus = STATUS_PASSWORD_MUST_CHANGE; 380 else 381 *SubStatus = STATUS_PASSWORD_EXPIRED; 382 383 Status = STATUS_ACCOUNT_RESTRICTION; 384 goto done; 385 } 386 387 /* Check logon hours */ 388 if (!MsvpCheckLogonHours(&UserInfo->All.LogonHours, LogonTime)) 389 { 390 ERR("Invalid logon hours!\n"); 391 *SubStatus = STATUS_INVALID_LOGON_HOURS; 392 Status = STATUS_ACCOUNT_RESTRICTION; 393 goto done; 394 } 395 396 /* Check workstations */ 397 if (!MsvpCheckWorkstations(&UserInfo->All.WorkStations, ComputerName->Buffer)) 398 { 399 ERR("Invalid workstation!\n"); 400 *SubStatus = STATUS_INVALID_WORKSTATION; 401 Status = STATUS_ACCOUNT_RESTRICTION; 402 goto done; 403 } 404 } 405 done: 406 if (NT_SUCCESS(Status)) 407 { 408 *UserHandlePtr = UserHandle; 409 *AccountDomainSidPtr = AccountDomainSid; 410 *UserInfoPtr = UserInfo; 411 } 412 else 413 { 414 if (AccountDomainSid != NULL) 415 RtlFreeHeap(RtlGetProcessHeap(), 0, AccountDomainSid); 416 417 if (UserHandle != NULL) 418 SamrCloseHandle(&UserHandle); 419 420 SamIFree_SAMPR_USER_INFO_BUFFER(UserInfo, 421 UserAllInformation); 422 } 423 424 SamIFree_SAMPR_ULONG_ARRAY(&RelativeIds); 425 SamIFree_SAMPR_ULONG_ARRAY(&Use); 426 427 if (DomainHandle != NULL) 428 SamrCloseHandle(&DomainHandle); 429 430 if (ServerHandle != NULL) 431 SamrCloseHandle(&ServerHandle); 432 433 return Status; 434 } 435 436 437 static 438 NTSTATUS 439 GetNtAuthorityDomainSid( 440 _In_ PRPC_SID *Sid) 441 { 442 SID_IDENTIFIER_AUTHORITY NtAuthority = {SECURITY_NT_AUTHORITY}; 443 ULONG Length = 0; 444 445 Length = RtlLengthRequiredSid(0); 446 *Sid = RtlAllocateHeap(RtlGetProcessHeap(), 0, Length); 447 if (*Sid == NULL) 448 { 449 ERR("Failed to allocate SID\n"); 450 return STATUS_INSUFFICIENT_RESOURCES; 451 } 452 453 RtlInitializeSid(*Sid,&NtAuthority, 0); 454 455 return STATUS_SUCCESS; 456 } 457 458 459 NTSTATUS 460 SamValidateUser( 461 _In_ SECURITY_LOGON_TYPE LogonType, 462 _In_ PUNICODE_STRING LogonUserName, 463 _In_ PUNICODE_STRING LogonDomain, 464 _In_ PLSA_SAM_PWD_DATA LogonPwdData, 465 _In_ PUNICODE_STRING ComputerName, 466 _Out_ PBOOL SpecialAccount, 467 _Out_ PRPC_SID* AccountDomainSidPtr, 468 _Out_ SAMPR_HANDLE* UserHandlePtr, 469 _Out_ PSAMPR_USER_INFO_BUFFER* UserInfoPtr, 470 _Out_ PNTSTATUS SubStatus) 471 { 472 static const UNICODE_STRING NtAuthorityU = RTL_CONSTANT_STRING(L"NT AUTHORITY"); 473 static const UNICODE_STRING LocalServiceU = RTL_CONSTANT_STRING(L"LocalService"); 474 static const UNICODE_STRING NetworkServiceU = RTL_CONSTANT_STRING(L"NetworkService"); 475 476 NTSTATUS Status = STATUS_SUCCESS; 477 478 *SpecialAccount = FALSE; 479 *UserInfoPtr = NULL; 480 *SubStatus = STATUS_SUCCESS; 481 482 /* Check for special accounts */ 483 // FIXME: Windows does not do this that way!! (msv1_0 does not contain these hardcoded values) 484 if (RtlEqualUnicodeString(LogonDomain, &NtAuthorityU, TRUE)) 485 { 486 *SpecialAccount = TRUE; 487 488 /* Get the authority domain SID */ 489 Status = GetNtAuthorityDomainSid(AccountDomainSidPtr); 490 if (!NT_SUCCESS(Status)) 491 { 492 ERR("GetNtAuthorityDomainSid() failed (Status 0x%08lx)\n", Status); 493 return Status; 494 } 495 496 if (RtlEqualUnicodeString(LogonUserName, &LocalServiceU, TRUE)) 497 { 498 TRACE("SpecialAccount: LocalService\n"); 499 500 if (LogonType != Service) 501 return STATUS_LOGON_FAILURE; 502 503 *UserInfoPtr = RtlAllocateHeap(RtlGetProcessHeap(), 504 HEAP_ZERO_MEMORY, 505 sizeof(SAMPR_USER_ALL_INFORMATION)); 506 if (*UserInfoPtr == NULL) 507 return STATUS_INSUFFICIENT_RESOURCES; 508 509 (*UserInfoPtr)->All.UserId = SECURITY_LOCAL_SERVICE_RID; 510 (*UserInfoPtr)->All.PrimaryGroupId = SECURITY_LOCAL_SERVICE_RID; 511 } 512 else if (RtlEqualUnicodeString(LogonUserName, &NetworkServiceU, TRUE)) 513 { 514 TRACE("SpecialAccount: NetworkService\n"); 515 516 if (LogonType != Service) 517 return STATUS_LOGON_FAILURE; 518 519 *UserInfoPtr = RtlAllocateHeap(RtlGetProcessHeap(), 520 HEAP_ZERO_MEMORY, 521 sizeof(SAMPR_USER_ALL_INFORMATION)); 522 if (*UserInfoPtr == NULL) 523 return STATUS_INSUFFICIENT_RESOURCES; 524 525 (*UserInfoPtr)->All.UserId = SECURITY_NETWORK_SERVICE_RID; 526 (*UserInfoPtr)->All.PrimaryGroupId = SECURITY_NETWORK_SERVICE_RID; 527 } 528 else 529 { 530 return STATUS_NO_SUCH_USER; 531 } 532 } 533 else 534 { 535 TRACE("NormalAccount\n"); 536 Status = SamValidateNormalUser(LogonUserName, 537 LogonPwdData, 538 ComputerName, 539 AccountDomainSidPtr, 540 UserHandlePtr, 541 UserInfoPtr, 542 SubStatus); 543 if (!NT_SUCCESS(Status)) 544 { 545 ERR("SamValidateNormalUser() failed (Status 0x%08lx)\n", Status); 546 return Status; 547 } 548 } 549 550 return Status; 551 } 552