Reactos
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
26BOOLEAN SmpDebug = TRUE;
27#else
28BOOLEAN 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 **/
49NTSTATUS
50NTAPI
51SmExecuteProgram(
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 **/
186NTSTATUS
187NTAPI
188SmLookupSubsystem(
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 */
367NTSTATUS
368NTAPI
369SmQueryInformation(
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 */