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 } 466