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 *******************************************************************/
15 /* GLOBALS ********************************************************************/
18 // Taken from an ASSERT
20 #define VALUE_BUFFER_SIZE (sizeof(KEY_VALUE_PARTIAL_INFORMATION) + 512)
22 typedef struct _SMP_PRIVILEGE_STATE
25 PTOKEN_PRIVILEGES OldPrivileges
;
26 PTOKEN_PRIVILEGES NewPrivileges
;
27 UCHAR OldBuffer
[1024];
28 TOKEN_PRIVILEGES NewBuffer
;
29 } SMP_PRIVILEGE_STATE
, *PSMP_PRIVILEGE_STATE
;
31 UNICODE_STRING SmpDebugKeyword
, SmpASyncKeyword
, SmpAutoChkKeyword
;
33 /* FUNCTIONS ******************************************************************/
37 SmpAcquirePrivilege(IN ULONG Privilege
,
38 OUT PVOID
*PrivilegeState
)
40 PSMP_PRIVILEGE_STATE State
;
45 *PrivilegeState
= NULL
;
47 /* Acquire the state structure to hold everything we need */
48 State
= RtlAllocateHeap(SmpHeap
,
50 sizeof(SMP_PRIVILEGE_STATE
) +
51 sizeof(TOKEN_PRIVILEGES
) +
52 sizeof(LUID_AND_ATTRIBUTES
));
53 if (!State
) return STATUS_NO_MEMORY
;
56 Status
= NtOpenProcessToken(NtCurrentProcess(),
57 TOKEN_ADJUST_PRIVILEGES
| TOKEN_QUERY
,
59 if (!NT_SUCCESS(Status
))
62 RtlFreeHeap(SmpHeap
, 0, State
);
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
;
73 /* Adjust the privileges in the token */
74 Size
= sizeof(State
->OldBuffer
);
75 Status
= NtAdjustPrivilegesToken(State
->TokenHandle
,
81 if (Status
== STATUS_BUFFER_TOO_SMALL
)
83 /* Our static buffer is not big enough, allocate a bigger one */
84 State
->OldPrivileges
= RtlAllocateHeap(SmpHeap
, 0, Size
);
85 if (!State
->OldPrivileges
)
87 /* Out of memory, fail */
88 Status
= STATUS_NO_MEMORY
;
93 Status
= NtAdjustPrivilegesToken(State
->TokenHandle
,
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
))
105 /* We got the privilege, return */
106 *PrivilegeState
= State
;
107 return STATUS_SUCCESS
;
111 /* Check if we used a dynamic buffer */
112 if (State
->OldPrivileges
!= (PTOKEN_PRIVILEGES
)&State
->OldBuffer
)
115 RtlFreeHeap(SmpHeap
, 0, State
->OldPrivileges
);
118 /* Close the token handle and free the state structure */
119 NtClose(State
->TokenHandle
);
120 RtlFreeHeap(SmpHeap
, 0, State
);
126 SmpReleasePrivilege(IN PVOID PrivState
)
128 PSMP_PRIVILEGE_STATE State
= (PSMP_PRIVILEGE_STATE
)PrivState
;
130 /* Adjust the privileges in the token */
131 NtAdjustPrivilegesToken(State
->TokenHandle
,
133 State
->OldPrivileges
,
138 /* Check if we used a dynamic buffer */
139 if (State
->OldPrivileges
!= (PTOKEN_PRIVILEGES
)&State
->OldBuffer
)
142 RtlFreeHeap(SmpHeap
, 0, State
->OldPrivileges
);
145 /* Close the token handle and free the state structure */
146 NtClose(State
->TokenHandle
);
147 RtlFreeHeap(SmpHeap
, 0, State
);
152 SmpParseToken(IN PUNICODE_STRING Input
,
153 IN BOOLEAN SecondPass
,
154 OUT PUNICODE_STRING Token
)
157 ULONG Length
, TokenLength
, InputLength
;
159 /* Initialize to NULL to start with */
160 RtlInitUnicodeString(Token
, NULL
);
162 /* Save the input length */
163 InputLength
= Input
->Length
;
165 /* If the input string is empty, just return */
166 if (InputLength
== 0) return STATUS_SUCCESS
;
168 /* Parse the buffer until the first character */
171 while (Length
< InputLength
)
173 if (*p
> L
' ' ) break;
175 Length
+= sizeof(WCHAR
);
178 /* Are we being called for argument pick-up? */
181 /* Then assume everything else is an argument */
182 TokenLength
= InputLength
- Length
* sizeof(WCHAR
);
183 pp
= (PWSTR
)((ULONG_PTR
)p
+ TokenLength
);
187 /* No -- so loop until the next space */
189 while (Length
< InputLength
)
191 if (*pp
<= L
' ' ) break;
193 Length
+= sizeof(WCHAR
);
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
)
200 if (*pp
> L
' ' ) break;
202 Length
+= sizeof(WCHAR
);
206 /* Did we find a token? */
209 /* Allocate a buffer for it */
210 Token
->Buffer
= RtlAllocateHeap(SmpHeap
,
212 TokenLength
+ sizeof(UNICODE_NULL
));
213 if (!Token
->Buffer
) return STATUS_NO_MEMORY
;
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
;
222 /* Modify the input string with the position of where the next token begins */
223 Input
->Length
-= (ULONG_PTR
)pp
- (ULONG_PTR
)Input
->Buffer
;
225 return STATUS_SUCCESS
;
230 SmpParseCommandLine(IN PUNICODE_STRING CommandLine
,
232 OUT PUNICODE_STRING FileName
,
233 OUT PUNICODE_STRING Directory
,
234 OUT PUNICODE_STRING Arguments
)
237 UNICODE_STRING EnvString
, PathString
, CmdLineCopy
, Token
;
238 WCHAR PathBuffer
[MAX_PATH
];
241 UNICODE_STRING FullPathString
;
243 /* Initialize output arguments to NULL */
244 RtlInitUnicodeString(FileName
, NULL
);
245 RtlInitUnicodeString(Arguments
, NULL
);
246 if (Directory
) RtlInitUnicodeString(Directory
, NULL
);
248 /* Check if we haven't yet built a full default path or system root yet */
249 if (!SmpSystemRoot
.Length
)
251 /* Initialize it based on shared user data string */
252 RtlInitUnicodeString(&SmpSystemRoot
, SharedUserData
->NtSystemRoot
);
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
),
260 if (FullPathString
.Buffer
)
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
;
271 /* Consume the command line */
272 CmdLineCopy
= *CommandLine
;
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
;
280 /* Debug requested? */
281 if (RtlEqualUnicodeString(&Token
, &SmpDebugKeyword
, TRUE
))
283 /* Convert into a flag */
284 *Flags
|= SMP_DEBUG_FLAG
;
286 else if (RtlEqualUnicodeString(&Token
, &SmpASyncKeyword
, TRUE
))
288 /* Asynch requested, convert into a flag */
289 *Flags
|= SMP_ASYNC_FLAG
;
291 else if (RtlEqualUnicodeString(&Token
, &SmpAutoChkKeyword
, TRUE
))
293 /* Autochk requested, convert into a flag */
294 *Flags
|= SMP_AUTOCHK_FLAG
;
298 /* No specifier found, keep going */
302 /* Get rid of this token and get the next */
303 RtlFreeHeap(SmpHeap
, 0, Token
.Buffer
);
306 /* Initialize a string to hold the current path */
307 RtlInitUnicodeString(&EnvString
, L
"Path");
309 RtlInitEmptyUnicodeString(&PathString
,
310 RtlAllocateHeap(SmpHeap
, SmBaseTag
, Length
),
312 if (!PathString
.Buffer
)
314 /* Fail if we have no memory for this */
315 RtlFreeHeap(SmpHeap
, 0, Token
.Buffer
);
316 return STATUS_INSUFFICIENT_RESOURCES
;
319 /* Query the path from the environment */
320 Status
= RtlQueryEnvironmentVariable_U(SmpDefaultEnvironment
,
323 if (Status
== STATUS_BUFFER_TOO_SMALL
)
325 /* Our buffer was too small, free it */
326 RtlFreeHeap(SmpHeap
, 0, PathString
.Buffer
);
328 /* And allocate one big enough */
329 Length
= PathString
.Length
+ sizeof(UNICODE_NULL
);
330 RtlInitEmptyUnicodeString(&PathString
,
331 RtlAllocateHeap(SmpHeap
, SmBaseTag
, Length
),
333 if (!PathString
.Buffer
)
335 /* Fail if we have no memory for this */
336 RtlFreeHeap(SmpHeap
, 0, Token
.Buffer
);
337 return STATUS_INSUFFICIENT_RESOURCES
;
340 /* Now try again, this should work */
341 Status
= RtlQueryEnvironmentVariable_U(SmpDefaultEnvironment
,
345 if (!NT_SUCCESS(Status
))
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
;
353 /* Check if the caller expects any flags out of here */
356 /* We can return the image not found flag -- so does the image exist */
357 if (!(RtlDosSearchPath_U(PathString
.Buffer
,
363 !(RtlDosSearchPath_U(SmpDefaultLibPath
.Buffer
,
370 /* It doesn't, let the caller know about it and exit */
371 *Flags
|= SMP_INVALID_PATH
;
373 RtlFreeHeap(SmpHeap
, 0, PathString
.Buffer
);
374 return STATUS_SUCCESS
;
379 /* Caller doesn't want flags, probably wants the image itself */
380 wcscpy(PathBuffer
, Token
.Buffer
);
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
;
389 /* Convert it and bail out if this failed */
390 if (!RtlDosPathNameToNtPathName_U(PathBuffer
, FileName
, NULL
, NULL
))
392 DPRINT1("SMSS: Unable to translate %ws into an NT File Name\n",
394 Status
= STATUS_OBJECT_PATH_INVALID
;
396 if (!NT_SUCCESS(Status
)) return Status
;
398 /* Finally, check if the caller also wanted the directory */
401 /* Is the file a relative name with no directory? */
402 if (FilePart
<= PathBuffer
)
405 RtlInitUnicodeString(Directory
, NULL
);
409 /* There is a directory, and a filename -- separate those two */
410 *--FilePart
= UNICODE_NULL
;
411 RtlCreateUnicodeString(Directory
, PathBuffer
);
415 /* We are done -- move on to the second pass to get the arguments */
416 return SmpParseToken(&CmdLineCopy
, TRUE
, Arguments
);
421 SmpQueryRegistrySosOption(VOID
)
424 UNICODE_STRING KeyName
, ValueName
;
425 OBJECT_ATTRIBUTES ObjectAttributes
;
427 WCHAR ValueBuffer
[VALUE_BUFFER_SIZE
];
428 PKEY_VALUE_PARTIAL_INFORMATION PartialInfo
= (PVOID
)ValueBuffer
;
432 RtlInitUnicodeString(&KeyName
,
433 L
"\\Registry\\Machine\\System\\CurrentControlSet\\Control");
434 InitializeObjectAttributes(&ObjectAttributes
,
436 OBJ_CASE_INSENSITIVE
,
439 Status
= NtOpenKey(&KeyHandle
, KEY_READ
, &ObjectAttributes
);
440 if (!NT_SUCCESS(Status
))
442 DPRINT1("SMSS: can't open control key: 0x%x\n", Status
);
446 /* Query the value */
447 RtlInitUnicodeString(&ValueName
, L
"SystemStartOptions");
448 Status
= NtQueryValueKey(KeyHandle
,
450 KeyValuePartialInformation
,
454 ASSERT(Length
< VALUE_BUFFER_SIZE
);
456 if (!NT_SUCCESS(Status
))
458 DPRINT1("SMSS: can't query value key: 0x%x\n", Status
);
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")))
466 /* It's not set, return FALSE */
470 /* It's set, return TRUE */
476 SmpSaveAndClearBootStatusData(OUT PBOOLEAN BootOkay
,
477 OUT PBOOLEAN ShutdownOkay
)
480 BOOLEAN Value
= TRUE
;
481 PVOID BootStatusDataHandle
;
485 *ShutdownOkay
= FALSE
;
487 /* Lock the BSD and fail if we couldn't */
488 Status
= RtlLockBootStatusData(&BootStatusDataHandle
);
489 if (!NT_SUCCESS(Status
)) return FALSE
;
491 /* Read the old settings */
492 RtlGetSetBootStatusData(BootStatusDataHandle
,
498 RtlGetSetBootStatusData(BootStatusDataHandle
,
500 RtlBsdItemBootShutdown
,
505 /* Set new ones indicating we got at least this far */
506 RtlGetSetBootStatusData(BootStatusDataHandle
,
512 RtlGetSetBootStatusData(BootStatusDataHandle
,
514 RtlBsdItemBootShutdown
,
519 /* Unlock the BSD and return */
520 RtlUnlockBootStatusData(BootStatusDataHandle
);
526 SmpRestoreBootStatusData(IN BOOLEAN BootOkay
,
527 IN BOOLEAN ShutdownOkay
)
533 Status
= RtlLockBootStatusData(&BootState
);
534 if (NT_SUCCESS(Status
))
536 /* Write the bootokay and bootshudown values */
537 RtlGetSetBootStatusData(BootState
,
543 RtlGetSetBootStatusData(BootState
,
545 RtlBsdItemBootShutdown
,
547 sizeof(ShutdownOkay
),
550 /* Unlock the BSD and return */
551 RtlUnlockBootStatusData(BootState
);