[SHELL32] Fix Control_RunDLLW (#5400)
[reactos.git] / sdk / lib / smlib / smclient.c
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 ExecPgm->ProcessInformation = *ProcessInformation;
284 ExecPgm->DebugFlag = DebugFlag;
285
286 /* Send the message and wait for a reply */
287 SmApiMsg.ApiNumber = SmpExecPgmApi;
288 Status = SmSendMsgToSm(SmApiPort, &SmApiMsg);
289
290 /* Close the handles that the parent passed in and return status */
291 NtClose(ProcessInformation->ProcessHandle);
292 NtClose(ProcessInformation->ThreadHandle);
293 return Status;
294 }
295
296 /**
297 * @brief
298 * This function is used to make the SM start an environment
299 * subsystem server process.
300 *
301 * @param[in] SmApiPort
302 * Port handle returned by SmConnectToSm().
303 *
304 * @param[in] DeferedSubsystem
305 * Name of the subsystem to start. This must be one of the subsystems
306 * listed by value's name in the SM registry key
307 * \Registry\SYSTEM\CurrentControlSet\Control\Session Manager\SubSystems
308 * (used by the SM to lookup the corresponding image name).
309 * Default valid names are: "Debug", "Windows", "Posix", "Os2".
310 *
311 * @return
312 * Success status as handed by the SM reply; otherwise a failure
313 * status code.
314 **/
315 NTSTATUS
316 NTAPI
317 SmLoadDeferedSubsystem(
318 _In_ HANDLE SmApiPort,
319 _In_ PUNICODE_STRING DeferedSubsystem)
320 {
321 SM_API_MSG SmApiMsg = {0};
322 PSM_LOAD_DEFERED_SUBSYSTEM_MSG LoadDefered = &SmApiMsg.u.LoadDefered;
323
324 #if 0 //def _WIN64
325 /* 64-bit SMSS needs to talk to 32-bit processes so do the LPC conversion */
326 if (SmpIsWow64Process())
327 {
328 return SmpWow64LoadDeferedSubsystem(SmApiPort, DeferedSubsystem);
329 }
330 #endif
331
332 /* Validate DeferedSubsystem's length */
333 if (DeferedSubsystem->Length > sizeof(LoadDefered->Buffer))
334 return STATUS_INVALID_PARAMETER;
335
336 /* Set the message data */
337 /* Buffer stores a counted non-NULL-terminated UNICODE string */
338 LoadDefered->Length = DeferedSubsystem->Length;
339 RtlCopyMemory(LoadDefered->Buffer,
340 DeferedSubsystem->Buffer,
341 DeferedSubsystem->Length);
342
343 /* Send the message and wait for a reply */
344 SmApiMsg.ApiNumber = SmpLoadDeferedSubsystemApi;
345 return SmSendMsgToSm(SmApiPort, &SmApiMsg);
346 }
347
348 /**
349 * @brief
350 * Requests the SM to create a new Terminal Services session
351 * and start an initial command.
352 *
353 * @param[in] SmApiPort
354 * Port handle returned by SmConnectToSm().
355 *
356 * @param[out] pMuSessionId
357 * Pointer to a variable that receives the session ID of the new
358 * Terminal Services session that has been created.
359 *
360 * @param[in] CommandLine
361 * Full path to the image to be used as the initial command.
362 *
363 * @param[out] pWindowsSubSysProcessId
364 * Pointer to a variable that receives the process ID of the environment
365 * subsystem that has been started in the new session.
366 *
367 * @param[out] pInitialCommandProcessId
368 * Pointer to a variable that receives the process ID of the initial command.
369 *
370 * @return
371 * Success status as handed by the SM reply; otherwise a failure
372 * status code.
373 **/
374 NTSTATUS
375 NTAPI
376 SmStartCsr(
377 _In_ HANDLE SmApiPort,
378 _Out_ PULONG pMuSessionId,
379 _In_opt_ PUNICODE_STRING CommandLine,
380 _Out_ PHANDLE pWindowsSubSysProcessId,
381 _Out_ PHANDLE pInitialCommandProcessId)
382 {
383 NTSTATUS Status;
384 SM_API_MSG SmApiMsg = {0};
385 PSM_START_CSR_MSG StartCsr = &SmApiMsg.u.StartCsr;
386
387 #if 0 //def _WIN64
388 /* 64-bit SMSS needs to talk to 32-bit processes so do the LPC conversion */
389 if (SmpIsWow64Process())
390 {
391 return SmpWow64StartCsr(SmApiPort,
392 pMuSessionId,
393 CommandLine,
394 pWindowsSubSysProcessId,
395 pInitialCommandProcessId);
396 }
397 #endif
398
399 /* Set the message data */
400 if (CommandLine)
401 {
402 /* Validate CommandLine's length */
403 if (CommandLine->Length > sizeof(StartCsr->Buffer))
404 return STATUS_INVALID_PARAMETER;
405
406 /* Buffer stores a counted non-NULL-terminated UNICODE string */
407 StartCsr->Length = CommandLine->Length;
408 RtlCopyMemory(StartCsr->Buffer,
409 CommandLine->Buffer,
410 CommandLine->Length);
411 }
412 else
413 {
414 StartCsr->Length = 0;
415 }
416
417 /* Send the message and wait for a reply */
418 SmApiMsg.ApiNumber = SmpStartCsrApi;
419 Status = SmSendMsgToSm(SmApiPort, &SmApiMsg);
420
421 /* Give back information to caller */
422 *pMuSessionId = StartCsr->MuSessionId;
423 *pWindowsSubSysProcessId = StartCsr->WindowsSubSysProcessId;
424 *pInitialCommandProcessId = StartCsr->SmpInitialCommandProcessId;
425
426 return Status;
427 }
428
429 /**
430 * @brief
431 * Requests the SM to terminate a Terminal Services session.
432 *
433 * @param[in] SmApiPort
434 * Port handle returned by SmConnectToSm().
435 *
436 * @param[in] MuSessionId
437 * The Terminal Services session ID, returned by SmStartCsr().
438 *
439 * @return
440 * Success status as handed by the SM reply; otherwise a failure
441 * status code.
442 **/
443 NTSTATUS
444 NTAPI
445 SmStopCsr(
446 _In_ HANDLE SmApiPort,
447 _In_ ULONG MuSessionId)
448 {
449 SM_API_MSG SmApiMsg = {0};
450
451 #if 0 //def _WIN64
452 /* 64-bit SMSS needs to talk to 32-bit processes so do the LPC conversion */
453 if (SmpIsWow64Process())
454 {
455 return SmpWow64StopCsr(SmApiPort, MuSessionId);
456 }
457 #endif
458
459 /* Set the message data */
460 SmApiMsg.u.StopCsr.MuSessionId = MuSessionId;
461
462 /* Send the message and wait for a reply */
463 SmApiMsg.ApiNumber = SmpStopCsrApi;
464 return SmSendMsgToSm(SmApiPort, &SmApiMsg);
465 }