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