xref: /reactos/base/system/smss/smutil.c (revision f43ce465)
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