xref: /reactos/sdk/lib/smlib/smclient.c (revision c47758b1)
1 /*
2  * PROJECT:     ReactOS NT-Compatible Session Manager
3  * LICENSE:     BSD 2-Clause License (https://spdx.org/licenses/BSD-2-Clause)
4  * PURPOSE:     SMSS Client Library (SMLIB) Client Stubs
5  * COPYRIGHT:   Copyright 2012-2013 Alex Ionescu <alex.ionescu@reactos.org>
6  *              Copyright 2021 Hervé Poussineau <hpoussin@reactos.org>
7  *              Copyright 2022 Hermès Bélusca-Maïto <hermes.belusca-maito@reactos.org>
8  */
9 
10 /* INCLUDES *******************************************************************/
11 
12 #include "precomp.h"
13 
14 #define NDEBUG
15 #include <debug.h>
16 
17 /* FUNCTIONS ******************************************************************/
18 
19 /**
20  * @brief
21  * Connects to the SM API port for registering a session callback port (Sb)
22  * associated to a subsystem, or for issuing API requests to the SM API port.
23  *
24  * There are only two ways to call this API:
25  * a) subsystems willing to register with SM will use it
26  *    with full parameters (the function checks them);
27  * b) regular SM clients, will set to 0 the 1st, the 2nd,
28  *    and the 3rd parameters.
29  *
30  * @param[in]   SbApiPortName
31  * Name of the Sb port the calling subsystem server already
32  * created in the system namespace.
33  *
34  * @param[in]   SbApiPort
35  * LPC port handle (checked, but not used: the subsystem is
36  * required to have already created the callback port before
37  * it connects to the SM).
38  *
39  * @param[in]   ImageType
40  * A valid IMAGE_SUBSYSTEM_xxx value. PE images having this
41  * subsystem value will be handled by the current subsystem.
42  *
43  * @param[out]  SmApiPort
44  * Pointer to a HANDLE, which will be filled with a valid
45  * client-side LPC communication port.
46  *
47  * @return
48  * If all three optional values are omitted, an LPC status.
49  * STATUS_INVALID_PARAMETER_MIX if PortName is defined and
50  * both SbApiPort and ImageType are 0.
51  *
52  * @remark
53  * Exported on Vista+ by NTDLL and called RtlConnectToSm().
54  **/
55 NTSTATUS
56 NTAPI
57 SmConnectToSm(
58     _In_opt_ PUNICODE_STRING SbApiPortName,
59     _In_opt_ HANDLE SbApiPort,
60     _In_opt_ ULONG ImageType,
61     _Out_ PHANDLE SmApiPort)
62 {
63     NTSTATUS Status;
64     SECURITY_QUALITY_OF_SERVICE SecurityQos;
65     UNICODE_STRING PortName;
66     SB_CONNECTION_INFO ConnectInfo = {0};
67     ULONG ConnectInfoLength = sizeof(ConnectInfo);
68 
69     /* Setup the QoS structure */
70     SecurityQos.ImpersonationLevel = SecurityIdentification;
71     SecurityQos.ContextTrackingMode = SECURITY_DYNAMIC_TRACKING;
72     SecurityQos.EffectiveOnly = TRUE;
73 
74     /* Set the SM API port name */
75     RtlInitUnicodeString(&PortName, L"\\SmApiPort"); // SM_API_PORT_NAME
76 
77     /* Check if this is a client connecting to SMSS, or SMSS to itself */
78     if (SbApiPortName)
79     {
80         /* A client SB port as well as an image type must be present */
81         if (!SbApiPort || (ImageType == IMAGE_SUBSYSTEM_UNKNOWN))
82             return STATUS_INVALID_PARAMETER_MIX;
83 
84         /* Validate SbApiPortName's length */
85         if (SbApiPortName->Length >= sizeof(ConnectInfo.SbApiPortName))
86             return STATUS_INVALID_PARAMETER;
87 
88         /* Copy the client port name, and NULL-terminate it */
89         RtlCopyMemory(ConnectInfo.SbApiPortName,
90                       SbApiPortName->Buffer,
91                       SbApiPortName->Length);
92         ConnectInfo.SbApiPortName[SbApiPortName->Length / sizeof(WCHAR)] = UNICODE_NULL;
93 
94         /* Save the subsystem type */
95         ConnectInfo.SubsystemType = ImageType;
96     }
97     else
98     {
99         /* No client port, and the subsystem type is not set */
100         ConnectInfo.SbApiPortName[0] = UNICODE_NULL;
101         ConnectInfo.SubsystemType = IMAGE_SUBSYSTEM_UNKNOWN;
102     }
103 
104     /* Connect to SMSS and exchange connection information */
105     Status = NtConnectPort(SmApiPort,
106                            &PortName,
107                            &SecurityQos,
108                            NULL,
109                            NULL,
110                            NULL,
111                            &ConnectInfo,
112                            &ConnectInfoLength);
113     if (!NT_SUCCESS(Status))
114     {
115         DPRINT1("SmConnectToSm: Connect to Sm failed %lx\n", Status);
116     }
117 #if (NTDDI_VERSION < NTDDI_VISTA)
118     else
119     {
120         /* Treat a warning or informational status as success */
121         Status = STATUS_SUCCESS;
122     }
123 #endif
124 
125     /* Return if the connection was successful or not */
126     return Status;
127 }
128 
129 /**
130  * @brief
131  * Sends a message to the SM via the SM API port.
132  *
133  * @param[in]   SmApiPort
134  * Port handle returned by SmConnectToSm().
135  *
136  * @param[in,out]   SmApiMsg
137  * Message to send to the SM. The API-specific data must be initialized,
138  * and the SmApiMsg->ApiNumber must be specified accordingly.
139  *
140  * @return
141  * Success status as handed by the SM reply; otherwise a failure
142  * status code.
143  *
144  * @remark
145  * Exported on Vista+ by NTDLL and called RtlSendMsgToSm().
146  **/
147 NTSTATUS
148 NTAPI
149 SmSendMsgToSm(
150     _In_ HANDLE SmApiPort,
151     _Inout_ PSM_API_MSG SmApiMsg)
152 {
153     static ULONG RtlpSmMessageInfo[SmpMaxApiNumber] =
154     {
155         0 /*sizeof(SM_CREATE_FOREIGN_SESSION_MSG)*/,
156         sizeof(SM_SESSION_COMPLETE_MSG),
157         0 /*sizeof(SM_TERMINATE_FOREIGN_SESSION_MSG)*/,
158         sizeof(SM_EXEC_PGM_MSG),
159         sizeof(SM_LOAD_DEFERED_SUBSYSTEM_MSG),
160         sizeof(SM_START_CSR_MSG),
161         sizeof(SM_STOP_CSR_MSG),
162     };
163 
164     NTSTATUS Status;
165     ULONG DataLength;
166 
167     if (SmApiMsg->ApiNumber >= SmpMaxApiNumber)
168         return STATUS_NOT_IMPLEMENTED;
169 
170     /* Obtain the necessary data length for this API */
171     DataLength = RtlpSmMessageInfo[SmApiMsg->ApiNumber];
172 
173     /* Fill out the Port Message Header */
174     // RtlZeroMemory(&SmApiMsg->h, sizeof(SmApiMsg->h));
175     SmApiMsg->h.u2.ZeroInit = 0;
176     /* DataLength = user_data_size + anything between
177      * header and data, including intermediate padding */
178     SmApiMsg->h.u1.s1.DataLength = (CSHORT)DataLength +
179         FIELD_OFFSET(SM_API_MSG, u) - sizeof(SmApiMsg->h);
180     /* TotalLength = sizeof(*SmApiMsg) on <= NT5.2, otherwise:
181      * DataLength + header_size == user_data_size + FIELD_OFFSET(SM_API_MSG, u)
182      * without structure trailing padding */
183     SmApiMsg->h.u1.s1.TotalLength = SmApiMsg->h.u1.s1.DataLength + sizeof(SmApiMsg->h);
184 
185     /* Send the LPC message and wait for a reply */
186     Status = NtRequestWaitReplyPort(SmApiPort, &SmApiMsg->h, &SmApiMsg->h);
187     if (!NT_SUCCESS(Status))
188     {
189         DPRINT1("SmSendMsgToSm: NtRequestWaitReplyPort failed, Status: 0x%08lx\n", Status);
190     }
191     else
192     {
193         /* Return the real status */
194         Status = SmApiMsg->ReturnValue;
195     }
196 
197     return Status;
198 }
199 
200 /**
201  * @brief
202  * This function is called by an environment subsystem server
203  * to tell the SM it has terminated the session it managed.
204  *
205  * @param[in]   SmApiPort
206  * Port handle returned by SmConnectToSm().
207  *
208  * @param[in]   SessionId
209  * The session ID of the session being terminated.
210  *
211  * @param[in]   SessionStatus
212  * An NT status code for the termination.
213  *
214  * @return
215  * Success status as handed by the SM reply; otherwise a failure
216  * status code.
217  **/
218 NTSTATUS
219 NTAPI
220 SmSessionComplete(
221     _In_ HANDLE SmApiPort,
222     _In_ ULONG SessionId,
223     _In_ NTSTATUS SessionStatus)
224 {
225     SM_API_MSG SmApiMsg = {0};
226     PSM_SESSION_COMPLETE_MSG SessionComplete = &SmApiMsg.u.SessionComplete;
227 
228 #if 0 //def _WIN64
229     /* 64-bit SMSS needs to talk to 32-bit processes so do the LPC conversion */
230     if (SmpIsWow64Process())
231     {
232         return SmpWow64SessionComplete(SmApiPort, SessionId, SessionStatus);
233     }
234 #endif
235 
236     /* Set the message data */
237     SessionComplete->SessionId = SessionId;
238     SessionComplete->SessionStatus = SessionStatus;
239 
240     /* Send the message and wait for a reply */
241     SmApiMsg.ApiNumber = SmpSessionCompleteApi;
242     return SmSendMsgToSm(SmApiPort, &SmApiMsg);
243 }
244 
245 /**
246  * @brief
247  * Requests the SM to start a process under a new environment session.
248  *
249  * @param[in]   SmApiPort
250  * Port handle returned by SmConnectToSm().
251  *
252  * @param[in]   ProcessInformation
253  * A process description as returned by RtlCreateUserProcess().
254  *
255  * @param[in]   DebugFlag
256  * If set, indicates that the caller wants to debug this process
257  * and act as its debug user interface.
258  *
259  * @return
260  * Success status as handed by the SM reply; otherwise a failure
261  * status code.
262  **/
263 NTSTATUS
264 NTAPI
265 SmExecPgm(
266     _In_ HANDLE SmApiPort,
267     _In_ PRTL_USER_PROCESS_INFORMATION ProcessInformation,
268     _In_ BOOLEAN DebugFlag)
269 {
270     NTSTATUS Status;
271     SM_API_MSG SmApiMsg = {0};
272     PSM_EXEC_PGM_MSG ExecPgm = &SmApiMsg.u.ExecPgm;
273 
274 #if 0 //def _WIN64
275     /* 64-bit SMSS needs to talk to 32-bit processes so do the LPC conversion */
276     if (SmpIsWow64Process())
277     {
278         return SmpWow64ExecPgm(SmApiPort, ProcessInformation, DebugFlag);
279     }
280 #endif
281 
282     /* Set the message data */
283     RtlCopyMemory(&ExecPgm->ProcessInformation,
284                   ProcessInformation,
285                   sizeof(ExecPgm->ProcessInformation));
286     ExecPgm->DebugFlag = DebugFlag;
287 
288     /* Send the message and wait for a reply */
289     SmApiMsg.ApiNumber = SmpExecPgmApi;
290     Status = SmSendMsgToSm(SmApiPort, &SmApiMsg);
291 
292     /* Close the handles that the parent passed in and return status */
293     NtClose(ProcessInformation->ProcessHandle);
294     NtClose(ProcessInformation->ThreadHandle);
295     return Status;
296 }
297 
298 /**
299  * @brief
300  * This function is used to make the SM start an environment
301  * subsystem server process.
302  *
303  * @param[in]   SmApiPort
304  * Port handle returned by SmConnectToSm().
305  *
306  * @param[in]   DeferedSubsystem
307  * Name of the subsystem to start. This must be one of the subsystems
308  * listed by value's name in the SM registry key
309  * \Registry\SYSTEM\CurrentControlSet\Control\Session Manager\SubSystems
310  * (used by the SM to lookup the corresponding image name).
311  * Default valid names are: "Debug", "Windows", "Posix", "Os2".
312  *
313  * @return
314  * Success status as handed by the SM reply; otherwise a failure
315  * status code.
316  **/
317 NTSTATUS
318 NTAPI
319 SmLoadDeferedSubsystem(
320     _In_ HANDLE SmApiPort,
321     _In_ PUNICODE_STRING DeferedSubsystem)
322 {
323     SM_API_MSG SmApiMsg = {0};
324     PSM_LOAD_DEFERED_SUBSYSTEM_MSG LoadDefered = &SmApiMsg.u.LoadDefered;
325 
326 #if 0 //def _WIN64
327     /* 64-bit SMSS needs to talk to 32-bit processes so do the LPC conversion */
328     if (SmpIsWow64Process())
329     {
330         return SmpWow64LoadDeferedSubsystem(SmApiPort, DeferedSubsystem);
331     }
332 #endif
333 
334     /* Validate DeferedSubsystem's length */
335     if (DeferedSubsystem->Length > sizeof(LoadDefered->Buffer))
336         return STATUS_INVALID_PARAMETER;
337 
338     /* Set the message data */
339     /* Buffer stores a counted non-NULL-terminated UNICODE string */
340     LoadDefered->Length = DeferedSubsystem->Length;
341     RtlCopyMemory(LoadDefered->Buffer,
342                   DeferedSubsystem->Buffer,
343                   DeferedSubsystem->Length);
344 
345     /* Send the message and wait for a reply */
346     SmApiMsg.ApiNumber = SmpLoadDeferedSubsystemApi;
347     return SmSendMsgToSm(SmApiPort, &SmApiMsg);
348 }
349 
350 /**
351  * @brief
352  * Requests the SM to create a new Terminal Services session
353  * and start an initial command.
354  *
355  * @param[in]   SmApiPort
356  * Port handle returned by SmConnectToSm().
357  *
358  * @param[out]  pMuSessionId
359  * Pointer to a variable that receives the session ID of the new
360  * Terminal Services session that has been created.
361  *
362  * @param[in]   CommandLine
363  * Full path to the image to be used as the initial command.
364  *
365  * @param[out]  pWindowsSubSysProcessId
366  * Pointer to a variable that receives the process ID of the environment
367  * subsystem that has been started in the new session.
368  *
369  * @param[out]  pInitialCommandProcessId
370  * Pointer to a variable that receives the process ID of the initial command.
371  *
372  * @return
373  * Success status as handed by the SM reply; otherwise a failure
374  * status code.
375  **/
376 NTSTATUS
377 NTAPI
378 SmStartCsr(
379     _In_ HANDLE SmApiPort,
380     _Out_ PULONG pMuSessionId,
381     _In_opt_ PUNICODE_STRING CommandLine,
382     _Out_ PHANDLE pWindowsSubSysProcessId,
383     _Out_ PHANDLE pInitialCommandProcessId)
384 {
385     NTSTATUS Status;
386     SM_API_MSG SmApiMsg = {0};
387     PSM_START_CSR_MSG StartCsr = &SmApiMsg.u.StartCsr;
388 
389 #if 0 //def _WIN64
390     /* 64-bit SMSS needs to talk to 32-bit processes so do the LPC conversion */
391     if (SmpIsWow64Process())
392     {
393         return SmpWow64StartCsr(SmApiPort,
394                                 pMuSessionId,
395                                 CommandLine,
396                                 pWindowsSubSysProcessId,
397                                 pInitialCommandProcessId);
398     }
399 #endif
400 
401     /* Set the message data */
402     if (CommandLine)
403     {
404         /* Validate CommandLine's length */
405         if (CommandLine->Length > sizeof(StartCsr->Buffer))
406             return STATUS_INVALID_PARAMETER;
407 
408         /* Buffer stores a counted non-NULL-terminated UNICODE string */
409         StartCsr->Length = CommandLine->Length;
410         RtlCopyMemory(StartCsr->Buffer,
411                       CommandLine->Buffer,
412                       CommandLine->Length);
413     }
414     else
415     {
416         StartCsr->Length = 0;
417     }
418 
419     /* Send the message and wait for a reply */
420     SmApiMsg.ApiNumber = SmpStartCsrApi;
421     Status = SmSendMsgToSm(SmApiPort, &SmApiMsg);
422 
423     /* Give back informations to caller */
424     *pMuSessionId = StartCsr->MuSessionId;
425     *pWindowsSubSysProcessId = StartCsr->WindowsSubSysProcessId;
426     *pInitialCommandProcessId = StartCsr->SmpInitialCommandProcessId;
427 
428     return Status;
429 }
430 
431 /**
432  * @brief
433  * Requests the SM to terminate a Terminal Services session.
434  *
435  * @param[in]   SmApiPort
436  * Port handle returned by SmConnectToSm().
437  *
438  * @param[in]   MuSessionId
439  * The Terminal Services session ID, returned by SmStartCsr().
440  *
441  * @return
442  * Success status as handed by the SM reply; otherwise a failure
443  * status code.
444  **/
445 NTSTATUS
446 NTAPI
447 SmStopCsr(
448     _In_ HANDLE SmApiPort,
449     _In_ ULONG MuSessionId)
450 {
451     SM_API_MSG SmApiMsg = {0};
452 
453 #if 0 //def _WIN64
454     /* 64-bit SMSS needs to talk to 32-bit processes so do the LPC conversion */
455     if (SmpIsWow64Process())
456     {
457         return SmpWow64StopCsr(SmApiPort, MuSessionId);
458     }
459 #endif
460 
461     /* Set the message data */
462     SmApiMsg.u.StopCsr.MuSessionId = MuSessionId;
463 
464     /* Send the message and wait for a reply */
465     SmApiMsg.ApiNumber = SmpStopCsrApi;
466     return SmSendMsgToSm(SmApiPort, &SmApiMsg);
467 }
468