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(*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 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, 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 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 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 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