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