1 /*
2 * PROJECT: ReactOS Windows-Compatible Session Manager
3 * LICENSE: BSD 2-Clause License
4 * FILE: base/system/smss/smutil.c
5 * PURPOSE: Main SMSS Code
6 * PROGRAMMERS: Alex Ionescu
7 */
8
9 /* INCLUDES *******************************************************************/
10
11 #include "smss.h"
12
13 #include <ndk/sefuncs.h>
14
15 #define NDEBUG
16 #include <debug.h>
17
18 /* GLOBALS ********************************************************************/
19
20 //
21 // Taken from an ASSERT
22 //
23 #define VALUE_BUFFER_SIZE (sizeof(KEY_VALUE_PARTIAL_INFORMATION) + 512)
24
25 typedef struct _SMP_PRIVILEGE_STATE
26 {
27 HANDLE TokenHandle;
28 PTOKEN_PRIVILEGES OldPrivileges;
29 PTOKEN_PRIVILEGES NewPrivileges;
30 UCHAR OldBuffer[1024];
31 TOKEN_PRIVILEGES NewBuffer;
32 } SMP_PRIVILEGE_STATE, *PSMP_PRIVILEGE_STATE;
33
34 UNICODE_STRING SmpDebugKeyword, SmpASyncKeyword, SmpAutoChkKeyword;
35
36 /* FUNCTIONS ******************************************************************/
37
38 NTSTATUS
39 NTAPI
SmpAcquirePrivilege(IN ULONG Privilege,OUT PVOID * PrivilegeState)40 SmpAcquirePrivilege(IN ULONG Privilege,
41 OUT PVOID *PrivilegeState)
42 {
43 PSMP_PRIVILEGE_STATE State;
44 ULONG Size;
45 NTSTATUS Status;
46
47 /* Assume failure */
48 *PrivilegeState = NULL;
49
50 /* Acquire the state structure to hold everything we need */
51 State = RtlAllocateHeap(SmpHeap,
52 0,
53 sizeof(SMP_PRIVILEGE_STATE) +
54 sizeof(TOKEN_PRIVILEGES) +
55 sizeof(LUID_AND_ATTRIBUTES));
56 if (!State) return STATUS_NO_MEMORY;
57
58 /* Open our token */
59 Status = NtOpenProcessToken(NtCurrentProcess(),
60 TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY,
61 &State->TokenHandle);
62 if (!NT_SUCCESS(Status))
63 {
64 /* Fail */
65 RtlFreeHeap(SmpHeap, 0, State);
66 return Status;
67 }
68
69 /* Set one privilege in the enabled state */
70 State->NewPrivileges = &State->NewBuffer;
71 State->OldPrivileges = (PTOKEN_PRIVILEGES)&State->OldBuffer;
72 State->NewPrivileges->PrivilegeCount = 1;
73 State->NewPrivileges->Privileges[0].Luid = RtlConvertUlongToLuid(Privilege);
74 State->NewPrivileges->Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
75
76 /* Adjust the privileges in the token */
77 Size = sizeof(State->OldBuffer);
78 Status = NtAdjustPrivilegesToken(State->TokenHandle,
79 FALSE,
80 State->NewPrivileges,
81 Size,
82 State->OldPrivileges,
83 &Size);
84 if (Status == STATUS_BUFFER_TOO_SMALL)
85 {
86 /* Our static buffer is not big enough, allocate a bigger one */
87 State->OldPrivileges = RtlAllocateHeap(SmpHeap, 0, Size);
88 if (!State->OldPrivileges)
89 {
90 /* Out of memory, fail */
91 Status = STATUS_NO_MEMORY;
92 goto Quickie;
93 }
94
95 /* Now try again */
96 Status = NtAdjustPrivilegesToken(State->TokenHandle,
97 FALSE,
98 State->NewPrivileges,
99 Size,
100 State->OldPrivileges,
101 &Size);
102 }
103
104 /* Normalize failure code and check for success */
105 if (Status == STATUS_NOT_ALL_ASSIGNED) Status = STATUS_PRIVILEGE_NOT_HELD;
106 if (NT_SUCCESS(Status))
107 {
108 /* We got the privilege, return */
109 *PrivilegeState = State;
110 return STATUS_SUCCESS;
111 }
112
113 Quickie:
114 /* Check if we used a dynamic buffer */
115 if (State->OldPrivileges != (PTOKEN_PRIVILEGES)&State->OldBuffer)
116 {
117 /* Free it */
118 RtlFreeHeap(SmpHeap, 0, State->OldPrivileges);
119 }
120
121 /* Close the token handle and free the state structure */
122 NtClose(State->TokenHandle);
123 RtlFreeHeap(SmpHeap, 0, State);
124 return Status;
125 }
126
127 VOID
128 NTAPI
SmpReleasePrivilege(IN PVOID PrivState)129 SmpReleasePrivilege(IN PVOID PrivState)
130 {
131 PSMP_PRIVILEGE_STATE State = (PSMP_PRIVILEGE_STATE)PrivState;
132
133 /* Adjust the privileges in the token */
134 NtAdjustPrivilegesToken(State->TokenHandle,
135 FALSE,
136 State->OldPrivileges,
137 0,
138 NULL,
139 NULL);
140
141 /* Check if we used a dynamic buffer */
142 if (State->OldPrivileges != (PTOKEN_PRIVILEGES)&State->OldBuffer)
143 {
144 /* Free it */
145 RtlFreeHeap(SmpHeap, 0, State->OldPrivileges);
146 }
147
148 /* Close the token handle and free the state structure */
149 NtClose(State->TokenHandle);
150 RtlFreeHeap(SmpHeap, 0, State);
151 }
152
153 NTSTATUS
154 NTAPI
SmpParseToken(IN PUNICODE_STRING Input,IN BOOLEAN SecondPass,OUT PUNICODE_STRING Token)155 SmpParseToken(IN PUNICODE_STRING Input,
156 IN BOOLEAN SecondPass,
157 OUT PUNICODE_STRING Token)
158 {
159 PWCHAR p, pp;
160 ULONG Length, TokenLength, InputLength;
161
162 /* Initialize to NULL to start with */
163 RtlInitUnicodeString(Token, NULL);
164
165 /* Save the input length */
166 InputLength = Input->Length;
167
168 /* If the input string is empty, just return */
169 if (InputLength == 0) return STATUS_SUCCESS;
170
171 /* Parse the buffer until the first character */
172 p = Input->Buffer;
173 Length = 0;
174 while (Length < InputLength)
175 {
176 if (*p > L' ') break;
177 ++p;
178 Length += sizeof(WCHAR);
179 }
180
181 /* Are we being called for argument pick-up? */
182 if (SecondPass)
183 {
184 /* Then assume everything else is an argument */
185 TokenLength = InputLength - Length * sizeof(WCHAR);
186 pp = (PWSTR)((ULONG_PTR)p + TokenLength);
187 }
188 else
189 {
190 /* No -- so loop until the next space */
191 pp = p;
192 while (Length < InputLength)
193 {
194 if (*pp <= L' ') break;
195 ++pp;
196 Length += sizeof(WCHAR);
197 }
198
199 /* Now compute how long this token is, and loop until the next char */
200 TokenLength = (ULONG_PTR)pp - (ULONG_PTR)p;
201 while (Length < InputLength)
202 {
203 if (*pp > L' ') break;
204 ++pp;
205 Length += sizeof(WCHAR);
206 }
207 }
208
209 /* Did we find a token? */
210 if (TokenLength)
211 {
212 /* Allocate a buffer for it */
213 Token->Buffer = RtlAllocateHeap(SmpHeap,
214 SmBaseTag,
215 TokenLength + sizeof(UNICODE_NULL));
216 if (!Token->Buffer) return STATUS_NO_MEMORY;
217
218 /* Fill in the unicode string to hold it */
219 Token->MaximumLength = (USHORT)(TokenLength + sizeof(UNICODE_NULL));
220 Token->Length = (USHORT)TokenLength;
221 RtlCopyMemory(Token->Buffer, p, TokenLength);
222 Token->Buffer[TokenLength / sizeof(WCHAR)] = UNICODE_NULL;
223 }
224
225 /* Modify the input string with the position of where the next token begins */
226 Input->Length -= (USHORT)((ULONG_PTR)pp - (ULONG_PTR)Input->Buffer);
227 Input->Buffer = pp;
228 return STATUS_SUCCESS;
229 }
230
231 NTSTATUS
232 NTAPI
SmpParseCommandLine(IN PUNICODE_STRING CommandLine,OUT PULONG Flags,OUT PUNICODE_STRING FileName,OUT PUNICODE_STRING Directory,OUT PUNICODE_STRING Arguments)233 SmpParseCommandLine(IN PUNICODE_STRING CommandLine,
234 OUT PULONG Flags,
235 OUT PUNICODE_STRING FileName,
236 OUT PUNICODE_STRING Directory,
237 OUT PUNICODE_STRING Arguments)
238 {
239 ULONG Length;
240 UNICODE_STRING EnvString, PathString, CmdLineCopy, Token;
241 WCHAR PathBuffer[MAX_PATH];
242 PWCHAR FilePart;
243 NTSTATUS Status;
244 UNICODE_STRING FullPathString;
245
246 /* Initialize output arguments to NULL */
247 RtlInitUnicodeString(FileName, NULL);
248 RtlInitUnicodeString(Arguments, NULL);
249 if (Directory) RtlInitUnicodeString(Directory, NULL);
250
251 /* Check if we haven't yet built a full default path or system root yet */
252 if (!SmpSystemRoot.Length)
253 {
254 /* Initialize it based on shared user data string */
255 RtlInitUnicodeString(&SmpSystemRoot, SharedUserData->NtSystemRoot);
256
257 /* Allocate an empty string for the path */
258 Length = SmpDefaultLibPath.MaximumLength + SmpSystemRoot.MaximumLength +
259 sizeof(L"\\system32;");
260 RtlInitEmptyUnicodeString(&FullPathString,
261 RtlAllocateHeap(SmpHeap, SmBaseTag, Length),
262 (USHORT)Length);
263 if (FullPathString.Buffer)
264 {
265 /* Append the root, system32;, and then the current library path */
266 RtlAppendUnicodeStringToString(&FullPathString, &SmpSystemRoot);
267 RtlAppendUnicodeToString(&FullPathString, L"\\system32;");
268 RtlAppendUnicodeStringToString(&FullPathString, &SmpDefaultLibPath);
269 RtlFreeHeap(SmpHeap, 0, SmpDefaultLibPath.Buffer);
270 SmpDefaultLibPath = FullPathString;
271 }
272 }
273
274 /* Consume the command line */
275 CmdLineCopy = *CommandLine;
276 while (TRUE)
277 {
278 /* Parse the first token and check for modifiers/specifiers */
279 Status = SmpParseToken(&CmdLineCopy, FALSE, &Token);
280 if (!(NT_SUCCESS(Status)) || !(Token.Buffer)) return STATUS_UNSUCCESSFUL;
281 if (!Flags) break;
282
283 /* Debug requested? */
284 if (RtlEqualUnicodeString(&Token, &SmpDebugKeyword, TRUE))
285 {
286 /* Convert into a flag */
287 *Flags |= SMP_DEBUG_FLAG;
288 }
289 else if (RtlEqualUnicodeString(&Token, &SmpASyncKeyword, TRUE))
290 {
291 /* Asynch requested, convert into a flag */
292 *Flags |= SMP_ASYNC_FLAG;
293 }
294 else if (RtlEqualUnicodeString(&Token, &SmpAutoChkKeyword, TRUE))
295 {
296 /* Autochk requested, convert into a flag */
297 *Flags |= SMP_AUTOCHK_FLAG;
298 }
299 else
300 {
301 /* No specifier found, keep going */
302 break;
303 }
304
305 /* Get rid of this token and get the next */
306 RtlFreeHeap(SmpHeap, 0, Token.Buffer);
307 }
308
309 /* Initialize a string to hold the current path */
310 RtlInitUnicodeString(&EnvString, L"Path");
311 Length = PAGE_SIZE;
312 RtlInitEmptyUnicodeString(&PathString,
313 RtlAllocateHeap(SmpHeap, SmBaseTag, Length),
314 (USHORT)Length);
315 if (!PathString.Buffer)
316 {
317 /* Fail if we have no memory for this */
318 RtlFreeHeap(SmpHeap, 0, Token.Buffer);
319 return STATUS_INSUFFICIENT_RESOURCES;
320 }
321
322 /* Query the path from the environment */
323 Status = RtlQueryEnvironmentVariable_U(SmpDefaultEnvironment,
324 &EnvString,
325 &PathString);
326 if (Status == STATUS_BUFFER_TOO_SMALL)
327 {
328 /* Our buffer was too small, free it */
329 RtlFreeHeap(SmpHeap, 0, PathString.Buffer);
330
331 /* And allocate one big enough */
332 Length = PathString.Length + sizeof(UNICODE_NULL);
333 RtlInitEmptyUnicodeString(&PathString,
334 RtlAllocateHeap(SmpHeap, SmBaseTag, Length),
335 (USHORT)Length);
336 if (!PathString.Buffer)
337 {
338 /* Fail if we have no memory for this */
339 RtlFreeHeap(SmpHeap, 0, Token.Buffer);
340 return STATUS_INSUFFICIENT_RESOURCES;
341 }
342
343 /* Now try again, this should work */
344 Status = RtlQueryEnvironmentVariable_U(SmpDefaultEnvironment,
345 &EnvString,
346 &PathString);
347 }
348 if (!NT_SUCCESS(Status))
349 {
350 /* Another failure means that the kernel hasn't passed the path correctly */
351 DPRINT1("SMSS: %wZ environment variable not defined.\n", &EnvString);
352 Status = STATUS_OBJECT_NAME_NOT_FOUND;
353 }
354 else
355 {
356 /* Check if the caller expects any flags out of here */
357 if (Flags)
358 {
359 /* We can return the image not found flag -- so does the image exist */
360 if (!(RtlDosSearchPath_U(PathString.Buffer,
361 Token.Buffer,
362 L".exe",
363 sizeof(PathBuffer),
364 PathBuffer,
365 &FilePart)) &&
366 !(RtlDosSearchPath_U(SmpDefaultLibPath.Buffer,
367 Token.Buffer,
368 L".exe",
369 sizeof(PathBuffer),
370 PathBuffer,
371 &FilePart)))
372 {
373 /* It doesn't, let the caller know about it and exit */
374 *Flags |= SMP_INVALID_PATH;
375 *FileName = Token;
376 RtlFreeHeap(SmpHeap, 0, PathString.Buffer);
377 return STATUS_SUCCESS;
378 }
379 }
380 else
381 {
382 /* Caller doesn't want flags, probably wants the image itself */
383 RtlStringCbCopyW(PathBuffer, sizeof(PathBuffer), Token.Buffer);
384 }
385 }
386
387 /* Free tokens and such, all that's left is to convert the image name */
388 RtlFreeHeap(SmpHeap, 0, Token.Buffer);
389 RtlFreeHeap(SmpHeap, 0, PathString.Buffer);
390 if (!NT_SUCCESS(Status)) return Status;
391
392 /* Convert it and bail out if this failed */
393 if (!RtlDosPathNameToNtPathName_U(PathBuffer, FileName, NULL, NULL))
394 {
395 DPRINT1("SMSS: Unable to translate %ws into an NT File Name\n",
396 &PathBuffer);
397 Status = STATUS_OBJECT_PATH_INVALID;
398 }
399 if (!NT_SUCCESS(Status)) return Status;
400
401 /* Finally, check if the caller also wanted the directory */
402 if (Directory)
403 {
404 /* Is the file a relative name with no directory? */
405 if (FilePart <= PathBuffer)
406 {
407 /* Clear it */
408 RtlInitUnicodeString(Directory, NULL);
409 }
410 else
411 {
412 /* There is a directory, and a filename -- separate those two */
413 *--FilePart = UNICODE_NULL;
414 RtlCreateUnicodeString(Directory, PathBuffer);
415 }
416 }
417
418 /* We are done -- move on to the second pass to get the arguments */
419 return SmpParseToken(&CmdLineCopy, TRUE, Arguments);
420 }
421
422 BOOLEAN
423 NTAPI
SmpQueryRegistrySosOption(VOID)424 SmpQueryRegistrySosOption(VOID)
425 {
426 NTSTATUS Status;
427 UNICODE_STRING KeyName, ValueName;
428 OBJECT_ATTRIBUTES ObjectAttributes;
429 HANDLE KeyHandle;
430 ULONG Length;
431 WCHAR ValueBuffer[VALUE_BUFFER_SIZE];
432 PKEY_VALUE_PARTIAL_INFORMATION PartialInfo = (PVOID)ValueBuffer;
433
434 /* Open the key */
435 RtlInitUnicodeString(&KeyName,
436 L"\\Registry\\Machine\\System\\CurrentControlSet\\Control");
437 InitializeObjectAttributes(&ObjectAttributes,
438 &KeyName,
439 OBJ_CASE_INSENSITIVE,
440 NULL,
441 NULL);
442 Status = NtOpenKey(&KeyHandle, KEY_READ, &ObjectAttributes);
443 if (!NT_SUCCESS(Status))
444 {
445 DPRINT1("SMSS: Cannot open control key (Status 0x%x)\n", Status);
446 return FALSE;
447 }
448
449 /* Query the value */
450 RtlInitUnicodeString(&ValueName, L"SystemStartOptions");
451 Status = NtQueryValueKey(KeyHandle,
452 &ValueName,
453 KeyValuePartialInformation,
454 PartialInfo,
455 sizeof(ValueBuffer),
456 &Length);
457 ASSERT(Length < VALUE_BUFFER_SIZE);
458 NtClose(KeyHandle);
459 if (!NT_SUCCESS(Status) ||
460 ((PartialInfo->Type != REG_SZ) && (PartialInfo->Type != REG_EXPAND_SZ)))
461 {
462 DPRINT1("SMSS: Cannot query value key (Type %lu, Status 0x%x)\n",
463 PartialInfo->Type, Status);
464 return FALSE;
465 }
466
467 /* Check if it's set to SOS or sos */
468 if (!(wcsstr((PWCHAR)PartialInfo->Data, L"SOS")) ||
469 (wcsstr((PWCHAR)PartialInfo->Data, L"sos")))
470 {
471 /* It's not set, return FALSE */
472 return FALSE;
473 }
474
475 /* It's set, return TRUE */
476 return TRUE;
477 }
478
479 BOOLEAN
480 NTAPI
SmpSaveAndClearBootStatusData(OUT PBOOLEAN BootOkay,OUT PBOOLEAN ShutdownOkay)481 SmpSaveAndClearBootStatusData(OUT PBOOLEAN BootOkay,
482 OUT PBOOLEAN ShutdownOkay)
483 {
484 NTSTATUS Status;
485 BOOLEAN Value = TRUE;
486 PVOID BootStatusDataHandle;
487
488 /* Assume failure */
489 *BootOkay = FALSE;
490 *ShutdownOkay = FALSE;
491
492 /* Lock the BSD and fail if we couldn't */
493 Status = RtlLockBootStatusData(&BootStatusDataHandle);
494 if (!NT_SUCCESS(Status)) return FALSE;
495
496 /* Read the old settings */
497 RtlGetSetBootStatusData(BootStatusDataHandle,
498 TRUE,
499 RtlBsdItemBootGood,
500 BootOkay,
501 sizeof(BOOLEAN),
502 NULL);
503 RtlGetSetBootStatusData(BootStatusDataHandle,
504 TRUE,
505 RtlBsdItemBootShutdown,
506 ShutdownOkay,
507 sizeof(BOOLEAN),
508 NULL);
509
510 /* Set new ones indicating we got at least this far */
511 RtlGetSetBootStatusData(BootStatusDataHandle,
512 FALSE,
513 RtlBsdItemBootGood,
514 &Value,
515 sizeof(Value),
516 NULL);
517 RtlGetSetBootStatusData(BootStatusDataHandle,
518 FALSE,
519 RtlBsdItemBootShutdown,
520 &Value,
521 sizeof(Value),
522 NULL);
523
524 /* Unlock the BSD and return */
525 RtlUnlockBootStatusData(BootStatusDataHandle);
526 return TRUE;
527 }
528
529 VOID
530 NTAPI
SmpRestoreBootStatusData(IN BOOLEAN BootOkay,IN BOOLEAN ShutdownOkay)531 SmpRestoreBootStatusData(IN BOOLEAN BootOkay,
532 IN BOOLEAN ShutdownOkay)
533 {
534 NTSTATUS Status;
535 PVOID BootState;
536
537 /* Lock the BSD */
538 Status = RtlLockBootStatusData(&BootState);
539 if (NT_SUCCESS(Status))
540 {
541 /* Write the bootokay and bootshutdown values */
542 RtlGetSetBootStatusData(BootState,
543 FALSE,
544 RtlBsdItemBootGood,
545 &BootOkay,
546 sizeof(BootOkay),
547 NULL);
548 RtlGetSetBootStatusData(BootState,
549 FALSE,
550 RtlBsdItemBootShutdown,
551 &ShutdownOkay,
552 sizeof(ShutdownOkay),
553 NULL);
554
555 /* Unlock the BSD and return */
556 RtlUnlockBootStatusData(BootState);
557 }
558 }
559