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 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(RTL_USER_PROCESS_INFORMATION); 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 */ 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, 0, 0); 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 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 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 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, ImageFileDirectory, ImageFileName; 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 &ImageFileName, 309 &ImageFileDirectory, 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", &ImageFileName); 315 if (ImageFileName.Buffer) RtlFreeHeap(RtlGetProcessHeap(), 0, ImageFileName.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(&ImageFileName, 329 &ImageFileDirectory, 330 InitialCommand, 331 MuSessionId, 332 SMP_DEFERRED_FLAG, 333 &ProcessInfo); 334 335 /* Free any buffers we had lying around */ 336 if (ImageFileName.Buffer) 337 { 338 RtlFreeHeap(RtlGetProcessHeap(), 0, ImageFileName.Buffer); 339 } 340 if (ImageFileDirectory.Buffer) 341 { 342 RtlFreeHeap(RtlGetProcessHeap(), 0, ImageFileDirectory.Buffer); 343 } 344 if (Arguments.Buffer) RtlFreeHeap(RtlGetProcessHeap(), 0, Arguments.Buffer); 345 346 /* Bail out if we couldn't execute the initial command */ 347 if (!NT_SUCCESS(Status)) return Status; 348 349 /* Now duplicate the handle to this process */ 350 Status = NtDuplicateObject(NtCurrentProcess(), 351 ProcessInfo.ProcessHandle, 352 NtCurrentProcess(), 353 InitialCommandProcess, 354 PROCESS_ALL_ACCESS, 355 0, 356 0); 357 if (!NT_SUCCESS(Status)) 358 { 359 /* Kill it utterly if duplication failed */ 360 DPRINT1("SMSS: DupObject Failed. Status == %lx\n", Status); 361 NtTerminateProcess(ProcessInfo.ProcessHandle, Status); 362 NtResumeThread(ProcessInfo.ThreadHandle, NULL); 363 NtClose(ProcessInfo.ThreadHandle); 364 NtClose(ProcessInfo.ProcessHandle); 365 return Status; 366 } 367 368 /* Return PID to the caller, and set this as the initial command PID */ 369 if (ReturnPid) *ReturnPid = ProcessInfo.ClientId.UniqueProcess; 370 if (!MuSessionId) SmpInitialCommandProcessId = ProcessInfo.ClientId.UniqueProcess; 371 372 /* Now call our server execution function to wrap up its initialization */ 373 Status = SmExecPgm(SmApiPort, &ProcessInfo, FALSE); 374 if (!NT_SUCCESS(Status)) DPRINT1("SMSS: SmExecPgm Failed. Status == %lx\n", Status); 375 return Status; 376 } 377 378 NTSTATUS 379 NTAPI 380 SmpTerminate(IN PULONG_PTR Parameters, 381 IN ULONG ParameterMask, 382 IN ULONG ParameterCount) 383 { 384 NTSTATUS Status; 385 BOOLEAN Old; 386 ULONG Response; 387 388 /* Give the shutdown privilege to the thread */ 389 if (RtlAdjustPrivilege(SE_SHUTDOWN_PRIVILEGE, TRUE, TRUE, &Old) == 390 STATUS_NO_TOKEN) 391 { 392 /* Thread doesn't have a token, give it to the entire process */ 393 RtlAdjustPrivilege(SE_SHUTDOWN_PRIVILEGE, TRUE, FALSE, &Old); 394 } 395 396 /* Take down the process/machine with a hard error */ 397 Status = NtRaiseHardError(STATUS_SYSTEM_PROCESS_TERMINATED, 398 ParameterCount, 399 ParameterMask, 400 Parameters, 401 OptionShutdownSystem, 402 &Response); 403 404 /* Terminate the process if the hard error didn't already */ 405 return NtTerminateProcess(NtCurrentProcess(), Status); 406 } 407 408 LONG 409 SmpUnhandledExceptionFilter(IN PEXCEPTION_POINTERS ExceptionInfo) 410 { 411 ULONG_PTR Parameters[4]; 412 UNICODE_STRING DestinationString; 413 414 /* Print and breakpoint into the debugger */ 415 DbgPrint("SMSS: Unhandled exception - Status == %x IP == %p\n", 416 ExceptionInfo->ExceptionRecord->ExceptionCode, 417 ExceptionInfo->ExceptionRecord->ExceptionAddress); 418 DbgPrint(" Memory Address: %x Read/Write: %x\n", 419 ExceptionInfo->ExceptionRecord->ExceptionInformation[0], 420 ExceptionInfo->ExceptionRecord->ExceptionInformation[1]); 421 DbgBreakPoint(); 422 423 /* Build the hard error and terminate */ 424 RtlInitUnicodeString(&DestinationString, L"Unhandled Exception in Session Manager"); 425 Parameters[0] = (ULONG_PTR)&DestinationString; 426 Parameters[1] = ExceptionInfo->ExceptionRecord->ExceptionCode; 427 Parameters[2] = (ULONG_PTR)ExceptionInfo->ExceptionRecord->ExceptionAddress; 428 Parameters[3] = (ULONG_PTR)ExceptionInfo->ContextRecord; 429 SmpTerminate(Parameters, 1, RTL_NUMBER_OF(Parameters)); 430 431 /* We should never get here */ 432 ASSERT(FALSE); 433 return EXCEPTION_EXECUTE_HANDLER; 434 } 435 436 NTSTATUS 437 __cdecl 438 _main(IN INT argc, 439 IN PCHAR argv[], 440 IN PCHAR envp[], 441 IN ULONG DebugFlag) 442 { 443 NTSTATUS Status; 444 KPRIORITY SetBasePriority; 445 ULONG_PTR Parameters[4]; 446 HANDLE Handles[2]; 447 PVOID State; 448 ULONG Flags; 449 PROCESS_BASIC_INFORMATION ProcessInfo; 450 UNICODE_STRING DbgString, InitialCommand; 451 452 /* Make us critical */ 453 RtlSetProcessIsCritical(TRUE, NULL, FALSE); 454 RtlSetThreadIsCritical(TRUE, NULL, FALSE); 455 456 /* Raise our priority */ 457 SetBasePriority = 11; 458 Status = NtSetInformationProcess(NtCurrentProcess(), 459 ProcessBasePriority, 460 (PVOID)&SetBasePriority, 461 sizeof(SetBasePriority)); 462 ASSERT(NT_SUCCESS(Status)); 463 464 /* Save the debug flag if it was passed */ 465 if (DebugFlag) SmpDebug = DebugFlag != 0; 466 467 /* Build the hard error parameters */ 468 Parameters[0] = (ULONG_PTR)&DbgString; 469 Parameters[1] = Parameters[2] = Parameters[3] = 0; 470 471 /* Enter SEH so we can terminate correctly if anything goes wrong */ 472 _SEH2_TRY 473 { 474 /* Initialize SMSS */ 475 Status = SmpInit(&InitialCommand, &Handles[0]); 476 if (!NT_SUCCESS(Status)) 477 { 478 DPRINT1("SMSS: SmpInit return failure - Status == %x\n", Status); 479 RtlInitUnicodeString(&DbgString, L"Session Manager Initialization"); 480 Parameters[1] = Status; 481 _SEH2_LEAVE; 482 } 483 484 /* Get the global flags */ 485 Status = NtQuerySystemInformation(SystemFlagsInformation, 486 &Flags, 487 sizeof(Flags), 488 NULL); 489 ASSERT(NT_SUCCESS(Status)); 490 491 /* Before executing the initial command check if the debug flag is on */ 492 if (Flags & (FLG_DEBUG_INITIAL_COMMAND | FLG_DEBUG_INITIAL_COMMAND_EX)) 493 { 494 /* SMSS should launch ntsd with a few parameters at this point */ 495 DPRINT1("Global Flags Set to SMSS Debugging: Not yet supported\n"); 496 } 497 498 /* Execute the initial command (Winlogon.exe) */ 499 Status = SmpExecuteInitialCommand(0, &InitialCommand, &Handles[1], NULL); 500 if (!NT_SUCCESS(Status)) 501 { 502 /* Fail and raise a hard error */ 503 DPRINT1("SMSS: Execute Initial Command failed\n"); 504 RtlInitUnicodeString(&DbgString, 505 L"Session Manager ExecuteInitialCommand"); 506 Parameters[1] = Status; 507 _SEH2_LEAVE; 508 } 509 510 /* Check if we're already attached to a session */ 511 Status = SmpAcquirePrivilege(SE_LOAD_DRIVER_PRIVILEGE, &State); 512 if (AttachedSessionId != -1) 513 { 514 /* Detach from it, we should be in no session right now */ 515 Status = NtSetSystemInformation(SystemSessionDetach, 516 &AttachedSessionId, 517 sizeof(AttachedSessionId)); 518 ASSERT(NT_SUCCESS(Status)); 519 AttachedSessionId = -1; 520 } 521 SmpReleasePrivilege(State); 522 523 /* Wait on either CSRSS or Winlogon to die */ 524 Status = NtWaitForMultipleObjects(RTL_NUMBER_OF(Handles), 525 Handles, 526 WaitAny, 527 FALSE, 528 NULL); 529 if (Status == STATUS_WAIT_0) 530 { 531 /* CSRSS is dead, get exit code and prepare for the hard error */ 532 RtlInitUnicodeString(&DbgString, L"Windows SubSystem"); 533 Status = NtQueryInformationProcess(Handles[0], 534 ProcessBasicInformation, 535 &ProcessInfo, 536 sizeof(ProcessInfo), 537 NULL); 538 DPRINT1("SMSS: Windows subsystem terminated when it wasn't supposed to.\n"); 539 } 540 else 541 { 542 /* The initial command is dead or we have another failure */ 543 RtlInitUnicodeString(&DbgString, L"Windows Logon Process"); 544 if (Status == STATUS_WAIT_1) 545 { 546 /* Winlogon.exe got terminated, get its exit code */ 547 Status = NtQueryInformationProcess(Handles[1], 548 ProcessBasicInformation, 549 &ProcessInfo, 550 sizeof(ProcessInfo), 551 NULL); 552 } 553 else 554 { 555 /* Something else satisfied our wait, so set the wait status */ 556 ProcessInfo.ExitStatus = Status; 557 Status = STATUS_SUCCESS; 558 } 559 DPRINT1("SMSS: Initial command '%wZ' terminated when it wasn't supposed to.\n", 560 &InitialCommand); 561 } 562 563 /* Check if NtQueryInformationProcess was successful */ 564 if (NT_SUCCESS(Status)) 565 { 566 /* Then we must have a valid exit status in the structure, use it */ 567 Parameters[1] = ProcessInfo.ExitStatus; 568 } 569 else 570 { 571 /* We really don't know what happened, so set a generic error */ 572 Parameters[1] = STATUS_UNSUCCESSFUL; 573 } 574 } 575 _SEH2_EXCEPT(SmpUnhandledExceptionFilter(_SEH2_GetExceptionInformation())) 576 { 577 /* The filter should never return here */ 578 ASSERT(FALSE); 579 } 580 _SEH2_END; 581 582 /* Something in the init loop failed, terminate SMSS */ 583 return SmpTerminate(Parameters, 1, RTL_NUMBER_OF(Parameters)); 584 } 585 586 /* EOF */ 587