* Sync up to trunk head (r64377).
[reactos.git] / base / system / smss / smutil.c
1 /*
2 * PROJECT: ReactOS Windows-Compatible Session Manager
3 * LICENSE: BSD 2-Clause License
4 * FILE: base/system/smss/smss.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
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
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
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
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 wcscpy(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
424 SmpQueryRegistrySosOption(VOID)
425 {
426 NTSTATUS Status;
427 UNICODE_STRING KeyName, ValueName;
428 OBJECT_ATTRIBUTES ObjectAttributes;
429 HANDLE KeyHandle;
430 WCHAR ValueBuffer[VALUE_BUFFER_SIZE];
431 PKEY_VALUE_PARTIAL_INFORMATION PartialInfo = (PVOID)ValueBuffer;
432 ULONG Length;
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: can't open control key: 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 {
461 DPRINT1("SMSS: can't query value key: 0x%x\n", Status);
462 return FALSE;
463 }
464
465 /* Check if it's set to SOS or sos */
466 if (!(wcsstr((PWCHAR)PartialInfo->Data, L"SOS")) ||
467 (wcsstr((PWCHAR)PartialInfo->Data, L"sos")))
468 {
469 /* It's not set, return FALSE */
470 return FALSE;
471 }
472
473 /* It's set, return TRUE */
474 return TRUE;
475 }
476
477 BOOLEAN
478 NTAPI
479 SmpSaveAndClearBootStatusData(OUT PBOOLEAN BootOkay,
480 OUT PBOOLEAN ShutdownOkay)
481 {
482 NTSTATUS Status;
483 BOOLEAN Value = TRUE;
484 PVOID BootStatusDataHandle;
485
486 /* Assume failure */
487 *BootOkay = FALSE;
488 *ShutdownOkay = FALSE;
489
490 /* Lock the BSD and fail if we couldn't */
491 Status = RtlLockBootStatusData(&BootStatusDataHandle);
492 if (!NT_SUCCESS(Status)) return FALSE;
493
494 /* Read the old settings */
495 RtlGetSetBootStatusData(BootStatusDataHandle,
496 TRUE,
497 RtlBsdItemBootGood,
498 BootOkay,
499 sizeof(BOOLEAN),
500 NULL);
501 RtlGetSetBootStatusData(BootStatusDataHandle,
502 TRUE,
503 RtlBsdItemBootShutdown,
504 ShutdownOkay,
505 sizeof(BOOLEAN),
506 NULL);
507
508 /* Set new ones indicating we got at least this far */
509 RtlGetSetBootStatusData(BootStatusDataHandle,
510 FALSE,
511 RtlBsdItemBootGood,
512 &Value,
513 sizeof(Value),
514 NULL);
515 RtlGetSetBootStatusData(BootStatusDataHandle,
516 FALSE,
517 RtlBsdItemBootShutdown,
518 &Value,
519 sizeof(Value),
520 NULL);
521
522 /* Unlock the BSD and return */
523 RtlUnlockBootStatusData(BootStatusDataHandle);
524 return TRUE;
525 }
526
527 VOID
528 NTAPI
529 SmpRestoreBootStatusData(IN BOOLEAN BootOkay,
530 IN BOOLEAN ShutdownOkay)
531 {
532 NTSTATUS Status;
533 PVOID BootState;
534
535 /* Lock the BSD */
536 Status = RtlLockBootStatusData(&BootState);
537 if (NT_SUCCESS(Status))
538 {
539 /* Write the bootokay and bootshutdown values */
540 RtlGetSetBootStatusData(BootState,
541 FALSE,
542 RtlBsdItemBootGood,
543 &BootOkay,
544 sizeof(BootOkay),
545 NULL);
546 RtlGetSetBootStatusData(BootState,
547 FALSE,
548 RtlBsdItemBootShutdown,
549 &ShutdownOkay,
550 sizeof(ShutdownOkay),
551 NULL);
552
553 /* Unlock the BSD and return */
554 RtlUnlockBootStatusData(BootState);
555 }
556 }