xref: /reactos/base/system/smss/smloop.c (revision 6924b8ff)
1 /*
2  * PROJECT:         ReactOS Windows-Compatible Session Manager
3  * LICENSE:         BSD 2-Clause License
4  * FILE:            base/system/smss/smloop.c
5  * PURPOSE:         Main SMSS Code
6  * PROGRAMMERS:     Alex Ionescu
7  */
8 
9 /* INCLUDES *******************************************************************/
10 
11 #include "smss.h"
12 
13 #define NDEBUG
14 #include <debug.h>
15 
16 /* GLOBALS ********************************************************************/
17 
18 typedef struct _SMP_CLIENT_CONTEXT
19 {
20     PVOID Subsystem;
21     HANDLE ProcessHandle;
22     HANDLE PortHandle;
23     ULONG dword10;
24 } SMP_CLIENT_CONTEXT, *PSMP_CLIENT_CONTEXT;
25 
26 typedef
27 NTSTATUS
28 (NTAPI *PSM_API_HANDLER)(
29      IN PSM_API_MSG SmApiMsg,
30      IN PSMP_CLIENT_CONTEXT ClientContext,
31      IN HANDLE SmApiPort
32 );
33 
34 volatile LONG SmTotalApiThreads;
35 HANDLE SmUniqueProcessId;
36 
37 /* API HANDLERS ***************************************************************/
38 
39 NTSTATUS
40 NTAPI
41 SmpCreateForeignSession(IN PSM_API_MSG SmApiMsg,
42                         IN PSMP_CLIENT_CONTEXT ClientContext,
43                         IN HANDLE SmApiPort)
44 {
45     DPRINT1("%s is not yet implemented\n", __FUNCTION__);
46     return STATUS_NOT_IMPLEMENTED;
47 }
48 
49 NTSTATUS
50 NTAPI
51 SmpSessionComplete(IN PSM_API_MSG SmApiMsg,
52                    IN PSMP_CLIENT_CONTEXT ClientContext,
53                    IN HANDLE SmApiPort)
54 {
55     DPRINT1("%s is not yet implemented\n", __FUNCTION__);
56     return STATUS_NOT_IMPLEMENTED;
57 }
58 
59 NTSTATUS
60 NTAPI
61 SmpTerminateForeignSession(IN PSM_API_MSG SmApiMsg,
62                            IN PSMP_CLIENT_CONTEXT ClientContext,
63                            IN HANDLE SmApiPort)
64 {
65     DPRINT1("%s is not yet implemented\n", __FUNCTION__);
66     return STATUS_NOT_IMPLEMENTED;
67 }
68 
69 NTSTATUS
70 NTAPI
71 SmpExecPgm(IN PSM_API_MSG SmApiMsg,
72            IN PSMP_CLIENT_CONTEXT ClientContext,
73            IN HANDLE SmApiPort)
74 {
75     HANDLE ProcessHandle;
76     NTSTATUS Status;
77     PSM_EXEC_PGM_MSG SmExecPgm;
78     RTL_USER_PROCESS_INFORMATION ProcessInformation;
79     OBJECT_ATTRIBUTES ObjectAttributes;
80 
81     /* Open the client process */
82     InitializeObjectAttributes(&ObjectAttributes, NULL, 0, NULL, NULL);
83     Status = NtOpenProcess(&ProcessHandle,
84                            PROCESS_DUP_HANDLE,
85                            &ObjectAttributes,
86                            &SmApiMsg->h.ClientId);
87     if (!NT_SUCCESS(Status))
88     {
89         /* Fail */
90         DPRINT1("SmExecPgm: NtOpenProcess Failed %lx\n", Status);
91         return Status;
92     }
93 
94     /* Copy the process information out of the message */
95     SmExecPgm = &SmApiMsg->u.ExecPgm;
96     ProcessInformation = SmExecPgm->ProcessInformation;
97 
98     /* Duplicate the process handle */
99     Status = NtDuplicateObject(ProcessHandle,
100                                SmExecPgm->ProcessInformation.ProcessHandle,
101                                NtCurrentProcess(),
102                                &ProcessInformation.ProcessHandle,
103                                PROCESS_ALL_ACCESS,
104                                0,
105                                0);
106     if (!NT_SUCCESS(Status))
107     {
108         /* Close the handle and fail */
109         NtClose(ProcessHandle);
110         DPRINT1("SmExecPgm: NtDuplicateObject (Process) Failed %lx\n", Status);
111         return Status;
112     }
113 
114     /* Duplicate the thread handle */
115     Status = NtDuplicateObject(ProcessHandle,
116                                SmExecPgm->ProcessInformation.ThreadHandle,
117                                NtCurrentProcess(),
118                                &ProcessInformation.ThreadHandle,
119                                THREAD_ALL_ACCESS,
120                                0,
121                                0);
122     if (!NT_SUCCESS(Status))
123     {
124         /* Close both handles and fail */
125         NtClose(ProcessInformation.ProcessHandle);
126         NtClose(ProcessHandle);
127         DPRINT1("SmExecPgm: NtDuplicateObject (Thread) Failed %lx\n", Status);
128         return Status;
129     }
130 
131     /* Close the process handle and call the internal client API */
132     NtClose(ProcessHandle);
133     return SmpSbCreateSession(NULL,
134                               NULL,
135                               &ProcessInformation,
136                               0,
137                               SmExecPgm->DebugFlag ? &SmApiMsg->h.ClientId : NULL);
138 }
139 
140 NTSTATUS
141 NTAPI
142 SmpLoadDeferedSubsystem(IN PSM_API_MSG SmApiMsg,
143                         IN PSMP_CLIENT_CONTEXT ClientContext,
144                         IN HANDLE SmApiPort)
145 {
146     DPRINT1("%s is not yet implemented\n", __FUNCTION__);
147     return STATUS_NOT_IMPLEMENTED;
148 }
149 
150 NTSTATUS
151 NTAPI
152 SmpStartCsr(IN PSM_API_MSG SmApiMsg,
153             IN PSMP_CLIENT_CONTEXT ClientContext,
154             IN HANDLE SmApiPort)
155 {
156     PSM_START_CSR_MSG SmStartCsr = &SmApiMsg->u.StartCsr;
157     UNICODE_STRING InitialCommand;
158     HANDLE InitialCommandProcess, InitialCommandProcessId, WindowsSubSysProcessId;
159     NTSTATUS Status;
160 
161     Status = SmpLoadSubSystemsForMuSession(&SmStartCsr->MuSessionId,
162                                            &WindowsSubSysProcessId,
163                                            &InitialCommand);
164     if (!NT_SUCCESS(Status))
165     {
166         DPRINT1("SMSS: SmpLoadSubSystemsForMuSession failed with status 0x%08x\n", Status);
167         return Status;
168     }
169 
170     if (SmStartCsr->Length)
171     {
172         InitialCommand.Length = InitialCommand.MaximumLength = SmStartCsr->Length;
173         InitialCommand.Buffer = SmStartCsr->Buffer;
174     }
175 
176     Status = SmpExecuteInitialCommand(SmStartCsr->MuSessionId,
177                                       &InitialCommand,
178                                       &InitialCommandProcess,
179                                       &InitialCommandProcessId);
180     if (!NT_SUCCESS(Status))
181     {
182         DPRINT1("SMSS: SmpExecuteInitialCommand failed with status 0x%08x\n", Status);
183         /* FIXME: undo effects of SmpLoadSubSystemsForMuSession */
184         ASSERT(FALSE);
185         return Status;
186     }
187 
188     NtClose(InitialCommandProcess);
189 
190     SmStartCsr->WindowsSubSysProcessId = WindowsSubSysProcessId;
191     SmStartCsr->SmpInitialCommandProcessId = InitialCommandProcessId;
192 
193     return STATUS_SUCCESS;
194 }
195 
196 NTSTATUS
197 NTAPI
198 SmpStopCsr(IN PSM_API_MSG SmApiMsg,
199            IN PSMP_CLIENT_CONTEXT ClientContext,
200            IN HANDLE SmApiPort)
201 {
202     DPRINT1("%s is not yet implemented\n", __FUNCTION__);
203     return STATUS_NOT_IMPLEMENTED;
204 }
205 
206 PSM_API_HANDLER SmpApiDispatch[SmpMaxApiNumber - SmpCreateForeignSessionApi] =
207 {
208     SmpCreateForeignSession,
209     SmpSessionComplete,
210     SmpTerminateForeignSession,
211     SmpExecPgm,
212     SmpLoadDeferedSubsystem,
213     SmpStartCsr,
214     SmpStopCsr
215 };
216 
217 /* FUNCTIONS ******************************************************************/
218 
219 NTSTATUS
220 NTAPI
221 SmpHandleConnectionRequest(IN HANDLE SmApiPort,
222                            IN PSB_API_MSG SbApiMsg)
223 {
224     BOOLEAN Accept = TRUE;
225     HANDLE PortHandle, ProcessHandle;
226     ULONG SessionId;
227     UNICODE_STRING SubsystemPort;
228     SMP_CLIENT_CONTEXT *ClientContext;
229     NTSTATUS Status;
230     OBJECT_ATTRIBUTES ObjectAttributes;
231     REMOTE_PORT_VIEW PortView;
232     SECURITY_QUALITY_OF_SERVICE SecurityQos;
233     PSMP_SUBSYSTEM CidSubsystem, TypeSubsystem;
234 
235     /* Initialize QoS data */
236     SecurityQos.ImpersonationLevel = SecurityIdentification;
237     SecurityQos.ContextTrackingMode = SECURITY_DYNAMIC_TRACKING;
238     SecurityQos.EffectiveOnly = TRUE;
239 
240     /* Check if this is SM connecting to itself */
241     if (SbApiMsg->h.ClientId.UniqueProcess == SmUniqueProcessId)
242     {
243         /* No need to get any handle -- assume session 0 */
244         ProcessHandle = NULL;
245         SessionId = 0;
246     }
247     else
248     {
249         /* Reference the foreign process */
250         InitializeObjectAttributes(&ObjectAttributes, NULL, 0, NULL, NULL);
251         Status = NtOpenProcess(&ProcessHandle,
252                                PROCESS_QUERY_INFORMATION,
253                                &ObjectAttributes,
254                                &SbApiMsg->h.ClientId);
255         if (!NT_SUCCESS(Status)) Accept = FALSE;
256 
257         /* Get its session ID */
258         SmpGetProcessMuSessionId(ProcessHandle, &SessionId);
259     }
260 
261     /* See if we already know about the caller's subsystem */
262     CidSubsystem = SmpLocateKnownSubSysByCid(&SbApiMsg->h.ClientId);
263     if ((CidSubsystem) && (Accept))
264     {
265         /* Check if we already have a subsystem for this kind of image */
266         TypeSubsystem = SmpLocateKnownSubSysByType(SessionId,
267                                                    SbApiMsg->ConnectionInfo.SubsystemType);
268         if (TypeSubsystem == CidSubsystem)
269         {
270             /* Someone is trying to take control of an existing subsystem, fail */
271             Accept = FALSE;
272             DPRINT1("SMSS: Connection from SubSystem rejected\n");
273             DPRINT1("SMSS: Image type already being served\n");
274         }
275         else
276         {
277             /* Set this image type as the type for this subsystem */
278             CidSubsystem->ImageType = SbApiMsg->ConnectionInfo.SubsystemType;
279         }
280 
281         /* Drop the reference we had acquired */
282         if (TypeSubsystem) SmpDereferenceSubsystem(TypeSubsystem);
283     }
284 
285     /* Check if we'll be accepting the connection */
286     if (Accept)
287     {
288         /* We will, so create a client context for it */
289         ClientContext = RtlAllocateHeap(SmpHeap, 0, sizeof(SMP_CLIENT_CONTEXT));
290         if (ClientContext)
291         {
292             ClientContext->ProcessHandle = ProcessHandle;
293             ClientContext->Subsystem = CidSubsystem;
294             ClientContext->dword10 = 0;
295             ClientContext->PortHandle = NULL;
296         }
297         else
298         {
299             /* Failed to allocate a client context, so reject the connection */
300             DPRINT1("Rejecting connectiond due to lack of memory\n");
301             Accept = FALSE;
302         }
303     }
304     else
305     {
306         /* Use a bogus context since we're going to reject the message */
307         ClientContext = (PSMP_CLIENT_CONTEXT)SbApiMsg;
308     }
309 
310     /* Now send the actual accept reply (which could be a rejection) */
311     PortView.Length = sizeof(PortView);
312     Status = NtAcceptConnectPort(&PortHandle,
313                                  ClientContext,
314                                  &SbApiMsg->h,
315                                  Accept,
316                                  NULL,
317                                  &PortView);
318     if (!(Accept) || !(NT_SUCCESS(Status)))
319     {
320         /* Close the process handle, reference the subsystem, and exit */
321         DPRINT1("Accept failed or rejected: %lx\n", Status);
322         if (ClientContext != (PVOID)SbApiMsg) RtlFreeHeap(SmpHeap, 0, ClientContext);
323         if (ProcessHandle) NtClose(ProcessHandle);
324         if (CidSubsystem) SmpDereferenceSubsystem(CidSubsystem);
325         return Status;
326     }
327 
328     /* Save the port handle now that we've accepted it */
329     if (ClientContext) ClientContext->PortHandle = PortHandle;
330     if (CidSubsystem) CidSubsystem->PortHandle = PortHandle;
331 
332     /* Complete the port connection */
333     Status = NtCompleteConnectPort(PortHandle);
334     if ((NT_SUCCESS(Status)) && (CidSubsystem))
335     {
336         /* This was an actual subsystem, so connect back to it */
337         SbApiMsg->ConnectionInfo.SbApiPortName[119] = UNICODE_NULL;
338         RtlCreateUnicodeString(&SubsystemPort,
339                                SbApiMsg->ConnectionInfo.SbApiPortName);
340         Status = NtConnectPort(&CidSubsystem->SbApiPort,
341                                &SubsystemPort,
342                                &SecurityQos,
343                                NULL,
344                                NULL,
345                                NULL,
346                                NULL,
347                                NULL);
348         if (!NT_SUCCESS(Status))
349         {
350             DPRINT1("SMSS: Connect back to Sb %wZ failed %lx\n", &SubsystemPort, Status);
351         }
352         RtlFreeUnicodeString(&SubsystemPort);
353 
354         /* Now that we're connected, signal the event handle */
355         NtSetEvent(CidSubsystem->Event, NULL);
356     }
357     else if (CidSubsystem)
358     {
359         /* We failed to complete the connection, so clear the port handle */
360         DPRINT1("Completing the connection failed: %lx\n", Status);
361         CidSubsystem->PortHandle = NULL;
362     }
363 
364     /* Dereference the subsystem and return the result */
365     if (CidSubsystem) SmpDereferenceSubsystem(CidSubsystem);
366     return Status;
367 }
368 
369 ULONG
370 NTAPI
371 SmpApiLoop(IN PVOID Parameter)
372 {
373     HANDLE SmApiPort = (HANDLE)Parameter;
374     NTSTATUS Status;
375     PSMP_CLIENT_CONTEXT ClientContext;
376     PSM_API_MSG ReplyMsg = NULL;
377     SM_API_MSG RequestMsg;
378     PROCESS_BASIC_INFORMATION ProcessInformation;
379     LARGE_INTEGER Timeout;
380 
381     /* Increase the number of API threads for throttling code for later */
382     _InterlockedExchangeAdd(&SmTotalApiThreads, 1);
383 
384     /* Mark us critical */
385     RtlSetThreadIsCritical(TRUE, NULL, TRUE);
386 
387     /* Set the PID of the SM process itself for later checking */
388     NtQueryInformationProcess(NtCurrentProcess(),
389                               ProcessBasicInformation,
390                               &ProcessInformation,
391                               sizeof(ProcessInformation),
392                               NULL);
393     SmUniqueProcessId = (HANDLE)ProcessInformation.UniqueProcessId;
394 
395     /* Now process incoming messages */
396     while (TRUE)
397     {
398         /* Begin waiting on a request */
399         Status = NtReplyWaitReceivePort(SmApiPort,
400                                         (PVOID*)&ClientContext,
401                                         &ReplyMsg->h,
402                                         &RequestMsg.h);
403         if (Status == STATUS_NO_MEMORY)
404         {
405             /* Ran out of memory, so do a little timeout and try again */
406             if (ReplyMsg) DPRINT1("SMSS: Failed to reply to calling thread, retrying.\n");
407             Timeout.QuadPart = -50000000;
408             NtDelayExecution(FALSE, &Timeout);
409             continue;
410         }
411 
412         /* Check what kind of request we received */
413         switch (RequestMsg.h.u2.s2.Type)
414         {
415             /* A new connection */
416             case LPC_CONNECTION_REQUEST:
417                 /* Create the right structures for it */
418                 SmpHandleConnectionRequest(SmApiPort, (PSB_API_MSG)&RequestMsg);
419                 ReplyMsg = NULL;
420                 break;
421 
422             /* A closed connection */
423             case LPC_PORT_CLOSED:
424                 /* Destroy any state we had for this client */
425                 DPRINT1("Port closed\n");
426                 //if (ClientContext) SmpPushDeferredClientContext(ClientContext);
427                 ReplyMsg = NULL;
428                 break;
429 
430             /* An actual API message */
431             default:
432                 if (!ClientContext)
433                 {
434                     ReplyMsg = NULL;
435                     break;
436                 }
437 
438                 RequestMsg.ReturnValue = STATUS_PENDING;
439 
440                 /* Check if the API is valid */
441                 if (RequestMsg.ApiNumber >= SmpMaxApiNumber)
442                 {
443                     /* It isn't, fail */
444                     DPRINT1("Invalid API: %lx\n", RequestMsg.ApiNumber);
445                     Status = STATUS_NOT_IMPLEMENTED;
446                 }
447                 else if ((RequestMsg.ApiNumber <= SmpTerminateForeignSessionApi) &&
448                          !(ClientContext->Subsystem))
449                 {
450                     /* It's valid, but doesn't have a subsystem with it */
451                     DPRINT1("Invalid session API\n");
452                     Status = STATUS_INVALID_PARAMETER;
453                 }
454                 else
455                 {
456                     /* It's totally okay, so call the dispatcher for it */
457                     Status = SmpApiDispatch[RequestMsg.ApiNumber](&RequestMsg,
458                                                                   ClientContext,
459                                                                   SmApiPort);
460                 }
461 
462                 /* Write the result value and return the message back */
463                 RequestMsg.ReturnValue = Status;
464                 ReplyMsg = &RequestMsg;
465                 break;
466         }
467     }
468     return STATUS_SUCCESS;
469 }
470