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>
10 /* INCLUDES *******************************************************************/
17 /* FUNCTIONS ******************************************************************/
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.
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.
30 * @param[in] SbApiPortName
31 * Name of the Sb port the calling subsystem server already
32 * created in the system namespace.
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).
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.
43 * @param[out] SmApiPort
44 * Pointer to a HANDLE, which will be filled with a valid
45 * client-side LPC communication port.
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.
53 * Exported on Vista+ by NTDLL and called RtlConnectToSm().
58 _In_opt_ PUNICODE_STRING SbApiPortName
,
59 _In_opt_ HANDLE SbApiPort
,
60 _In_opt_ ULONG ImageType
,
61 _Out_ PHANDLE SmApiPort
)
64 SECURITY_QUALITY_OF_SERVICE SecurityQos
;
65 UNICODE_STRING PortName
;
66 SB_CONNECTION_INFO ConnectInfo
= {0};
67 ULONG ConnectInfoLength
= sizeof(ConnectInfo
);
69 /* Setup the QoS structure */
70 SecurityQos
.ImpersonationLevel
= SecurityIdentification
;
71 SecurityQos
.ContextTrackingMode
= SECURITY_DYNAMIC_TRACKING
;
72 SecurityQos
.EffectiveOnly
= TRUE
;
74 /* Set the SM API port name */
75 RtlInitUnicodeString(&PortName
, L
"\\SmApiPort"); // SM_API_PORT_NAME
77 /* Check if this is a client connecting to SMSS, or SMSS to itself */
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
;
84 /* Validate SbApiPortName's length */
85 if (SbApiPortName
->Length
>= sizeof(ConnectInfo
.SbApiPortName
))
86 return STATUS_INVALID_PARAMETER
;
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
;
94 /* Save the subsystem type */
95 ConnectInfo
.SubsystemType
= ImageType
;
99 /* No client port, and the subsystem type is not set */
100 ConnectInfo
.SbApiPortName
[0] = UNICODE_NULL
;
101 ConnectInfo
.SubsystemType
= IMAGE_SUBSYSTEM_UNKNOWN
;
104 /* Connect to SMSS and exchange connection information */
105 Status
= NtConnectPort(SmApiPort
,
113 if (!NT_SUCCESS(Status
))
115 DPRINT1("SmConnectToSm: Connect to Sm failed %lx\n", Status
);
117 #if (NTDDI_VERSION < NTDDI_VISTA)
120 /* Treat a warning or informational status as success */
121 Status
= STATUS_SUCCESS
;
125 /* Return if the connection was successful or not */
131 * Sends a message to the SM via the SM API port.
133 * @param[in] SmApiPort
134 * Port handle returned by SmConnectToSm().
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.
141 * Success status as handed by the SM reply; otherwise a failure
145 * Exported on Vista+ by NTDLL and called RtlSendMsgToSm().
150 _In_ HANDLE SmApiPort
,
151 _Inout_ PSM_API_MSG SmApiMsg
)
153 static ULONG RtlpSmMessageInfo
[SmpMaxApiNumber
] =
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
),
167 if (SmApiMsg
->ApiNumber
>= SmpMaxApiNumber
)
168 return STATUS_NOT_IMPLEMENTED
;
170 /* Obtain the necessary data length for this API */
171 DataLength
= RtlpSmMessageInfo
[SmApiMsg
->ApiNumber
];
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
);
185 /* Send the LPC message and wait for a reply */
186 Status
= NtRequestWaitReplyPort(SmApiPort
, &SmApiMsg
->h
, &SmApiMsg
->h
);
187 if (!NT_SUCCESS(Status
))
189 DPRINT1("SmSendMsgToSm: NtRequestWaitReplyPort failed, Status: 0x%08lx\n", Status
);
193 /* Return the real status */
194 Status
= SmApiMsg
->ReturnValue
;
202 * This function is called by an environment subsystem server
203 * to tell the SM it has terminated the session it managed.
205 * @param[in] SmApiPort
206 * Port handle returned by SmConnectToSm().
208 * @param[in] SessionId
209 * The session ID of the session being terminated.
211 * @param[in] SessionStatus
212 * An NT status code for the termination.
215 * Success status as handed by the SM reply; otherwise a failure
221 _In_ HANDLE SmApiPort
,
222 _In_ ULONG SessionId
,
223 _In_ NTSTATUS SessionStatus
)
225 SM_API_MSG SmApiMsg
= {0};
226 PSM_SESSION_COMPLETE_MSG SessionComplete
= &SmApiMsg
.u
.SessionComplete
;
229 /* 64-bit SMSS needs to talk to 32-bit processes so do the LPC conversion */
230 if (SmpIsWow64Process())
232 return SmpWow64SessionComplete(SmApiPort
, SessionId
, SessionStatus
);
236 /* Set the message data */
237 SessionComplete
->SessionId
= SessionId
;
238 SessionComplete
->SessionStatus
= SessionStatus
;
240 /* Send the message and wait for a reply */
241 SmApiMsg
.ApiNumber
= SmpSessionCompleteApi
;
242 return SmSendMsgToSm(SmApiPort
, &SmApiMsg
);
247 * Requests the SM to start a process under a new environment session.
249 * @param[in] SmApiPort
250 * Port handle returned by SmConnectToSm().
252 * @param[in] ProcessInformation
253 * A process description as returned by RtlCreateUserProcess().
255 * @param[in] DebugFlag
256 * If set, indicates that the caller wants to debug this process
257 * and act as its debug user interface.
260 * Success status as handed by the SM reply; otherwise a failure
266 _In_ HANDLE SmApiPort
,
267 _In_ PRTL_USER_PROCESS_INFORMATION ProcessInformation
,
268 _In_ BOOLEAN DebugFlag
)
271 SM_API_MSG SmApiMsg
= {0};
272 PSM_EXEC_PGM_MSG ExecPgm
= &SmApiMsg
.u
.ExecPgm
;
275 /* 64-bit SMSS needs to talk to 32-bit processes so do the LPC conversion */
276 if (SmpIsWow64Process())
278 return SmpWow64ExecPgm(SmApiPort
, ProcessInformation
, DebugFlag
);
282 /* Set the message data */
283 ExecPgm
->ProcessInformation
= *ProcessInformation
;
284 ExecPgm
->DebugFlag
= DebugFlag
;
286 /* Send the message and wait for a reply */
287 SmApiMsg
.ApiNumber
= SmpExecPgmApi
;
288 Status
= SmSendMsgToSm(SmApiPort
, &SmApiMsg
);
290 /* Close the handles that the parent passed in and return status */
291 NtClose(ProcessInformation
->ProcessHandle
);
292 NtClose(ProcessInformation
->ThreadHandle
);
298 * This function is used to make the SM start an environment
299 * subsystem server process.
301 * @param[in] SmApiPort
302 * Port handle returned by SmConnectToSm().
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".
312 * Success status as handed by the SM reply; otherwise a failure
317 SmLoadDeferedSubsystem(
318 _In_ HANDLE SmApiPort
,
319 _In_ PUNICODE_STRING DeferedSubsystem
)
321 SM_API_MSG SmApiMsg
= {0};
322 PSM_LOAD_DEFERED_SUBSYSTEM_MSG LoadDefered
= &SmApiMsg
.u
.LoadDefered
;
325 /* 64-bit SMSS needs to talk to 32-bit processes so do the LPC conversion */
326 if (SmpIsWow64Process())
328 return SmpWow64LoadDeferedSubsystem(SmApiPort
, DeferedSubsystem
);
332 /* Validate DeferedSubsystem's length */
333 if (DeferedSubsystem
->Length
> sizeof(LoadDefered
->Buffer
))
334 return STATUS_INVALID_PARAMETER
;
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
);
343 /* Send the message and wait for a reply */
344 SmApiMsg
.ApiNumber
= SmpLoadDeferedSubsystemApi
;
345 return SmSendMsgToSm(SmApiPort
, &SmApiMsg
);
350 * Requests the SM to create a new Terminal Services session
351 * and start an initial command.
353 * @param[in] SmApiPort
354 * Port handle returned by SmConnectToSm().
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.
360 * @param[in] CommandLine
361 * Full path to the image to be used as the initial command.
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.
367 * @param[out] pInitialCommandProcessId
368 * Pointer to a variable that receives the process ID of the initial command.
371 * Success status as handed by the SM reply; otherwise a failure
377 _In_ HANDLE SmApiPort
,
378 _Out_ PULONG pMuSessionId
,
379 _In_opt_ PUNICODE_STRING CommandLine
,
380 _Out_ PHANDLE pWindowsSubSysProcessId
,
381 _Out_ PHANDLE pInitialCommandProcessId
)
384 SM_API_MSG SmApiMsg
= {0};
385 PSM_START_CSR_MSG StartCsr
= &SmApiMsg
.u
.StartCsr
;
388 /* 64-bit SMSS needs to talk to 32-bit processes so do the LPC conversion */
389 if (SmpIsWow64Process())
391 return SmpWow64StartCsr(SmApiPort
,
394 pWindowsSubSysProcessId
,
395 pInitialCommandProcessId
);
399 /* Set the message data */
402 /* Validate CommandLine's length */
403 if (CommandLine
->Length
> sizeof(StartCsr
->Buffer
))
404 return STATUS_INVALID_PARAMETER
;
406 /* Buffer stores a counted non-NULL-terminated UNICODE string */
407 StartCsr
->Length
= CommandLine
->Length
;
408 RtlCopyMemory(StartCsr
->Buffer
,
410 CommandLine
->Length
);
414 StartCsr
->Length
= 0;
417 /* Send the message and wait for a reply */
418 SmApiMsg
.ApiNumber
= SmpStartCsrApi
;
419 Status
= SmSendMsgToSm(SmApiPort
, &SmApiMsg
);
421 /* Give back information to caller */
422 *pMuSessionId
= StartCsr
->MuSessionId
;
423 *pWindowsSubSysProcessId
= StartCsr
->WindowsSubSysProcessId
;
424 *pInitialCommandProcessId
= StartCsr
->SmpInitialCommandProcessId
;
431 * Requests the SM to terminate a Terminal Services session.
433 * @param[in] SmApiPort
434 * Port handle returned by SmConnectToSm().
436 * @param[in] MuSessionId
437 * The Terminal Services session ID, returned by SmStartCsr().
440 * Success status as handed by the SM reply; otherwise a failure
446 _In_ HANDLE SmApiPort
,
447 _In_ ULONG MuSessionId
)
449 SM_API_MSG SmApiMsg
= {0};
452 /* 64-bit SMSS needs to talk to 32-bit processes so do the LPC conversion */
453 if (SmpIsWow64Process())
455 return SmpWow64StopCsr(SmApiPort
, MuSessionId
);
459 /* Set the message data */
460 SmApiMsg
.u
.StopCsr
.MuSessionId
= MuSessionId
;
462 /* Send the message and wait for a reply */
463 SmApiMsg
.ApiNumber
= SmpStopCsrApi
;
464 return SmSendMsgToSm(SmApiPort
, &SmApiMsg
);