1 /* 2 * COPYRIGHT: See COPYING in the top level directory 3 * PROJECT: ReactOS Client/Server Runtime SubSystem 4 * FILE: subsystems/win32/csrsrv/session.c 5 * PURPOSE: CSR Server DLL Session Implementation 6 * PROGRAMMERS: Alex Ionescu (alex@relsoft.net) 7 */ 8 9 /* INCLUDES *******************************************************************/ 10 11 #include "srv.h" 12 13 #define NDEBUG 14 #include <debug.h> 15 16 /* DATA ***********************************************************************/ 17 18 RTL_CRITICAL_SECTION CsrNtSessionLock; 19 LIST_ENTRY CsrNtSessionList; 20 21 PSB_API_ROUTINE CsrServerSbApiDispatch[SbpMaxApiNumber - SbpCreateSession] = 22 { 23 CsrSbCreateSession, 24 CsrSbTerminateSession, 25 CsrSbForeignSessionComplete, 26 CsrSbCreateProcess 27 }; 28 29 PCHAR CsrServerSbApiName[SbpMaxApiNumber - SbpCreateSession] = 30 { 31 "SbCreateSession", 32 "SbTerminateSession", 33 "SbForeignSessionComplete", 34 "SbCreateProcess" 35 }; 36 37 /* PRIVATE FUNCTIONS **********************************************************/ 38 39 /*++ 40 * @name CsrInitializeNtSessionList 41 * 42 * The CsrInitializeNtSessionList routine sets up support for CSR Sessions. 43 * 44 * @param None 45 * 46 * @return None 47 * 48 * @remarks None. 49 * 50 *--*/ 51 NTSTATUS 52 NTAPI 53 CsrInitializeNtSessionList(VOID) 54 { 55 /* Initialize the Session List */ 56 InitializeListHead(&CsrNtSessionList); 57 58 /* Initialize the Session Lock */ 59 return RtlInitializeCriticalSection(&CsrNtSessionLock); 60 } 61 62 /*++ 63 * @name CsrAllocateNtSession 64 * 65 * The CsrAllocateNtSession routine allocates a new CSR NT Session. 66 * 67 * @param SessionId 68 * Session ID of the CSR NT Session to allocate. 69 * 70 * @return Pointer to the newly allocated CSR NT Session. 71 * 72 * @remarks None. 73 * 74 *--*/ 75 PCSR_NT_SESSION 76 NTAPI 77 CsrAllocateNtSession(IN ULONG SessionId) 78 { 79 PCSR_NT_SESSION NtSession; 80 81 /* Allocate an NT Session Object */ 82 NtSession = RtlAllocateHeap(CsrHeap, HEAP_ZERO_MEMORY, sizeof(CSR_NT_SESSION)); 83 if (NtSession) 84 { 85 /* Setup the Session Object */ 86 NtSession->SessionId = SessionId; 87 NtSession->ReferenceCount = 1; 88 89 /* Insert it into the Session List */ 90 CsrAcquireNtSessionLock(); 91 InsertHeadList(&CsrNtSessionList, &NtSession->SessionLink); 92 CsrReleaseNtSessionLock(); 93 } 94 else 95 { 96 ASSERT(NtSession != NULL); 97 } 98 99 /* Return the Session (or NULL) */ 100 return NtSession; 101 } 102 103 /*++ 104 * @name CsrReferenceNtSession 105 * 106 * The CsrReferenceNtSession increases the reference count of a CSR NT Session. 107 * 108 * @param Session 109 * Pointer to the CSR NT Session to reference. 110 * 111 * @return None. 112 * 113 * @remarks None. 114 * 115 *--*/ 116 VOID 117 NTAPI 118 CsrReferenceNtSession(IN PCSR_NT_SESSION Session) 119 { 120 /* Acquire the lock */ 121 CsrAcquireNtSessionLock(); 122 123 /* Sanity checks */ 124 ASSERT(!IsListEmpty(&Session->SessionLink)); 125 ASSERT(Session->SessionId != 0); 126 ASSERT(Session->ReferenceCount != 0); 127 128 /* Increase the reference count */ 129 Session->ReferenceCount++; 130 131 /* Release the lock */ 132 CsrReleaseNtSessionLock(); 133 } 134 135 /*++ 136 * @name CsrDereferenceNtSession 137 * 138 * The CsrDereferenceNtSession decreases the reference count of a 139 * CSR NT Session. 140 * 141 * @param Session 142 * Pointer to the CSR NT Session to reference. 143 * 144 * @param ExitStatus 145 * If this is the last reference to the session, this argument 146 * specifies the exit status. 147 * 148 * @return None. 149 * 150 * @remarks CsrDereferenceNtSession will complete the session if 151 * the last reference to it has been closed. 152 * 153 *--*/ 154 VOID 155 NTAPI 156 CsrDereferenceNtSession(IN PCSR_NT_SESSION Session, 157 IN NTSTATUS ExitStatus) 158 { 159 /* Acquire the lock */ 160 CsrAcquireNtSessionLock(); 161 162 /* Sanity checks */ 163 ASSERT(!IsListEmpty(&Session->SessionLink)); 164 ASSERT(Session->SessionId != 0); 165 ASSERT(Session->ReferenceCount != 0); 166 167 /* Dereference the Session Object */ 168 if ((--Session->ReferenceCount) == 0) 169 { 170 /* Remove it from the list */ 171 RemoveEntryList(&Session->SessionLink); 172 173 /* Release the lock */ 174 CsrReleaseNtSessionLock(); 175 176 /* Tell SM that we're done here */ 177 SmSessionComplete(CsrSmApiPort, Session->SessionId, ExitStatus); 178 179 /* Free the Session Object */ 180 RtlFreeHeap(CsrHeap, 0, Session); 181 } 182 else 183 { 184 /* Release the lock, the Session is still active */ 185 CsrReleaseNtSessionLock(); 186 } 187 } 188 189 /* SESSION MANAGER FUNCTIONS **************************************************/ 190 191 /*++ 192 * @name CsrSbCreateSession 193 * 194 * The CsrSbCreateSession API is called by the Session Manager whenever a new 195 * session is created. 196 * 197 * @param ApiMessage 198 * Pointer to the Session Manager API Message. 199 * 200 * @return TRUE in case of success, FALSE otherwise. 201 * 202 * @remarks The CsrSbCreateSession routine will initialize a new CSR NT 203 * Session and allocate a new CSR Process for the subsystem process. 204 * 205 *--*/ 206 BOOLEAN 207 NTAPI 208 CsrSbCreateSession(IN PSB_API_MSG ApiMessage) 209 { 210 PSB_CREATE_SESSION_MSG CreateSession = &ApiMessage->CreateSession; 211 HANDLE hProcess, hThread; 212 PCSR_PROCESS CsrProcess; 213 PCSR_THREAD CsrThread; 214 PCSR_SERVER_DLL ServerDll; 215 PVOID ProcessData; 216 NTSTATUS Status; 217 KERNEL_USER_TIMES KernelTimes; 218 ULONG i; 219 220 /* Save the Process and Thread Handles */ 221 hProcess = CreateSession->ProcessInfo.ProcessHandle; 222 hThread = CreateSession->ProcessInfo.ThreadHandle; 223 224 /* Lock the Processes */ 225 CsrAcquireProcessLock(); 226 227 /* Allocate a new process */ 228 CsrProcess = CsrAllocateProcess(); 229 if (!CsrProcess) 230 { 231 /* Fail */ 232 ApiMessage->ReturnValue = STATUS_NO_MEMORY; 233 CsrReleaseProcessLock(); 234 return TRUE; 235 } 236 237 /* Set the Exception Port for us */ 238 Status = NtSetInformationProcess(hProcess, 239 ProcessExceptionPort, 240 &CsrApiPort, 241 sizeof(CsrApiPort)); 242 243 /* Check for success */ 244 if (!NT_SUCCESS(Status)) 245 { 246 /* Fail the request */ 247 CsrDeallocateProcess(CsrProcess); 248 CsrReleaseProcessLock(); 249 250 /* Strange as it seems, NTSTATUSes are actually returned */ 251 return (BOOLEAN)STATUS_NO_MEMORY; 252 } 253 254 /* Get the Create Time */ 255 Status = NtQueryInformationThread(hThread, 256 ThreadTimes, 257 &KernelTimes, 258 sizeof(KernelTimes), 259 NULL); 260 261 /* Check for success */ 262 if (!NT_SUCCESS(Status)) 263 { 264 /* Fail the request */ 265 CsrDeallocateProcess(CsrProcess); 266 CsrReleaseProcessLock(); 267 268 /* Strange as it seems, NTSTATUSes are actually returned */ 269 return (BOOLEAN)Status; 270 } 271 272 /* Allocate a new Thread */ 273 CsrThread = CsrAllocateThread(CsrProcess); 274 if (!CsrThread) 275 { 276 /* Fail the request */ 277 CsrDeallocateProcess(CsrProcess); 278 CsrReleaseProcessLock(); 279 280 ApiMessage->ReturnValue = STATUS_NO_MEMORY; 281 return TRUE; 282 } 283 284 /* Setup the Thread Object */ 285 CsrThread->CreateTime = KernelTimes.CreateTime; 286 CsrThread->ClientId = CreateSession->ProcessInfo.ClientId; 287 CsrThread->ThreadHandle = hThread; 288 ProtectHandle(hThread); 289 CsrThread->Flags = 0; 290 291 /* Insert it into the Process List */ 292 Status = CsrInsertThread(CsrProcess, CsrThread); 293 if (!NT_SUCCESS(Status)) 294 { 295 /* Bail out */ 296 CsrDeallocateProcess(CsrProcess); 297 CsrDeallocateThread(CsrThread); 298 CsrReleaseProcessLock(); 299 300 /* Strange as it seems, NTSTATUSes are actually returned */ 301 return (BOOLEAN)Status; 302 } 303 304 /* Setup Process Data */ 305 CsrProcess->ClientId = CreateSession->ProcessInfo.ClientId; 306 CsrProcess->ProcessHandle = hProcess; 307 CsrProcess->NtSession = CsrAllocateNtSession(CreateSession->SessionId); 308 309 /* Set the Process Priority */ 310 CsrSetBackgroundPriority(CsrProcess); 311 312 /* Get the first data location */ 313 ProcessData = &CsrProcess->ServerData[CSR_SERVER_DLL_MAX]; 314 315 /* Loop every DLL */ 316 for (i = 0; i < CSR_SERVER_DLL_MAX; i++) 317 { 318 /* Get the current Server */ 319 ServerDll = CsrLoadedServerDll[i]; 320 321 /* Check if the DLL is loaded and has Process Data */ 322 if (ServerDll && ServerDll->SizeOfProcessData) 323 { 324 /* Write the pointer to the data */ 325 CsrProcess->ServerData[i] = ProcessData; 326 327 /* Move to the next data location */ 328 ProcessData = (PVOID)((ULONG_PTR)ProcessData + 329 ServerDll->SizeOfProcessData); 330 } 331 else 332 { 333 /* Nothing for this Process */ 334 CsrProcess->ServerData[i] = NULL; 335 } 336 } 337 338 /* Insert the Process */ 339 CsrInsertProcess(NULL, CsrProcess); 340 341 /* Activate the Thread */ 342 ApiMessage->ReturnValue = NtResumeThread(hThread, NULL); 343 344 /* Release lock and return */ 345 CsrReleaseProcessLock(); 346 return TRUE; 347 } 348 349 /*++ 350 * @name CsrSbForeignSessionComplete 351 * 352 * The CsrSbForeignSessionComplete API is called by the Session Manager 353 * whenever a foreign session is completed (ie: terminated). 354 * 355 * @param ApiMessage 356 * Pointer to the Session Manager API Message. 357 * 358 * @return TRUE in case of success, FALSE otherwise. 359 * 360 * @remarks The CsrSbForeignSessionComplete API is not yet implemented. 361 * 362 *--*/ 363 BOOLEAN 364 NTAPI 365 CsrSbForeignSessionComplete(IN PSB_API_MSG ApiMessage) 366 { 367 /* Deprecated/Unimplemented in NT */ 368 ApiMessage->ReturnValue = STATUS_NOT_IMPLEMENTED; 369 return TRUE; 370 } 371 372 /*++ 373 * @name CsrSbTerminateSession 374 * 375 * The CsrSbTerminateSession API is called by the Session Manager 376 * whenever a foreign session should be destroyed. 377 * 378 * @param ApiMessage 379 * Pointer to the Session Manager API Message. 380 * 381 * @return TRUE in case of success, FALSE otherwise. 382 * 383 * @remarks The CsrSbTerminateSession API is not yet implemented. 384 * 385 *--*/ 386 BOOLEAN 387 NTAPI 388 CsrSbTerminateSession(IN PSB_API_MSG ApiMessage) 389 { 390 ApiMessage->ReturnValue = STATUS_NOT_IMPLEMENTED; 391 return TRUE; 392 } 393 394 /*++ 395 * @name CsrSbCreateProcess 396 * 397 * The CsrSbCreateProcess API is called by the Session Manager 398 * whenever a foreign session is created and a new process should be started. 399 * 400 * @param ApiMessage 401 * Pointer to the Session Manager API Message. 402 * 403 * @return TRUE in case of success, FALSE otherwise. 404 * 405 * @remarks The CsrSbCreateProcess API is not yet implemented. 406 * 407 *--*/ 408 BOOLEAN 409 NTAPI 410 CsrSbCreateProcess(IN PSB_API_MSG ApiMessage) 411 { 412 ApiMessage->ReturnValue = STATUS_NOT_IMPLEMENTED; 413 return TRUE; 414 } 415 416 /*++ 417 * @name CsrSbApiHandleConnectionRequest 418 * 419 * The CsrSbApiHandleConnectionRequest routine handles and accepts a new 420 * connection request to the SM API LPC Port. 421 * 422 * @param ApiMessage 423 * Pointer to the incoming CSR API Message which contains the 424 * connection request. 425 * 426 * @return STATUS_SUCCESS in case of success, or status code which caused 427 * the routine to error. 428 * 429 * @remarks None. 430 * 431 *--*/ 432 NTSTATUS 433 NTAPI 434 CsrSbApiHandleConnectionRequest(IN PSB_API_MSG Message) 435 { 436 NTSTATUS Status; 437 REMOTE_PORT_VIEW RemotePortView; 438 HANDLE hPort; 439 440 /* Set the Port View Structure Length */ 441 RemotePortView.Length = sizeof(REMOTE_PORT_VIEW); 442 443 /* Accept the connection */ 444 Status = NtAcceptConnectPort(&hPort, 445 NULL, 446 &Message->h, 447 TRUE, 448 NULL, 449 &RemotePortView); 450 if (!NT_SUCCESS(Status)) 451 { 452 DPRINT1("CSRSS: Sb Accept Connection failed %lx\n", Status); 453 return Status; 454 } 455 456 /* Complete the Connection */ 457 Status = NtCompleteConnectPort(hPort); 458 if (!NT_SUCCESS(Status)) 459 { 460 DPRINT1("CSRSS: Sb Complete Connection failed %lx\n",Status); 461 } 462 463 /* Return status */ 464 return Status; 465 } 466 467 /*++ 468 * @name CsrSbApiRequestThread 469 * 470 * The CsrSbApiRequestThread routine handles incoming messages or connection 471 * requests on the SM API LPC Port. 472 * 473 * @param Parameter 474 * System-default user-defined parameter. Unused. 475 * 476 * @return The thread exit code, if the thread is terminated. 477 * 478 * @remarks Before listening on the port, the routine will first attempt 479 * to connect to the user subsystem. 480 * 481 *--*/ 482 VOID 483 NTAPI 484 CsrSbApiRequestThread(IN PVOID Parameter) 485 { 486 NTSTATUS Status; 487 SB_API_MSG ReceiveMsg; 488 PSB_API_MSG ReplyMsg = NULL; 489 PVOID PortContext; 490 ULONG MessageType; 491 492 /* Start the loop */ 493 while (TRUE) 494 { 495 /* Wait for a message to come in */ 496 Status = NtReplyWaitReceivePort(CsrSbApiPort, 497 &PortContext, 498 &ReplyMsg->h, 499 &ReceiveMsg.h); 500 501 /* Check if we didn't get success */ 502 if (Status != STATUS_SUCCESS) 503 { 504 /* If we only got a warning, keep going */ 505 if (NT_SUCCESS(Status)) continue; 506 507 /* We failed big time, so start out fresh */ 508 ReplyMsg = NULL; 509 DPRINT1("CSRSS: ReceivePort failed - Status == %X\n", Status); 510 continue; 511 } 512 513 /* Save the message type */ 514 MessageType = ReceiveMsg.h.u2.s2.Type; 515 516 /* Check if this is a connection request */ 517 if (MessageType == LPC_CONNECTION_REQUEST) 518 { 519 /* Handle connection request */ 520 CsrSbApiHandleConnectionRequest(&ReceiveMsg); 521 522 /* Start over */ 523 ReplyMsg = NULL; 524 continue; 525 } 526 527 /* Check if the port died */ 528 if (MessageType == LPC_PORT_CLOSED) 529 { 530 /* Close the handle if we have one */ 531 if (PortContext) NtClose((HANDLE)PortContext); 532 533 /* Client died, start over */ 534 ReplyMsg = NULL; 535 continue; 536 } 537 else if (MessageType == LPC_CLIENT_DIED) 538 { 539 /* Client died, start over */ 540 ReplyMsg = NULL; 541 continue; 542 } 543 544 /* 545 * It's an API Message, check if it's within limits. If it's not, 546 * the NT Behaviour is to set this to the Maximum API. 547 */ 548 if (ReceiveMsg.ApiNumber > SbpMaxApiNumber) 549 { 550 ReceiveMsg.ApiNumber = SbpMaxApiNumber; 551 DPRINT1("CSRSS: %lx is invalid Sb ApiNumber\n", ReceiveMsg.ApiNumber); 552 } 553 554 /* Reuse the message */ 555 ReplyMsg = &ReceiveMsg; 556 557 /* Make sure that the message is supported */ 558 if (ReceiveMsg.ApiNumber < SbpMaxApiNumber) 559 { 560 /* Call the API */ 561 if (!CsrServerSbApiDispatch[ReceiveMsg.ApiNumber](&ReceiveMsg)) 562 { 563 DPRINT1("CSRSS: %s Session Api called and failed\n", 564 CsrServerSbApiName[ReceiveMsg.ApiNumber]); 565 566 /* It failed, so return nothing */ 567 ReplyMsg = NULL; 568 } 569 } 570 else 571 { 572 /* We don't support this API Number */ 573 ReplyMsg->ReturnValue = STATUS_NOT_IMPLEMENTED; 574 } 575 } 576 } 577 578 /* EOF */ 579