reactos/sdk/lib/smlib/smclient.c
Hermès Bélusca-Maïto 0e14378d3e
[SMDLL][SMLIB] Deprecate the legacy ROS-specific SMDLL and improve SM client functions. (#4821)
This DLL was exporting legacy NT-incompatible or ROS-specific SM client
functions, that have been since 10 years now (2012) replaced by the new
NT-compatible SM:

- SmConnectApiPort(): was just SmConnectToSm().

- SmCompleteSession():
  The legacy SMSS used it for when a subsystem initialization was finished.
  Now (NT-compatible) this function is called by subsystems **only** when a
  subsystem session **terminates**: SmSessionComplete().

- SmExecuteProgram(): was just the client side of SmLoadDeferedSubSystem()
  (whose server side is not implemented yet). The legacy SM "old" SmExecPgm
  implementation actually was "SmLoadDeferedSubSystem"...

- SmLookupSubsystem(): is a utility-only function to read any registry value
  inside "Session Manager\SubSystems".

Move SMDLL's readme into SMLIB and update its contents.

Collect some residual useful functions into smutils.c (and moved in SMLIB,
though not compiled yet):
- SmExecuteProgram(), now implemented as a wrapper around SmExecPgm();
- SmLookupSubsystem(), described above;
- SmQueryInformation(), that retrieves a list of currently-running subsystems.

[SMLIB] Validate SbApiPortName's length in SmConnectToSm().
Fix CommandLine length validation in SmStartCsr().

Add documentation (+ SAL annotations) to the NT-compatible SMSS client functions.

smmsg.h: Add both Win32 and Win64 struct sizes C_ASSERTs for those whose size
change between these two processor architecture sizes.

[SMLIB] Introduce SmSendMsgToSm() as helper to send data into the SM LPC port.
+ Make the other API functions use it.

It should be observed that in Vista+, both functions SmConnectToSm() and this
new SmSendMsgToSm() are exported by NTDLL under the names RtlConnectToSm()
and RtlSendMsgToSm() (and use the same signature).
See: https://www.geoffchappell.com/studies/windows/win32/ntdll/history/names60.htm

[NTDLL] Correctly stub RtlConnectToSm() and RtlSendMsgToSm().
[NTDLL_VISTA] Link to SMLIB and simply export RtlConnectToSm() and RtlSendMsgToSm().
2022-11-08 17:40:53 +01:00

416 lines
12 KiB
C

/*
* PROJECT: ReactOS NT-Compatible Session Manager
* LICENSE: BSD 2-Clause License (https://spdx.org/licenses/BSD-2-Clause)
* PURPOSE: SMSS Client Library (SMLIB) Client Stubs
* COPYRIGHT: Copyright 2012-2013 Alex Ionescu <alex.ionescu@reactos.org>
* Copyright 2021 Hervé Poussineau <hpoussin@reactos.org>
* Copyright 2022 Hermès Bélusca-Maïto <hermes.belusca-maito@reactos.org>
*/
/* INCLUDES *******************************************************************/
#include "precomp.h"
#define NDEBUG
#include <debug.h>
/* FUNCTIONS ******************************************************************/
/**
* @brief
* Connects to the SM API port for registering a session callback port (Sb)
* associated to a subsystem, or for issuing API requests to the SM API port.
*
* There are only two ways to call this API:
* a) subsystems willing to register with SM will use it
* with full parameters (the function checks them);
* b) regular SM clients, will set to 0 the 1st, the 2nd,
* and the 3rd parameters.
*
* @param[in] SbApiPortName
* Name of the Sb port the calling subsystem server already
* created in the system namespace.
*
* @param[in] SbApiPort
* LPC port handle (checked, but not used: the subsystem is
* required to have already created the callback port before
* it connects to the SM).
*
* @param[in] ImageType
* A valid IMAGE_SUBSYSTEM_xxx value. PE images having this
* subsystem value will be handled by the current subsystem.
*
* @param[out] SmApiPort
* Pointer to a HANDLE, which will be filled with a valid
* client-side LPC communication port.
*
* @return
* If all three optional values are omitted, an LPC status.
* STATUS_INVALID_PARAMETER_MIX if PortName is defined and
* both SbApiPort and ImageType are 0.
*
* @remark
* Exported on Vista+ by NTDLL and called RtlConnectToSm().
**/
NTSTATUS
NTAPI
SmConnectToSm(
_In_opt_ PUNICODE_STRING SbApiPortName,
_In_opt_ HANDLE SbApiPort,
_In_opt_ ULONG ImageType,
_Out_ PHANDLE SmApiPort)
{
NTSTATUS Status;
SECURITY_QUALITY_OF_SERVICE SecurityQos;
UNICODE_STRING PortName;
SB_CONNECTION_INFO ConnectInfo = {0};
ULONG ConnectInfoLength = sizeof(ConnectInfo);
/* Setup the QoS structure */
SecurityQos.ImpersonationLevel = SecurityIdentification;
SecurityQos.ContextTrackingMode = SECURITY_DYNAMIC_TRACKING;
SecurityQos.EffectiveOnly = TRUE;
/* Set the SM API port name */
RtlInitUnicodeString(&PortName, L"\\SmApiPort"); // SM_API_PORT_NAME
/* Check if this is a client connecting to SMSS, or SMSS to itself */
if (SbApiPortName)
{
/* A client SB port as well as an image type must be present */
if (!SbApiPort || (ImageType == IMAGE_SUBSYSTEM_UNKNOWN))
return STATUS_INVALID_PARAMETER_MIX;
/* Validate SbApiPortName's length */
if (SbApiPortName->Length >= sizeof(ConnectInfo.SbApiPortName))
return STATUS_INVALID_PARAMETER;
/* Copy the client port name, and NULL-terminate it */
RtlCopyMemory(ConnectInfo.SbApiPortName,
SbApiPortName->Buffer,
SbApiPortName->Length);
ConnectInfo.SbApiPortName[SbApiPortName->Length / sizeof(WCHAR)] = UNICODE_NULL;
/* Save the subsystem type */
ConnectInfo.SubsystemType = ImageType;
}
else
{
/* No client port, and the subsystem type is not set */
ConnectInfo.SbApiPortName[0] = UNICODE_NULL;
ConnectInfo.SubsystemType = IMAGE_SUBSYSTEM_UNKNOWN;
}
/* Connect to SMSS and exchange connection information */
Status = NtConnectPort(SmApiPort,
&PortName,
&SecurityQos,
NULL,
NULL,
NULL,
&ConnectInfo,
&ConnectInfoLength);
if (!NT_SUCCESS(Status))
{
DPRINT1("SmConnectToSm: Connect to Sm failed %lx\n", Status);
}
#if (NTDDI_VERSION < NTDDI_VISTA)
else
{
/* Treat a warning or informational status as success */
Status = STATUS_SUCCESS;
}
#endif
/* Return if the connection was successful or not */
return Status;
}
/**
* @brief
* Sends a message to the SM via the SM API port.
*
* @param[in] SmApiPort
* Port handle returned by SmConnectToSm().
*
* @param[in,out] SmApiMsg
* Message to send to the SM. The API-specific data must be initialized,
* and the SmApiMsg->ApiNumber must be specified accordingly.
*
* @return
* Success status as handed by the SM reply; otherwise a failure
* status code.
*
* @remark
* Exported on Vista+ by NTDLL and called RtlSendMsgToSm().
**/
NTSTATUS
NTAPI
SmSendMsgToSm(
_In_ HANDLE SmApiPort,
_Inout_ PSM_API_MSG SmApiMsg)
{
static ULONG RtlpSmMessageInfo[SmpMaxApiNumber] =
{
0 /*sizeof(SM_CREATE_FOREIGN_SESSION_MSG)*/,
sizeof(SM_SESSION_COMPLETE_MSG),
0 /*sizeof(SM_TERMINATE_FOREIGN_SESSION_MSG)*/,
sizeof(SM_EXEC_PGM_MSG),
sizeof(SM_LOAD_DEFERED_SUBSYSTEM_MSG),
sizeof(SM_START_CSR_MSG),
sizeof(SM_STOP_CSR_MSG),
};
NTSTATUS Status;
ULONG DataLength;
if (SmApiMsg->ApiNumber >= SmpMaxApiNumber)
return STATUS_NOT_IMPLEMENTED;
/* Obtain the necessary data length for this API */
DataLength = RtlpSmMessageInfo[SmApiMsg->ApiNumber];
/* Fill out the Port Message Header */
// RtlZeroMemory(&SmApiMsg->h, sizeof(SmApiMsg->h));
SmApiMsg->h.u2.ZeroInit = 0;
/* DataLength = user_data_size + anything between
* header and data, including intermediate padding */
SmApiMsg->h.u1.s1.DataLength = (CSHORT)DataLength +
FIELD_OFFSET(SM_API_MSG, u) - sizeof(SmApiMsg->h);
/* TotalLength = sizeof(*SmApiMsg) on <= NT5.2, otherwise:
* DataLength + header_size == user_data_size + FIELD_OFFSET(SM_API_MSG, u)
* without structure trailing padding */
SmApiMsg->h.u1.s1.TotalLength = SmApiMsg->h.u1.s1.DataLength + sizeof(SmApiMsg->h);
/* Send the LPC message and wait for a reply */
Status = NtRequestWaitReplyPort(SmApiPort, &SmApiMsg->h, &SmApiMsg->h);
if (!NT_SUCCESS(Status))
{
DPRINT1("SmSendMsgToSm: NtRequestWaitReplyPort failed, Status: 0x%08lx\n", Status);
}
else
{
/* Return the real status */
Status = SmApiMsg->ReturnValue;
}
return Status;
}
/**
* @brief
* This function is called by an environment subsystem server
* to tell the SM it has terminated the session it managed.
*
* @param[in] SmApiPort
* Port handle returned by SmConnectToSm().
*
* @param[in] SessionId
* The session ID of the session being terminated.
*
* @param[in] SessionStatus
* An NT status code for the termination.
*
* @return
* Success status as handed by the SM reply; otherwise a failure
* status code.
**/
NTSTATUS
NTAPI
SmSessionComplete(
_In_ HANDLE SmApiPort,
_In_ ULONG SessionId,
_In_ NTSTATUS SessionStatus)
{
SM_API_MSG SmApiMsg = {0};
PSM_SESSION_COMPLETE_MSG SessionComplete = &SmApiMsg.u.SessionComplete;
#if 0 //def _WIN64
/* 64-bit SMSS needs to talk to 32-bit processes so do the LPC conversion */
if (SmpIsWow64Process())
{
return SmpWow64SessionComplete(SmApiPort, SessionId, SessionStatus);
}
#endif
/* Set the message data */
SessionComplete->SessionId = SessionId;
SessionComplete->SessionStatus = SessionStatus;
/* Send the message and wait for a reply */
SmApiMsg.ApiNumber = SmpSessionCompleteApi;
return SmSendMsgToSm(SmApiPort, &SmApiMsg);
}
/**
* @brief
* Requests the SM to start a process under a new environment session.
*
* @param[in] SmApiPort
* Port handle returned by SmConnectToSm().
*
* @param[in] ProcessInformation
* A process description as returned by RtlCreateUserProcess().
*
* @param[in] DebugFlag
* If set, indicates that the caller wants to debug this process
* and act as its debug user interface.
*
* @return
* Success status as handed by the SM reply; otherwise a failure
* status code.
**/
NTSTATUS
NTAPI
SmExecPgm(
_In_ HANDLE SmApiPort,
_In_ PRTL_USER_PROCESS_INFORMATION ProcessInformation,
_In_ BOOLEAN DebugFlag)
{
NTSTATUS Status;
SM_API_MSG SmApiMsg = {0};
PSM_EXEC_PGM_MSG ExecPgm = &SmApiMsg.u.ExecPgm;
#if 0 //def _WIN64
/* 64-bit SMSS needs to talk to 32-bit processes so do the LPC conversion */
if (SmpIsWow64Process())
{
return SmpWow64ExecPgm(SmApiPort, ProcessInformation, DebugFlag);
}
#endif
/* Set the message data */
RtlCopyMemory(&ExecPgm->ProcessInformation,
ProcessInformation,
sizeof(ExecPgm->ProcessInformation));
ExecPgm->DebugFlag = DebugFlag;
/* Send the message and wait for a reply */
SmApiMsg.ApiNumber = SmpExecPgmApi;
Status = SmSendMsgToSm(SmApiPort, &SmApiMsg);
/* Close the handles that the parent passed in and return status */
NtClose(ProcessInformation->ProcessHandle);
NtClose(ProcessInformation->ThreadHandle);
return Status;
}
/**
* @brief
* Requests the SM to create a new Terminal Services session
* and start an initial command.
*
* @param[in] SmApiPort
* Port handle returned by SmConnectToSm().
*
* @param[out] pMuSessionId
* Pointer to a variable that receives the session ID of the new
* Terminal Services session that has been created.
*
* @param[in] CommandLine
* Full path to the image to be used as the initial command.
*
* @param[out] pWindowsSubSysProcessId
* Pointer to a variable that receives the process ID of the environment
* subsystem that has been started in the new session.
*
* @param[out] pInitialCommandProcessId
* Pointer to a variable that receives the process ID of the initial command.
*
* @return
* Success status as handed by the SM reply; otherwise a failure
* status code.
**/
NTSTATUS
NTAPI
SmStartCsr(
_In_ HANDLE SmApiPort,
_Out_ PULONG pMuSessionId,
_In_opt_ PUNICODE_STRING CommandLine,
_Out_ PHANDLE pWindowsSubSysProcessId,
_Out_ PHANDLE pInitialCommandProcessId)
{
NTSTATUS Status;
SM_API_MSG SmApiMsg = {0};
PSM_START_CSR_MSG StartCsr = &SmApiMsg.u.StartCsr;
#if 0 //def _WIN64
/* 64-bit SMSS needs to talk to 32-bit processes so do the LPC conversion */
if (SmpIsWow64Process())
{
return SmpWow64StartCsr(SmApiPort,
pMuSessionId,
CommandLine,
pWindowsSubSysProcessId,
pInitialCommandProcessId);
}
#endif
/* Set the message data */
if (CommandLine)
{
/* Validate CommandLine's length */
if (CommandLine->Length > sizeof(StartCsr->Buffer))
return STATUS_INVALID_PARAMETER;
/* Buffer stores a counted non-NULL-terminated UNICODE string */
StartCsr->Length = CommandLine->Length;
RtlCopyMemory(StartCsr->Buffer,
CommandLine->Buffer,
CommandLine->Length);
}
else
{
StartCsr->Length = 0;
}
/* Send the message and wait for a reply */
SmApiMsg.ApiNumber = SmpStartCsrApi;
Status = SmSendMsgToSm(SmApiPort, &SmApiMsg);
/* Give back informations to caller */
*pMuSessionId = StartCsr->MuSessionId;
*pWindowsSubSysProcessId = StartCsr->WindowsSubSysProcessId;
*pInitialCommandProcessId = StartCsr->SmpInitialCommandProcessId;
return Status;
}
/**
* @brief
* Requests the SM to terminate a Terminal Services session.
*
* @param[in] SmApiPort
* Port handle returned by SmConnectToSm().
*
* @param[in] MuSessionId
* The Terminal Services session ID, returned by SmStartCsr().
*
* @return
* Success status as handed by the SM reply; otherwise a failure
* status code.
**/
NTSTATUS
NTAPI
SmStopCsr(
_In_ HANDLE SmApiPort,
_In_ ULONG MuSessionId)
{
SM_API_MSG SmApiMsg = {0};
#if 0 //def _WIN64
/* 64-bit SMSS needs to talk to 32-bit processes so do the LPC conversion */
if (SmpIsWow64Process())
{
return SmpWow64StopCsr(SmApiPort, MuSessionId);
}
#endif
/* Set the message data */
SmApiMsg.u.StopCsr.MuSessionId = MuSessionId;
/* Send the message and wait for a reply */
SmApiMsg.ApiNumber = SmpStopCsrApi;
return SmSendMsgToSm(SmApiPort, &SmApiMsg);
}