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 PVOID Subsystem; 21 HANDLE ProcessHandle; 22 HANDLE PortHandle; 23 ULONG dword10; 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 34 volatile LONG SmTotalApiThreads; 35 HANDLE SmUniqueProcessId; 36 37 /* API HANDLERS ***************************************************************/ 38 39 NTSTATUS 40 NTAPI 41 SmpCreateForeignSession(IN PSM_API_MSG SmApiMsg, 42 IN PSMP_CLIENT_CONTEXT ClientContext, 43 IN HANDLE SmApiPort) 44 { 45 DPRINT1("%s is not yet implemented\n", __FUNCTION__); 46 return STATUS_NOT_IMPLEMENTED; 47 } 48 49 NTSTATUS 50 NTAPI 51 SmpSessionComplete(IN PSM_API_MSG SmApiMsg, 52 IN PSMP_CLIENT_CONTEXT ClientContext, 53 IN HANDLE SmApiPort) 54 { 55 DPRINT1("%s is not yet implemented\n", __FUNCTION__); 56 return STATUS_NOT_IMPLEMENTED; 57 } 58 59 NTSTATUS 60 NTAPI 61 SmpTerminateForeignSession(IN PSM_API_MSG SmApiMsg, 62 IN PSMP_CLIENT_CONTEXT ClientContext, 63 IN HANDLE SmApiPort) 64 { 65 DPRINT1("%s is not yet implemented\n", __FUNCTION__); 66 return STATUS_NOT_IMPLEMENTED; 67 } 68 69 NTSTATUS 70 NTAPI 71 SmpExecPgm(IN PSM_API_MSG SmApiMsg, 72 IN PSMP_CLIENT_CONTEXT ClientContext, 73 IN HANDLE SmApiPort) 74 { 75 HANDLE ProcessHandle; 76 NTSTATUS Status; 77 PSM_EXEC_PGM_MSG SmExecPgm; 78 RTL_USER_PROCESS_INFORMATION ProcessInformation; 79 OBJECT_ATTRIBUTES ObjectAttributes; 80 81 /* Open the client process */ 82 InitializeObjectAttributes(&ObjectAttributes, NULL, 0, NULL, NULL); 83 Status = NtOpenProcess(&ProcessHandle, 84 PROCESS_DUP_HANDLE, 85 &ObjectAttributes, 86 &SmApiMsg->h.ClientId); 87 if (!NT_SUCCESS(Status)) 88 { 89 /* Fail */ 90 DPRINT1("SmExecPgm: NtOpenProcess Failed %lx\n", Status); 91 return Status; 92 } 93 94 /* Copy the process information out of the message */ 95 SmExecPgm = &SmApiMsg->u.ExecPgm; 96 ProcessInformation = SmExecPgm->ProcessInformation; 97 98 /* Duplicate the process handle */ 99 Status = NtDuplicateObject(ProcessHandle, 100 SmExecPgm->ProcessInformation.ProcessHandle, 101 NtCurrentProcess(), 102 &ProcessInformation.ProcessHandle, 103 PROCESS_ALL_ACCESS, 104 0, 105 0); 106 if (!NT_SUCCESS(Status)) 107 { 108 /* Close the handle and fail */ 109 NtClose(ProcessHandle); 110 DPRINT1("SmExecPgm: NtDuplicateObject (Process) Failed %lx\n", Status); 111 return Status; 112 } 113 114 /* Duplicate the thread handle */ 115 Status = NtDuplicateObject(ProcessHandle, 116 SmExecPgm->ProcessInformation.ThreadHandle, 117 NtCurrentProcess(), 118 &ProcessInformation.ThreadHandle, 119 THREAD_ALL_ACCESS, 120 0, 121 0); 122 if (!NT_SUCCESS(Status)) 123 { 124 /* Close both handles and fail */ 125 NtClose(ProcessInformation.ProcessHandle); 126 NtClose(ProcessHandle); 127 DPRINT1("SmExecPgm: NtDuplicateObject (Thread) Failed %lx\n", Status); 128 return Status; 129 } 130 131 /* Close the process handle and call the internal client API */ 132 NtClose(ProcessHandle); 133 return SmpSbCreateSession(NULL, 134 NULL, 135 &ProcessInformation, 136 0, 137 SmExecPgm->DebugFlag ? &SmApiMsg->h.ClientId : NULL); 138 } 139 140 NTSTATUS 141 NTAPI 142 SmpLoadDeferedSubsystem(IN PSM_API_MSG SmApiMsg, 143 IN PSMP_CLIENT_CONTEXT ClientContext, 144 IN HANDLE SmApiPort) 145 { 146 DPRINT1("%s is not yet implemented\n", __FUNCTION__); 147 return STATUS_NOT_IMPLEMENTED; 148 } 149 150 NTSTATUS 151 NTAPI 152 SmpStartCsr(IN PSM_API_MSG SmApiMsg, 153 IN PSMP_CLIENT_CONTEXT ClientContext, 154 IN HANDLE SmApiPort) 155 { 156 PSM_START_CSR_MSG SmStartCsr = &SmApiMsg->u.StartCsr; 157 UNICODE_STRING InitialCommand; 158 HANDLE InitialCommandProcess, InitialCommandProcessId, WindowsSubSysProcessId; 159 NTSTATUS Status; 160 161 Status = SmpLoadSubSystemsForMuSession(&SmStartCsr->MuSessionId, 162 &WindowsSubSysProcessId, 163 &InitialCommand); 164 if (!NT_SUCCESS(Status)) 165 { 166 DPRINT1("SMSS: SmpLoadSubSystemsForMuSession failed with status 0x%08x\n", Status); 167 return Status; 168 } 169 170 if (SmStartCsr->Length) 171 { 172 InitialCommand.Length = InitialCommand.MaximumLength = SmStartCsr->Length; 173 InitialCommand.Buffer = SmStartCsr->Buffer; 174 } 175 176 Status = SmpExecuteInitialCommand(SmStartCsr->MuSessionId, 177 &InitialCommand, 178 &InitialCommandProcess, 179 &InitialCommandProcessId); 180 if (!NT_SUCCESS(Status)) 181 { 182 DPRINT1("SMSS: SmpExecuteInitialCommand failed with status 0x%08x\n", Status); 183 /* FIXME: undo effects of SmpLoadSubSystemsForMuSession */ 184 ASSERT(FALSE); 185 return Status; 186 } 187 188 NtClose(InitialCommandProcess); 189 190 SmStartCsr->WindowsSubSysProcessId = WindowsSubSysProcessId; 191 SmStartCsr->SmpInitialCommandProcessId = InitialCommandProcessId; 192 193 return STATUS_SUCCESS; 194 } 195 196 NTSTATUS 197 NTAPI 198 SmpStopCsr(IN PSM_API_MSG SmApiMsg, 199 IN PSMP_CLIENT_CONTEXT ClientContext, 200 IN HANDLE SmApiPort) 201 { 202 DPRINT1("%s is not yet implemented\n", __FUNCTION__); 203 return STATUS_NOT_IMPLEMENTED; 204 } 205 206 PSM_API_HANDLER SmpApiDispatch[SmpMaxApiNumber - SmpCreateForeignSessionApi] = 207 { 208 SmpCreateForeignSession, 209 SmpSessionComplete, 210 SmpTerminateForeignSession, 211 SmpExecPgm, 212 SmpLoadDeferedSubsystem, 213 SmpStartCsr, 214 SmpStopCsr 215 }; 216 217 /* FUNCTIONS ******************************************************************/ 218 219 NTSTATUS 220 NTAPI 221 SmpHandleConnectionRequest(IN HANDLE SmApiPort, 222 IN PSB_API_MSG SbApiMsg) 223 { 224 BOOLEAN Accept = TRUE; 225 HANDLE PortHandle, ProcessHandle; 226 ULONG SessionId; 227 UNICODE_STRING SubsystemPort; 228 SMP_CLIENT_CONTEXT *ClientContext; 229 NTSTATUS Status; 230 OBJECT_ATTRIBUTES ObjectAttributes; 231 REMOTE_PORT_VIEW PortView; 232 SECURITY_QUALITY_OF_SERVICE SecurityQos; 233 PSMP_SUBSYSTEM CidSubsystem, TypeSubsystem; 234 235 /* Initialize QoS data */ 236 SecurityQos.ImpersonationLevel = SecurityIdentification; 237 SecurityQos.ContextTrackingMode = SECURITY_DYNAMIC_TRACKING; 238 SecurityQos.EffectiveOnly = TRUE; 239 240 /* Check if this is SM connecting to itself */ 241 if (SbApiMsg->h.ClientId.UniqueProcess == SmUniqueProcessId) 242 { 243 /* No need to get any handle -- assume session 0 */ 244 ProcessHandle = NULL; 245 SessionId = 0; 246 } 247 else 248 { 249 /* Reference the foreign process */ 250 InitializeObjectAttributes(&ObjectAttributes, NULL, 0, NULL, NULL); 251 Status = NtOpenProcess(&ProcessHandle, 252 PROCESS_QUERY_INFORMATION, 253 &ObjectAttributes, 254 &SbApiMsg->h.ClientId); 255 if (!NT_SUCCESS(Status)) Accept = FALSE; 256 257 /* Get its session ID */ 258 SmpGetProcessMuSessionId(ProcessHandle, &SessionId); 259 } 260 261 /* See if we already know about the caller's subsystem */ 262 CidSubsystem = SmpLocateKnownSubSysByCid(&SbApiMsg->h.ClientId); 263 if ((CidSubsystem) && (Accept)) 264 { 265 /* Check if we already have a subsystem for this kind of image */ 266 TypeSubsystem = SmpLocateKnownSubSysByType(SessionId, 267 SbApiMsg->ConnectionInfo.SubsystemType); 268 if (TypeSubsystem == CidSubsystem) 269 { 270 /* Someone is trying to take control of an existing subsystem, fail */ 271 Accept = FALSE; 272 DPRINT1("SMSS: Connection from SubSystem rejected\n"); 273 DPRINT1("SMSS: Image type already being served\n"); 274 } 275 else 276 { 277 /* Set this image type as the type for this subsystem */ 278 CidSubsystem->ImageType = SbApiMsg->ConnectionInfo.SubsystemType; 279 } 280 281 /* Drop the reference we had acquired */ 282 if (TypeSubsystem) SmpDereferenceSubsystem(TypeSubsystem); 283 } 284 285 /* Check if we'll be accepting the connection */ 286 if (Accept) 287 { 288 /* We will, so create a client context for it */ 289 ClientContext = RtlAllocateHeap(SmpHeap, 0, sizeof(SMP_CLIENT_CONTEXT)); 290 if (ClientContext) 291 { 292 ClientContext->ProcessHandle = ProcessHandle; 293 ClientContext->Subsystem = CidSubsystem; 294 ClientContext->dword10 = 0; 295 ClientContext->PortHandle = NULL; 296 } 297 else 298 { 299 /* Failed to allocate a client context, so reject the connection */ 300 DPRINT1("Rejecting connectiond due to lack of memory\n"); 301 Accept = FALSE; 302 } 303 } 304 else 305 { 306 /* Use a bogus context since we're going to reject the message */ 307 ClientContext = (PSMP_CLIENT_CONTEXT)SbApiMsg; 308 } 309 310 /* Now send the actual accept reply (which could be a rejection) */ 311 PortView.Length = sizeof(PortView); 312 Status = NtAcceptConnectPort(&PortHandle, 313 ClientContext, 314 &SbApiMsg->h, 315 Accept, 316 NULL, 317 &PortView); 318 if (!(Accept) || !(NT_SUCCESS(Status))) 319 { 320 /* Close the process handle, reference the subsystem, and exit */ 321 DPRINT1("Accept failed or rejected: %lx\n", Status); 322 if (ClientContext != (PVOID)SbApiMsg) RtlFreeHeap(SmpHeap, 0, ClientContext); 323 if (ProcessHandle) NtClose(ProcessHandle); 324 if (CidSubsystem) SmpDereferenceSubsystem(CidSubsystem); 325 return Status; 326 } 327 328 /* Save the port handle now that we've accepted it */ 329 if (ClientContext) ClientContext->PortHandle = PortHandle; 330 if (CidSubsystem) CidSubsystem->PortHandle = PortHandle; 331 332 /* Complete the port connection */ 333 Status = NtCompleteConnectPort(PortHandle); 334 if ((NT_SUCCESS(Status)) && (CidSubsystem)) 335 { 336 /* This was an actual subsystem, so connect back to it */ 337 SbApiMsg->ConnectionInfo.SbApiPortName[119] = UNICODE_NULL; 338 RtlCreateUnicodeString(&SubsystemPort, 339 SbApiMsg->ConnectionInfo.SbApiPortName); 340 Status = NtConnectPort(&CidSubsystem->SbApiPort, 341 &SubsystemPort, 342 &SecurityQos, 343 NULL, 344 NULL, 345 NULL, 346 NULL, 347 NULL); 348 if (!NT_SUCCESS(Status)) 349 { 350 DPRINT1("SMSS: Connect back to Sb %wZ failed %lx\n", &SubsystemPort, Status); 351 } 352 RtlFreeUnicodeString(&SubsystemPort); 353 354 /* Now that we're connected, signal the event handle */ 355 NtSetEvent(CidSubsystem->Event, NULL); 356 } 357 else if (CidSubsystem) 358 { 359 /* We failed to complete the connection, so clear the port handle */ 360 DPRINT1("Completing the connection failed: %lx\n", Status); 361 CidSubsystem->PortHandle = NULL; 362 } 363 364 /* Dereference the subsystem and return the result */ 365 if (CidSubsystem) SmpDereferenceSubsystem(CidSubsystem); 366 return Status; 367 } 368 369 ULONG 370 NTAPI 371 SmpApiLoop(IN PVOID Parameter) 372 { 373 HANDLE SmApiPort = (HANDLE)Parameter; 374 NTSTATUS Status; 375 PSMP_CLIENT_CONTEXT ClientContext; 376 PSM_API_MSG ReplyMsg = NULL; 377 SM_API_MSG RequestMsg; 378 PROCESS_BASIC_INFORMATION ProcessInformation; 379 LARGE_INTEGER Timeout; 380 381 /* Increase the number of API threads for throttling code for later */ 382 _InterlockedExchangeAdd(&SmTotalApiThreads, 1); 383 384 /* Mark us critical */ 385 RtlSetThreadIsCritical(TRUE, NULL, TRUE); 386 387 /* Set the PID of the SM process itself for later checking */ 388 NtQueryInformationProcess(NtCurrentProcess(), 389 ProcessBasicInformation, 390 &ProcessInformation, 391 sizeof(ProcessInformation), 392 NULL); 393 SmUniqueProcessId = (HANDLE)ProcessInformation.UniqueProcessId; 394 395 /* Now process incoming messages */ 396 while (TRUE) 397 { 398 /* Begin waiting on a request */ 399 Status = NtReplyWaitReceivePort(SmApiPort, 400 (PVOID*)&ClientContext, 401 &ReplyMsg->h, 402 &RequestMsg.h); 403 if (Status == STATUS_NO_MEMORY) 404 { 405 /* Ran out of memory, so do a little timeout and try again */ 406 if (ReplyMsg) DPRINT1("SMSS: Failed to reply to calling thread, retrying.\n"); 407 Timeout.QuadPart = -50000000; 408 NtDelayExecution(FALSE, &Timeout); 409 continue; 410 } 411 412 /* Check what kind of request we received */ 413 switch (RequestMsg.h.u2.s2.Type) 414 { 415 /* A new connection */ 416 case LPC_CONNECTION_REQUEST: 417 /* Create the right structures for it */ 418 SmpHandleConnectionRequest(SmApiPort, (PSB_API_MSG)&RequestMsg); 419 ReplyMsg = NULL; 420 break; 421 422 /* A closed connection */ 423 case LPC_PORT_CLOSED: 424 /* Destroy any state we had for this client */ 425 DPRINT1("Port closed\n"); 426 //if (ClientContext) SmpPushDeferredClientContext(ClientContext); 427 ReplyMsg = NULL; 428 break; 429 430 /* An actual API message */ 431 default: 432 if (!ClientContext) 433 { 434 ReplyMsg = NULL; 435 break; 436 } 437 438 RequestMsg.ReturnValue = STATUS_PENDING; 439 440 /* Check if the API is valid */ 441 if (RequestMsg.ApiNumber >= SmpMaxApiNumber) 442 { 443 /* It isn't, fail */ 444 DPRINT1("Invalid API: %lx\n", RequestMsg.ApiNumber); 445 Status = STATUS_NOT_IMPLEMENTED; 446 } 447 else if ((RequestMsg.ApiNumber <= SmpTerminateForeignSessionApi) && 448 !(ClientContext->Subsystem)) 449 { 450 /* It's valid, but doesn't have a subsystem with it */ 451 DPRINT1("Invalid session API\n"); 452 Status = STATUS_INVALID_PARAMETER; 453 } 454 else 455 { 456 /* It's totally okay, so call the dispatcher for it */ 457 Status = SmpApiDispatch[RequestMsg.ApiNumber](&RequestMsg, 458 ClientContext, 459 SmApiPort); 460 } 461 462 /* Write the result value and return the message back */ 463 RequestMsg.ReturnValue = Status; 464 ReplyMsg = &RequestMsg; 465 break; 466 } 467 } 468 return STATUS_SUCCESS; 469 } 470