xref: /reactos/subsystems/csr/csrsrv/session.c (revision 06a04514)
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
CsrInitializeNtSessionList(VOID)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
CsrAllocateNtSession(IN ULONG SessionId)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
CsrReferenceNtSession(IN PCSR_NT_SESSION Session)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
CsrDereferenceNtSession(IN PCSR_NT_SESSION Session,IN NTSTATUS ExitStatus)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
CsrSbCreateSession(IN PSB_API_MSG ApiMessage)208 CsrSbCreateSession(IN PSB_API_MSG ApiMessage)
209 {
210     PSB_CREATE_SESSION_MSG CreateSession = &ApiMessage->u.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
CsrSbForeignSessionComplete(IN PSB_API_MSG ApiMessage)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
CsrSbTerminateSession(IN PSB_API_MSG ApiMessage)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
CsrSbCreateProcess(IN PSB_API_MSG ApiMessage)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
CsrSbApiHandleConnectionRequest(IN PSB_API_MSG Message)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
CsrSbApiRequestThread(IN PVOID Parameter)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