Create a branch for Aleksandar Andrejevic for his work on NTVDM. See http://jira...
[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 /* Parse the buffer until the first character */
166 p = Input->Buffer;
167 Length = 0;
168 while (Length < InputLength)
169 {
170 if (*p > L' ' ) break;
171 ++p;
172 Length += sizeof(WCHAR);
173 }
174
175 /* Are we being called for argument pick-up? */
176 if (SecondPass)
177 {
178 /* Then assume everything else is an argument */
179 TokenLength = InputLength - Length * sizeof(WCHAR);
180 pp = (PWSTR)((ULONG_PTR)p + TokenLength);
181 }
182 else
183 {
184 /* No -- so loop until the next space */
185 pp = p;
186 while (Length < InputLength)
187 {
188 if (*pp <= L' ' ) break;
189 ++pp;
190 Length += sizeof(WCHAR);
191 }
192
193 /* Now compute how long this token is, and loop until the next char */
194 TokenLength = (ULONG_PTR)pp - (ULONG_PTR)p;
195 while (Length < InputLength)
196 {
197 if (*pp > L' ' ) break;
198 ++pp;
199 Length += sizeof(WCHAR);
200 }
201 }
202
203 /* Did we find a token? */
204 if (TokenLength)
205 {
206 /* Allocate a buffer for it */
207 Token->Buffer = RtlAllocateHeap(SmpHeap,
208 SmBaseTag,
209 TokenLength + sizeof(UNICODE_NULL));
210 if (!Token->Buffer) return STATUS_NO_MEMORY;
211
212 /* Fill in the unicode string to hold it */
213 Token->MaximumLength = TokenLength + sizeof(UNICODE_NULL);
214 Token->Length = TokenLength;
215 RtlCopyMemory(Token->Buffer, p, TokenLength);
216 Token->Buffer[TokenLength / sizeof(WCHAR)] = UNICODE_NULL;
217 }
218
219 /* Modify the input string with the position of where the next token begins */
220 Input->Length -= (ULONG_PTR)pp - (ULONG_PTR)Input->Buffer;
221 Input->Buffer = pp;
222 return STATUS_SUCCESS;
223 }
224
225 NTSTATUS
226 NTAPI
227 SmpParseCommandLine(IN PUNICODE_STRING CommandLine,
228 OUT PULONG Flags,
229 OUT PUNICODE_STRING FileName,
230 OUT PUNICODE_STRING Directory,
231 OUT PUNICODE_STRING Arguments)
232 {
233 ULONG Length;
234 UNICODE_STRING EnvString, PathString, CmdLineCopy, Token;
235 WCHAR PathBuffer[MAX_PATH];
236 PWCHAR FilePart;
237 NTSTATUS Status;
238 UNICODE_STRING FullPathString;
239
240 /* Initialize output arguments to NULL */
241 RtlInitUnicodeString(FileName, NULL);
242 RtlInitUnicodeString(Arguments, NULL);
243 if (Directory) RtlInitUnicodeString(Directory, NULL);
244
245 /* Check if we haven't yet built a full default path or system root yet */
246 if (!SmpSystemRoot.Length)
247 {
248 /* Initialize it based on shared user data string */
249 RtlInitUnicodeString(&SmpSystemRoot, SharedUserData->NtSystemRoot);
250
251 /* Allocate an empty string for the path */
252 Length = SmpDefaultLibPath.MaximumLength + SmpSystemRoot.MaximumLength +
253 sizeof(L"\\system32;");
254 RtlInitEmptyUnicodeString(&FullPathString,
255 RtlAllocateHeap(SmpHeap, SmBaseTag, Length),
256 Length);
257 if (FullPathString.Buffer)
258 {
259 /* Append the root, system32;, and then the current library path */
260 RtlAppendUnicodeStringToString(&FullPathString, &SmpSystemRoot);
261 RtlAppendUnicodeToString(&FullPathString, L"\\system32;");
262 RtlAppendUnicodeStringToString(&FullPathString, &SmpDefaultLibPath);
263 RtlFreeHeap(SmpHeap, 0, SmpDefaultLibPath.Buffer);
264 SmpDefaultLibPath = FullPathString;
265 }
266 }
267
268 /* Consume the command line */
269 CmdLineCopy = *CommandLine;
270 while (TRUE)
271 {
272 /* Parse the first token and check for modifiers/specifiers */
273 Status = SmpParseToken(&CmdLineCopy, FALSE, &Token);
274 if (!(NT_SUCCESS(Status)) || !(Token.Buffer)) return STATUS_UNSUCCESSFUL;
275 if (!Flags) break;
276
277 /* Debug requested? */
278 if (RtlEqualUnicodeString(&Token, &SmpDebugKeyword, TRUE))
279 {
280 /* Convert into a flag */
281 *Flags |= SMP_DEBUG_FLAG;
282 }
283 else if (RtlEqualUnicodeString(&Token, &SmpASyncKeyword, TRUE))
284 {
285 /* Asynch requested, convert into a flag */
286 *Flags |= SMP_ASYNC_FLAG;
287 }
288 else if (RtlEqualUnicodeString(&Token, &SmpAutoChkKeyword, TRUE))
289 {
290 /* Autochk requested, convert into a flag */
291 *Flags |= SMP_AUTOCHK_FLAG;
292 }
293 else
294 {
295 /* No specifier found, keep going */
296 break;
297 }
298
299 /* Get rid of this token and get the next */
300 RtlFreeHeap(SmpHeap, 0, Token.Buffer);
301 }
302
303 /* Initialize a string to hold the current path */
304 RtlInitUnicodeString(&EnvString, L"Path");
305 Length = PAGE_SIZE;
306 RtlInitEmptyUnicodeString(&PathString,
307 RtlAllocateHeap(SmpHeap, SmBaseTag, Length),
308 Length);
309 if (!PathString.Buffer)
310 {
311 /* Fail if we have no memory for this */
312 RtlFreeHeap(SmpHeap, 0, Token.Buffer);
313 return STATUS_INSUFFICIENT_RESOURCES;
314 }
315
316 /* Query the path from the environment */
317 Status = RtlQueryEnvironmentVariable_U(SmpDefaultEnvironment,
318 &EnvString,
319 &PathString);
320 if (Status == STATUS_BUFFER_TOO_SMALL)
321 {
322 /* Our buffer was too small, free it */
323 RtlFreeHeap(SmpHeap, 0, PathString.Buffer);
324
325 /* And allocate one big enough */
326 Length = PathString.Length + sizeof(UNICODE_NULL);
327 RtlInitEmptyUnicodeString(&PathString,
328 RtlAllocateHeap(SmpHeap, SmBaseTag, Length),
329 Length);
330 if (!PathString.Buffer)
331 {
332 /* Fail if we have no memory for this */
333 RtlFreeHeap(SmpHeap, 0, Token.Buffer);
334 return STATUS_INSUFFICIENT_RESOURCES;
335 }
336
337 /* Now try again, this should work */
338 Status = RtlQueryEnvironmentVariable_U(SmpDefaultEnvironment,
339 &EnvString,
340 &PathString);
341 }
342 if (!NT_SUCCESS(Status))
343 {
344 /* Another failure means that the kernel hasn't passed the path correctly */
345 DPRINT1("SMSS: %wZ environment variable not defined.\n", &EnvString);
346 Status = STATUS_OBJECT_NAME_NOT_FOUND;
347 }
348 else
349 {
350 /* Check if the caller expects any flags out of here */
351 if (Flags)
352 {
353 /* We can return the image not found flag -- so does the image exist */
354 if (!(RtlDosSearchPath_U(PathString.Buffer,
355 Token.Buffer,
356 L".exe",
357 sizeof(PathBuffer),
358 PathBuffer,
359 &FilePart)) &&
360 !(RtlDosSearchPath_U(SmpDefaultLibPath.Buffer,
361 Token.Buffer,
362 L".exe",
363 sizeof(PathBuffer),
364 PathBuffer,
365 &FilePart)))
366 {
367 /* It doesn't, let the caller know about it and exit */
368 *Flags |= SMP_INVALID_PATH;
369 *FileName = Token;
370 RtlFreeHeap(SmpHeap, 0, PathString.Buffer);
371 return STATUS_SUCCESS;
372 }
373 }
374 else
375 {
376 /* Caller doesn't want flags, probably wants the image itself */
377 wcscpy(PathBuffer, Token.Buffer);
378 }
379 }
380
381 /* Free tokens and such, all that's left is to convert the image name */
382 RtlFreeHeap(SmpHeap, 0, Token.Buffer);
383 RtlFreeHeap(SmpHeap, 0, PathString.Buffer);
384 if (!NT_SUCCESS(Status)) return Status;
385
386 /* Convert it and bail out if this failed */
387 if (!RtlDosPathNameToNtPathName_U(PathBuffer, FileName, NULL, NULL))
388 {
389 DPRINT1("SMSS: Unable to translate %ws into an NT File Name\n",
390 &PathBuffer);
391 Status = STATUS_OBJECT_PATH_INVALID;
392 }
393 if (!NT_SUCCESS(Status)) return Status;
394
395 /* Finally, check if the caller also wanted the directory */
396 if (Directory)
397 {
398 /* Is the file a relative name with no directory? */
399 if (FilePart <= PathBuffer)
400 {
401 /* Clear it */
402 RtlInitUnicodeString(Directory, NULL);
403 }
404 else
405 {
406 /* There is a directory, and a filename -- separate those two */
407 *--FilePart = UNICODE_NULL;
408 RtlCreateUnicodeString(Directory, PathBuffer);
409 }
410 }
411
412 /* We are done -- move on to the second pass to get the arguments */
413 return SmpParseToken(&CmdLineCopy, TRUE, Arguments);
414 }
415
416 BOOLEAN
417 NTAPI
418 SmpQueryRegistrySosOption(VOID)
419 {
420 NTSTATUS Status;
421 UNICODE_STRING KeyName, ValueName;
422 OBJECT_ATTRIBUTES ObjectAttributes;
423 HANDLE KeyHandle;
424 WCHAR ValueBuffer[VALUE_BUFFER_SIZE];
425 PKEY_VALUE_PARTIAL_INFORMATION PartialInfo = (PVOID)ValueBuffer;
426 ULONG Length;
427
428 /* Open the key */
429 RtlInitUnicodeString(&KeyName,
430 L"\\Registry\\Machine\\System\\CurrentControlSet\\Control");
431 InitializeObjectAttributes(&ObjectAttributes,
432 &KeyName,
433 OBJ_CASE_INSENSITIVE,
434 NULL,
435 NULL);
436 Status = NtOpenKey(&KeyHandle, KEY_READ, &ObjectAttributes);
437 if (!NT_SUCCESS(Status))
438 {
439 DPRINT1("SMSS: can't open control key: 0x%x\n", Status);
440 return FALSE;
441 }
442
443 /* Query the value */
444 RtlInitUnicodeString(&ValueName, L"SystemStartOptions");
445 Status = NtQueryValueKey(KeyHandle,
446 &ValueName,
447 KeyValuePartialInformation,
448 PartialInfo,
449 sizeof(ValueBuffer),
450 &Length);
451 ASSERT(Length < VALUE_BUFFER_SIZE);
452 NtClose(KeyHandle);
453 if (!NT_SUCCESS(Status))
454 {
455 DPRINT1("SMSS: can't query value key: 0x%x\n", Status);
456 return FALSE;
457 }
458
459 /* Check if it's set to SOS or sos */
460 if (!(wcsstr((PWCHAR)PartialInfo->Data, L"SOS")) ||
461 (wcsstr((PWCHAR)PartialInfo->Data, L"sos")))
462 {
463 /* It's not set, return FALSE */
464 return FALSE;
465 }
466
467 /* It's set, return TRUE */
468 return TRUE;
469 }
470
471 BOOLEAN
472 NTAPI
473 SmpSaveAndClearBootStatusData(OUT PBOOLEAN BootOkay,
474 OUT PBOOLEAN ShutdownOkay)
475 {
476 NTSTATUS Status;
477 BOOLEAN Value = TRUE;
478 PVOID BootStatusDataHandle;
479
480 /* Assume failure */
481 *BootOkay = FALSE;
482 *ShutdownOkay = FALSE;
483
484 /* Lock the BSD and fail if we couldn't */
485 Status = RtlLockBootStatusData(&BootStatusDataHandle);
486 if (!NT_SUCCESS(Status)) return FALSE;
487
488 /* Read the old settings */
489 RtlGetSetBootStatusData(BootStatusDataHandle,
490 TRUE,
491 RtlBsdItemBootGood,
492 BootOkay,
493 sizeof(BOOLEAN),
494 NULL);
495 RtlGetSetBootStatusData(BootStatusDataHandle,
496 TRUE,
497 RtlBsdItemBootShutdown,
498 ShutdownOkay,
499 sizeof(BOOLEAN),
500 NULL);
501
502 /* Set new ones indicating we got at least this far */
503 RtlGetSetBootStatusData(BootStatusDataHandle,
504 FALSE,
505 RtlBsdItemBootGood,
506 &Value,
507 sizeof(Value),
508 NULL);
509 RtlGetSetBootStatusData(BootStatusDataHandle,
510 FALSE,
511 RtlBsdItemBootShutdown,
512 &Value,
513 sizeof(Value),
514 NULL);
515
516 /* Unlock the BSD and return */
517 RtlUnlockBootStatusData(BootStatusDataHandle);
518 return TRUE;
519 }
520
521 VOID
522 NTAPI
523 SmpRestoreBootStatusData(IN BOOLEAN BootOkay,
524 IN BOOLEAN ShutdownOkay)
525 {
526 NTSTATUS Status;
527 PVOID BootState;
528
529 /* Lock the BSD */
530 Status = RtlLockBootStatusData(&BootState);
531 if (NT_SUCCESS(Status))
532 {
533 /* Write the bootokay and bootshudown values */
534 RtlGetSetBootStatusData(BootState,
535 FALSE,
536 RtlBsdItemBootGood,
537 &BootOkay,
538 sizeof(BootOkay),
539 NULL);
540 RtlGetSetBootStatusData(BootState,
541 FALSE,
542 RtlBsdItemBootShutdown,
543 &ShutdownOkay,
544 sizeof(ShutdownOkay),
545 NULL);
546
547 /* Unlock the BSD and return */
548 RtlUnlockBootStatusData(BootState);
549 }
550 }