1 /* 2 * PROJECT: ReactOS SM Helper Library 3 * LICENSE: GPL-2.0-or-later (https://spdx.org/licenses/GPL-2.0-or-later) 4 * PURPOSE: Utility functions built around the client SM API 5 * COPYRIGHT: Copyright 2005 Emanuele Aliberti <ea@reactos.com> 6 * Copyright 2022 Hermès Bélusca-Maïto <hermes.belusca-maito@reactos.org> 7 */ 8 9 #include "precomp.h" 10 11 #define WIN32_NO_STATUS 12 #define _INC_WINDOWS 13 #define COM_NO_WINDOWS_H 14 #include <windef.h> 15 #include <winreg.h> 16 17 #define NTOS_MODE_USER 18 #include <ndk/cmfuncs.h> 19 20 #include <sm/helper.h> 21 22 #define NDEBUG 23 #include <debug.h> 24 25 #if DBG 26 BOOLEAN SmpDebug = TRUE; 27 #else 28 BOOLEAN SmpDebug = FALSE; 29 #endif 30 31 /** 32 * @brief 33 * This function is used to make the SM start an external process 34 * under an already-loaded environment subsystem server. 35 * 36 * @param[in] SmApiPort 37 * Port handle returned by SmConnectToSm(). 38 * 39 * @param[in] Program 40 * Fully qualified NT name of the executable to load. 41 * 42 * @return 43 * Success status as handed by the SM reply; otherwise a failure 44 * status code. 45 * 46 * @remark 47 * Adapted from SMSS' SmpExecuteInitialCommand() and SmpExecuteImage(). 48 **/ 49 NTSTATUS 50 NTAPI 51 SmExecuteProgram( 52 _In_ HANDLE SmApiPort, 53 _In_ PUNICODE_STRING Program /*, 54 _Out_opt_ PRTL_USER_PROCESS_INFORMATION ProcessInformation*/) 55 { 56 // ULONG MuSessionId; 57 58 NTSTATUS Status; 59 RTL_USER_PROCESS_INFORMATION ProcessInfo; 60 PRTL_USER_PROCESS_PARAMETERS ProcessParameters; 61 62 PUNICODE_STRING FileName = Program; // FIXME! 63 PUNICODE_STRING Directory = NULL; // FIXME! 64 PUNICODE_STRING CommandLine = NULL; // FIXME! 65 66 PVOID SmpDefaultEnvironment = NtCurrentPeb()->ProcessParameters->Environment; 67 // UNICODE_STRING SmpDefaultLibPath; 68 69 DPRINT("SMLIB: %s(%p, '%wZ') called\n", 70 __FUNCTION__, SmApiPort, Program); 71 72 /* Parameters validation */ 73 if (!SmApiPort) 74 return STATUS_INVALID_PARAMETER_1; 75 if (!Program) 76 return STATUS_INVALID_PARAMETER_2; 77 78 /* Create parameters for the target process, using the current environment */ 79 Status = RtlCreateProcessParameters(&ProcessParameters, 80 FileName, 81 /* SmpDefaultLibPath.Length ? 82 &SmpDefaultLibPath : */ NULL, 83 Directory, 84 CommandLine, 85 SmpDefaultEnvironment, 86 NULL, 87 NULL, 88 NULL, 89 0); 90 if (!NT_SUCCESS(Status)) 91 { 92 DPRINT1("SMLIB: RtlCreateProcessParameters failed for %wZ - Status == %lx\n", 93 FileName, Status); 94 return Status; 95 } 96 97 /* Set the size field as required */ 98 ProcessInfo.Size = sizeof(ProcessInfo); 99 100 /* Otherwise inherit the flag that was passed to SMSS itself */ 101 ProcessParameters->DebugFlags = SmpDebug; 102 103 /* And always force NX for anything that SMSS launches */ 104 ProcessParameters->Flags |= RTL_USER_PROCESS_PARAMETERS_NX; 105 106 /* Now create the process in suspended state */ 107 Status = RtlCreateUserProcess(FileName, 108 OBJ_CASE_INSENSITIVE, 109 ProcessParameters, 110 NULL, 111 NULL, 112 NULL, 113 FALSE, 114 NULL, 115 NULL, 116 &ProcessInfo); 117 RtlDestroyProcessParameters(ProcessParameters); 118 if (!NT_SUCCESS(Status)) 119 { 120 /* If we couldn't create it, fail back to the caller */ 121 DPRINT1("SMLIB: Failed load of %wZ - Status == %lx\n", 122 FileName, Status); 123 return Status; 124 } 125 126 // /* Now duplicate the handle to this process */ 127 // Status = NtDuplicateObject(NtCurrentProcess(), 128 // ProcessInfo.ProcessHandle, 129 // NtCurrentProcess(), 130 // InitialCommandProcess, 131 // PROCESS_ALL_ACCESS, 132 // 0, 133 // 0); 134 // if (!NT_SUCCESS(Status)) 135 // { 136 // /* Kill it utterly if duplication failed */ 137 // DPRINT1("SMLIB: DupObject Failed. Status == %lx\n", Status); 138 // NtTerminateProcess(ProcessInfo.ProcessHandle, Status); 139 // NtResumeThread(ProcessInfo.ThreadHandle, NULL); 140 // NtClose(ProcessInfo.ThreadHandle); 141 // NtClose(ProcessInfo.ProcessHandle); 142 // return Status; 143 // } 144 145 /* Call SM and wait for a reply */ 146 Status = SmExecPgm(SmApiPort, &ProcessInfo, TRUE); 147 148 NtClose(ProcessInfo.ThreadHandle); 149 NtClose(ProcessInfo.ProcessHandle); 150 151 // if (ProcessInformation) 152 // *ProcessInformation = ProcessInfo; 153 154 DPRINT("SMLIB: %s returned (Status=0x%08lx)\n", __FUNCTION__, Status); 155 return Status; 156 } 157 158 /** 159 * @brief 160 * Reads from the registry key 161 * \Registry\SYSTEM\CurrentControlSet\Control\Session Manager\SubSystems 162 * the value specified by Name. 163 * 164 * @param[in] Name 165 * Name of the program to run, that is a value's name in 166 * the SM registry key "SubSystems". 167 * 168 * @param[out] Data 169 * What the registry gave back for Name. 170 * 171 * @param[in,out] DataLength 172 * How much Data the registry returns. 173 * 174 * @param[out] DataType 175 * Optional pointer to a variable that receives the type of data 176 * stored in the specified value. 177 * 178 * @param[in] Environment 179 * Optional environment to be used to possibly expand Data before 180 * returning it back; if set to NULL, no expansion will be performed. 181 * 182 * @return 183 * Success status if the specified subsystem existed and its information 184 * has been retrieved successfully; otherwise a failure status code. 185 **/ 186 NTSTATUS 187 NTAPI 188 SmLookupSubsystem( 189 _In_ PWSTR Name, 190 _Out_ PWSTR Data, 191 _Inout_ PULONG DataLength, 192 _Out_opt_ PULONG DataType, 193 _In_opt_ PVOID Environment) 194 { 195 NTSTATUS Status; 196 UNICODE_STRING usKeyName; 197 OBJECT_ATTRIBUTES Oa; 198 HANDLE hKey = NULL; 199 200 UNICODE_STRING usValueName; 201 PWCHAR KeyValueInformation = NULL; 202 ULONG KeyValueInformationLength = 1024; 203 ULONG ResultLength = 0; 204 PKEY_VALUE_PARTIAL_INFORMATION kvpi; 205 206 DPRINT("SMLIB: %s(Name='%S') called\n", __FUNCTION__, Name); 207 208 /* 209 * Prepare the key name to scan and 210 * related object attributes. 211 */ 212 RtlInitUnicodeString(&usKeyName, 213 L"\\Registry\\Machine\\SYSTEM\\CurrentControlSet\\Control\\Session Manager\\SubSystems"); 214 215 InitializeObjectAttributes(&Oa, 216 &usKeyName, 217 OBJ_CASE_INSENSITIVE, 218 NULL, 219 NULL); 220 /* 221 * Open the key. This MUST NOT fail, if the 222 * request is for a legitimate subsystem. 223 */ 224 Status = NtOpenKey(&hKey, 225 MAXIMUM_ALLOWED, 226 &Oa); 227 if (!NT_SUCCESS(Status)) 228 { 229 DPRINT1("%s: NtOpenKey failed (Status=0x%08lx)\n", __FUNCTION__, Status); 230 return Status; 231 } 232 233 KeyValueInformation = RtlAllocateHeap(RtlGetProcessHeap(), 234 0, 235 KeyValueInformationLength); 236 if (!KeyValueInformation) 237 { 238 NtClose(hKey); 239 return STATUS_NO_MEMORY; 240 } 241 242 kvpi = (PKEY_VALUE_PARTIAL_INFORMATION)KeyValueInformation; 243 RtlInitUnicodeString(&usValueName, Name); 244 Status = NtQueryValueKey(hKey, 245 &usValueName, 246 KeyValuePartialInformation, 247 KeyValueInformation, 248 KeyValueInformationLength, 249 &ResultLength); 250 if (!NT_SUCCESS(Status)) 251 { 252 DPRINT1("%s: NtQueryValueKey failed (Status=0x%08lx)\n", __FUNCTION__, Status); 253 254 RtlFreeHeap(RtlGetProcessHeap(), 0, KeyValueInformation); 255 NtClose(hKey); 256 257 return Status; 258 } 259 260 DPRINT("kvpi.TitleIndex = %lu\n", kvpi->TitleIndex); 261 DPRINT("kvpi.Type = %lu\n", kvpi->Type); 262 DPRINT("kvpi.DataLength = %lu\n", kvpi->DataLength); 263 264 if (Data && DataLength) 265 { 266 if (DataType) 267 *DataType = kvpi->Type; 268 269 if (Environment && (kvpi->Type == REG_EXPAND_SZ)) 270 { 271 UNICODE_STRING Source; 272 UNICODE_STRING Destination; 273 PWCHAR DestinationBuffer = NULL; 274 ULONG Length; 275 276 DPRINT("SMLIB: %s: value will be expanded\n", __FUNCTION__); 277 278 Length = 2 * KeyValueInformationLength; 279 DestinationBuffer = RtlAllocateHeap(RtlGetProcessHeap(), 280 0, 281 Length); 282 if (!DestinationBuffer) 283 { 284 Status = STATUS_NO_MEMORY; 285 } 286 else 287 { 288 Source.Length = (USHORT)kvpi->DataLength; 289 Source.MaximumLength = Source.Length; 290 Source.Buffer = (PWCHAR)&kvpi->Data; 291 292 RtlInitEmptyUnicodeString(&Destination, 293 DestinationBuffer, 294 (USHORT)Length); 295 296 Length = 0; 297 Status = RtlExpandEnvironmentStrings_U(Environment, 298 &Source, 299 &Destination, 300 &Length); 301 if (NT_SUCCESS(Status)) 302 { 303 *DataLength = min(*DataLength, Destination.Length); 304 RtlCopyMemory(Data, Destination.Buffer, *DataLength); 305 } 306 RtlFreeHeap(RtlGetProcessHeap(), 0, DestinationBuffer); 307 } 308 } 309 else 310 { 311 DPRINT("SMLIB: %s: value won't be expanded\n", __FUNCTION__); 312 *DataLength = min(*DataLength, kvpi->DataLength); 313 RtlCopyMemory(Data, &kvpi->Data, *DataLength); 314 } 315 } 316 else 317 { 318 DPRINT1("SMLIB: %s: Data or DataLength is NULL!\n", __FUNCTION__); 319 Status = STATUS_INVALID_PARAMETER; 320 } 321 322 RtlFreeHeap(RtlGetProcessHeap(), 0, KeyValueInformation); 323 NtClose(hKey); 324 325 return Status; 326 } 327 328 /** 329 * @brief 330 * Retrieves information about subsystems registered with the SM. 331 * 332 * @param[in] SmApiPort 333 * Port handle returned by SmConnectToSm(). 334 * 335 * @param[in] SmInformationClass 336 * An SM information class ID: 337 * - SmBasicInformation: 338 * The number and list of registered subsystems. Data is returned 339 * in a SM_BASIC_INFORMATION structure. 340 * - SmSubSystemInformation: 341 * Information about a particular registered subsystem. Data is 342 * returned in a SM_SUBSYSTEM_INFORMATION structure. 343 * 344 * @param[in,out] Data 345 * Pointer to storage for the information to request. Either a 346 * SM_BASIC_INFORMATION or a SM_SUBSYSTEM_INFORMATION, depending 347 * on the information class. 348 * 349 * @param[in] DataLength 350 * Length in bytes of the Data buffer; it must be set and must 351 * match the SmInformationClass information size. 352 * 353 * @param[in,out] ReturnedDataLength 354 * Optional pointer to storage to receive the size of the returned data. 355 * 356 * @return 357 * STATUS_SUCCESS when the information asked for has been retrieved. 358 * STATUS_INVALID_PARAMETER_2 if an invalid information class has been provided. 359 * STATUS_INFO_LENGTH_MISMATCH in case either DataLength was set to 0 360 * or to a value that does not match what the SmInformationClass requires. 361 * Otherwise, a success status as handed by the SM reply, or a failure 362 * status code. 363 * 364 * @remark 365 * This API is ReactOS-specific and not in NT. 366 */ 367 NTSTATUS 368 NTAPI 369 SmQueryInformation( 370 _In_ HANDLE SmApiPort, 371 _In_ SM_INFORMATION_CLASS SmInformationClass, 372 _Inout_ PVOID Data, 373 _In_ ULONG DataLength, 374 _Inout_opt_ PULONG ReturnedDataLength) 375 { 376 #if defined(__REACTOS__) && DBG 377 NTSTATUS Status; 378 SM_API_MSG SmApiMsg = {0}; 379 PSM_QUERYINFO_MSG QueryInfo = &SmApiMsg.u.QueryInfo; 380 381 /* Marshal data in the port message */ 382 switch (SmInformationClass) 383 { 384 case SmBasicInformation: 385 if (DataLength != sizeof(SM_BASIC_INFORMATION)) 386 { 387 return STATUS_INFO_LENGTH_MISMATCH; 388 } 389 QueryInfo->SmInformationClass = SmBasicInformation; 390 QueryInfo->DataLength = DataLength; 391 QueryInfo->BasicInformation.SubSystemCount = 0; 392 break; 393 394 case SmSubSystemInformation: 395 if (DataLength != sizeof(SM_SUBSYSTEM_INFORMATION)) 396 { 397 return STATUS_INFO_LENGTH_MISMATCH; 398 } 399 QueryInfo->SmInformationClass = SmSubSystemInformation; 400 QueryInfo->DataLength = DataLength; 401 QueryInfo->SubSystemInformation.SubSystemId = 402 ((PSM_SUBSYSTEM_INFORMATION)Data)->SubSystemId; 403 break; 404 405 default: 406 return STATUS_INVALID_PARAMETER_2; 407 } 408 409 /* SM API to invoke */ 410 SmApiMsg.ApiNumber = SM_API_QUERY_INFORMATION; 411 412 /* NOTE: Repurpose the DataLength variable */ 413 DataLength = sizeof(SM_QUERYINFO_MSG); 414 415 /* Fill out the Port Message Header */ 416 // SmApiMsg.h.u2.s2.Type = LPC_NEW_MESSAGE; 417 SmApiMsg.h.u2.ZeroInit = 0; 418 /* DataLength = user_data_size + anything between 419 * header and data, including intermediate padding */ 420 SmApiMsg.h.u1.s1.DataLength = (CSHORT)DataLength + 421 FIELD_OFFSET(SM_API_MSG, u) - sizeof(SmApiMsg.h); 422 /* TotalLength = sizeof(SmApiMsg) on <= NT5.2, otherwise: 423 * DataLength + header_size == user_data_size + FIELD_OFFSET(SM_API_MSG, u) 424 * without structure trailing padding */ 425 SmApiMsg.h.u1.s1.TotalLength = SmApiMsg.h.u1.s1.DataLength + sizeof(SmApiMsg.h); 426 427 /* Send the LPC message and wait for a reply */ 428 Status = NtRequestWaitReplyPort(SmApiPort, &SmApiMsg.h, &SmApiMsg.h); 429 if (!NT_SUCCESS(Status)) 430 { 431 DPRINT1("SMLIB: %s: NtRequestWaitReplyPort failed, Status: 0x%08lx\n", 432 __FUNCTION__, Status); 433 return Status; 434 } 435 436 /* Unmarshal data */ 437 RtlCopyMemory(Data, 438 &QueryInfo->BasicInformation, 439 QueryInfo->DataLength); 440 441 /* Use caller provided storage to store data size */ 442 if (ReturnedDataLength) 443 *ReturnedDataLength = QueryInfo->DataLength; 444 445 /* Return the real status */ 446 return SmApiMsg.ReturnValue; 447 #else 448 return STATUS_NOT_IMPLEMENTED; 449 #endif /* defined(__REACTOS__) && DBG */ 450 } 451 452 /* EOF */ 453