Something went wrong. Try again.
Reactos
Something went wrong. Try again.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522/* * 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;
typedefNTSTATUS(NTAPI *PSM_API_HANDLER)( _In_ PSM_API_MSG SmApiMsg, _In_ PSMP_CLIENT_CONTEXT ClientContext, _In_ HANDLE SmApiPort);
volatile LONG SmTotalApiThreads;HANDLE SmUniqueProcessId;
/* API HANDLERS ***************************************************************/
NTSTATUSNTAPISmpCreateForeignSession(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;}
NTSTATUSNTAPISmpSessionComplete(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;}
NTSTATUSNTAPISmpTerminateForeignSession(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;}
NTSTATUSNTAPISmpExecPgm(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);}
NTSTATUSNTAPISmpLoadDeferedSubsystem( _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;}
NTSTATUSNTAPISmpStartCsr(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;}
NTSTATUSNTAPISmpStopCsr(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 ******************************************************************/
NTSTATUSNTAPISmpHandleConnectionRequest(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;}
ULONGNTAPISmpApiLoop(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;}