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
9 /* INCLUDES *******************************************************************/
13 #include <ndk/sefuncs.h>
18 /* GLOBALS ********************************************************************/
21 // Taken from an ASSERT
23 #define VALUE_BUFFER_SIZE (sizeof(KEY_VALUE_PARTIAL_INFORMATION) + 512)
25 typedef struct _SMP_PRIVILEGE_STATE
28 PTOKEN_PRIVILEGES OldPrivileges
;
29 PTOKEN_PRIVILEGES NewPrivileges
;
30 UCHAR OldBuffer
[1024];
31 TOKEN_PRIVILEGES NewBuffer
;
32 } SMP_PRIVILEGE_STATE
, *PSMP_PRIVILEGE_STATE
;
34 UNICODE_STRING SmpDebugKeyword
, SmpASyncKeyword
, SmpAutoChkKeyword
;
36 /* FUNCTIONS ******************************************************************/
40 SmpAcquirePrivilege(IN ULONG Privilege
,
41 OUT PVOID
*PrivilegeState
)
43 PSMP_PRIVILEGE_STATE State
;
48 *PrivilegeState
= NULL
;
50 /* Acquire the state structure to hold everything we need */
51 State
= RtlAllocateHeap(SmpHeap
,
53 sizeof(SMP_PRIVILEGE_STATE
) +
54 sizeof(TOKEN_PRIVILEGES
) +
55 sizeof(LUID_AND_ATTRIBUTES
));
56 if (!State
) return STATUS_NO_MEMORY
;
59 Status
= NtOpenProcessToken(NtCurrentProcess(),
60 TOKEN_ADJUST_PRIVILEGES
| TOKEN_QUERY
,
62 if (!NT_SUCCESS(Status
))
65 RtlFreeHeap(SmpHeap
, 0, State
);
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
;
76 /* Adjust the privileges in the token */
77 Size
= sizeof(State
->OldBuffer
);
78 Status
= NtAdjustPrivilegesToken(State
->TokenHandle
,
84 if (Status
== STATUS_BUFFER_TOO_SMALL
)
86 /* Our static buffer is not big enough, allocate a bigger one */
87 State
->OldPrivileges
= RtlAllocateHeap(SmpHeap
, 0, Size
);
88 if (!State
->OldPrivileges
)
90 /* Out of memory, fail */
91 Status
= STATUS_NO_MEMORY
;
96 Status
= NtAdjustPrivilegesToken(State
->TokenHandle
,
100 State
->OldPrivileges
,
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
))
108 /* We got the privilege, return */
109 *PrivilegeState
= State
;
110 return STATUS_SUCCESS
;
114 /* Check if we used a dynamic buffer */
115 if (State
->OldPrivileges
!= (PTOKEN_PRIVILEGES
)&State
->OldBuffer
)
118 RtlFreeHeap(SmpHeap
, 0, State
->OldPrivileges
);
121 /* Close the token handle and free the state structure */
122 NtClose(State
->TokenHandle
);
123 RtlFreeHeap(SmpHeap
, 0, State
);
129 SmpReleasePrivilege(IN PVOID PrivState
)
131 PSMP_PRIVILEGE_STATE State
= (PSMP_PRIVILEGE_STATE
)PrivState
;
133 /* Adjust the privileges in the token */
134 NtAdjustPrivilegesToken(State
->TokenHandle
,
136 State
->OldPrivileges
,
141 /* Check if we used a dynamic buffer */
142 if (State
->OldPrivileges
!= (PTOKEN_PRIVILEGES
)&State
->OldBuffer
)
145 RtlFreeHeap(SmpHeap
, 0, State
->OldPrivileges
);
148 /* Close the token handle and free the state structure */
149 NtClose(State
->TokenHandle
);
150 RtlFreeHeap(SmpHeap
, 0, State
);
155 SmpParseToken(IN PUNICODE_STRING Input
,
156 IN BOOLEAN SecondPass
,
157 OUT PUNICODE_STRING Token
)
160 ULONG Length
, TokenLength
, InputLength
;
162 /* Initialize to NULL to start with */
163 RtlInitUnicodeString(Token
, NULL
);
165 /* Save the input length */
166 InputLength
= Input
->Length
;
168 /* If the input string is empty, just return */
169 if (InputLength
== 0) return STATUS_SUCCESS
;
171 /* Parse the buffer until the first character */
174 while (Length
< InputLength
)
176 if (*p
> L
' ' ) break;
178 Length
+= sizeof(WCHAR
);
181 /* Are we being called for argument pick-up? */
184 /* Then assume everything else is an argument */
185 TokenLength
= InputLength
- Length
* sizeof(WCHAR
);
186 pp
= (PWSTR
)((ULONG_PTR
)p
+ TokenLength
);
190 /* No -- so loop until the next space */
192 while (Length
< InputLength
)
194 if (*pp
<= L
' ' ) break;
196 Length
+= sizeof(WCHAR
);
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
)
203 if (*pp
> L
' ' ) break;
205 Length
+= sizeof(WCHAR
);
209 /* Did we find a token? */
212 /* Allocate a buffer for it */
213 Token
->Buffer
= RtlAllocateHeap(SmpHeap
,
215 TokenLength
+ sizeof(UNICODE_NULL
));
216 if (!Token
->Buffer
) return STATUS_NO_MEMORY
;
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
;
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
);
228 return STATUS_SUCCESS
;
233 SmpParseCommandLine(IN PUNICODE_STRING CommandLine
,
235 OUT PUNICODE_STRING FileName
,
236 OUT PUNICODE_STRING Directory
,
237 OUT PUNICODE_STRING Arguments
)
240 UNICODE_STRING EnvString
, PathString
, CmdLineCopy
, Token
;
241 WCHAR PathBuffer
[MAX_PATH
];
244 UNICODE_STRING FullPathString
;
246 /* Initialize output arguments to NULL */
247 RtlInitUnicodeString(FileName
, NULL
);
248 RtlInitUnicodeString(Arguments
, NULL
);
249 if (Directory
) RtlInitUnicodeString(Directory
, NULL
);
251 /* Check if we haven't yet built a full default path or system root yet */
252 if (!SmpSystemRoot
.Length
)
254 /* Initialize it based on shared user data string */
255 RtlInitUnicodeString(&SmpSystemRoot
, SharedUserData
->NtSystemRoot
);
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
),
263 if (FullPathString
.Buffer
)
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
;
274 /* Consume the command line */
275 CmdLineCopy
= *CommandLine
;
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
;
283 /* Debug requested? */
284 if (RtlEqualUnicodeString(&Token
, &SmpDebugKeyword
, TRUE
))
286 /* Convert into a flag */
287 *Flags
|= SMP_DEBUG_FLAG
;
289 else if (RtlEqualUnicodeString(&Token
, &SmpASyncKeyword
, TRUE
))
291 /* Asynch requested, convert into a flag */
292 *Flags
|= SMP_ASYNC_FLAG
;
294 else if (RtlEqualUnicodeString(&Token
, &SmpAutoChkKeyword
, TRUE
))
296 /* Autochk requested, convert into a flag */
297 *Flags
|= SMP_AUTOCHK_FLAG
;
301 /* No specifier found, keep going */
305 /* Get rid of this token and get the next */
306 RtlFreeHeap(SmpHeap
, 0, Token
.Buffer
);
309 /* Initialize a string to hold the current path */
310 RtlInitUnicodeString(&EnvString
, L
"Path");
312 RtlInitEmptyUnicodeString(&PathString
,
313 RtlAllocateHeap(SmpHeap
, SmBaseTag
, Length
),
315 if (!PathString
.Buffer
)
317 /* Fail if we have no memory for this */
318 RtlFreeHeap(SmpHeap
, 0, Token
.Buffer
);
319 return STATUS_INSUFFICIENT_RESOURCES
;
322 /* Query the path from the environment */
323 Status
= RtlQueryEnvironmentVariable_U(SmpDefaultEnvironment
,
326 if (Status
== STATUS_BUFFER_TOO_SMALL
)
328 /* Our buffer was too small, free it */
329 RtlFreeHeap(SmpHeap
, 0, PathString
.Buffer
);
331 /* And allocate one big enough */
332 Length
= PathString
.Length
+ sizeof(UNICODE_NULL
);
333 RtlInitEmptyUnicodeString(&PathString
,
334 RtlAllocateHeap(SmpHeap
, SmBaseTag
, Length
),
336 if (!PathString
.Buffer
)
338 /* Fail if we have no memory for this */
339 RtlFreeHeap(SmpHeap
, 0, Token
.Buffer
);
340 return STATUS_INSUFFICIENT_RESOURCES
;
343 /* Now try again, this should work */
344 Status
= RtlQueryEnvironmentVariable_U(SmpDefaultEnvironment
,
348 if (!NT_SUCCESS(Status
))
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
;
356 /* Check if the caller expects any flags out of here */
359 /* We can return the image not found flag -- so does the image exist */
360 if (!(RtlDosSearchPath_U(PathString
.Buffer
,
366 !(RtlDosSearchPath_U(SmpDefaultLibPath
.Buffer
,
373 /* It doesn't, let the caller know about it and exit */
374 *Flags
|= SMP_INVALID_PATH
;
376 RtlFreeHeap(SmpHeap
, 0, PathString
.Buffer
);
377 return STATUS_SUCCESS
;
382 /* Caller doesn't want flags, probably wants the image itself */
383 wcscpy(PathBuffer
, Token
.Buffer
);
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
;
392 /* Convert it and bail out if this failed */
393 if (!RtlDosPathNameToNtPathName_U(PathBuffer
, FileName
, NULL
, NULL
))
395 DPRINT1("SMSS: Unable to translate %ws into an NT File Name\n",
397 Status
= STATUS_OBJECT_PATH_INVALID
;
399 if (!NT_SUCCESS(Status
)) return Status
;
401 /* Finally, check if the caller also wanted the directory */
404 /* Is the file a relative name with no directory? */
405 if (FilePart
<= PathBuffer
)
408 RtlInitUnicodeString(Directory
, NULL
);
412 /* There is a directory, and a filename -- separate those two */
413 *--FilePart
= UNICODE_NULL
;
414 RtlCreateUnicodeString(Directory
, PathBuffer
);
418 /* We are done -- move on to the second pass to get the arguments */
419 return SmpParseToken(&CmdLineCopy
, TRUE
, Arguments
);
424 SmpQueryRegistrySosOption(VOID
)
427 UNICODE_STRING KeyName
, ValueName
;
428 OBJECT_ATTRIBUTES ObjectAttributes
;
430 WCHAR ValueBuffer
[VALUE_BUFFER_SIZE
];
431 PKEY_VALUE_PARTIAL_INFORMATION PartialInfo
= (PVOID
)ValueBuffer
;
435 RtlInitUnicodeString(&KeyName
,
436 L
"\\Registry\\Machine\\System\\CurrentControlSet\\Control");
437 InitializeObjectAttributes(&ObjectAttributes
,
439 OBJ_CASE_INSENSITIVE
,
442 Status
= NtOpenKey(&KeyHandle
, KEY_READ
, &ObjectAttributes
);
443 if (!NT_SUCCESS(Status
))
445 DPRINT1("SMSS: can't open control key: 0x%x\n", Status
);
449 /* Query the value */
450 RtlInitUnicodeString(&ValueName
, L
"SystemStartOptions");
451 Status
= NtQueryValueKey(KeyHandle
,
453 KeyValuePartialInformation
,
457 ASSERT(Length
< VALUE_BUFFER_SIZE
);
459 if (!NT_SUCCESS(Status
))
461 DPRINT1("SMSS: can't query value key: 0x%x\n", Status
);
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")))
469 /* It's not set, return FALSE */
473 /* It's set, return TRUE */
479 SmpSaveAndClearBootStatusData(OUT PBOOLEAN BootOkay
,
480 OUT PBOOLEAN ShutdownOkay
)
483 BOOLEAN Value
= TRUE
;
484 PVOID BootStatusDataHandle
;
488 *ShutdownOkay
= FALSE
;
490 /* Lock the BSD and fail if we couldn't */
491 Status
= RtlLockBootStatusData(&BootStatusDataHandle
);
492 if (!NT_SUCCESS(Status
)) return FALSE
;
494 /* Read the old settings */
495 RtlGetSetBootStatusData(BootStatusDataHandle
,
501 RtlGetSetBootStatusData(BootStatusDataHandle
,
503 RtlBsdItemBootShutdown
,
508 /* Set new ones indicating we got at least this far */
509 RtlGetSetBootStatusData(BootStatusDataHandle
,
515 RtlGetSetBootStatusData(BootStatusDataHandle
,
517 RtlBsdItemBootShutdown
,
522 /* Unlock the BSD and return */
523 RtlUnlockBootStatusData(BootStatusDataHandle
);
529 SmpRestoreBootStatusData(IN BOOLEAN BootOkay
,
530 IN BOOLEAN ShutdownOkay
)
536 Status
= RtlLockBootStatusData(&BootState
);
537 if (NT_SUCCESS(Status
))
539 /* Write the bootokay and bootshutdown values */
540 RtlGetSetBootStatusData(BootState
,
546 RtlGetSetBootStatusData(BootState
,
548 RtlBsdItemBootShutdown
,
550 sizeof(ShutdownOkay
),
553 /* Unlock the BSD and return */
554 RtlUnlockBootStatusData(BootState
);