1 /*
2 * PROJECT: ReactOS Windows-Compatible Session Manager
3 * LICENSE: BSD 2-Clause License
4 * FILE: base/system/smss/smss.c
5 * PURPOSE: Main SMSS Code
6 * PROGRAMMERS: Alex Ionescu
7 */
8
9 /* INCLUDES *******************************************************************/
10
11 #include "smss.h"
12
13 #include <pseh/pseh2.h>
14
15 #define NDEBUG
16 #include <debug.h>
17
18 /* GLOBALS ********************************************************************/
19
20 UNICODE_STRING SmpSystemRoot;
21 ULONG AttachedSessionId = -1;
22 BOOLEAN SmpDebug, SmpEnableDots;
23 HANDLE SmApiPort;
24 HANDLE SmpInitialCommandProcessId;
25
26 /* FUNCTIONS ******************************************************************/
27
28 NTSTATUS
29 NTAPI
SmpExecuteImage(IN PUNICODE_STRING FileName,IN PUNICODE_STRING Directory,IN PUNICODE_STRING CommandLine,IN ULONG MuSessionId,IN ULONG Flags,IN PRTL_USER_PROCESS_INFORMATION ProcessInformation)30 SmpExecuteImage(IN PUNICODE_STRING FileName,
31 IN PUNICODE_STRING Directory,
32 IN PUNICODE_STRING CommandLine,
33 IN ULONG MuSessionId,
34 IN ULONG Flags,
35 IN PRTL_USER_PROCESS_INFORMATION ProcessInformation)
36 {
37 PRTL_USER_PROCESS_INFORMATION ProcessInfo;
38 NTSTATUS Status;
39 RTL_USER_PROCESS_INFORMATION LocalProcessInfo;
40 PRTL_USER_PROCESS_PARAMETERS ProcessParameters;
41
42 /* Use the input process information if we have it, otherwise use local */
43 ProcessInfo = ProcessInformation;
44 if (!ProcessInfo) ProcessInfo = &LocalProcessInfo;
45
46 /* Create parameters for the target process */
47 Status = RtlCreateProcessParameters(&ProcessParameters,
48 FileName,
49 SmpDefaultLibPath.Length ?
50 &SmpDefaultLibPath : NULL,
51 Directory,
52 CommandLine,
53 SmpDefaultEnvironment,
54 NULL,
55 NULL,
56 NULL,
57 0);
58 if (!NT_SUCCESS(Status))
59 {
60 /* This is a pretty bad failure. ASSERT on checked builds and exit */
61 ASSERTMSG("RtlCreateProcessParameters failed.\n", NT_SUCCESS(Status));
62 DPRINT1("SMSS: RtlCreateProcessParameters failed for %wZ - Status == %lx\n",
63 FileName, Status);
64 return Status;
65 }
66
67 /* Set the size field as required */
68 ProcessInfo->Size = sizeof(*ProcessInfo);
69
70 /* Check if the debug flag was requested */
71 if (Flags & SMP_DEBUG_FLAG)
72 {
73 /* Write it in the process parameters */
74 ProcessParameters->DebugFlags = 1;
75 }
76 else
77 {
78 /* Otherwise inherit the flag that was passed to SMSS itself */
79 ProcessParameters->DebugFlags = SmpDebug;
80 }
81
82 /* Subsystems get the first 1MB of memory reserved for DOS/IVT purposes */
83 if (Flags & SMP_SUBSYSTEM_FLAG)
84 {
85 ProcessParameters->Flags |= RTL_USER_PROCESS_PARAMETERS_RESERVE_1MB;
86 }
87
88 /* And always force NX for anything that SMSS launches */
89 ProcessParameters->Flags |= RTL_USER_PROCESS_PARAMETERS_NX;
90
91 /* Now create the process in suspended state */
92 Status = RtlCreateUserProcess(FileName,
93 OBJ_CASE_INSENSITIVE,
94 ProcessParameters,
95 NULL,
96 NULL,
97 NULL,
98 FALSE,
99 NULL,
100 NULL,
101 ProcessInfo);
102 RtlDestroyProcessParameters(ProcessParameters);
103 if (!NT_SUCCESS(Status))
104 {
105 /* If we couldn't create it, fail back to the caller */
106 DPRINT1("SMSS: Failed load of %wZ - Status == %lx\n",
107 FileName, Status);
108 return Status;
109 }
110
111 /* Associate a session with this process */
112 Status = SmpSetProcessMuSessionId(ProcessInfo->ProcessHandle, MuSessionId);
113
114 /* If the application is deferred (suspended), there's nothing to do */
115 if (Flags & SMP_DEFERRED_FLAG) return Status;
116
117 /* Otherwise, get ready to start it, but make sure it's a native app */
118 if (ProcessInfo->ImageInformation.SubSystemType == IMAGE_SUBSYSTEM_NATIVE)
119 {
120 /* Resume it */
121 NtResumeThread(ProcessInfo->ThreadHandle, NULL);
122 if (!(Flags & SMP_ASYNC_FLAG))
123 {
124 /* Block on it unless Async was requested */
125 NtWaitForSingleObject(ProcessInfo->ThreadHandle, FALSE, NULL);
126 }
127
128 /* It's up and running now, close our handles */
129 NtClose(ProcessInfo->ThreadHandle);
130 NtClose(ProcessInfo->ProcessHandle);
131 }
132 else
133 {
134 /* This image is invalid, so kill it, close our handles, and fail */
135 Status = STATUS_INVALID_IMAGE_FORMAT;
136 NtTerminateProcess(ProcessInfo->ProcessHandle, Status);
137 NtWaitForSingleObject(ProcessInfo->ThreadHandle, FALSE, NULL);
138 NtClose(ProcessInfo->ThreadHandle);
139 NtClose(ProcessInfo->ProcessHandle);
140 DPRINT1("SMSS: Not an NT image - %wZ\n", FileName);
141 }
142
143 /* Return the outcome of the process create */
144 return Status;
145 }
146
147 NTSTATUS
148 NTAPI
SmpInvokeAutoChk(IN PUNICODE_STRING FileName,IN PUNICODE_STRING Directory,IN PUNICODE_STRING Arguments,IN ULONG Flags)149 SmpInvokeAutoChk(IN PUNICODE_STRING FileName,
150 IN PUNICODE_STRING Directory,
151 IN PUNICODE_STRING Arguments,
152 IN ULONG Flags)
153 {
154 ANSI_STRING MessageString;
155 CHAR MessageBuffer[256];
156 UNICODE_STRING Destination;
157 WCHAR Buffer[1024];
158 BOOLEAN BootState, BootOkay, ShutdownOkay;
159
160 /* Check if autochk should show dots (if the user booted with /SOS) */
161 if (SmpQueryRegistrySosOption()) SmpEnableDots = FALSE;
162
163 /* Make sure autochk was actually found */
164 if (Flags & SMP_INVALID_PATH)
165 {
166 /* It wasn't, so create an error message to print on the screen */
167 RtlStringCbPrintfA(MessageBuffer,
168 sizeof(MessageBuffer),
169 "%wZ program not found - skipping AUTOCHECK\r\n",
170 FileName);
171 RtlInitAnsiString(&MessageString, MessageBuffer);
172 if (NT_SUCCESS(RtlAnsiStringToUnicodeString(&Destination,
173 &MessageString,
174 TRUE)))
175 {
176 /* And show it */
177 NtDisplayString(&Destination);
178 RtlFreeUnicodeString(&Destination);
179 }
180 }
181 else
182 {
183 /* Autochk is there, so record the BSD state */
184 BootState = SmpSaveAndClearBootStatusData(&BootOkay, &ShutdownOkay);
185
186 /* Build the path to autochk and place its arguments */
187 RtlInitEmptyUnicodeString(&Destination, Buffer, sizeof(Buffer));
188 RtlAppendUnicodeStringToString(&Destination, FileName);
189 RtlAppendUnicodeToString(&Destination, L" ");
190 RtlAppendUnicodeStringToString(&Destination, Arguments);
191
192 /* Execute it */
193 SmpExecuteImage(FileName,
194 Directory,
195 &Destination,
196 0,
197 Flags & ~SMP_AUTOCHK_FLAG,
198 NULL);
199
200 /* Restore the BSD state */
201 if (BootState) SmpRestoreBootStatusData(BootOkay, ShutdownOkay);
202 }
203
204 /* We're all done! */
205 return STATUS_SUCCESS;
206 }
207
208 NTSTATUS
209 NTAPI
SmpExecuteCommand(IN PUNICODE_STRING CommandLine,IN ULONG MuSessionId,OUT PHANDLE ProcessId,IN ULONG Flags)210 SmpExecuteCommand(IN PUNICODE_STRING CommandLine,
211 IN ULONG MuSessionId,
212 OUT PHANDLE ProcessId,
213 IN ULONG Flags)
214 {
215 NTSTATUS Status;
216 UNICODE_STRING Arguments, Directory, FileName;
217
218 /* There's no longer a debugging subsystem */
219 if (Flags & SMP_DEBUG_FLAG) return STATUS_SUCCESS;
220
221 /* Parse the command line to see what execution flags are requested */
222 Status = SmpParseCommandLine(CommandLine,
223 &Flags,
224 &FileName,
225 &Directory,
226 &Arguments);
227 if (!NT_SUCCESS(Status))
228 {
229 /* Fail if we couldn't do that */
230 DPRINT1("SMSS: SmpParseCommandLine( %wZ ) failed - Status == %lx\n",
231 CommandLine, Status);
232 return Status;
233 }
234
235 /* Check if autochk is requested */
236 if (Flags & SMP_AUTOCHK_FLAG)
237 {
238 /* Run it */
239 Status = SmpInvokeAutoChk(&FileName, &Directory, &Arguments, Flags);
240 }
241 else if (Flags & SMP_SUBSYSTEM_FLAG)
242 {
243 Status = SmpLoadSubSystem(&FileName,
244 &Directory,
245 CommandLine,
246 MuSessionId,
247 ProcessId,
248 Flags);
249 }
250 else if (Flags & SMP_INVALID_PATH)
251 {
252 /* An invalid image was specified, fail */
253 DPRINT1("SMSS: Image file (%wZ) not found\n", &FileName);
254 Status = STATUS_OBJECT_NAME_NOT_FOUND;
255 }
256 else
257 {
258 /* An actual image name was present, execute it */
259 Status = SmpExecuteImage(&FileName,
260 &Directory,
261 CommandLine,
262 MuSessionId,
263 Flags,
264 NULL);
265 }
266
267 /* Free all the token parameters */
268 if (FileName.Buffer) RtlFreeHeap(RtlGetProcessHeap(), 0, FileName.Buffer);
269 if (Directory.Buffer) RtlFreeHeap(RtlGetProcessHeap(), 0, Directory.Buffer);
270 if (Arguments.Buffer) RtlFreeHeap(RtlGetProcessHeap(), 0, Arguments.Buffer);
271
272 /* Return to the caller */
273 if (!NT_SUCCESS(Status))
274 {
275 DPRINT1("SMSS: Command '%wZ' failed - Status == %x\n",
276 CommandLine, Status);
277 }
278 return Status;
279 }
280
281 NTSTATUS
282 NTAPI
SmpExecuteInitialCommand(IN ULONG MuSessionId,IN PUNICODE_STRING InitialCommand,IN HANDLE InitialCommandProcess,OUT PHANDLE ReturnPid)283 SmpExecuteInitialCommand(IN ULONG MuSessionId,
284 IN PUNICODE_STRING InitialCommand,
285 IN HANDLE InitialCommandProcess,
286 OUT PHANDLE ReturnPid)
287 {
288 NTSTATUS Status;
289 RTL_USER_PROCESS_INFORMATION ProcessInfo;
290 UNICODE_STRING Arguments, Directory, FileName;
291 ULONG Flags = 0;
292
293 /* Check if we haven't yet connected to ourselves */
294 if (!SmApiPort)
295 {
296 /* Connect to ourselves, as a client */
297 Status = SmConnectToSm(NULL, NULL, 0, &SmApiPort);
298 if (!NT_SUCCESS(Status))
299 {
300 DPRINT1("SMSS: Unable to connect to SM - Status == %lx\n", Status);
301 return Status;
302 }
303 }
304
305 /* Parse the initial command line */
306 Status = SmpParseCommandLine(InitialCommand,
307 &Flags,
308 &FileName,
309 &Directory,
310 &Arguments);
311 if (Flags & SMP_INVALID_PATH)
312 {
313 /* Fail if it doesn't exist */
314 DPRINT1("SMSS: Initial command image (%wZ) not found\n", &FileName);
315 if (FileName.Buffer) RtlFreeHeap(RtlGetProcessHeap(), 0, FileName.Buffer);
316 return STATUS_OBJECT_NAME_NOT_FOUND;
317 }
318
319 /* And fail if any other reason is also true */
320 if (!NT_SUCCESS(Status))
321 {
322 DPRINT1("SMSS: SmpParseCommandLine( %wZ ) failed - Status == %lx\n",
323 InitialCommand, Status);
324 return Status;
325 }
326
327 /* Execute the initial command, but defer its full execution */
328 Status = SmpExecuteImage(&FileName,
329 &Directory,
330 InitialCommand,
331 MuSessionId,
332 SMP_DEFERRED_FLAG,
333 &ProcessInfo);
334
335 /* Free all the token parameters */
336 if (FileName.Buffer) RtlFreeHeap(RtlGetProcessHeap(), 0, FileName.Buffer);
337 if (Directory.Buffer) RtlFreeHeap(RtlGetProcessHeap(), 0, Directory.Buffer);
338 if (Arguments.Buffer) RtlFreeHeap(RtlGetProcessHeap(), 0, Arguments.Buffer);
339
340 /* Bail out if we couldn't execute the initial command */
341 if (!NT_SUCCESS(Status)) return Status;
342
343 /* Now duplicate the handle to this process */
344 Status = NtDuplicateObject(NtCurrentProcess(),
345 ProcessInfo.ProcessHandle,
346 NtCurrentProcess(),
347 InitialCommandProcess,
348 PROCESS_ALL_ACCESS,
349 0,
350 0);
351 if (!NT_SUCCESS(Status))
352 {
353 /* Kill it utterly if duplication failed */
354 DPRINT1("SMSS: DupObject Failed. Status == %lx\n", Status);
355 NtTerminateProcess(ProcessInfo.ProcessHandle, Status);
356 NtResumeThread(ProcessInfo.ThreadHandle, NULL);
357 NtClose(ProcessInfo.ThreadHandle);
358 NtClose(ProcessInfo.ProcessHandle);
359 return Status;
360 }
361
362 /* Return PID to the caller, and set this as the initial command PID */
363 if (ReturnPid) *ReturnPid = ProcessInfo.ClientId.UniqueProcess;
364 if (!MuSessionId) SmpInitialCommandProcessId = ProcessInfo.ClientId.UniqueProcess;
365
366 /* Now call our server execution function to wrap up its initialization */
367 Status = SmExecPgm(SmApiPort, &ProcessInfo, FALSE);
368 if (!NT_SUCCESS(Status)) DPRINT1("SMSS: SmExecPgm Failed. Status == %lx\n", Status);
369 return Status;
370 }
371
372 NTSTATUS
373 NTAPI
SmpTerminate(IN PULONG_PTR Parameters,IN ULONG ParameterMask,IN ULONG ParameterCount)374 SmpTerminate(IN PULONG_PTR Parameters,
375 IN ULONG ParameterMask,
376 IN ULONG ParameterCount)
377 {
378 NTSTATUS Status;
379 BOOLEAN Old;
380 ULONG Response;
381
382 /* Give the shutdown privilege to the thread */
383 if (RtlAdjustPrivilege(SE_SHUTDOWN_PRIVILEGE, TRUE, TRUE, &Old) ==
384 STATUS_NO_TOKEN)
385 {
386 /* Thread doesn't have a token, give it to the entire process */
387 RtlAdjustPrivilege(SE_SHUTDOWN_PRIVILEGE, TRUE, FALSE, &Old);
388 }
389
390 /* Take down the process/machine with a hard error */
391 Status = NtRaiseHardError(STATUS_SYSTEM_PROCESS_TERMINATED,
392 ParameterCount,
393 ParameterMask,
394 Parameters,
395 OptionShutdownSystem,
396 &Response);
397
398 /* Terminate the process if the hard error didn't already */
399 return NtTerminateProcess(NtCurrentProcess(), Status);
400 }
401
402 LONG
SmpUnhandledExceptionFilter(IN PEXCEPTION_POINTERS ExceptionInfo)403 SmpUnhandledExceptionFilter(IN PEXCEPTION_POINTERS ExceptionInfo)
404 {
405 ULONG_PTR Parameters[4];
406 UNICODE_STRING ErrorString;
407
408 /* Print and breakpoint into the debugger */
409 DbgPrint("SMSS: Unhandled exception - Status == %x IP == %p\n",
410 ExceptionInfo->ExceptionRecord->ExceptionCode,
411 ExceptionInfo->ExceptionRecord->ExceptionAddress);
412 DbgPrint(" Memory Address: %x Read/Write: %x\n",
413 ExceptionInfo->ExceptionRecord->ExceptionInformation[0],
414 ExceptionInfo->ExceptionRecord->ExceptionInformation[1]);
415 DbgBreakPoint();
416
417 /* Build the hard error and terminate */
418 RtlInitUnicodeString(&ErrorString, L"Unhandled Exception in Session Manager");
419 Parameters[0] = (ULONG_PTR)&ErrorString;
420 Parameters[1] = ExceptionInfo->ExceptionRecord->ExceptionCode;
421 Parameters[2] = (ULONG_PTR)ExceptionInfo->ExceptionRecord->ExceptionAddress;
422 Parameters[3] = (ULONG_PTR)ExceptionInfo->ContextRecord;
423 SmpTerminate(Parameters, 1, RTL_NUMBER_OF(Parameters));
424
425 /* We should never get here */
426 ASSERT(FALSE);
427 return EXCEPTION_EXECUTE_HANDLER;
428 }
429
430 NTSTATUS
431 __cdecl
_main(IN INT argc,IN PCHAR argv[],IN PCHAR envp[],IN ULONG DebugFlag)432 _main(IN INT argc,
433 IN PCHAR argv[],
434 IN PCHAR envp[],
435 IN ULONG DebugFlag)
436 {
437 NTSTATUS Status;
438 KPRIORITY SetBasePriority;
439 ULONG_PTR Parameters[4];
440 HANDLE Handles[2];
441 PVOID State;
442 ULONG Flags;
443 PROCESS_BASIC_INFORMATION ProcessInfo;
444 UNICODE_STRING DbgString, InitialCommand;
445
446 /* Make us critical */
447 RtlSetProcessIsCritical(TRUE, NULL, FALSE);
448 RtlSetThreadIsCritical(TRUE, NULL, FALSE);
449
450 /* Raise our priority */
451 SetBasePriority = 11;
452 Status = NtSetInformationProcess(NtCurrentProcess(),
453 ProcessBasePriority,
454 (PVOID)&SetBasePriority,
455 sizeof(SetBasePriority));
456 ASSERT(NT_SUCCESS(Status));
457
458 /* Save the debug flag if it was passed */
459 if (DebugFlag) SmpDebug = DebugFlag != 0;
460
461 /* Build the hard error parameters */
462 Parameters[0] = (ULONG_PTR)&DbgString;
463 Parameters[1] = Parameters[2] = Parameters[3] = 0;
464
465 /* Enter SEH so we can terminate correctly if anything goes wrong */
466 _SEH2_TRY
467 {
468 /* Initialize SMSS */
469 Status = SmpInit(&InitialCommand, &Handles[0]);
470 if (!NT_SUCCESS(Status))
471 {
472 DPRINT1("SMSS: SmpInit return failure - Status == %x\n", Status);
473 RtlInitUnicodeString(&DbgString, L"Session Manager Initialization");
474 Parameters[1] = Status;
475 _SEH2_LEAVE;
476 }
477
478 /* Get the global flags */
479 Status = NtQuerySystemInformation(SystemFlagsInformation,
480 &Flags,
481 sizeof(Flags),
482 NULL);
483 ASSERT(NT_SUCCESS(Status));
484
485 /* Before executing the initial command check if the debug flag is on */
486 if (Flags & (FLG_DEBUG_INITIAL_COMMAND | FLG_DEBUG_INITIAL_COMMAND_EX))
487 {
488 /* SMSS should launch ntsd with a few parameters at this point */
489 DPRINT1("Global Flags Set to SMSS Debugging: Not yet supported\n");
490 }
491
492 /* Execute the initial command (Winlogon.exe) */
493 Status = SmpExecuteInitialCommand(0, &InitialCommand, &Handles[1], NULL);
494 if (!NT_SUCCESS(Status))
495 {
496 /* Fail and raise a hard error */
497 DPRINT1("SMSS: Execute Initial Command failed\n");
498 RtlInitUnicodeString(&DbgString,
499 L"Session Manager ExecuteInitialCommand");
500 Parameters[1] = Status;
501 _SEH2_LEAVE;
502 }
503
504 /* Check if we're already attached to a session */
505 Status = SmpAcquirePrivilege(SE_LOAD_DRIVER_PRIVILEGE, &State);
506 if (AttachedSessionId != -1)
507 {
508 /* Detach from it, we should be in no session right now */
509 Status = NtSetSystemInformation(SystemSessionDetach,
510 &AttachedSessionId,
511 sizeof(AttachedSessionId));
512 ASSERT(NT_SUCCESS(Status));
513 AttachedSessionId = -1;
514 }
515 SmpReleasePrivilege(State);
516
517 /* Wait on either CSRSS or Winlogon to die */
518 Status = NtWaitForMultipleObjects(RTL_NUMBER_OF(Handles),
519 Handles,
520 WaitAny,
521 FALSE,
522 NULL);
523 if (Status == STATUS_WAIT_0)
524 {
525 /* CSRSS is dead, get exit code and prepare for the hard error */
526 RtlInitUnicodeString(&DbgString, L"Windows SubSystem");
527 Status = NtQueryInformationProcess(Handles[0],
528 ProcessBasicInformation,
529 &ProcessInfo,
530 sizeof(ProcessInfo),
531 NULL);
532 DPRINT1("SMSS: Windows subsystem terminated when it wasn't supposed to.\n");
533 }
534 else
535 {
536 /* The initial command is dead or we have another failure */
537 RtlInitUnicodeString(&DbgString, L"Windows Logon Process");
538 if (Status == STATUS_WAIT_1)
539 {
540 /* Winlogon.exe got terminated, get its exit code */
541 Status = NtQueryInformationProcess(Handles[1],
542 ProcessBasicInformation,
543 &ProcessInfo,
544 sizeof(ProcessInfo),
545 NULL);
546 }
547 else
548 {
549 /* Something else satisfied our wait, so set the wait status */
550 ProcessInfo.ExitStatus = Status;
551 Status = STATUS_SUCCESS;
552 }
553 DPRINT1("SMSS: Initial command '%wZ' terminated when it wasn't supposed to.\n",
554 &InitialCommand);
555 }
556
557 /* Check if NtQueryInformationProcess was successful */
558 if (NT_SUCCESS(Status))
559 {
560 /* Then we must have a valid exit status in the structure, use it */
561 Parameters[1] = ProcessInfo.ExitStatus;
562 }
563 else
564 {
565 /* We really don't know what happened, so set a generic error */
566 Parameters[1] = STATUS_UNSUCCESSFUL;
567 }
568 }
569 _SEH2_EXCEPT(SmpUnhandledExceptionFilter(_SEH2_GetExceptionInformation()))
570 {
571 /* The filter should never return here */
572 ASSERT(FALSE);
573 }
574 _SEH2_END;
575
576 /* Something in the init loop failed, terminate SMSS */
577 return SmpTerminate(Parameters, 1, RTL_NUMBER_OF(Parameters));
578 }
579
580 /* EOF */
581