xref: /reactos/base/system/smss/smloop.c (revision 40462c92)
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     DPRINT1("%s is not yet implemented\n", __FUNCTION__);
157     return STATUS_NOT_IMPLEMENTED;
158 }
159 
160 NTSTATUS
161 NTAPI
162 SmpStopCsr(IN PSM_API_MSG SmApiMsg,
163            IN PSMP_CLIENT_CONTEXT ClientContext,
164            IN HANDLE SmApiPort)
165 {
166     DPRINT1("%s is not yet implemented\n", __FUNCTION__);
167     return STATUS_NOT_IMPLEMENTED;
168 }
169 
170 PSM_API_HANDLER SmpApiDispatch[SmpMaxApiNumber - SmpCreateForeignSessionApi] =
171 {
172     SmpCreateForeignSession,
173     SmpSessionComplete,
174     SmpTerminateForeignSession,
175     SmpExecPgm,
176     SmpLoadDeferedSubsystem,
177     SmpStartCsr,
178     SmpStopCsr
179 };
180 
181 /* FUNCTIONS ******************************************************************/
182 
183 NTSTATUS
184 NTAPI
185 SmpHandleConnectionRequest(IN HANDLE SmApiPort,
186                            IN PSB_API_MSG SbApiMsg)
187 {
188     BOOLEAN Accept = TRUE;
189     HANDLE PortHandle, ProcessHandle;
190     ULONG SessionId;
191     UNICODE_STRING SubsystemPort;
192     SMP_CLIENT_CONTEXT *ClientContext;
193     NTSTATUS Status;
194     OBJECT_ATTRIBUTES ObjectAttributes;
195     REMOTE_PORT_VIEW PortView;
196     SECURITY_QUALITY_OF_SERVICE SecurityQos;
197     PSMP_SUBSYSTEM CidSubsystem, TypeSubsystem;
198 
199     /* Initialize QoS data */
200     SecurityQos.ImpersonationLevel = SecurityIdentification;
201     SecurityQos.ContextTrackingMode = SECURITY_DYNAMIC_TRACKING;
202     SecurityQos.EffectiveOnly = TRUE;
203 
204     /* Check if this is SM connecting to itself */
205     if (SbApiMsg->h.ClientId.UniqueProcess == SmUniqueProcessId)
206     {
207         /* No need to get any handle -- assume session 0 */
208         ProcessHandle = NULL;
209         SessionId = 0;
210     }
211     else
212     {
213         /* Reference the foreign process */
214         InitializeObjectAttributes(&ObjectAttributes, NULL, 0, NULL, NULL);
215         Status = NtOpenProcess(&ProcessHandle,
216                                PROCESS_QUERY_INFORMATION,
217                                &ObjectAttributes,
218                                &SbApiMsg->h.ClientId);
219         if (!NT_SUCCESS(Status)) Accept = FALSE;
220 
221         /* Get its session ID */
222         SmpGetProcessMuSessionId(ProcessHandle, &SessionId);
223     }
224 
225     /* See if we already know about the caller's subsystem */
226     CidSubsystem = SmpLocateKnownSubSysByCid(&SbApiMsg->h.ClientId);
227     if ((CidSubsystem) && (Accept))
228     {
229         /* Check if we already have a subsystem for this kind of image */
230         TypeSubsystem = SmpLocateKnownSubSysByType(SessionId,
231                                                    SbApiMsg->ConnectionInfo.SubsystemType);
232         if (TypeSubsystem == CidSubsystem)
233         {
234             /* Someone is trying to take control of an existing subsystem, fail */
235             Accept = FALSE;
236             DPRINT1("SMSS: Connection from SubSystem rejected\n");
237             DPRINT1("SMSS: Image type already being served\n");
238         }
239         else
240         {
241             /* Set this image type as the type for this subsystem */
242             CidSubsystem->ImageType = SbApiMsg->ConnectionInfo.SubsystemType;
243         }
244 
245         /* Drop the reference we had acquired */
246         if (TypeSubsystem) SmpDereferenceSubsystem(TypeSubsystem);
247     }
248 
249     /* Check if we'll be accepting the connection */
250     if (Accept)
251     {
252         /* We will, so create a client context for it */
253         ClientContext = RtlAllocateHeap(SmpHeap, 0, sizeof(SMP_CLIENT_CONTEXT));
254         if (ClientContext)
255         {
256             ClientContext->ProcessHandle = ProcessHandle;
257             ClientContext->Subsystem = CidSubsystem;
258             ClientContext->dword10 = 0;
259             ClientContext->PortHandle = NULL;
260         }
261         else
262         {
263             /* Failed to allocate a client context, so reject the connection */
264             DPRINT1("Rejecting connectiond due to lack of memory\n");
265             Accept = FALSE;
266         }
267     }
268     else
269     {
270         /* Use a bogus context since we're going to reject the message */
271         ClientContext = (PSMP_CLIENT_CONTEXT)SbApiMsg;
272     }
273 
274     /* Now send the actual accept reply (which could be a rejection) */
275     PortView.Length = sizeof(PortView);
276     Status = NtAcceptConnectPort(&PortHandle,
277                                  ClientContext,
278                                  &SbApiMsg->h,
279                                  Accept,
280                                  NULL,
281                                  &PortView);
282     if (!(Accept) || !(NT_SUCCESS(Status)))
283     {
284         /* Close the process handle, reference the subsystem, and exit */
285         DPRINT1("Accept failed or rejected: %lx\n", Status);
286         if (ClientContext != (PVOID)SbApiMsg) RtlFreeHeap(SmpHeap, 0, ClientContext);
287         if (ProcessHandle) NtClose(ProcessHandle);
288         if (CidSubsystem) SmpDereferenceSubsystem(CidSubsystem);
289         return Status;
290     }
291 
292     /* Save the port handle now that we've accepted it */
293     if (ClientContext) ClientContext->PortHandle = PortHandle;
294     if (CidSubsystem) CidSubsystem->PortHandle = PortHandle;
295 
296     /* Complete the port connection */
297     Status = NtCompleteConnectPort(PortHandle);
298     if ((NT_SUCCESS(Status)) && (CidSubsystem))
299     {
300         /* This was an actual subsystem, so connect back to it */
301         SbApiMsg->ConnectionInfo.SbApiPortName[119] = UNICODE_NULL;
302         RtlCreateUnicodeString(&SubsystemPort,
303                                SbApiMsg->ConnectionInfo.SbApiPortName);
304         Status = NtConnectPort(&CidSubsystem->SbApiPort,
305                                &SubsystemPort,
306                                &SecurityQos,
307                                NULL,
308                                NULL,
309                                NULL,
310                                NULL,
311                                NULL);
312         if (!NT_SUCCESS(Status))
313         {
314             DPRINT1("SMSS: Connect back to Sb %wZ failed %lx\n", &SubsystemPort, Status);
315         }
316         RtlFreeUnicodeString(&SubsystemPort);
317 
318         /* Now that we're connected, signal the event handle */
319         NtSetEvent(CidSubsystem->Event, NULL);
320     }
321     else if (CidSubsystem)
322     {
323         /* We failed to complete the connection, so clear the port handle */
324         DPRINT1("Completing the connection failed: %lx\n", Status);
325         CidSubsystem->PortHandle = NULL;
326     }
327 
328     /* Dereference the subsystem and return the result */
329     if (CidSubsystem) SmpDereferenceSubsystem(CidSubsystem);
330     return Status;
331 }
332 
333 ULONG
334 NTAPI
335 SmpApiLoop(IN PVOID Parameter)
336 {
337     HANDLE SmApiPort = (HANDLE)Parameter;
338     NTSTATUS Status;
339     PSMP_CLIENT_CONTEXT ClientContext;
340     PSM_API_MSG ReplyMsg = NULL;
341     SM_API_MSG RequestMsg;
342     PROCESS_BASIC_INFORMATION ProcessInformation;
343     LARGE_INTEGER Timeout;
344 
345     /* Increase the number of API threads for throttling code for later */
346     _InterlockedExchangeAdd(&SmTotalApiThreads, 1);
347 
348     /* Mark us critical */
349     RtlSetThreadIsCritical(TRUE, NULL, TRUE);
350 
351     /* Set the PID of the SM process itself for later checking */
352     NtQueryInformationProcess(NtCurrentProcess(),
353                               ProcessBasicInformation,
354                               &ProcessInformation,
355                               sizeof(ProcessInformation),
356                               NULL);
357     SmUniqueProcessId = (HANDLE)ProcessInformation.UniqueProcessId;
358 
359     /* Now process incoming messages */
360     while (TRUE)
361     {
362         /* Begin waiting on a request */
363         Status = NtReplyWaitReceivePort(SmApiPort,
364                                         (PVOID*)&ClientContext,
365                                         &ReplyMsg->h,
366                                         &RequestMsg.h);
367         if (Status == STATUS_NO_MEMORY)
368         {
369             /* Ran out of memory, so do a little timeout and try again */
370             if (ReplyMsg) DPRINT1("SMSS: Failed to reply to calling thread, retrying.\n");
371             Timeout.QuadPart = -50000000;
372             NtDelayExecution(FALSE, &Timeout);
373             continue;
374         }
375 
376         /* Check what kind of request we received */
377         switch (RequestMsg.h.u2.s2.Type)
378         {
379             /* A new connection */
380             case LPC_CONNECTION_REQUEST:
381                 /* Create the right structures for it */
382                 SmpHandleConnectionRequest(SmApiPort, (PSB_API_MSG)&RequestMsg);
383                 ReplyMsg = NULL;
384                 break;
385 
386             /* A closed connection */
387             case LPC_PORT_CLOSED:
388                 /* Destroy any state we had for this client */
389                 DPRINT1("Port closed\n");
390                 //if (ClientContext) SmpPushDeferredClientContext(ClientContext);
391                 ReplyMsg = NULL;
392                 break;
393 
394             /* An actual API message */
395             default:
396                 if (!ClientContext)
397                 {
398                     ReplyMsg = NULL;
399                     break;
400                 }
401 
402                 RequestMsg.ReturnValue = STATUS_PENDING;
403 
404                 /* Check if the API is valid */
405                 if (RequestMsg.ApiNumber >= SmpMaxApiNumber)
406                 {
407                     /* It isn't, fail */
408                     DPRINT1("Invalid API: %lx\n", RequestMsg.ApiNumber);
409                     Status = STATUS_NOT_IMPLEMENTED;
410                 }
411                 else if ((RequestMsg.ApiNumber <= SmpTerminateForeignSessionApi) &&
412                          !(ClientContext->Subsystem))
413                 {
414                     /* It's valid, but doesn't have a subsystem with it */
415                     DPRINT1("Invalid session API\n");
416                     Status = STATUS_INVALID_PARAMETER;
417                 }
418                 else
419                 {
420                     /* It's totally okay, so call the dispatcher for it */
421                     Status = SmpApiDispatch[RequestMsg.ApiNumber](&RequestMsg,
422                                                                   ClientContext,
423                                                                   SmApiPort);
424                 }
425 
426                 /* Write the result value and return the message back */
427                 RequestMsg.ReturnValue = Status;
428                 ReplyMsg = &RequestMsg;
429                 break;
430         }
431     }
432     return STATUS_SUCCESS;
433 }
434