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 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 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 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 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 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 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 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 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 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