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 DPRINT1("%s is not yet implemented\n", __FUNCTION__); 157 return STATUS_NOT_IMPLEMENTED; 158 } 159 160 NTSTATUS 161 NTAPI 162 SmpStopCsr(IN PSM_API_MSG SmApiMsg, 163 IN PSMP_CLIENT_CONTEXT ClientContext, 164 IN HANDLE SmApiPort) 165 { 166 DPRINT1("%s is not yet implemented\n", __FUNCTION__); 167 return STATUS_NOT_IMPLEMENTED; 168 } 169 170 PSM_API_HANDLER SmpApiDispatch[SmpMaxApiNumber - SmpCreateForeignSessionApi] = 171 { 172 SmpCreateForeignSession, 173 SmpSessionComplete, 174 SmpTerminateForeignSession, 175 SmpExecPgm, 176 SmpLoadDeferedSubsystem, 177 SmpStartCsr, 178 SmpStopCsr 179 }; 180 181 /* FUNCTIONS ******************************************************************/ 182 183 NTSTATUS 184 NTAPI 185 SmpHandleConnectionRequest(IN HANDLE SmApiPort, 186 IN PSB_API_MSG SbApiMsg) 187 { 188 BOOLEAN Accept = TRUE; 189 HANDLE PortHandle, ProcessHandle; 190 ULONG SessionId; 191 UNICODE_STRING SubsystemPort; 192 SMP_CLIENT_CONTEXT *ClientContext; 193 NTSTATUS Status; 194 OBJECT_ATTRIBUTES ObjectAttributes; 195 REMOTE_PORT_VIEW PortView; 196 SECURITY_QUALITY_OF_SERVICE SecurityQos; 197 PSMP_SUBSYSTEM CidSubsystem, TypeSubsystem; 198 199 /* Initialize QoS data */ 200 SecurityQos.ImpersonationLevel = SecurityIdentification; 201 SecurityQos.ContextTrackingMode = SECURITY_DYNAMIC_TRACKING; 202 SecurityQos.EffectiveOnly = TRUE; 203 204 /* Check if this is SM connecting to itself */ 205 if (SbApiMsg->h.ClientId.UniqueProcess == SmUniqueProcessId) 206 { 207 /* No need to get any handle -- assume session 0 */ 208 ProcessHandle = NULL; 209 SessionId = 0; 210 } 211 else 212 { 213 /* Reference the foreign process */ 214 InitializeObjectAttributes(&ObjectAttributes, NULL, 0, NULL, NULL); 215 Status = NtOpenProcess(&ProcessHandle, 216 PROCESS_QUERY_INFORMATION, 217 &ObjectAttributes, 218 &SbApiMsg->h.ClientId); 219 if (!NT_SUCCESS(Status)) Accept = FALSE; 220 221 /* Get its session ID */ 222 SmpGetProcessMuSessionId(ProcessHandle, &SessionId); 223 } 224 225 /* See if we already know about the caller's subsystem */ 226 CidSubsystem = SmpLocateKnownSubSysByCid(&SbApiMsg->h.ClientId); 227 if ((CidSubsystem) && (Accept)) 228 { 229 /* Check if we already have a subsystem for this kind of image */ 230 TypeSubsystem = SmpLocateKnownSubSysByType(SessionId, 231 SbApiMsg->ConnectionInfo.SubsystemType); 232 if (TypeSubsystem == CidSubsystem) 233 { 234 /* Someone is trying to take control of an existing subsystem, fail */ 235 Accept = FALSE; 236 DPRINT1("SMSS: Connection from SubSystem rejected\n"); 237 DPRINT1("SMSS: Image type already being served\n"); 238 } 239 else 240 { 241 /* Set this image type as the type for this subsystem */ 242 CidSubsystem->ImageType = SbApiMsg->ConnectionInfo.SubsystemType; 243 } 244 245 /* Drop the reference we had acquired */ 246 if (TypeSubsystem) SmpDereferenceSubsystem(TypeSubsystem); 247 } 248 249 /* Check if we'll be accepting the connection */ 250 if (Accept) 251 { 252 /* We will, so create a client context for it */ 253 ClientContext = RtlAllocateHeap(SmpHeap, 0, sizeof(SMP_CLIENT_CONTEXT)); 254 if (ClientContext) 255 { 256 ClientContext->ProcessHandle = ProcessHandle; 257 ClientContext->Subsystem = CidSubsystem; 258 ClientContext->dword10 = 0; 259 ClientContext->PortHandle = NULL; 260 } 261 else 262 { 263 /* Failed to allocate a client context, so reject the connection */ 264 DPRINT1("Rejecting connectiond due to lack of memory\n"); 265 Accept = FALSE; 266 } 267 } 268 else 269 { 270 /* Use a bogus context since we're going to reject the message */ 271 ClientContext = (PSMP_CLIENT_CONTEXT)SbApiMsg; 272 } 273 274 /* Now send the actual accept reply (which could be a rejection) */ 275 PortView.Length = sizeof(PortView); 276 Status = NtAcceptConnectPort(&PortHandle, 277 ClientContext, 278 &SbApiMsg->h, 279 Accept, 280 NULL, 281 &PortView); 282 if (!(Accept) || !(NT_SUCCESS(Status))) 283 { 284 /* Close the process handle, reference the subsystem, and exit */ 285 DPRINT1("Accept failed or rejected: %lx\n", Status); 286 if (ClientContext != (PVOID)SbApiMsg) RtlFreeHeap(SmpHeap, 0, ClientContext); 287 if (ProcessHandle) NtClose(ProcessHandle); 288 if (CidSubsystem) SmpDereferenceSubsystem(CidSubsystem); 289 return Status; 290 } 291 292 /* Save the port handle now that we've accepted it */ 293 if (ClientContext) ClientContext->PortHandle = PortHandle; 294 if (CidSubsystem) CidSubsystem->PortHandle = PortHandle; 295 296 /* Complete the port connection */ 297 Status = NtCompleteConnectPort(PortHandle); 298 if ((NT_SUCCESS(Status)) && (CidSubsystem)) 299 { 300 /* This was an actual subsystem, so connect back to it */ 301 SbApiMsg->ConnectionInfo.SbApiPortName[119] = UNICODE_NULL; 302 RtlCreateUnicodeString(&SubsystemPort, 303 SbApiMsg->ConnectionInfo.SbApiPortName); 304 Status = NtConnectPort(&CidSubsystem->SbApiPort, 305 &SubsystemPort, 306 &SecurityQos, 307 NULL, 308 NULL, 309 NULL, 310 NULL, 311 NULL); 312 if (!NT_SUCCESS(Status)) 313 { 314 DPRINT1("SMSS: Connect back to Sb %wZ failed %lx\n", &SubsystemPort, Status); 315 } 316 RtlFreeUnicodeString(&SubsystemPort); 317 318 /* Now that we're connected, signal the event handle */ 319 NtSetEvent(CidSubsystem->Event, NULL); 320 } 321 else if (CidSubsystem) 322 { 323 /* We failed to complete the connection, so clear the port handle */ 324 DPRINT1("Completing the connection failed: %lx\n", Status); 325 CidSubsystem->PortHandle = NULL; 326 } 327 328 /* Dereference the subsystem and return the result */ 329 if (CidSubsystem) SmpDereferenceSubsystem(CidSubsystem); 330 return Status; 331 } 332 333 ULONG 334 NTAPI 335 SmpApiLoop(IN PVOID Parameter) 336 { 337 HANDLE SmApiPort = (HANDLE)Parameter; 338 NTSTATUS Status; 339 PSMP_CLIENT_CONTEXT ClientContext; 340 PSM_API_MSG ReplyMsg = NULL; 341 SM_API_MSG RequestMsg; 342 PROCESS_BASIC_INFORMATION ProcessInformation; 343 LARGE_INTEGER Timeout; 344 345 /* Increase the number of API threads for throttling code for later */ 346 _InterlockedExchangeAdd(&SmTotalApiThreads, 1); 347 348 /* Mark us critical */ 349 RtlSetThreadIsCritical(TRUE, NULL, TRUE); 350 351 /* Set the PID of the SM process itself for later checking */ 352 NtQueryInformationProcess(NtCurrentProcess(), 353 ProcessBasicInformation, 354 &ProcessInformation, 355 sizeof(ProcessInformation), 356 NULL); 357 SmUniqueProcessId = (HANDLE)ProcessInformation.UniqueProcessId; 358 359 /* Now process incoming messages */ 360 while (TRUE) 361 { 362 /* Begin waiting on a request */ 363 Status = NtReplyWaitReceivePort(SmApiPort, 364 (PVOID*)&ClientContext, 365 &ReplyMsg->h, 366 &RequestMsg.h); 367 if (Status == STATUS_NO_MEMORY) 368 { 369 /* Ran out of memory, so do a little timeout and try again */ 370 if (ReplyMsg) DPRINT1("SMSS: Failed to reply to calling thread, retrying.\n"); 371 Timeout.QuadPart = -50000000; 372 NtDelayExecution(FALSE, &Timeout); 373 continue; 374 } 375 376 /* Check what kind of request we received */ 377 switch (RequestMsg.h.u2.s2.Type) 378 { 379 /* A new connection */ 380 case LPC_CONNECTION_REQUEST: 381 /* Create the right structures for it */ 382 SmpHandleConnectionRequest(SmApiPort, (PSB_API_MSG)&RequestMsg); 383 ReplyMsg = NULL; 384 break; 385 386 /* A closed connection */ 387 case LPC_PORT_CLOSED: 388 /* Destroy any state we had for this client */ 389 DPRINT1("Port closed\n"); 390 //if (ClientContext) SmpPushDeferredClientContext(ClientContext); 391 ReplyMsg = NULL; 392 break; 393 394 /* An actual API message */ 395 default: 396 if (!ClientContext) 397 { 398 ReplyMsg = NULL; 399 break; 400 } 401 402 RequestMsg.ReturnValue = STATUS_PENDING; 403 404 /* Check if the API is valid */ 405 if (RequestMsg.ApiNumber >= SmpMaxApiNumber) 406 { 407 /* It isn't, fail */ 408 DPRINT1("Invalid API: %lx\n", RequestMsg.ApiNumber); 409 Status = STATUS_NOT_IMPLEMENTED; 410 } 411 else if ((RequestMsg.ApiNumber <= SmpTerminateForeignSessionApi) && 412 !(ClientContext->Subsystem)) 413 { 414 /* It's valid, but doesn't have a subsystem with it */ 415 DPRINT1("Invalid session API\n"); 416 Status = STATUS_INVALID_PARAMETER; 417 } 418 else 419 { 420 /* It's totally okay, so call the dispatcher for it */ 421 Status = SmpApiDispatch[RequestMsg.ApiNumber](&RequestMsg, 422 ClientContext, 423 SmApiPort); 424 } 425 426 /* Write the result value and return the message back */ 427 RequestMsg.ReturnValue = Status; 428 ReplyMsg = &RequestMsg; 429 break; 430 } 431 } 432 return STATUS_SUCCESS; 433 } 434