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