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