/* * COPYRIGHT: See COPYING in the top level directory * PROJECT: ReactOS kernel * FILE: ntoskrnl/se/srm.c * PURPOSE: Security Reference Monitor Server * * PROGRAMMERS: Timo Kreuzer (timo.kreuzer@reactos.org) */ /* INCLUDES *******************************************************************/ #include #define NDEBUG #include extern LUID SeSystemAuthenticationId; extern LUID SeAnonymousAuthenticationId; /* PRIVATE DEFINITIONS ********************************************************/ #define SEP_LOGON_SESSION_TAG 'sLeS' typedef struct _SEP_LOGON_SESSION_REFERENCES { struct _SEP_LOGON_SESSION_REFERENCES *Next; LUID LogonId; ULONG ReferenceCount; ULONG Flags; PDEVICE_MAP pDeviceMap; LIST_ENTRY TokenList; } SEP_LOGON_SESSION_REFERENCES, *PSEP_LOGON_SESSION_REFERENCES; VOID NTAPI SepRmCommandServerThread( PVOID StartContext); static NTSTATUS SepRmCreateLogonSession( PLUID LogonLuid); /* GLOBALS ********************************************************************/ HANDLE SeRmCommandPort; HANDLE SeLsaInitEvent; PVOID SepCommandPortViewBase; PVOID SepCommandPortViewRemoteBase; ULONG_PTR SepCommandPortViewBaseOffset; static HANDLE SepRmCommandMessagePort; BOOLEAN SepAdtAuditingEnabled; ULONG SepAdtMinListLength = 0x2000; ULONG SepAdtMaxListLength = 0x3000; #define POLICY_AUDIT_EVENT_TYPE_COUNT 9 // (AuditCategoryAccountLogon - AuditCategorySystem + 1) UCHAR SeAuditingState[POLICY_AUDIT_EVENT_TYPE_COUNT]; KGUARDED_MUTEX SepRmDbLock; PSEP_LOGON_SESSION_REFERENCES SepLogonSessions; /* PRIVATE FUNCTIONS **********************************************************/ NTSTATUS NTAPI SepRegQueryHelper( PCWSTR KeyName, PCWSTR ValueName, ULONG ValueType, ULONG DataLength, PVOID ValueData) { UNICODE_STRING ValueNameString; UNICODE_STRING KeyNameString; ULONG ResultLength; OBJECT_ATTRIBUTES ObjectAttributes; HANDLE KeyHandle = NULL; struct { KEY_VALUE_PARTIAL_INFORMATION Partial; UCHAR Buffer[64]; } KeyValueInformation; NTSTATUS Status, CloseStatus; PAGED_CODE(); RtlInitUnicodeString(&KeyNameString, KeyName); InitializeObjectAttributes(&ObjectAttributes, &KeyNameString, OBJ_KERNEL_HANDLE | OBJ_CASE_INSENSITIVE, NULL, NULL); Status = ZwOpenKey(&KeyHandle, KEY_QUERY_VALUE, &ObjectAttributes); if (!NT_SUCCESS(Status)) { return Status; } RtlInitUnicodeString(&ValueNameString, ValueName); Status = ZwQueryValueKey(KeyHandle, &ValueNameString, KeyValuePartialInformation, &KeyValueInformation.Partial, sizeof(KeyValueInformation), &ResultLength); if (!NT_SUCCESS(Status)) { goto Cleanup; } if ((KeyValueInformation.Partial.Type != ValueType) || (KeyValueInformation.Partial.DataLength != DataLength)) { Status = STATUS_OBJECT_TYPE_MISMATCH; goto Cleanup; } if (ValueType == REG_BINARY) { RtlCopyMemory(ValueData, KeyValueInformation.Partial.Data, DataLength); } else if (ValueType == REG_DWORD) { *(PULONG)ValueData = *(PULONG)KeyValueInformation.Partial.Data; } else { Status = STATUS_INVALID_PARAMETER; } Cleanup: CloseStatus = ZwClose(KeyHandle); ASSERT(NT_SUCCESS( CloseStatus )); return Status; } BOOLEAN NTAPI SeRmInitPhase0(VOID) { NTSTATUS Status; /* Initialize the database lock */ KeInitializeGuardedMutex(&SepRmDbLock); /* Create the system logon session */ Status = SepRmCreateLogonSession(&SeSystemAuthenticationId); if (!NT_VERIFY(NT_SUCCESS(Status))) { return FALSE; } /* Create the anonymous logon session */ Status = SepRmCreateLogonSession(&SeAnonymousAuthenticationId); if (!NT_VERIFY(NT_SUCCESS(Status))) { return FALSE; } return TRUE; } BOOLEAN NTAPI SeRmInitPhase1(VOID) { UNICODE_STRING Name; OBJECT_ATTRIBUTES ObjectAttributes; HANDLE ThreadHandle; NTSTATUS Status; /* Create the SeRm command port */ RtlInitUnicodeString(&Name, L"\\SeRmCommandPort"); InitializeObjectAttributes(&ObjectAttributes, &Name, 0, NULL, NULL); Status = ZwCreatePort(&SeRmCommandPort, &ObjectAttributes, sizeof(ULONG), PORT_MAXIMUM_MESSAGE_LENGTH, 2 * PAGE_SIZE); if (!NT_SUCCESS(Status)) { DPRINT1("Security: Rm Create Command Port failed 0x%lx\n", Status); return FALSE; } /* Create SeLsaInitEvent */ RtlInitUnicodeString(&Name, L"\\SeLsaInitEvent"); InitializeObjectAttributes(&ObjectAttributes, &Name, 0, NULL, NULL); Status = ZwCreateEvent(&SeLsaInitEvent, GENERIC_WRITE, &ObjectAttributes, NotificationEvent, FALSE); if (!NT_VERIFY((NT_SUCCESS(Status)))) { DPRINT1("Security: LSA init event creation failed.0x%xl\n", Status); return FALSE; } /* Create the SeRm server thread */ Status = PsCreateSystemThread(&ThreadHandle, THREAD_ALL_ACCESS, NULL, NULL, NULL, SepRmCommandServerThread, NULL); if (!NT_SUCCESS(Status)) { DPRINT1("Security: Rm Server Thread creation failed 0x%lx\n", Status); return FALSE; } ObCloseHandle(ThreadHandle, KernelMode); return TRUE; } static VOID SepAdtInitializeBounds(VOID) { struct { ULONG MaxLength; ULONG MinLength; } ListBounds; NTSTATUS Status; PAGED_CODE(); Status = SepRegQueryHelper(L"\\Registry\\Machine\\System\\CurrentControlSet\\Control\\Lsa", L"Bounds", REG_BINARY, sizeof(ListBounds), &ListBounds); if (!NT_SUCCESS(Status)) { /* No registry values, so keep hardcoded defaults */ return; } /* Check if the bounds are valid */ if ((ListBounds.MaxLength < ListBounds.MinLength) || (ListBounds.MinLength < 16) || (ListBounds.MaxLength - ListBounds.MinLength < 16)) { DPRINT1("ListBounds are invalid: %u, %u\n", ListBounds.MinLength, ListBounds.MaxLength); return; } /* Set the new bounds globally */ SepAdtMinListLength = ListBounds.MinLength; SepAdtMaxListLength = ListBounds.MaxLength; } static NTSTATUS SepRmSetAuditEvent( PSEP_RM_API_MESSAGE Message) { ULONG i; PAGED_CODE(); /* First re-initialize the bounds from the registry */ SepAdtInitializeBounds(); /* Make sure we have the right message and clear */ ASSERT(Message->ApiNumber == RmAuditSetCommand); Message->ApiNumber = 0; /* Store the enable flag in the global variable */ SepAdtAuditingEnabled = Message->u.SetAuditEvent.Enabled; /* Loop all audit event types */ for (i = 0; i < POLICY_AUDIT_EVENT_TYPE_COUNT; i++) { /* Save the provided flags in the global array */ SeAuditingState[i] = (UCHAR)Message->u.SetAuditEvent.Flags[i]; } return STATUS_SUCCESS; } static NTSTATUS SepRmCreateLogonSession( PLUID LogonLuid) { PSEP_LOGON_SESSION_REFERENCES CurrentSession, NewSession; NTSTATUS Status; PAGED_CODE(); DPRINT("SepRmCreateLogonSession(%08lx:%08lx)\n", LogonLuid->HighPart, LogonLuid->LowPart); /* Allocate a new session structure */ NewSession = ExAllocatePoolWithTag(PagedPool, sizeof(SEP_LOGON_SESSION_REFERENCES), SEP_LOGON_SESSION_TAG); if (NewSession == NULL) { return STATUS_INSUFFICIENT_RESOURCES; } /* Initialize it */ NewSession->LogonId = *LogonLuid; NewSession->ReferenceCount = 0; NewSession->Flags = 0; NewSession->pDeviceMap = NULL; InitializeListHead(&NewSession->TokenList); /* Acquire the database lock */ KeAcquireGuardedMutex(&SepRmDbLock); /* Loop all existing sessions */ for (CurrentSession = SepLogonSessions; CurrentSession != NULL; CurrentSession = CurrentSession->Next) { /* Check if the LUID matches the new one */ if (RtlEqualLuid(&CurrentSession->LogonId, LogonLuid)) { Status = STATUS_LOGON_SESSION_EXISTS; goto Leave; } } /* Insert the new session */ NewSession->Next = SepLogonSessions; SepLogonSessions = NewSession; Status = STATUS_SUCCESS; Leave: /* Release the database lock */ KeReleaseGuardedMutex(&SepRmDbLock); if (!NT_SUCCESS(Status)) { ExFreePoolWithTag(NewSession, SEP_LOGON_SESSION_TAG); } return Status; } static NTSTATUS SepRmDeleteLogonSession( PLUID LogonLuid) { DPRINT("SepRmDeleteLogonSession(%08lx:%08lx)\n", LogonLuid->HighPart, LogonLuid->LowPart); UNIMPLEMENTED; NT_ASSERT(FALSE); return STATUS_NOT_IMPLEMENTED; } NTSTATUS SepRmReferenceLogonSession( PLUID LogonLuid) { PSEP_LOGON_SESSION_REFERENCES CurrentSession; PAGED_CODE(); DPRINT("SepRmReferenceLogonSession(%08lx:%08lx)\n", LogonLuid->HighPart, LogonLuid->LowPart); /* Acquire the database lock */ KeAcquireGuardedMutex(&SepRmDbLock); /* Loop all existing sessions */ for (CurrentSession = SepLogonSessions; CurrentSession != NULL; CurrentSession = CurrentSession->Next) { /* Check if the LUID matches the new one */ if (RtlEqualLuid(&CurrentSession->LogonId, LogonLuid)) { /* Reference the session */ CurrentSession->ReferenceCount += 1; DPRINT("ReferenceCount: %lu\n", CurrentSession->ReferenceCount); /* Release the database lock */ KeReleaseGuardedMutex(&SepRmDbLock); return STATUS_SUCCESS; } } /* Release the database lock */ KeReleaseGuardedMutex(&SepRmDbLock); return STATUS_NO_SUCH_LOGON_SESSION; } NTSTATUS SepRmDereferenceLogonSession( PLUID LogonLuid) { PSEP_LOGON_SESSION_REFERENCES CurrentSession; DPRINT("SepRmDereferenceLogonSession(%08lx:%08lx)\n", LogonLuid->HighPart, LogonLuid->LowPart); /* Acquire the database lock */ KeAcquireGuardedMutex(&SepRmDbLock); /* Loop all existing sessions */ for (CurrentSession = SepLogonSessions; CurrentSession != NULL; CurrentSession = CurrentSession->Next) { /* Check if the LUID matches the new one */ if (RtlEqualLuid(&CurrentSession->LogonId, LogonLuid)) { /* Dereference the session */ CurrentSession->ReferenceCount -= 1; DPRINT("ReferenceCount: %lu\n", CurrentSession->ReferenceCount); /* Release the database lock */ KeReleaseGuardedMutex(&SepRmDbLock); return STATUS_SUCCESS; } } /* Release the database lock */ KeReleaseGuardedMutex(&SepRmDbLock); return STATUS_NO_SUCH_LOGON_SESSION; } BOOLEAN NTAPI SepRmCommandServerThreadInit(VOID) { SECURITY_QUALITY_OF_SERVICE SecurityQos; SEP_RM_API_MESSAGE Message; UNICODE_STRING PortName; REMOTE_PORT_VIEW RemotePortView; PORT_VIEW PortView; LARGE_INTEGER SectionSize; HANDLE SectionHandle; HANDLE PortHandle; NTSTATUS Status; BOOLEAN Result; SectionHandle = NULL; PortHandle = NULL; /* Assume success */ Result = TRUE; /* Wait until LSASS is ready */ Status = ZwWaitForSingleObject(SeLsaInitEvent, FALSE, NULL); if (!NT_SUCCESS(Status)) { DPRINT1("Security Rm Init: Waiting for LSA Init Event failed 0x%lx\n", Status); goto Cleanup; } /* We don't need this event anymore */ ObCloseHandle(SeLsaInitEvent, KernelMode); /* Initialize the connection message */ Message.Header.u1.s1.TotalLength = sizeof(Message); Message.Header.u1.s1.DataLength = 0; /* Only LSASS can connect, so handle the connection right now */ Status = ZwListenPort(SeRmCommandPort, &Message.Header); if (!NT_SUCCESS(Status)) { DPRINT1("Security Rm Init: Listen to Command Port failed 0x%lx\n", Status); goto Cleanup; } /* Set the Port View structure length */ RemotePortView.Length = sizeof(RemotePortView); /* Accept the connection */ Status = ZwAcceptConnectPort(&SepRmCommandMessagePort, NULL, &Message.Header, TRUE, NULL, &RemotePortView); if (!NT_SUCCESS(Status)) { DPRINT1("Security Rm Init: Accept Connect to Command Port failed 0x%lx\n", Status); goto Cleanup; } /* Complete the connection */ Status = ZwCompleteConnectPort(SepRmCommandMessagePort); if (!NT_SUCCESS(Status)) { DPRINT1("Security Rm Init: Complete Connect to Command Port failed 0x%lx\n", Status); goto Cleanup; } /* Create a section for messages */ SectionSize.QuadPart = PAGE_SIZE; Status = ZwCreateSection(&SectionHandle, SECTION_ALL_ACCESS, NULL, &SectionSize, PAGE_READWRITE, SEC_COMMIT, NULL); if (!NT_SUCCESS(Status)) { DPRINT1("Security Rm Init: Create Memory Section for LSA port failed: %X\n", Status); goto Cleanup; } /* Setup the PORT_VIEW structure */ PortView.Length = sizeof(PortView); PortView.SectionHandle = SectionHandle; PortView.SectionOffset = 0; PortView.ViewSize = SectionSize.LowPart; PortView.ViewBase = NULL; PortView.ViewRemoteBase = NULL; /* Setup security QOS */ SecurityQos.Length = sizeof(SecurityQos); SecurityQos.ImpersonationLevel = SecurityImpersonation; SecurityQos.ContextTrackingMode = SECURITY_DYNAMIC_TRACKING; SecurityQos.EffectiveOnly = TRUE; /* Connect to LSASS */ RtlInitUnicodeString(&PortName, L"\\SeLsaCommandPort"); Status = ZwConnectPort(&PortHandle, &PortName, &SecurityQos, &PortView, NULL, 0, 0, 0); if (!NT_SUCCESS(Status)) { DPRINT1("Security Rm Init: Connect to LSA Port failed 0x%lx\n", Status); goto Cleanup; } /* Remember section base and view offset */ SepCommandPortViewBase = PortView.ViewBase; SepCommandPortViewRemoteBase = PortView.ViewRemoteBase; SepCommandPortViewBaseOffset = (ULONG_PTR)SepCommandPortViewRemoteBase - (ULONG_PTR)SepCommandPortViewBase; DPRINT("SepRmCommandServerThreadInit: done\n"); Cleanup: /* Check for failure */ if (!NT_SUCCESS(Status)) { if (PortHandle != NULL) { ObCloseHandle(PortHandle, KernelMode); } Result = FALSE; } /* Did we create a section? */ if (SectionHandle != NULL) { ObCloseHandle(SectionHandle, KernelMode); } return Result; } VOID NTAPI SepRmCommandServerThread( PVOID StartContext) { SEP_RM_API_MESSAGE Message; PPORT_MESSAGE ReplyMessage; HANDLE DummyPortHandle; NTSTATUS Status; /* Initialize the server thread */ if (!SepRmCommandServerThreadInit()) { DPRINT1("Security: Terminating Rm Command Server Thread\n"); return; } /* No reply yet */ ReplyMessage = NULL; /* Start looping */ while (TRUE) { /* Wait for a message */ Status = ZwReplyWaitReceivePort(SepRmCommandMessagePort, NULL, ReplyMessage, &Message.Header); if (!NT_SUCCESS(Status)) { DPRINT1("Failed to get message: 0x%lx", Status); ReplyMessage = NULL; continue; } /* Check if this is a connection request */ if (Message.Header.u2.s2.Type == LPC_CONNECTION_REQUEST) { /* Reject connection request */ ZwAcceptConnectPort(&DummyPortHandle, NULL, &Message.Header, FALSE, NULL, NULL); /* Start over */ ReplyMessage = NULL; continue; } /* Check if the port died */ if ((Message.Header.u2.s2.Type == LPC_PORT_CLOSED) || (Message.Header.u2.s2.Type == LPC_CLIENT_DIED)) { /* LSASS is dead, so let's quit as well */ break; } /* Check if this is an actual request */ if (Message.Header.u2.s2.Type != LPC_REQUEST) { DPRINT1("SepRmCommandServerThread: unexpected message type: 0x%lx\n", Message.Header.u2.s2.Type); /* Restart without replying */ ReplyMessage = NULL; continue; } ReplyMessage = &Message.Header; switch (Message.ApiNumber) { case RmAuditSetCommand: Status = SepRmSetAuditEvent(&Message); break; case RmCreateLogonSession: Status = SepRmCreateLogonSession(&Message.u.LogonLuid); break; case RmDeleteLogonSession: Status = SepRmDeleteLogonSession(&Message.u.LogonLuid); break; default: DPRINT1("SepRmDispatchRequest: invalid API number: 0x%lx\n", Message.ApiNumber); ReplyMessage = NULL; } Message.u.ResultStatus = Status; } /* Close the port handles */ ObCloseHandle(SepRmCommandMessagePort, KernelMode); ObCloseHandle(SeRmCommandPort, KernelMode); } /* PUBLIC FUNCTIONS ***********************************************************/ /* * @unimplemented */ NTSTATUS NTAPI SeMarkLogonSessionForTerminationNotification( IN PLUID LogonId) { UNIMPLEMENTED; return STATUS_NOT_IMPLEMENTED; } /* * @unimplemented */ NTSTATUS NTAPI SeRegisterLogonSessionTerminatedRoutine( IN PSE_LOGON_SESSION_TERMINATED_ROUTINE CallbackRoutine) { UNIMPLEMENTED; return STATUS_NOT_IMPLEMENTED; } /* * @unimplemented */ NTSTATUS NTAPI SeUnregisterLogonSessionTerminatedRoutine( IN PSE_LOGON_SESSION_TERMINATED_ROUTINE CallbackRoutine) { UNIMPLEMENTED; return STATUS_NOT_IMPLEMENTED; }