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