1 /* 2 * COPYRIGHT: See COPYING in the top level directory 3 * PROJECT: ReactOS kernel 4 * FILE: ntoskrnl/se/srm.c 5 * PURPOSE: Security Reference Monitor Server 6 * 7 * PROGRAMMERS: Timo Kreuzer (timo.kreuzer@reactos.org) 8 * Pierre Schweitzer (pierre@reactos.org) 9 */ 10 11 /* INCLUDES *******************************************************************/ 12 13 #include <ntoskrnl.h> 14 #define NDEBUG 15 #include <debug.h> 16 17 extern LUID SeSystemAuthenticationId; 18 extern LUID SeAnonymousAuthenticationId; 19 20 /* PRIVATE DEFINITIONS ********************************************************/ 21 22 #define SEP_LOGON_SESSION_TAG 'sLeS' 23 #define SEP_LOGON_NOTIFICATION_TAG 'nLeS' 24 25 typedef struct _SEP_LOGON_SESSION_TERMINATED_NOTIFICATION 26 { 27 struct _SEP_LOGON_SESSION_TERMINATED_NOTIFICATION *Next; 28 PSE_LOGON_SESSION_TERMINATED_ROUTINE CallbackRoutine; 29 } SEP_LOGON_SESSION_TERMINATED_NOTIFICATION, *PSEP_LOGON_SESSION_TERMINATED_NOTIFICATION; 30 31 VOID 32 NTAPI 33 SepRmCommandServerThread( 34 PVOID StartContext); 35 36 static 37 NTSTATUS 38 SepCleanupLUIDDeviceMapDirectory( 39 _In_ PLUID LogonLuid); 40 41 static 42 NTSTATUS 43 SepRmCreateLogonSession( 44 PLUID LogonLuid); 45 46 47 /* GLOBALS ********************************************************************/ 48 49 HANDLE SeRmCommandPort; 50 HANDLE SeLsaInitEvent; 51 52 PVOID SepCommandPortViewBase; 53 PVOID SepCommandPortViewRemoteBase; 54 ULONG_PTR SepCommandPortViewBaseOffset; 55 56 static HANDLE SepRmCommandMessagePort; 57 58 BOOLEAN SepAdtAuditingEnabled; 59 ULONG SepAdtMinListLength = 0x2000; 60 ULONG SepAdtMaxListLength = 0x3000; 61 62 #define POLICY_AUDIT_EVENT_TYPE_COUNT 9 // (AuditCategoryAccountLogon - AuditCategorySystem + 1) 63 UCHAR SeAuditingState[POLICY_AUDIT_EVENT_TYPE_COUNT]; 64 65 KGUARDED_MUTEX SepRmDbLock; 66 PSEP_LOGON_SESSION_REFERENCES SepLogonSessions = NULL; 67 PSEP_LOGON_SESSION_TERMINATED_NOTIFICATION SepLogonNotifications = NULL; 68 69 70 /* PRIVATE FUNCTIONS **********************************************************/ 71 72 /** 73 * @brief 74 * A private registry helper that returns the desired value 75 * data based on the specifics requested by the caller. 76 * 77 * @param[in] KeyName 78 * Name of the key. 79 * 80 * @param[in] ValueName 81 * Name of the registry value. 82 * 83 * @param[in] ValueType 84 * The type of the registry value. 85 * 86 * @param[in] DataLength 87 * The data length, in bytes, representing the size of the registry value. 88 * 89 * @param[out] ValueData 90 * The requested value data provided by the function. 91 * 92 * @return 93 * Returns STATUS_SUCCESS if the operations have completed successfully, 94 * otherwise a failure NTSTATUS code is returned. 95 */ 96 NTSTATUS 97 NTAPI 98 SepRegQueryHelper( 99 _In_ PCWSTR KeyName, 100 _In_ PCWSTR ValueName, 101 _In_ ULONG ValueType, 102 _In_ ULONG DataLength, 103 _Out_ PVOID ValueData) 104 { 105 UNICODE_STRING ValueNameString; 106 UNICODE_STRING KeyNameString; 107 ULONG ResultLength; 108 OBJECT_ATTRIBUTES ObjectAttributes; 109 HANDLE KeyHandle = NULL; 110 struct 111 { 112 KEY_VALUE_PARTIAL_INFORMATION Partial; 113 UCHAR Buffer[64]; 114 } KeyValueInformation; 115 NTSTATUS Status, CloseStatus; 116 PAGED_CODE(); 117 118 RtlInitUnicodeString(&KeyNameString, KeyName); 119 InitializeObjectAttributes(&ObjectAttributes, 120 &KeyNameString, 121 OBJ_KERNEL_HANDLE | OBJ_CASE_INSENSITIVE, 122 NULL, 123 NULL); 124 125 Status = ZwOpenKey(&KeyHandle, KEY_QUERY_VALUE, &ObjectAttributes); 126 if (!NT_SUCCESS(Status)) 127 { 128 return Status; 129 } 130 131 RtlInitUnicodeString(&ValueNameString, ValueName); 132 Status = ZwQueryValueKey(KeyHandle, 133 &ValueNameString, 134 KeyValuePartialInformation, 135 &KeyValueInformation.Partial, 136 sizeof(KeyValueInformation), 137 &ResultLength); 138 if (!NT_SUCCESS(Status)) 139 { 140 goto Cleanup; 141 } 142 143 if ((KeyValueInformation.Partial.Type != ValueType) || 144 (KeyValueInformation.Partial.DataLength != DataLength)) 145 { 146 Status = STATUS_OBJECT_TYPE_MISMATCH; 147 goto Cleanup; 148 } 149 150 if (ValueType == REG_BINARY) 151 { 152 RtlCopyMemory(ValueData, KeyValueInformation.Partial.Data, DataLength); 153 } 154 else if (ValueType == REG_DWORD) 155 { 156 *(PULONG)ValueData = *(PULONG)KeyValueInformation.Partial.Data; 157 } 158 else 159 { 160 Status = STATUS_INVALID_PARAMETER; 161 } 162 163 Cleanup: 164 CloseStatus = ZwClose(KeyHandle); 165 ASSERT(NT_SUCCESS( CloseStatus )); 166 167 return Status; 168 } 169 170 171 BOOLEAN 172 NTAPI 173 SeRmInitPhase0(VOID) 174 { 175 NTSTATUS Status; 176 177 /* Initialize the database lock */ 178 KeInitializeGuardedMutex(&SepRmDbLock); 179 180 /* Create the system logon session */ 181 Status = SepRmCreateLogonSession(&SeSystemAuthenticationId); 182 if (!NT_VERIFY(NT_SUCCESS(Status))) 183 { 184 return FALSE; 185 } 186 187 /* Create the anonymous logon session */ 188 Status = SepRmCreateLogonSession(&SeAnonymousAuthenticationId); 189 if (!NT_VERIFY(NT_SUCCESS(Status))) 190 { 191 return FALSE; 192 } 193 194 return TRUE; 195 } 196 197 198 BOOLEAN 199 NTAPI 200 SeRmInitPhase1(VOID) 201 { 202 UNICODE_STRING Name; 203 OBJECT_ATTRIBUTES ObjectAttributes; 204 HANDLE ThreadHandle; 205 NTSTATUS Status; 206 207 /* Create the SeRm command port */ 208 RtlInitUnicodeString(&Name, L"\\SeRmCommandPort"); 209 InitializeObjectAttributes(&ObjectAttributes, &Name, 0, NULL, NULL); 210 Status = ZwCreatePort(&SeRmCommandPort, 211 &ObjectAttributes, 212 sizeof(ULONG), 213 PORT_MAXIMUM_MESSAGE_LENGTH, 214 2 * PAGE_SIZE); 215 if (!NT_SUCCESS(Status)) 216 { 217 DPRINT1("Security: Rm Create Command Port failed 0x%lx\n", Status); 218 return FALSE; 219 } 220 221 /* Create SeLsaInitEvent */ 222 RtlInitUnicodeString(&Name, L"\\SeLsaInitEvent"); 223 InitializeObjectAttributes(&ObjectAttributes, &Name, 0, NULL, NULL); 224 Status = ZwCreateEvent(&SeLsaInitEvent, 225 GENERIC_WRITE, 226 &ObjectAttributes, 227 NotificationEvent, 228 FALSE); 229 if (!NT_VERIFY((NT_SUCCESS(Status)))) 230 { 231 DPRINT1("Security: LSA init event creation failed.0x%xl\n", Status); 232 return FALSE; 233 } 234 235 /* Create the SeRm server thread */ 236 Status = PsCreateSystemThread(&ThreadHandle, 237 THREAD_ALL_ACCESS, 238 NULL, 239 NULL, 240 NULL, 241 SepRmCommandServerThread, 242 NULL); 243 if (!NT_SUCCESS(Status)) 244 { 245 DPRINT1("Security: Rm Server Thread creation failed 0x%lx\n", Status); 246 return FALSE; 247 } 248 249 ObCloseHandle(ThreadHandle, KernelMode); 250 251 return TRUE; 252 } 253 254 static 255 VOID 256 SepAdtInitializeBounds(VOID) 257 { 258 struct 259 { 260 ULONG MaxLength; 261 ULONG MinLength; 262 } ListBounds; 263 NTSTATUS Status; 264 PAGED_CODE(); 265 266 Status = SepRegQueryHelper(L"\\Registry\\Machine\\System\\CurrentControlSet\\Control\\Lsa", 267 L"Bounds", 268 REG_BINARY, 269 sizeof(ListBounds), 270 &ListBounds); 271 if (!NT_SUCCESS(Status)) 272 { 273 /* No registry values, so keep hardcoded defaults */ 274 return; 275 } 276 277 /* Check if the bounds are valid */ 278 if ((ListBounds.MaxLength < ListBounds.MinLength) || 279 (ListBounds.MinLength < 16) || 280 (ListBounds.MaxLength - ListBounds.MinLength < 16)) 281 { 282 DPRINT1("ListBounds are invalid: %u, %u\n", 283 ListBounds.MinLength, ListBounds.MaxLength); 284 return; 285 } 286 287 /* Set the new bounds globally */ 288 SepAdtMinListLength = ListBounds.MinLength; 289 SepAdtMaxListLength = ListBounds.MaxLength; 290 } 291 292 293 static 294 NTSTATUS 295 SepRmSetAuditEvent( 296 PSEP_RM_API_MESSAGE Message) 297 { 298 ULONG i; 299 PAGED_CODE(); 300 301 /* First re-initialize the bounds from the registry */ 302 SepAdtInitializeBounds(); 303 304 /* Make sure we have the right message and clear */ 305 ASSERT(Message->ApiNumber == RmAuditSetCommand); 306 Message->ApiNumber = 0; 307 308 /* Store the enable flag in the global variable */ 309 SepAdtAuditingEnabled = Message->u.SetAuditEvent.Enabled; 310 311 /* Loop all audit event types */ 312 for (i = 0; i < POLICY_AUDIT_EVENT_TYPE_COUNT; i++) 313 { 314 /* Save the provided flags in the global array */ 315 SeAuditingState[i] = (UCHAR)Message->u.SetAuditEvent.Flags[i]; 316 } 317 318 return STATUS_SUCCESS; 319 } 320 321 322 static 323 NTSTATUS 324 SepRmCreateLogonSession( 325 PLUID LogonLuid) 326 { 327 PSEP_LOGON_SESSION_REFERENCES CurrentSession, NewSession; 328 NTSTATUS Status; 329 PAGED_CODE(); 330 331 DPRINT("SepRmCreateLogonSession(%08lx:%08lx)\n", 332 LogonLuid->HighPart, LogonLuid->LowPart); 333 334 /* Allocate a new session structure */ 335 NewSession = ExAllocatePoolWithTag(PagedPool, 336 sizeof(SEP_LOGON_SESSION_REFERENCES), 337 SEP_LOGON_SESSION_TAG); 338 if (NewSession == NULL) 339 { 340 return STATUS_INSUFFICIENT_RESOURCES; 341 } 342 343 /* Initialize it */ 344 NewSession->LogonId = *LogonLuid; 345 NewSession->ReferenceCount = 0; 346 NewSession->Flags = 0; 347 NewSession->pDeviceMap = NULL; 348 InitializeListHead(&NewSession->TokenList); 349 350 /* Acquire the database lock */ 351 KeAcquireGuardedMutex(&SepRmDbLock); 352 353 /* Loop all existing sessions */ 354 for (CurrentSession = SepLogonSessions; 355 CurrentSession != NULL; 356 CurrentSession = CurrentSession->Next) 357 { 358 /* Check if the LUID matches the new one */ 359 if (RtlEqualLuid(&CurrentSession->LogonId, LogonLuid)) 360 { 361 Status = STATUS_LOGON_SESSION_EXISTS; 362 goto Leave; 363 } 364 } 365 366 /* Insert the new session */ 367 NewSession->Next = SepLogonSessions; 368 SepLogonSessions = NewSession; 369 370 Status = STATUS_SUCCESS; 371 372 Leave: 373 /* Release the database lock */ 374 KeReleaseGuardedMutex(&SepRmDbLock); 375 376 if (!NT_SUCCESS(Status)) 377 { 378 ExFreePoolWithTag(NewSession, SEP_LOGON_SESSION_TAG); 379 } 380 381 return Status; 382 } 383 384 /** 385 * @brief 386 * Deletes a logon session from the logon sessions database. 387 * 388 * @param[in] LogonLuid 389 * A logon ID represented as a LUID. This LUID is used to point 390 * the exact logon session saved within the database. 391 * 392 * @return 393 * STATUS_SUCCESS is returned if the logon session has been deleted successfully. 394 * STATUS_NO_SUCH_LOGON_SESSION is returned if the logon session with the submitted 395 * LUID doesn't exist. STATUS_BAD_LOGON_SESSION_STATE is returned if the logon session 396 * is still in use and we're not allowed to delete it, or if a system or anonymous session 397 * is submitted and we're not allowed to delete them as they're internal parts of the system. 398 * Otherwise a failure NTSTATUS code is returned. 399 */ 400 static 401 NTSTATUS 402 SepRmDeleteLogonSession( 403 _In_ PLUID LogonLuid) 404 { 405 PSEP_LOGON_SESSION_REFERENCES SessionToDelete; 406 NTSTATUS Status; 407 PAGED_CODE(); 408 409 DPRINT("SepRmDeleteLogonSession(%08lx:%08lx)\n", 410 LogonLuid->HighPart, LogonLuid->LowPart); 411 412 /* Acquire the database lock */ 413 KeAcquireGuardedMutex(&SepRmDbLock); 414 415 /* Loop over the existing logon sessions */ 416 for (SessionToDelete = SepLogonSessions; 417 SessionToDelete != NULL; 418 SessionToDelete = SessionToDelete->Next) 419 { 420 /* 421 * Does the actual logon session exist in the 422 * saved logon sessions database with the LUID 423 * provided? 424 */ 425 if (RtlEqualLuid(&SessionToDelete->LogonId, LogonLuid)) 426 { 427 /* Did the caller supply one of these internal sessions? */ 428 if (RtlEqualLuid(&SessionToDelete->LogonId, &SeSystemAuthenticationId) || 429 RtlEqualLuid(&SessionToDelete->LogonId, &SeAnonymousAuthenticationId)) 430 { 431 /* These logons are critical stuff, we can't delete them */ 432 DPRINT1("SepRmDeleteLogonSession(): We're not allowed to delete anonymous/system sessions!\n"); 433 Status = STATUS_BAD_LOGON_SESSION_STATE; 434 goto Leave; 435 } 436 else 437 { 438 /* We found the logon as exactly as we wanted, break the loop */ 439 break; 440 } 441 } 442 } 443 444 /* 445 * If we reach this then that means we've exhausted all the logon 446 * sessions and couldn't find one with the desired LUID. 447 */ 448 if (SessionToDelete == NULL) 449 { 450 DPRINT1("SepRmDeleteLogonSession(): The logon session with this LUID doesn't exist!\n"); 451 Status = STATUS_NO_SUCH_LOGON_SESSION; 452 goto Leave; 453 } 454 455 /* Is somebody still using this logon session? */ 456 if (SessionToDelete->ReferenceCount != 0) 457 { 458 /* The logon session is still in use, we cannot delete it... */ 459 DPRINT1("SepRmDeleteLogonSession(): The logon session is still in use!\n"); 460 Status = STATUS_BAD_LOGON_SESSION_STATE; 461 goto Leave; 462 } 463 464 /* If we have a LUID device map, clean it */ 465 if (SessionToDelete->pDeviceMap != NULL) 466 { 467 Status = SepCleanupLUIDDeviceMapDirectory(LogonLuid); 468 if (!NT_SUCCESS(Status)) 469 { 470 /* 471 * We had one job on cleaning the device map directory 472 * of the logon session but we failed, quit... 473 */ 474 DPRINT1("SepRmDeleteLogonSession(): Failed to clean the LUID device map directory of the logon (Status: 0x%lx)\n", Status); 475 goto Leave; 476 } 477 478 /* And dereference the device map of the logon */ 479 ObfDereferenceDeviceMap(SessionToDelete->pDeviceMap); 480 } 481 482 /* If we're here then we've deleted the logon session successfully */ 483 DPRINT("SepRmDeleteLogonSession(): Logon session deleted with success!\n"); 484 Status = STATUS_SUCCESS; 485 ExFreePoolWithTag(SessionToDelete, SEP_LOGON_SESSION_TAG); 486 487 Leave: 488 /* Release the database lock */ 489 KeReleaseGuardedMutex(&SepRmDbLock); 490 return Status; 491 } 492 493 494 NTSTATUS 495 SepRmReferenceLogonSession( 496 PLUID LogonLuid) 497 { 498 PSEP_LOGON_SESSION_REFERENCES CurrentSession; 499 500 PAGED_CODE(); 501 502 DPRINT("SepRmReferenceLogonSession(%08lx:%08lx)\n", 503 LogonLuid->HighPart, LogonLuid->LowPart); 504 505 /* Acquire the database lock */ 506 KeAcquireGuardedMutex(&SepRmDbLock); 507 508 /* Loop all existing sessions */ 509 for (CurrentSession = SepLogonSessions; 510 CurrentSession != NULL; 511 CurrentSession = CurrentSession->Next) 512 { 513 /* Check if the LUID matches the new one */ 514 if (RtlEqualLuid(&CurrentSession->LogonId, LogonLuid)) 515 { 516 /* Reference the session */ 517 ++CurrentSession->ReferenceCount; 518 DPRINT("ReferenceCount: %lu\n", CurrentSession->ReferenceCount); 519 520 /* Release the database lock */ 521 KeReleaseGuardedMutex(&SepRmDbLock); 522 523 return STATUS_SUCCESS; 524 } 525 } 526 527 /* Release the database lock */ 528 KeReleaseGuardedMutex(&SepRmDbLock); 529 530 return STATUS_NO_SUCH_LOGON_SESSION; 531 } 532 533 static 534 NTSTATUS 535 SepCleanupLUIDDeviceMapDirectory( 536 _In_ PLUID LogonLuid) 537 { 538 BOOLEAN UseCurrentProc; 539 KAPC_STATE ApcState; 540 WCHAR Buffer[63]; 541 UNICODE_STRING DirectoryName; 542 OBJECT_ATTRIBUTES ObjectAttributes; 543 NTSTATUS Status; 544 HANDLE DirectoryHandle, LinkHandle; 545 PHANDLE LinksBuffer; 546 POBJECT_DIRECTORY_INFORMATION DirectoryInfo; 547 ULONG LinksCount, LinksSize, DirInfoLength, ReturnLength, Context, CurrentLinks, i; 548 BOOLEAN RestartScan; 549 550 PAGED_CODE(); 551 552 /* We need a logon LUID */ 553 if (LogonLuid == NULL) 554 { 555 return STATUS_INVALID_PARAMETER; 556 } 557 558 /* Use current process */ 559 UseCurrentProc = ObReferenceObjectSafe(PsGetCurrentProcess()); 560 if (UseCurrentProc) 561 { 562 ObDereferenceObject(PsGetCurrentProcess()); 563 } 564 /* Unless it's gone, then use system process */ 565 else 566 { 567 KeStackAttachProcess(&PsInitialSystemProcess->Pcb, &ApcState); 568 } 569 570 /* Initialize our directory name */ 571 _snwprintf(Buffer, 572 sizeof(Buffer) / sizeof(WCHAR), 573 L"\\Sessions\\0\\DosDevices\\%08x-%08x", 574 LogonLuid->HighPart, 575 LogonLuid->LowPart); 576 RtlInitUnicodeString(&DirectoryName, Buffer); 577 578 /* And open it */ 579 InitializeObjectAttributes(&ObjectAttributes, 580 &DirectoryName, 581 OBJ_KERNEL_HANDLE | OBJ_CASE_INSENSITIVE, 582 NULL, 583 NULL); 584 Status = ZwOpenDirectoryObject(&DirectoryHandle, 585 DIRECTORY_QUERY, 586 &ObjectAttributes); 587 if (!NT_SUCCESS(Status)) 588 { 589 if (!UseCurrentProc) 590 { 591 KeUnstackDetachProcess(&ApcState); 592 } 593 594 return Status; 595 } 596 597 /* Some initialization needed for browsing all our links... */ 598 Context = 0; 599 DirectoryInfo = NULL; 600 DirInfoLength = 0; 601 /* In our buffer, we'll store at max 100 HANDLE */ 602 LinksCount = 100; 603 CurrentLinks = 0; 604 /* Which gives a certain size */ 605 LinksSize = LinksCount * sizeof(HANDLE); 606 607 /* 608 * This label is hit if we need to store more than a hundred 609 * of links. In that case, we jump here after having cleaned 610 * and deleted previous buffer. 611 * All handles have been already closed 612 */ 613 AllocateLinksAgain: 614 LinksBuffer = ExAllocatePoolWithTag(PagedPool, 615 LinksSize, 616 TAG_SE_HANDLES_TAB); 617 if (LinksBuffer == NULL) 618 { 619 /* 620 * Failure path: no need to clear handles: 621 * already closed and the buffer is already gone 622 */ 623 ZwClose(DirectoryHandle); 624 625 /* 626 * On the first round, DirectoryInfo is NULL, 627 * if we grow LinksBuffer, it has been allocated 628 */ 629 if (DirectoryInfo != NULL) 630 { 631 ExFreePoolWithTag(DirectoryInfo, TAG_SE_DIR_BUFFER); 632 } 633 634 if (!UseCurrentProc) 635 { 636 KeUnstackDetachProcess(&ApcState); 637 } 638 639 return STATUS_NO_MEMORY; 640 } 641 642 /* 643 * We always restart scan, but on the first loop 644 * if we couldn't fit everything in our buffer, 645 * then, we continue scan. 646 * But we restart if link buffer was too small 647 */ 648 for (RestartScan = TRUE; ; RestartScan = FALSE) 649 { 650 /* 651 * Loop until our buffer is big enough to store 652 * one entry 653 */ 654 while (TRUE) 655 { 656 Status = ZwQueryDirectoryObject(DirectoryHandle, 657 DirectoryInfo, 658 DirInfoLength, 659 TRUE, 660 RestartScan, 661 &Context, 662 &ReturnLength); 663 /* Only handle buffer growth in that loop */ 664 if (Status != STATUS_BUFFER_TOO_SMALL) 665 { 666 break; 667 } 668 669 /* Get output length as new length */ 670 DirInfoLength = ReturnLength; 671 /* Delete old buffer if any */ 672 if (DirectoryInfo != NULL) 673 { 674 ExFreePoolWithTag(DirectoryInfo, 'bDeS'); 675 } 676 677 /* And reallocate a bigger one */ 678 DirectoryInfo = ExAllocatePoolWithTag(PagedPool, 679 DirInfoLength, 680 TAG_SE_DIR_BUFFER); 681 /* Fail if we cannot allocate */ 682 if (DirectoryInfo == NULL) 683 { 684 Status = STATUS_INSUFFICIENT_RESOURCES; 685 break; 686 } 687 } 688 689 /* If querying the entry failed, quit */ 690 if (!NT_SUCCESS(Status)) 691 { 692 break; 693 } 694 695 /* We only look for symbolic links, the rest, we ignore */ 696 if (wcscmp(DirectoryInfo->TypeName.Buffer, L"SymbolicLink")) 697 { 698 continue; 699 } 700 701 /* If our link buffer is out of space, reallocate */ 702 if (CurrentLinks >= LinksCount) 703 { 704 /* First, close the links */ 705 for (i = 0; i < CurrentLinks; ++i) 706 { 707 ZwClose(LinksBuffer[i]); 708 } 709 710 /* Allow 20 more HANDLEs */ 711 LinksCount += 20; 712 CurrentLinks = 0; 713 ExFreePoolWithTag(LinksBuffer, TAG_SE_HANDLES_TAB); 714 LinksSize = LinksCount * sizeof(HANDLE); 715 716 /* And reloop again */ 717 goto AllocateLinksAgain; 718 } 719 720 /* Open the found link */ 721 InitializeObjectAttributes(&ObjectAttributes, 722 &DirectoryInfo->Name, 723 OBJ_KERNEL_HANDLE | OBJ_CASE_INSENSITIVE, 724 DirectoryHandle, 725 NULL); 726 if (NT_SUCCESS(ZwOpenSymbolicLinkObject(&LinkHandle, 727 SYMBOLIC_LINK_ALL_ACCESS, 728 &ObjectAttributes))) 729 { 730 /* If we cannot make it temporary, just close the link handle */ 731 if (!NT_SUCCESS(ZwMakeTemporaryObject(LinkHandle))) 732 { 733 ZwClose(LinkHandle); 734 } 735 /* Otherwise, store it to defer deletion */ 736 else 737 { 738 LinksBuffer[CurrentLinks] = LinkHandle; 739 ++CurrentLinks; 740 } 741 } 742 } 743 744 /* No more entries means we handled all links, that's not a failure */ 745 if (Status == STATUS_NO_MORE_ENTRIES) 746 { 747 Status = STATUS_SUCCESS; 748 } 749 750 /* Close all the links we stored, this will like cause their deletion */ 751 for (i = 0; i < CurrentLinks; ++i) 752 { 753 ZwClose(LinksBuffer[i]); 754 } 755 /* And free our links buffer */ 756 ExFreePoolWithTag(LinksBuffer, TAG_SE_HANDLES_TAB); 757 758 /* Free our directory info buffer - it might be NULL if we failed realloc */ 759 if (DirectoryInfo != NULL) 760 { 761 ExFreePoolWithTag(DirectoryInfo, TAG_SE_DIR_BUFFER); 762 } 763 764 /* Close our session directory */ 765 ZwClose(DirectoryHandle); 766 767 /* And detach from system */ 768 if (!UseCurrentProc) 769 { 770 KeUnstackDetachProcess(&ApcState); 771 } 772 773 return Status; 774 } 775 776 777 NTSTATUS 778 SepRmDereferenceLogonSession( 779 PLUID LogonLuid) 780 { 781 ULONG RefCount; 782 PDEVICE_MAP DeviceMap; 783 PSEP_LOGON_SESSION_REFERENCES CurrentSession; 784 785 DPRINT("SepRmDereferenceLogonSession(%08lx:%08lx)\n", 786 LogonLuid->HighPart, LogonLuid->LowPart); 787 788 /* Acquire the database lock */ 789 KeAcquireGuardedMutex(&SepRmDbLock); 790 791 /* Loop all existing sessions */ 792 for (CurrentSession = SepLogonSessions; 793 CurrentSession != NULL; 794 CurrentSession = CurrentSession->Next) 795 { 796 /* Check if the LUID matches the new one */ 797 if (RtlEqualLuid(&CurrentSession->LogonId, LogonLuid)) 798 { 799 /* Dereference the session */ 800 RefCount = --CurrentSession->ReferenceCount; 801 DPRINT("ReferenceCount: %lu\n", CurrentSession->ReferenceCount); 802 803 /* Release the database lock */ 804 KeReleaseGuardedMutex(&SepRmDbLock); 805 806 /* We're done with the session */ 807 if (RefCount == 0) 808 { 809 /* Get rid of the LUID device map */ 810 DeviceMap = CurrentSession->pDeviceMap; 811 if (DeviceMap != NULL) 812 { 813 CurrentSession->pDeviceMap = NULL; 814 SepCleanupLUIDDeviceMapDirectory(LogonLuid); 815 ObfDereferenceDeviceMap(DeviceMap); 816 } 817 } 818 819 return STATUS_SUCCESS; 820 } 821 } 822 823 /* Release the database lock */ 824 KeReleaseGuardedMutex(&SepRmDbLock); 825 826 return STATUS_NO_SUCH_LOGON_SESSION; 827 } 828 829 830 BOOLEAN 831 NTAPI 832 SepRmCommandServerThreadInit(VOID) 833 { 834 SECURITY_QUALITY_OF_SERVICE SecurityQos; 835 SEP_RM_API_MESSAGE Message; 836 UNICODE_STRING PortName; 837 REMOTE_PORT_VIEW RemotePortView; 838 PORT_VIEW PortView; 839 LARGE_INTEGER SectionSize; 840 HANDLE SectionHandle; 841 HANDLE PortHandle; 842 NTSTATUS Status; 843 BOOLEAN Result; 844 845 SectionHandle = NULL; 846 PortHandle = NULL; 847 848 /* Assume success */ 849 Result = TRUE; 850 851 /* Wait until LSASS is ready */ 852 Status = ZwWaitForSingleObject(SeLsaInitEvent, FALSE, NULL); 853 if (!NT_SUCCESS(Status)) 854 { 855 DPRINT1("Security Rm Init: Waiting for LSA Init Event failed 0x%lx\n", Status); 856 goto Cleanup; 857 } 858 859 /* We don't need this event anymore */ 860 ObCloseHandle(SeLsaInitEvent, KernelMode); 861 862 /* Initialize the connection message */ 863 Message.Header.u1.s1.TotalLength = sizeof(Message); 864 Message.Header.u1.s1.DataLength = 0; 865 866 /* Only LSASS can connect, so handle the connection right now */ 867 Status = ZwListenPort(SeRmCommandPort, &Message.Header); 868 if (!NT_SUCCESS(Status)) 869 { 870 DPRINT1("Security Rm Init: Listen to Command Port failed 0x%lx\n", Status); 871 goto Cleanup; 872 } 873 874 /* Set the Port View structure length */ 875 RemotePortView.Length = sizeof(RemotePortView); 876 877 /* Accept the connection */ 878 Status = ZwAcceptConnectPort(&SepRmCommandMessagePort, 879 NULL, 880 &Message.Header, 881 TRUE, 882 NULL, 883 &RemotePortView); 884 if (!NT_SUCCESS(Status)) 885 { 886 DPRINT1("Security Rm Init: Accept Connect to Command Port failed 0x%lx\n", Status); 887 goto Cleanup; 888 } 889 890 /* Complete the connection */ 891 Status = ZwCompleteConnectPort(SepRmCommandMessagePort); 892 if (!NT_SUCCESS(Status)) 893 { 894 DPRINT1("Security Rm Init: Complete Connect to Command Port failed 0x%lx\n", Status); 895 goto Cleanup; 896 } 897 898 /* Create a section for messages */ 899 SectionSize.QuadPart = PAGE_SIZE; 900 Status = ZwCreateSection(&SectionHandle, 901 SECTION_ALL_ACCESS, 902 NULL, 903 &SectionSize, 904 PAGE_READWRITE, 905 SEC_COMMIT, 906 NULL); 907 if (!NT_SUCCESS(Status)) 908 { 909 DPRINT1("Security Rm Init: Create Memory Section for LSA port failed: %X\n", Status); 910 goto Cleanup; 911 } 912 913 /* Setup the PORT_VIEW structure */ 914 PortView.Length = sizeof(PortView); 915 PortView.SectionHandle = SectionHandle; 916 PortView.SectionOffset = 0; 917 PortView.ViewSize = SectionSize.LowPart; 918 PortView.ViewBase = NULL; 919 PortView.ViewRemoteBase = NULL; 920 921 /* Setup security QOS */ 922 SecurityQos.Length = sizeof(SecurityQos); 923 SecurityQos.ImpersonationLevel = SecurityImpersonation; 924 SecurityQos.ContextTrackingMode = SECURITY_DYNAMIC_TRACKING; 925 SecurityQos.EffectiveOnly = TRUE; 926 927 /* Connect to LSASS */ 928 RtlInitUnicodeString(&PortName, L"\\SeLsaCommandPort"); 929 Status = ZwConnectPort(&PortHandle, 930 &PortName, 931 &SecurityQos, 932 &PortView, 933 NULL, 934 0, 935 0, 936 0); 937 if (!NT_SUCCESS(Status)) 938 { 939 DPRINT1("Security Rm Init: Connect to LSA Port failed 0x%lx\n", Status); 940 goto Cleanup; 941 } 942 943 /* Remember section base and view offset */ 944 SepCommandPortViewBase = PortView.ViewBase; 945 SepCommandPortViewRemoteBase = PortView.ViewRemoteBase; 946 SepCommandPortViewBaseOffset = (ULONG_PTR)SepCommandPortViewRemoteBase - 947 (ULONG_PTR)SepCommandPortViewBase; 948 949 DPRINT("SepRmCommandServerThreadInit: done\n"); 950 951 Cleanup: 952 /* Check for failure */ 953 if (!NT_SUCCESS(Status)) 954 { 955 if (PortHandle != NULL) 956 { 957 ObCloseHandle(PortHandle, KernelMode); 958 } 959 960 Result = FALSE; 961 } 962 963 /* Did we create a section? */ 964 if (SectionHandle != NULL) 965 { 966 ObCloseHandle(SectionHandle, KernelMode); 967 } 968 969 return Result; 970 } 971 972 VOID 973 NTAPI 974 SepRmCommandServerThread( 975 PVOID StartContext) 976 { 977 SEP_RM_API_MESSAGE Message; 978 PPORT_MESSAGE ReplyMessage; 979 HANDLE DummyPortHandle; 980 NTSTATUS Status; 981 982 /* Initialize the server thread */ 983 if (!SepRmCommandServerThreadInit()) 984 { 985 DPRINT1("Security: Terminating Rm Command Server Thread\n"); 986 return; 987 } 988 989 /* No reply yet */ 990 ReplyMessage = NULL; 991 992 /* Start looping */ 993 while (TRUE) 994 { 995 /* Wait for a message */ 996 Status = ZwReplyWaitReceivePort(SepRmCommandMessagePort, 997 NULL, 998 ReplyMessage, 999 &Message.Header); 1000 if (!NT_SUCCESS(Status)) 1001 { 1002 DPRINT1("Failed to get message: 0x%lx", Status); 1003 ReplyMessage = NULL; 1004 continue; 1005 } 1006 1007 /* Check if this is a connection request */ 1008 if (Message.Header.u2.s2.Type == LPC_CONNECTION_REQUEST) 1009 { 1010 /* Reject connection request */ 1011 ZwAcceptConnectPort(&DummyPortHandle, 1012 NULL, 1013 &Message.Header, 1014 FALSE, 1015 NULL, 1016 NULL); 1017 1018 /* Start over */ 1019 ReplyMessage = NULL; 1020 continue; 1021 } 1022 1023 /* Check if the port died */ 1024 if ((Message.Header.u2.s2.Type == LPC_PORT_CLOSED) || 1025 (Message.Header.u2.s2.Type == LPC_CLIENT_DIED)) 1026 { 1027 /* LSASS is dead, so let's quit as well */ 1028 break; 1029 } 1030 1031 /* Check if this is an actual request */ 1032 if (Message.Header.u2.s2.Type != LPC_REQUEST) 1033 { 1034 DPRINT1("SepRmCommandServerThread: unexpected message type: 0x%lx\n", 1035 Message.Header.u2.s2.Type); 1036 1037 /* Restart without replying */ 1038 ReplyMessage = NULL; 1039 continue; 1040 } 1041 1042 ReplyMessage = &Message.Header; 1043 1044 switch (Message.ApiNumber) 1045 { 1046 case RmAuditSetCommand: 1047 Status = SepRmSetAuditEvent(&Message); 1048 break; 1049 1050 case RmCreateLogonSession: 1051 Status = SepRmCreateLogonSession(&Message.u.LogonLuid); 1052 break; 1053 1054 case RmDeleteLogonSession: 1055 Status = SepRmDeleteLogonSession(&Message.u.LogonLuid); 1056 break; 1057 1058 default: 1059 DPRINT1("SepRmDispatchRequest: invalid API number: 0x%lx\n", 1060 Message.ApiNumber); 1061 ReplyMessage = NULL; 1062 } 1063 1064 Message.u.ResultStatus = Status; 1065 } 1066 1067 /* Close the port handles */ 1068 ObCloseHandle(SepRmCommandMessagePort, KernelMode); 1069 ObCloseHandle(SeRmCommandPort, KernelMode); 1070 } 1071 1072 1073 /* PUBLIC FUNCTIONS ***********************************************************/ 1074 1075 /* 1076 * @unimplemented 1077 */ 1078 NTSTATUS 1079 NTAPI 1080 SeGetLogonIdDeviceMap( 1081 IN PLUID LogonId, 1082 OUT PDEVICE_MAP * DeviceMap 1083 ) 1084 { 1085 NTSTATUS Status; 1086 WCHAR Buffer[63]; 1087 PDEVICE_MAP LocalMap; 1088 HANDLE DirectoryHandle, LinkHandle; 1089 OBJECT_ATTRIBUTES ObjectAttributes; 1090 PSEP_LOGON_SESSION_REFERENCES CurrentSession; 1091 UNICODE_STRING DirectoryName, LinkName, TargetName; 1092 1093 PAGED_CODE(); 1094 1095 if (LogonId == NULL || 1096 DeviceMap == NULL) 1097 { 1098 return STATUS_INVALID_PARAMETER; 1099 } 1100 1101 /* Acquire the database lock */ 1102 KeAcquireGuardedMutex(&SepRmDbLock); 1103 1104 /* Loop all existing sessions */ 1105 for (CurrentSession = SepLogonSessions; 1106 CurrentSession != NULL; 1107 CurrentSession = CurrentSession->Next) 1108 { 1109 /* Check if the LUID matches the provided one */ 1110 if (RtlEqualLuid(&CurrentSession->LogonId, LogonId)) 1111 { 1112 break; 1113 } 1114 } 1115 1116 /* No session found, fail */ 1117 if (CurrentSession == NULL) 1118 { 1119 /* Release the database lock */ 1120 KeReleaseGuardedMutex(&SepRmDbLock); 1121 1122 return STATUS_NO_SUCH_LOGON_SESSION; 1123 } 1124 1125 /* The found session has a device map, return it! */ 1126 if (CurrentSession->pDeviceMap != NULL) 1127 { 1128 *DeviceMap = CurrentSession->pDeviceMap; 1129 1130 /* Release the database lock */ 1131 KeReleaseGuardedMutex(&SepRmDbLock); 1132 1133 return STATUS_SUCCESS; 1134 } 1135 1136 /* At that point, we'll setup a new device map for the session */ 1137 LocalMap = NULL; 1138 1139 /* Reference the session so that it doesn't go away */ 1140 CurrentSession->ReferenceCount += 1; 1141 1142 /* Release the database lock */ 1143 KeReleaseGuardedMutex(&SepRmDbLock); 1144 1145 /* Create our object directory given the LUID */ 1146 _snwprintf(Buffer, 1147 sizeof(Buffer) / sizeof(WCHAR), 1148 L"\\Sessions\\0\\DosDevices\\%08x-%08x", 1149 LogonId->HighPart, 1150 LogonId->LowPart); 1151 RtlInitUnicodeString(&DirectoryName, Buffer); 1152 1153 InitializeObjectAttributes(&ObjectAttributes, 1154 &DirectoryName, 1155 OBJ_KERNEL_HANDLE | OBJ_OPENIF | OBJ_CASE_INSENSITIVE, 1156 NULL, 1157 NULL); 1158 Status = ZwCreateDirectoryObject(&DirectoryHandle, 1159 DIRECTORY_ALL_ACCESS, 1160 &ObjectAttributes); 1161 if (NT_SUCCESS(Status)) 1162 { 1163 /* Create the associated device map */ 1164 Status = ObSetDirectoryDeviceMap(&LocalMap, DirectoryHandle); 1165 if (NT_SUCCESS(Status)) 1166 { 1167 /* Make Global point to \Global?? in the directory */ 1168 RtlInitUnicodeString(&LinkName, L"Global"); 1169 RtlInitUnicodeString(&TargetName, L"\\Global??"); 1170 1171 InitializeObjectAttributes(&ObjectAttributes, 1172 &LinkName, 1173 OBJ_KERNEL_HANDLE | OBJ_OPENIF | OBJ_CASE_INSENSITIVE | OBJ_PERMANENT, 1174 DirectoryHandle, 1175 NULL); 1176 Status = ZwCreateSymbolicLinkObject(&LinkHandle, 1177 SYMBOLIC_LINK_ALL_ACCESS, 1178 &ObjectAttributes, 1179 &TargetName); 1180 if (!NT_SUCCESS(Status)) 1181 { 1182 ObfDereferenceDeviceMap(LocalMap); 1183 } 1184 else 1185 { 1186 ZwClose(LinkHandle); 1187 } 1188 } 1189 1190 ZwClose(DirectoryHandle); 1191 } 1192 1193 /* Acquire the database lock */ 1194 KeAcquireGuardedMutex(&SepRmDbLock); 1195 1196 /* If we succeed... */ 1197 if (NT_SUCCESS(Status)) 1198 { 1199 /* The session now has a device map? We raced with someone else */ 1200 if (CurrentSession->pDeviceMap != NULL) 1201 { 1202 /* Give up on our new device map */ 1203 ObfDereferenceDeviceMap(LocalMap); 1204 } 1205 /* Otherwise use our newly allocated device map */ 1206 else 1207 { 1208 CurrentSession->pDeviceMap = LocalMap; 1209 } 1210 1211 /* Return the device map */ 1212 *DeviceMap = CurrentSession->pDeviceMap; 1213 } 1214 /* Zero output */ 1215 else 1216 { 1217 *DeviceMap = NULL; 1218 } 1219 1220 /* Release the database lock */ 1221 KeReleaseGuardedMutex(&SepRmDbLock); 1222 1223 /* We're done with the session */ 1224 SepRmDereferenceLogonSession(&CurrentSession->LogonId); 1225 1226 return Status; 1227 } 1228 1229 /* 1230 * @unimplemented 1231 */ 1232 NTSTATUS 1233 NTAPI 1234 SeMarkLogonSessionForTerminationNotification( 1235 IN PLUID LogonId) 1236 { 1237 UNIMPLEMENTED; 1238 return STATUS_NOT_IMPLEMENTED; 1239 } 1240 1241 1242 /* 1243 * @implemented 1244 */ 1245 NTSTATUS 1246 NTAPI 1247 SeRegisterLogonSessionTerminatedRoutine( 1248 IN PSE_LOGON_SESSION_TERMINATED_ROUTINE CallbackRoutine) 1249 { 1250 PSEP_LOGON_SESSION_TERMINATED_NOTIFICATION Notification; 1251 PAGED_CODE(); 1252 1253 /* Fail, if we don not have a callback routine */ 1254 if (CallbackRoutine == NULL) 1255 return STATUS_INVALID_PARAMETER; 1256 1257 /* Allocate a new notification item */ 1258 Notification = ExAllocatePoolWithTag(PagedPool, 1259 sizeof(SEP_LOGON_SESSION_TERMINATED_NOTIFICATION), 1260 SEP_LOGON_NOTIFICATION_TAG); 1261 if (Notification == NULL) 1262 return STATUS_INSUFFICIENT_RESOURCES; 1263 1264 /* Acquire the database lock */ 1265 KeAcquireGuardedMutex(&SepRmDbLock); 1266 1267 /* Set the callback routine */ 1268 Notification->CallbackRoutine = CallbackRoutine; 1269 1270 /* Insert the new notification item into the list */ 1271 Notification->Next = SepLogonNotifications; 1272 SepLogonNotifications = Notification; 1273 1274 /* Release the database lock */ 1275 KeReleaseGuardedMutex(&SepRmDbLock); 1276 1277 return STATUS_SUCCESS; 1278 } 1279 1280 1281 /* 1282 * @implemented 1283 */ 1284 NTSTATUS 1285 NTAPI 1286 SeUnregisterLogonSessionTerminatedRoutine( 1287 IN PSE_LOGON_SESSION_TERMINATED_ROUTINE CallbackRoutine) 1288 { 1289 PSEP_LOGON_SESSION_TERMINATED_NOTIFICATION Current, Previous = NULL; 1290 NTSTATUS Status; 1291 PAGED_CODE(); 1292 1293 /* Fail, if we don not have a callback routine */ 1294 if (CallbackRoutine == NULL) 1295 return STATUS_INVALID_PARAMETER; 1296 1297 /* Acquire the database lock */ 1298 KeAcquireGuardedMutex(&SepRmDbLock); 1299 1300 /* Loop all registered notification items */ 1301 for (Current = SepLogonNotifications; 1302 Current != NULL; 1303 Current = Current->Next) 1304 { 1305 /* Check if the callback routine matches the provided one */ 1306 if (Current->CallbackRoutine == CallbackRoutine) 1307 break; 1308 1309 Previous = Current; 1310 } 1311 1312 if (Current == NULL) 1313 { 1314 Status = STATUS_NOT_FOUND; 1315 } 1316 else 1317 { 1318 /* Remove the current notification item from the list */ 1319 if (Previous == NULL) 1320 SepLogonNotifications = Current->Next; 1321 else 1322 Previous->Next = Current->Next; 1323 1324 /* Free the current notification item */ 1325 ExFreePoolWithTag(Current, 1326 SEP_LOGON_NOTIFICATION_TAG); 1327 1328 Status = STATUS_SUCCESS; 1329 } 1330 1331 /* Release the database lock */ 1332 KeReleaseGuardedMutex(&SepRmDbLock); 1333 1334 return Status; 1335 } 1336