mirror of
https://github.com/reactos/reactos.git
synced 2024-12-28 18:15:11 +00:00
521 lines
18 KiB
C
521 lines
18 KiB
C
/*
|
|
* PROJECT: ReactOS Windows-Compatible Session Manager
|
|
* LICENSE: BSD 2-Clause License
|
|
* FILE: base/system/smss/smloop.c
|
|
* PURPOSE: Main SMSS Code
|
|
* PROGRAMMERS: Alex Ionescu
|
|
*/
|
|
|
|
/* INCLUDES *******************************************************************/
|
|
|
|
#include "smss.h"
|
|
|
|
#define NDEBUG
|
|
#include <debug.h>
|
|
|
|
/* GLOBALS ********************************************************************/
|
|
|
|
typedef struct _SMP_CLIENT_CONTEXT
|
|
{
|
|
PSMP_SUBSYSTEM Subsystem;
|
|
HANDLE ProcessHandle;
|
|
HANDLE PortHandle;
|
|
PVOID Reserved;
|
|
} SMP_CLIENT_CONTEXT, *PSMP_CLIENT_CONTEXT;
|
|
|
|
typedef
|
|
NTSTATUS
|
|
(NTAPI *PSM_API_HANDLER)(
|
|
_In_ PSM_API_MSG SmApiMsg,
|
|
_In_ PSMP_CLIENT_CONTEXT ClientContext,
|
|
_In_ HANDLE SmApiPort);
|
|
|
|
volatile LONG SmTotalApiThreads;
|
|
HANDLE SmUniqueProcessId;
|
|
|
|
/* API HANDLERS ***************************************************************/
|
|
|
|
NTSTATUS
|
|
NTAPI
|
|
SmpCreateForeignSession(IN PSM_API_MSG SmApiMsg,
|
|
IN PSMP_CLIENT_CONTEXT ClientContext,
|
|
IN HANDLE SmApiPort)
|
|
{
|
|
DPRINT1("%s is not yet implemented\n", __FUNCTION__);
|
|
return STATUS_NOT_IMPLEMENTED;
|
|
}
|
|
|
|
NTSTATUS
|
|
NTAPI
|
|
SmpSessionComplete(IN PSM_API_MSG SmApiMsg,
|
|
IN PSMP_CLIENT_CONTEXT ClientContext,
|
|
IN HANDLE SmApiPort)
|
|
{
|
|
DPRINT1("%s is not yet implemented\n", __FUNCTION__);
|
|
return STATUS_NOT_IMPLEMENTED;
|
|
}
|
|
|
|
NTSTATUS
|
|
NTAPI
|
|
SmpTerminateForeignSession(IN PSM_API_MSG SmApiMsg,
|
|
IN PSMP_CLIENT_CONTEXT ClientContext,
|
|
IN HANDLE SmApiPort)
|
|
{
|
|
DPRINT1("%s is not yet implemented\n", __FUNCTION__);
|
|
return STATUS_NOT_IMPLEMENTED;
|
|
}
|
|
|
|
NTSTATUS
|
|
NTAPI
|
|
SmpExecPgm(IN PSM_API_MSG SmApiMsg,
|
|
IN PSMP_CLIENT_CONTEXT ClientContext,
|
|
IN HANDLE SmApiPort)
|
|
{
|
|
HANDLE ProcessHandle;
|
|
NTSTATUS Status;
|
|
PSM_EXEC_PGM_MSG SmExecPgm;
|
|
RTL_USER_PROCESS_INFORMATION ProcessInformation;
|
|
OBJECT_ATTRIBUTES ObjectAttributes;
|
|
|
|
/* Open the client process */
|
|
InitializeObjectAttributes(&ObjectAttributes, NULL, 0, NULL, NULL);
|
|
Status = NtOpenProcess(&ProcessHandle,
|
|
PROCESS_DUP_HANDLE,
|
|
&ObjectAttributes,
|
|
&SmApiMsg->h.ClientId);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
/* Fail */
|
|
DPRINT1("SmExecPgm: NtOpenProcess Failed %lx\n", Status);
|
|
return Status;
|
|
}
|
|
|
|
/* Copy the process information out of the message */
|
|
SmExecPgm = &SmApiMsg->u.ExecPgm;
|
|
ProcessInformation = SmExecPgm->ProcessInformation;
|
|
|
|
/* Duplicate the process handle */
|
|
Status = NtDuplicateObject(ProcessHandle,
|
|
SmExecPgm->ProcessInformation.ProcessHandle,
|
|
NtCurrentProcess(),
|
|
&ProcessInformation.ProcessHandle,
|
|
PROCESS_ALL_ACCESS,
|
|
0,
|
|
0);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
/* Close the handle and fail */
|
|
NtClose(ProcessHandle);
|
|
DPRINT1("SmExecPgm: NtDuplicateObject (Process) Failed %lx\n", Status);
|
|
return Status;
|
|
}
|
|
|
|
/* Duplicate the thread handle */
|
|
Status = NtDuplicateObject(ProcessHandle,
|
|
SmExecPgm->ProcessInformation.ThreadHandle,
|
|
NtCurrentProcess(),
|
|
&ProcessInformation.ThreadHandle,
|
|
THREAD_ALL_ACCESS,
|
|
0,
|
|
0);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
/* Close both handles and fail */
|
|
NtClose(ProcessInformation.ProcessHandle);
|
|
NtClose(ProcessHandle);
|
|
DPRINT1("SmExecPgm: NtDuplicateObject (Thread) Failed %lx\n", Status);
|
|
return Status;
|
|
}
|
|
|
|
/* Close the process handle and call the internal client API */
|
|
NtClose(ProcessHandle);
|
|
return SmpSbCreateSession(NULL,
|
|
NULL,
|
|
&ProcessInformation,
|
|
0,
|
|
SmExecPgm->DebugFlag ? &SmApiMsg->h.ClientId : NULL);
|
|
}
|
|
|
|
NTSTATUS
|
|
NTAPI
|
|
SmpLoadDeferedSubsystem(
|
|
_In_ PSM_API_MSG SmApiMsg,
|
|
_In_ PSMP_CLIENT_CONTEXT ClientContext,
|
|
_In_ HANDLE SmApiPort)
|
|
{
|
|
NTSTATUS Status = STATUS_OBJECT_NAME_NOT_FOUND;
|
|
PSM_LOAD_DEFERED_SUBSYSTEM_MSG SmLoadDefered = &SmApiMsg->u.LoadDefered;
|
|
UNICODE_STRING DeferedSubsystem;
|
|
ULONG MuSessionId;
|
|
PLIST_ENTRY NextEntry;
|
|
PSMP_REGISTRY_VALUE RegEntry;
|
|
|
|
/* Validate DeferedSubsystem's length */
|
|
if ((SmLoadDefered->Length <= 0) ||
|
|
(SmLoadDefered->Length > sizeof(SmLoadDefered->Buffer)))
|
|
{
|
|
return STATUS_INVALID_PARAMETER;
|
|
}
|
|
|
|
/* Get the name of the subsystem to start */
|
|
DeferedSubsystem.Length = (USHORT)SmLoadDefered->Length;
|
|
DeferedSubsystem.MaximumLength = DeferedSubsystem.Length;
|
|
DeferedSubsystem.Buffer = SmLoadDefered->Buffer;
|
|
|
|
/* Find a subsystem responsible for this session */
|
|
SmpGetProcessMuSessionId(ClientContext->ProcessHandle, &MuSessionId);
|
|
if (!SmpCheckDuplicateMuSessionId(MuSessionId))
|
|
{
|
|
DPRINT1("SMSS: Deferred subsystem load (%wZ) for MuSessionId %u, status=0x%x\n",
|
|
&DeferedSubsystem, MuSessionId, Status);
|
|
return Status;
|
|
}
|
|
|
|
/* Now process the deferred subsystems list */
|
|
for (NextEntry = SmpSubSystemsToDefer.Flink;
|
|
NextEntry != &SmpSubSystemsToDefer;
|
|
NextEntry = NextEntry->Flink)
|
|
{
|
|
/* Get each entry and check if it's the subsystem we are looking for */
|
|
RegEntry = CONTAINING_RECORD(NextEntry, SMP_REGISTRY_VALUE, Entry);
|
|
if (RtlEqualUnicodeString(&RegEntry->Name, &DeferedSubsystem, TRUE))
|
|
{
|
|
// TODO: One may want to extra-flag the command for
|
|
// specific POSIX or OS2 processing...
|
|
|
|
/* Load the deferred subsystem */
|
|
Status = SmpExecuteCommand(&RegEntry->Value,
|
|
MuSessionId,
|
|
NULL,
|
|
SMP_SUBSYSTEM_FLAG);
|
|
if (!NT_SUCCESS(Status))
|
|
DPRINT1("SMSS: Subsystem execute failed (%wZ)\n", &RegEntry->Value);
|
|
|
|
break;
|
|
}
|
|
}
|
|
|
|
/* Return status */
|
|
return Status;
|
|
}
|
|
|
|
NTSTATUS
|
|
NTAPI
|
|
SmpStartCsr(IN PSM_API_MSG SmApiMsg,
|
|
IN PSMP_CLIENT_CONTEXT ClientContext,
|
|
IN HANDLE SmApiPort)
|
|
{
|
|
PSM_START_CSR_MSG SmStartCsr = &SmApiMsg->u.StartCsr;
|
|
UNICODE_STRING InitialCommand;
|
|
HANDLE InitialCommandProcess, InitialCommandProcessId, WindowsSubSysProcessId;
|
|
NTSTATUS Status;
|
|
|
|
Status = SmpLoadSubSystemsForMuSession(&SmStartCsr->MuSessionId,
|
|
&WindowsSubSysProcessId,
|
|
&InitialCommand);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
DPRINT1("SMSS: SmpLoadSubSystemsForMuSession failed with status 0x%08x\n", Status);
|
|
return Status;
|
|
}
|
|
|
|
if (SmStartCsr->Length)
|
|
{
|
|
InitialCommand.Length = InitialCommand.MaximumLength = SmStartCsr->Length;
|
|
InitialCommand.Buffer = SmStartCsr->Buffer;
|
|
}
|
|
|
|
Status = SmpExecuteInitialCommand(SmStartCsr->MuSessionId,
|
|
&InitialCommand,
|
|
&InitialCommandProcess,
|
|
&InitialCommandProcessId);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
DPRINT1("SMSS: SmpExecuteInitialCommand failed with status 0x%08x\n", Status);
|
|
/* FIXME: undo effects of SmpLoadSubSystemsForMuSession */
|
|
ASSERT(FALSE);
|
|
return Status;
|
|
}
|
|
|
|
NtClose(InitialCommandProcess);
|
|
|
|
SmStartCsr->WindowsSubSysProcessId = WindowsSubSysProcessId;
|
|
SmStartCsr->SmpInitialCommandProcessId = InitialCommandProcessId;
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
NTSTATUS
|
|
NTAPI
|
|
SmpStopCsr(IN PSM_API_MSG SmApiMsg,
|
|
IN PSMP_CLIENT_CONTEXT ClientContext,
|
|
IN HANDLE SmApiPort)
|
|
{
|
|
DPRINT1("%s is not yet implemented\n", __FUNCTION__);
|
|
return STATUS_NOT_IMPLEMENTED;
|
|
}
|
|
|
|
PSM_API_HANDLER SmpApiDispatch[SmpMaxApiNumber - SmpCreateForeignSessionApi] =
|
|
{
|
|
SmpCreateForeignSession,
|
|
SmpSessionComplete,
|
|
SmpTerminateForeignSession,
|
|
SmpExecPgm,
|
|
SmpLoadDeferedSubsystem,
|
|
SmpStartCsr,
|
|
SmpStopCsr
|
|
};
|
|
|
|
/* FUNCTIONS ******************************************************************/
|
|
|
|
NTSTATUS
|
|
NTAPI
|
|
SmpHandleConnectionRequest(IN HANDLE SmApiPort,
|
|
IN PSB_API_MSG SbApiMsg)
|
|
{
|
|
BOOLEAN Accept = TRUE;
|
|
HANDLE PortHandle, ProcessHandle;
|
|
ULONG SessionId;
|
|
UNICODE_STRING SubsystemPort;
|
|
PSMP_CLIENT_CONTEXT ClientContext;
|
|
NTSTATUS Status;
|
|
OBJECT_ATTRIBUTES ObjectAttributes;
|
|
REMOTE_PORT_VIEW PortView;
|
|
SECURITY_QUALITY_OF_SERVICE SecurityQos;
|
|
PSMP_SUBSYSTEM CidSubsystem, TypeSubsystem;
|
|
|
|
/* Initialize QoS data */
|
|
SecurityQos.ImpersonationLevel = SecurityIdentification;
|
|
SecurityQos.ContextTrackingMode = SECURITY_DYNAMIC_TRACKING;
|
|
SecurityQos.EffectiveOnly = TRUE;
|
|
|
|
/* Check if this is SM connecting to itself */
|
|
if (SbApiMsg->h.ClientId.UniqueProcess == SmUniqueProcessId)
|
|
{
|
|
/* No need to get any handle -- assume session 0 */
|
|
ProcessHandle = NULL;
|
|
SessionId = 0;
|
|
}
|
|
else
|
|
{
|
|
/* Reference the foreign process */
|
|
InitializeObjectAttributes(&ObjectAttributes, NULL, 0, NULL, NULL);
|
|
Status = NtOpenProcess(&ProcessHandle,
|
|
PROCESS_QUERY_INFORMATION,
|
|
&ObjectAttributes,
|
|
&SbApiMsg->h.ClientId);
|
|
if (!NT_SUCCESS(Status)) Accept = FALSE;
|
|
|
|
/* Get its session ID */
|
|
SmpGetProcessMuSessionId(ProcessHandle, &SessionId);
|
|
}
|
|
|
|
/* See if we already know about the caller's subsystem */
|
|
CidSubsystem = SmpLocateKnownSubSysByCid(&SbApiMsg->h.ClientId);
|
|
if ((CidSubsystem) && (Accept))
|
|
{
|
|
/* Check if we already have a subsystem for this kind of image */
|
|
TypeSubsystem = SmpLocateKnownSubSysByType(SessionId,
|
|
SbApiMsg->ConnectionInfo.SubsystemType);
|
|
if (TypeSubsystem == CidSubsystem)
|
|
{
|
|
/* Someone is trying to take control of an existing subsystem, fail */
|
|
Accept = FALSE;
|
|
DPRINT1("SMSS: Connection from SubSystem rejected\n");
|
|
DPRINT1("SMSS: Image type already being served\n");
|
|
}
|
|
else
|
|
{
|
|
/* Set this image type as the type for this subsystem */
|
|
CidSubsystem->ImageType = SbApiMsg->ConnectionInfo.SubsystemType;
|
|
}
|
|
|
|
/* Drop the reference we had acquired */
|
|
if (TypeSubsystem) SmpDereferenceSubsystem(TypeSubsystem);
|
|
}
|
|
|
|
/* Check if we'll be accepting the connection */
|
|
if (Accept)
|
|
{
|
|
/* We will, so create a client context for it */
|
|
ClientContext = RtlAllocateHeap(SmpHeap, 0, sizeof(SMP_CLIENT_CONTEXT));
|
|
if (ClientContext)
|
|
{
|
|
ClientContext->ProcessHandle = ProcessHandle;
|
|
ClientContext->Subsystem = CidSubsystem;
|
|
ClientContext->Reserved = NULL;
|
|
ClientContext->PortHandle = NULL;
|
|
}
|
|
else
|
|
{
|
|
/* Failed to allocate a client context, so reject the connection */
|
|
DPRINT1("Rejecting connection due to lack of memory\n");
|
|
Accept = FALSE;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/* Use a bogus context since we're going to reject the message */
|
|
ClientContext = (PSMP_CLIENT_CONTEXT)SbApiMsg;
|
|
}
|
|
|
|
/* Now send the actual accept reply (which could be a rejection) */
|
|
PortView.Length = sizeof(PortView);
|
|
Status = NtAcceptConnectPort(&PortHandle,
|
|
ClientContext,
|
|
&SbApiMsg->h,
|
|
Accept,
|
|
NULL,
|
|
&PortView);
|
|
if (!(Accept) || !(NT_SUCCESS(Status)))
|
|
{
|
|
/* Close the process handle, reference the subsystem, and exit */
|
|
DPRINT1("Accept failed or rejected: %lx\n", Status);
|
|
if (ClientContext != (PVOID)SbApiMsg) RtlFreeHeap(SmpHeap, 0, ClientContext);
|
|
if (ProcessHandle) NtClose(ProcessHandle);
|
|
if (CidSubsystem) SmpDereferenceSubsystem(CidSubsystem);
|
|
return Status;
|
|
}
|
|
|
|
/* Save the port handle now that we've accepted it */
|
|
if (ClientContext) ClientContext->PortHandle = PortHandle;
|
|
if (CidSubsystem) CidSubsystem->PortHandle = PortHandle;
|
|
|
|
/* Complete the port connection */
|
|
Status = NtCompleteConnectPort(PortHandle);
|
|
if ((NT_SUCCESS(Status)) && (CidSubsystem))
|
|
{
|
|
/* This was an actual subsystem, so connect back to it */
|
|
SbApiMsg->ConnectionInfo.SbApiPortName[119] = UNICODE_NULL;
|
|
RtlCreateUnicodeString(&SubsystemPort,
|
|
SbApiMsg->ConnectionInfo.SbApiPortName);
|
|
Status = NtConnectPort(&CidSubsystem->SbApiPort,
|
|
&SubsystemPort,
|
|
&SecurityQos,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
NULL);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
DPRINT1("SMSS: Connect back to Sb %wZ failed %lx\n", &SubsystemPort, Status);
|
|
}
|
|
RtlFreeUnicodeString(&SubsystemPort);
|
|
|
|
/* Now that we're connected, signal the event handle */
|
|
NtSetEvent(CidSubsystem->Event, NULL);
|
|
}
|
|
else if (CidSubsystem)
|
|
{
|
|
/* We failed to complete the connection, so clear the port handle */
|
|
DPRINT1("Completing the connection failed: %lx\n", Status);
|
|
CidSubsystem->PortHandle = NULL;
|
|
}
|
|
|
|
/* Dereference the subsystem and return the result */
|
|
if (CidSubsystem) SmpDereferenceSubsystem(CidSubsystem);
|
|
return Status;
|
|
}
|
|
|
|
ULONG
|
|
NTAPI
|
|
SmpApiLoop(IN PVOID Parameter)
|
|
{
|
|
HANDLE SmApiPort = (HANDLE)Parameter;
|
|
NTSTATUS Status;
|
|
PSMP_CLIENT_CONTEXT ClientContext;
|
|
PSM_API_MSG ReplyMsg = NULL;
|
|
SM_API_MSG RequestMsg;
|
|
PROCESS_BASIC_INFORMATION ProcessInformation;
|
|
LARGE_INTEGER Timeout;
|
|
|
|
/* Increase the number of API threads for throttling code for later */
|
|
_InterlockedExchangeAdd(&SmTotalApiThreads, 1);
|
|
|
|
/* Mark us critical */
|
|
RtlSetThreadIsCritical(TRUE, NULL, TRUE);
|
|
|
|
/* Set the PID of the SM process itself for later checking */
|
|
NtQueryInformationProcess(NtCurrentProcess(),
|
|
ProcessBasicInformation,
|
|
&ProcessInformation,
|
|
sizeof(ProcessInformation),
|
|
NULL);
|
|
SmUniqueProcessId = (HANDLE)ProcessInformation.UniqueProcessId;
|
|
|
|
/* Now process incoming messages */
|
|
while (TRUE)
|
|
{
|
|
/* Begin waiting on a request */
|
|
Status = NtReplyWaitReceivePort(SmApiPort,
|
|
(PVOID*)&ClientContext,
|
|
&ReplyMsg->h,
|
|
&RequestMsg.h);
|
|
if (Status == STATUS_NO_MEMORY)
|
|
{
|
|
/* Ran out of memory, so do a little timeout and try again */
|
|
if (ReplyMsg) DPRINT1("SMSS: Failed to reply to calling thread, retrying.\n");
|
|
Timeout.QuadPart = -50000000;
|
|
NtDelayExecution(FALSE, &Timeout);
|
|
continue;
|
|
}
|
|
|
|
/* Check what kind of request we received */
|
|
switch (RequestMsg.h.u2.s2.Type)
|
|
{
|
|
/* A new connection */
|
|
case LPC_CONNECTION_REQUEST:
|
|
/* Create the right structures for it */
|
|
SmpHandleConnectionRequest(SmApiPort, (PSB_API_MSG)&RequestMsg);
|
|
ReplyMsg = NULL;
|
|
break;
|
|
|
|
/* A closed connection */
|
|
case LPC_PORT_CLOSED:
|
|
/* Destroy any state we had for this client */
|
|
DPRINT1("Port closed\n");
|
|
//if (ClientContext) SmpPushDeferredClientContext(ClientContext);
|
|
ReplyMsg = NULL;
|
|
break;
|
|
|
|
/* An actual API message */
|
|
default:
|
|
if (!ClientContext)
|
|
{
|
|
ReplyMsg = NULL;
|
|
break;
|
|
}
|
|
|
|
RequestMsg.ReturnValue = STATUS_PENDING;
|
|
|
|
/* Check if the API is valid */
|
|
if (RequestMsg.ApiNumber >= SmpMaxApiNumber)
|
|
{
|
|
/* It isn't, fail */
|
|
DPRINT1("Invalid API: %lx\n", RequestMsg.ApiNumber);
|
|
Status = STATUS_NOT_IMPLEMENTED;
|
|
}
|
|
else if ((RequestMsg.ApiNumber <= SmpTerminateForeignSessionApi) &&
|
|
!(ClientContext->Subsystem))
|
|
{
|
|
/* It's valid, but doesn't have a subsystem with it */
|
|
DPRINT1("Invalid session API\n");
|
|
Status = STATUS_INVALID_PARAMETER;
|
|
}
|
|
else
|
|
{
|
|
/* It's totally okay, so call the dispatcher for it */
|
|
Status = SmpApiDispatch[RequestMsg.ApiNumber](&RequestMsg,
|
|
ClientContext,
|
|
SmApiPort);
|
|
}
|
|
|
|
/* Write the result value and return the message back */
|
|
RequestMsg.ReturnValue = Status;
|
|
ReplyMsg = &RequestMsg;
|
|
break;
|
|
}
|
|
}
|
|
return STATUS_SUCCESS;
|
|
}
|