reactos/sdk/lib/smlib/smclient.c

465 lines
14 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 */
ExecPgm->ProcessInformation = *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
* This function is used to make the SM start an environment
* subsystem server process.
*
* @param[in] SmApiPort
* Port handle returned by SmConnectToSm().
*
* @param[in] DeferedSubsystem
* Name of the subsystem to start. This must be one of the subsystems
* listed by value's name in the SM registry key
* \Registry\SYSTEM\CurrentControlSet\Control\Session Manager\SubSystems
* (used by the SM to lookup the corresponding image name).
* Default valid names are: "Debug", "Windows", "Posix", "Os2".
*
* @return
* Success status as handed by the SM reply; otherwise a failure
* status code.
**/
NTSTATUS
NTAPI
SmLoadDeferedSubsystem(
_In_ HANDLE SmApiPort,
_In_ PUNICODE_STRING DeferedSubsystem)
{
SM_API_MSG SmApiMsg = {0};
PSM_LOAD_DEFERED_SUBSYSTEM_MSG LoadDefered = &SmApiMsg.u.LoadDefered;
#if 0 //def _WIN64
/* 64-bit SMSS needs to talk to 32-bit processes so do the LPC conversion */
if (SmpIsWow64Process())
{
return SmpWow64LoadDeferedSubsystem(SmApiPort, DeferedSubsystem);
}
#endif
/* Validate DeferedSubsystem's length */
if (DeferedSubsystem->Length > sizeof(LoadDefered->Buffer))
return STATUS_INVALID_PARAMETER;
/* Set the message data */
/* Buffer stores a counted non-NULL-terminated UNICODE string */
LoadDefered->Length = DeferedSubsystem->Length;
RtlCopyMemory(LoadDefered->Buffer,
DeferedSubsystem->Buffer,
DeferedSubsystem->Length);
/* Send the message and wait for a reply */
SmApiMsg.ApiNumber = SmpLoadDeferedSubsystemApi;
return SmSendMsgToSm(SmApiPort, &SmApiMsg);
}
/**
* @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 information 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);
}