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