1 /* 2 * PROJECT: Local Security Authority Server DLL 3 * LICENSE: GPL - See COPYING in the top level directory 4 * FILE: dll/win32/samsrv/user.c 5 * PURPOSE: User specific helper functions 6 * COPYRIGHT: Copyright 2013 Eric Kohl 7 */ 8 9 #include "samsrv.h" 10 11 /* FUNCTIONS ***************************************************************/ 12 13 NTSTATUS 14 SampOpenUserObject(IN PSAM_DB_OBJECT DomainObject, 15 IN ULONG UserId, 16 IN ACCESS_MASK DesiredAccess, 17 OUT PSAM_DB_OBJECT *UserObject) 18 { 19 WCHAR szRid[9]; 20 21 TRACE("(%p %lu %lx %p)\n", 22 DomainObject, UserId, DesiredAccess, UserObject); 23 24 /* Convert the RID into a string (hex) */ 25 swprintf(szRid, L"%08lX", UserId); 26 27 /* Create the user object */ 28 return SampOpenDbObject(DomainObject, 29 L"Users", 30 szRid, 31 UserId, 32 SamDbUserObject, 33 DesiredAccess, 34 UserObject); 35 } 36 37 38 NTSTATUS 39 SampAddGroupMembershipToUser(IN PSAM_DB_OBJECT UserObject, 40 IN ULONG GroupId, 41 IN ULONG Attributes) 42 { 43 PGROUP_MEMBERSHIP GroupsBuffer = NULL; 44 ULONG GroupsCount = 0; 45 ULONG Length = 0; 46 ULONG i; 47 NTSTATUS Status; 48 49 TRACE("(%p %lu %lx)\n", 50 UserObject, GroupId, Attributes); 51 52 Status = SampGetObjectAttribute(UserObject, 53 L"Groups", 54 NULL, 55 NULL, 56 &Length); 57 if (!NT_SUCCESS(Status) && Status != STATUS_OBJECT_NAME_NOT_FOUND) 58 goto done; 59 60 GroupsBuffer = midl_user_allocate(Length + sizeof(GROUP_MEMBERSHIP)); 61 if (GroupsBuffer == NULL) 62 { 63 Status = STATUS_INSUFFICIENT_RESOURCES; 64 goto done; 65 } 66 67 if (Status != STATUS_OBJECT_NAME_NOT_FOUND) 68 { 69 Status = SampGetObjectAttribute(UserObject, 70 L"Groups", 71 NULL, 72 GroupsBuffer, 73 &Length); 74 if (!NT_SUCCESS(Status)) 75 goto done; 76 77 GroupsCount = Length / sizeof(GROUP_MEMBERSHIP); 78 } 79 80 for (i = 0; i < GroupsCount; i++) 81 { 82 if (GroupsBuffer[i].RelativeId == GroupId) 83 { 84 Status = STATUS_MEMBER_IN_GROUP; 85 goto done; 86 } 87 } 88 89 GroupsBuffer[GroupsCount].RelativeId = GroupId; 90 GroupsBuffer[GroupsCount].Attributes = Attributes; 91 Length += sizeof(GROUP_MEMBERSHIP); 92 93 Status = SampSetObjectAttribute(UserObject, 94 L"Groups", 95 REG_BINARY, 96 GroupsBuffer, 97 Length); 98 99 done: 100 if (GroupsBuffer != NULL) 101 midl_user_free(GroupsBuffer); 102 103 return Status; 104 } 105 106 107 NTSTATUS 108 SampRemoveGroupMembershipFromUser(IN PSAM_DB_OBJECT UserObject, 109 IN ULONG GroupId) 110 { 111 PGROUP_MEMBERSHIP GroupsBuffer = NULL; 112 ULONG GroupsCount = 0; 113 ULONG Length = 0; 114 ULONG i; 115 NTSTATUS Status = STATUS_SUCCESS; 116 117 TRACE("(%p %lu)\n", 118 UserObject, GroupId); 119 120 SampGetObjectAttribute(UserObject, 121 L"Groups", 122 NULL, 123 NULL, 124 &Length); 125 126 if (Length == 0) 127 return STATUS_MEMBER_NOT_IN_GROUP; 128 129 GroupsBuffer = midl_user_allocate(Length); 130 if (GroupsBuffer == NULL) 131 { 132 Status = STATUS_INSUFFICIENT_RESOURCES; 133 goto done; 134 } 135 136 Status = SampGetObjectAttribute(UserObject, 137 L"Groups", 138 NULL, 139 GroupsBuffer, 140 &Length); 141 if (!NT_SUCCESS(Status)) 142 goto done; 143 144 Status = STATUS_MEMBER_NOT_IN_GROUP; 145 146 GroupsCount = Length / sizeof(GROUP_MEMBERSHIP); 147 for (i = 0; i < GroupsCount; i++) 148 { 149 if (GroupsBuffer[i].RelativeId == GroupId) 150 { 151 Length -= sizeof(GROUP_MEMBERSHIP); 152 Status = STATUS_SUCCESS; 153 154 if (GroupsCount - i - 1 > 0) 155 { 156 CopyMemory(&GroupsBuffer[i], 157 &GroupsBuffer[i + 1], 158 (GroupsCount - i - 1) * sizeof(GROUP_MEMBERSHIP)); 159 } 160 161 break; 162 } 163 } 164 165 if (!NT_SUCCESS(Status)) 166 goto done; 167 168 Status = SampSetObjectAttribute(UserObject, 169 L"Groups", 170 REG_BINARY, 171 GroupsBuffer, 172 Length); 173 174 done: 175 if (GroupsBuffer != NULL) 176 midl_user_free(GroupsBuffer); 177 178 return Status; 179 } 180 181 182 NTSTATUS 183 SampGetUserGroupAttributes(IN PSAM_DB_OBJECT DomainObject, 184 IN ULONG UserId, 185 IN ULONG GroupId, 186 OUT PULONG GroupAttributes) 187 { 188 PSAM_DB_OBJECT UserObject = NULL; 189 PGROUP_MEMBERSHIP GroupsBuffer = NULL; 190 ULONG Length = 0; 191 ULONG i; 192 NTSTATUS Status; 193 194 Status = SampOpenUserObject(DomainObject, 195 UserId, 196 0, 197 &UserObject); 198 if (!NT_SUCCESS(Status)) 199 { 200 return Status; 201 } 202 203 SampGetObjectAttribute(UserObject, 204 L"Groups", 205 NULL, 206 NULL, 207 &Length); 208 209 if (Length == 0) 210 return STATUS_UNSUCCESSFUL; /* FIXME */ 211 212 GroupsBuffer = midl_user_allocate(Length); 213 if (GroupsBuffer == NULL) 214 { 215 Status = STATUS_INSUFFICIENT_RESOURCES; 216 goto done; 217 } 218 219 Status = SampGetObjectAttribute(UserObject, 220 L"Groups", 221 NULL, 222 GroupsBuffer, 223 &Length); 224 if (!NT_SUCCESS(Status)) 225 goto done; 226 227 for (i = 0; i < (Length / sizeof(GROUP_MEMBERSHIP)); i++) 228 { 229 if (GroupsBuffer[i].RelativeId == GroupId) 230 { 231 *GroupAttributes = GroupsBuffer[i].Attributes; 232 goto done; 233 } 234 } 235 236 done: 237 if (GroupsBuffer != NULL) 238 midl_user_free(GroupsBuffer); 239 240 if (UserObject != NULL) 241 SampCloseDbObject(UserObject); 242 243 return Status; 244 } 245 246 247 NTSTATUS 248 SampSetUserGroupAttributes(IN PSAM_DB_OBJECT DomainObject, 249 IN ULONG UserId, 250 IN ULONG GroupId, 251 IN ULONG GroupAttributes) 252 { 253 PSAM_DB_OBJECT UserObject = NULL; 254 PGROUP_MEMBERSHIP GroupsBuffer = NULL; 255 ULONG Length = 0; 256 ULONG i; 257 NTSTATUS Status; 258 259 Status = SampOpenUserObject(DomainObject, 260 UserId, 261 0, 262 &UserObject); 263 if (!NT_SUCCESS(Status)) 264 { 265 return Status; 266 } 267 268 SampGetObjectAttribute(UserObject, 269 L"Groups", 270 NULL, 271 NULL, 272 &Length); 273 274 if (Length == 0) 275 return STATUS_UNSUCCESSFUL; /* FIXME */ 276 277 GroupsBuffer = midl_user_allocate(Length); 278 if (GroupsBuffer == NULL) 279 { 280 Status = STATUS_INSUFFICIENT_RESOURCES; 281 goto done; 282 } 283 284 Status = SampGetObjectAttribute(UserObject, 285 L"Groups", 286 NULL, 287 GroupsBuffer, 288 &Length); 289 if (!NT_SUCCESS(Status)) 290 goto done; 291 292 for (i = 0; i < (Length / sizeof(GROUP_MEMBERSHIP)); i++) 293 { 294 if (GroupsBuffer[i].RelativeId == GroupId) 295 { 296 GroupsBuffer[i].Attributes = GroupAttributes; 297 break; 298 } 299 } 300 301 Status = SampSetObjectAttribute(UserObject, 302 L"Groups", 303 REG_BINARY, 304 GroupsBuffer, 305 Length); 306 307 done: 308 if (GroupsBuffer != NULL) 309 midl_user_free(GroupsBuffer); 310 311 if (UserObject != NULL) 312 SampCloseDbObject(UserObject); 313 314 return Status; 315 } 316 317 318 NTSTATUS 319 SampRemoveUserFromAllGroups(IN PSAM_DB_OBJECT UserObject) 320 { 321 PGROUP_MEMBERSHIP GroupsBuffer = NULL; 322 PSAM_DB_OBJECT GroupObject; 323 ULONG Length = 0; 324 ULONG i; 325 NTSTATUS Status; 326 327 SampGetObjectAttribute(UserObject, 328 L"Groups", 329 NULL, 330 NULL, 331 &Length); 332 333 if (Length == 0) 334 return STATUS_SUCCESS; 335 336 GroupsBuffer = midl_user_allocate(Length); 337 if (GroupsBuffer == NULL) 338 { 339 Status = STATUS_INSUFFICIENT_RESOURCES; 340 goto done; 341 } 342 343 Status = SampGetObjectAttribute(UserObject, 344 L"Groups", 345 NULL, 346 GroupsBuffer, 347 &Length); 348 if (!NT_SUCCESS(Status)) 349 goto done; 350 351 for (i = 0; i < (Length / sizeof(GROUP_MEMBERSHIP)); i++) 352 { 353 Status = SampOpenGroupObject(UserObject->ParentObject, 354 GroupsBuffer[i].RelativeId, 355 0, 356 &GroupObject); 357 if (!NT_SUCCESS(Status)) 358 { 359 goto done; 360 } 361 362 Status = SampRemoveMemberFromGroup(GroupObject, 363 UserObject->RelativeId); 364 if (Status == STATUS_MEMBER_NOT_IN_GROUP) 365 Status = STATUS_SUCCESS; 366 367 SampCloseDbObject(GroupObject); 368 369 if (!NT_SUCCESS(Status)) 370 { 371 goto done; 372 } 373 } 374 375 /* Remove all groups from the Groups attribute */ 376 Status = SampSetObjectAttribute(UserObject, 377 L"Groups", 378 REG_BINARY, 379 NULL, 380 0); 381 382 done: 383 if (GroupsBuffer != NULL) 384 midl_user_free(GroupsBuffer); 385 386 return Status; 387 } 388 389 390 NTSTATUS 391 SampRemoveUserFromAllAliases(IN PSAM_DB_OBJECT UserObject) 392 { 393 FIXME("(%p)\n", UserObject); 394 return STATUS_SUCCESS; 395 } 396 397 398 NTSTATUS 399 SampSetUserPassword(IN PSAM_DB_OBJECT UserObject, 400 IN PENCRYPTED_NT_OWF_PASSWORD NtPassword, 401 IN BOOLEAN NtPasswordPresent, 402 IN PENCRYPTED_LM_OWF_PASSWORD LmPassword, 403 IN BOOLEAN LmPasswordPresent) 404 { 405 PENCRYPTED_NT_OWF_PASSWORD NtHistory = NULL; 406 PENCRYPTED_LM_OWF_PASSWORD LmHistory = NULL; 407 ULONG NtHistoryLength = 0; 408 ULONG LmHistoryLength = 0; 409 ULONG CurrentHistoryLength; 410 ULONG MaxHistoryLength = 3; 411 ULONG Length = 0; 412 BOOLEAN UseNtPassword; 413 BOOLEAN UseLmPassword; 414 NTSTATUS Status; 415 416 UseNtPassword = 417 ((NtPasswordPresent != FALSE) && 418 (NtPassword != NULL) && 419 (memcmp(NtPassword, &EmptyNtHash, sizeof(ENCRYPTED_NT_OWF_PASSWORD)) != 0)); 420 421 UseLmPassword = 422 ((LmPasswordPresent != FALSE) && 423 (LmPassword != NULL) && 424 (memcmp(LmPassword, &EmptyLmHash, sizeof(ENCRYPTED_LM_OWF_PASSWORD)) != 0)); 425 426 /* Update the NT password history only if we have a new non-empty NT password */ 427 if (UseNtPassword) 428 { 429 /* Get the size of the NT history */ 430 SampGetObjectAttribute(UserObject, 431 L"NTPwdHistory", 432 NULL, 433 NULL, 434 &Length); 435 436 CurrentHistoryLength = Length / sizeof(ENCRYPTED_NT_OWF_PASSWORD); 437 if (CurrentHistoryLength < MaxHistoryLength) 438 { 439 NtHistoryLength = (CurrentHistoryLength + 1) * sizeof(ENCRYPTED_NT_OWF_PASSWORD); 440 } 441 else 442 { 443 NtHistoryLength = MaxHistoryLength * sizeof(ENCRYPTED_NT_OWF_PASSWORD); 444 } 445 446 /* Allocate the history buffer */ 447 NtHistory = midl_user_allocate(NtHistoryLength); 448 if (NtHistory == NULL) 449 return STATUS_INSUFFICIENT_RESOURCES; 450 451 if (Length > 0) 452 { 453 /* Get the history */ 454 Status = SampGetObjectAttribute(UserObject, 455 L"NTPwdHistory", 456 NULL, 457 NtHistory, 458 &Length); 459 if (!NT_SUCCESS(Status)) 460 goto done; 461 } 462 463 /* Move the old passwords down by one entry */ 464 if (NtHistoryLength > sizeof(ENCRYPTED_NT_OWF_PASSWORD)) 465 { 466 MoveMemory(&(NtHistory[1]), 467 &(NtHistory[0]), 468 NtHistoryLength - sizeof(ENCRYPTED_NT_OWF_PASSWORD)); 469 } 470 471 /* Add the new password to the top of the history */ 472 if (NtPasswordPresent) 473 { 474 CopyMemory(&(NtHistory[0]), 475 NtPassword, 476 sizeof(ENCRYPTED_NT_OWF_PASSWORD)); 477 } 478 else 479 { 480 ZeroMemory(&(NtHistory[0]), 481 sizeof(ENCRYPTED_NT_OWF_PASSWORD)); 482 } 483 484 /* Set the history */ 485 Status = SampSetObjectAttribute(UserObject, 486 L"NTPwdHistory", 487 REG_BINARY, 488 (PVOID)NtHistory, 489 NtHistoryLength); 490 if (!NT_SUCCESS(Status)) 491 goto done; 492 } 493 494 /* Update the LM password history only if we have a new non-empty LM password */ 495 if (UseLmPassword) 496 { 497 /* Get the size of the LM history */ 498 Length = 0; 499 SampGetObjectAttribute(UserObject, 500 L"LMPwdHistory", 501 NULL, 502 NULL, 503 &Length); 504 505 CurrentHistoryLength = Length / sizeof(ENCRYPTED_LM_OWF_PASSWORD); 506 if (CurrentHistoryLength < MaxHistoryLength) 507 { 508 LmHistoryLength = (CurrentHistoryLength + 1) * sizeof(ENCRYPTED_LM_OWF_PASSWORD); 509 } 510 else 511 { 512 LmHistoryLength = MaxHistoryLength * sizeof(ENCRYPTED_LM_OWF_PASSWORD); 513 } 514 515 /* Allocate the history buffer */ 516 LmHistory = midl_user_allocate(LmHistoryLength); 517 if (LmHistory == NULL) 518 return STATUS_INSUFFICIENT_RESOURCES; 519 520 if (Length > 0) 521 { 522 /* Get the history */ 523 Status = SampGetObjectAttribute(UserObject, 524 L"LMPwdHistory", 525 NULL, 526 LmHistory, 527 &Length); 528 if (!NT_SUCCESS(Status)) 529 goto done; 530 } 531 532 /* Move the old passwords down by one entry */ 533 if (LmHistoryLength > sizeof(ENCRYPTED_LM_OWF_PASSWORD)) 534 { 535 MoveMemory(&(LmHistory[1]), 536 &(LmHistory[0]), 537 LmHistoryLength - sizeof(ENCRYPTED_LM_OWF_PASSWORD)); 538 } 539 540 /* Add the new password to the top of the history */ 541 if (LmPasswordPresent) 542 { 543 CopyMemory(&(LmHistory[0]), 544 LmPassword, 545 sizeof(ENCRYPTED_LM_OWF_PASSWORD)); 546 } 547 else 548 { 549 ZeroMemory(&(LmHistory[0]), 550 sizeof(ENCRYPTED_LM_OWF_PASSWORD)); 551 } 552 553 /* Set the LM password history */ 554 Status = SampSetObjectAttribute(UserObject, 555 L"LMPwdHistory", 556 REG_BINARY, 557 (PVOID)LmHistory, 558 LmHistoryLength); 559 if (!NT_SUCCESS(Status)) 560 goto done; 561 } 562 563 /* Set the new NT password */ 564 if (UseNtPassword) 565 { 566 Status = SampSetObjectAttribute(UserObject, 567 L"NTPwd", 568 REG_BINARY, 569 (PVOID)NtPassword, 570 sizeof(ENCRYPTED_NT_OWF_PASSWORD)); 571 if (!NT_SUCCESS(Status)) 572 goto done; 573 } 574 else 575 { 576 Status = SampSetObjectAttribute(UserObject, 577 L"NTPwd", 578 REG_BINARY, 579 &EmptyNtHash, 580 sizeof(ENCRYPTED_NT_OWF_PASSWORD)); 581 if (!NT_SUCCESS(Status)) 582 goto done; 583 } 584 585 /* Set the new LM password */ 586 if (UseLmPassword) 587 { 588 Status = SampSetObjectAttribute(UserObject, 589 L"LMPwd", 590 REG_BINARY, 591 (PVOID)LmPassword, 592 sizeof(ENCRYPTED_LM_OWF_PASSWORD)); 593 if (!NT_SUCCESS(Status)) 594 goto done; 595 } 596 else 597 { 598 Status = SampSetObjectAttribute(UserObject, 599 L"LMPwd", 600 REG_BINARY, 601 &EmptyLmHash, 602 sizeof(ENCRYPTED_LM_OWF_PASSWORD)); 603 if (!NT_SUCCESS(Status)) 604 goto done; 605 } 606 607 done: 608 if (NtHistory != NULL) 609 midl_user_free(NtHistory); 610 611 if (LmHistory != NULL) 612 midl_user_free(LmHistory); 613 614 return Status; 615 } 616 617 618 NTSTATUS 619 SampGetLogonHoursAttribute(IN PSAM_DB_OBJECT UserObject, 620 IN OUT PSAMPR_LOGON_HOURS LogonHours) 621 { 622 PUCHAR RawBuffer = NULL; 623 ULONG Length = 0; 624 ULONG BufferLength = 0; 625 NTSTATUS Status; 626 627 Status = SampGetObjectAttribute(UserObject, 628 L"LogonHours", 629 NULL, 630 NULL, 631 &Length); 632 if (Status != STATUS_BUFFER_OVERFLOW) 633 { 634 TRACE("SampGetObjectAttribute failed (Status 0x%08lx)\n", Status); 635 return Status; 636 } 637 638 Status = STATUS_SUCCESS; 639 640 if (Length == 0) 641 { 642 LogonHours->UnitsPerWeek = 0; 643 LogonHours->LogonHours = NULL; 644 } 645 else 646 { 647 RawBuffer = midl_user_allocate(Length); 648 if (RawBuffer == NULL) 649 { 650 Status = STATUS_INSUFFICIENT_RESOURCES; 651 goto done; 652 } 653 654 Status = SampGetObjectAttribute(UserObject, 655 L"LogonHours", 656 NULL, 657 (PVOID)RawBuffer, 658 &Length); 659 if (!NT_SUCCESS(Status)) 660 goto done; 661 662 LogonHours->UnitsPerWeek = *((PUSHORT)RawBuffer); 663 664 BufferLength = (((ULONG)LogonHours->UnitsPerWeek) + 7) / 8; 665 666 LogonHours->LogonHours = midl_user_allocate(BufferLength); 667 if (LogonHours->LogonHours == NULL) 668 { 669 TRACE("Failed to allocate LogonHours buffer!\n"); 670 Status = STATUS_INSUFFICIENT_RESOURCES; 671 goto done; 672 } 673 674 memcpy(LogonHours->LogonHours, 675 &(RawBuffer[2]), 676 BufferLength); 677 } 678 679 done: 680 681 if (RawBuffer != NULL) 682 midl_user_free(RawBuffer); 683 684 return Status; 685 } 686 687 688 NTSTATUS 689 SampSetLogonHoursAttribute(IN PSAM_DB_OBJECT UserObject, 690 IN PSAMPR_LOGON_HOURS LogonHours) 691 { 692 PUCHAR RawBuffer = NULL; 693 ULONG BufferLength; 694 ULONG Length = 0; 695 NTSTATUS Status; 696 697 if (LogonHours->UnitsPerWeek > 0) 698 { 699 BufferLength = (((ULONG)LogonHours->UnitsPerWeek) + 7) / 8; 700 701 Length = BufferLength + sizeof(USHORT); 702 703 RawBuffer = midl_user_allocate(Length); 704 if (RawBuffer == NULL) 705 { 706 Status = STATUS_INSUFFICIENT_RESOURCES; 707 goto done; 708 } 709 710 *((PUSHORT)RawBuffer) = LogonHours->UnitsPerWeek; 711 712 memcpy(&(RawBuffer[2]), 713 LogonHours->LogonHours, 714 BufferLength); 715 } 716 717 Status = SampSetObjectAttribute(UserObject, 718 L"LogonHours", 719 REG_BINARY, 720 RawBuffer, 721 Length); 722 723 done: 724 if (RawBuffer != NULL) 725 midl_user_free(RawBuffer); 726 727 return Status; 728 } 729 730 /* EOF */ 731