mirror of
https://github.com/reactos/reactos.git
synced 2024-09-12 05:43:24 +00:00
![Hermès Bélusca-Maïto](/assets/img/avatar_default.png)
- Not all the wcscpy() / swprintf() calls have been converted to their string-safe equivalents. Instead I used the string-safe functions only for places where strings of unknown length were copied into fixed-size internal buffers. On the contrary, for known-fixed-length strings being copied or numbers being converted to string representations in large enough buffers, I kept the original function calls. - Verify the registry data that has been returned by NtQueryValueKey(): * When expecting (not multi) strings, check whether the data type is either REG_SZ or REG_EXPAND_SZ. * When expecting DWORD values, check whether the data type is REG_DWORD and whether the data length is (greater or) equal to sizeof(ULONG).
2542 lines
91 KiB
C
2542 lines
91 KiB
C
/*
|
|
* PROJECT: ReactOS Windows-Compatible Session Manager
|
|
* LICENSE: BSD 2-Clause License
|
|
* FILE: base/system/smss/sminit.c
|
|
* PURPOSE: Main SMSS Code
|
|
* PROGRAMMERS: Alex Ionescu
|
|
*/
|
|
|
|
/* INCLUDES *******************************************************************/
|
|
|
|
#include "smss.h"
|
|
|
|
#define NDEBUG
|
|
#include <debug.h>
|
|
|
|
/* GLOBALS ********************************************************************/
|
|
|
|
UNICODE_STRING SmpSubsystemName, PosixName, Os2Name;
|
|
LIST_ENTRY SmpBootExecuteList, SmpSetupExecuteList, SmpPagingFileList;
|
|
LIST_ENTRY SmpDosDevicesList, SmpFileRenameList, SmpKnownDllsList;
|
|
LIST_ENTRY SmpExcludeKnownDllsList, SmpSubSystemList, SmpSubSystemsToLoad;
|
|
LIST_ENTRY SmpSubSystemsToDefer, SmpExecuteList, NativeProcessList;
|
|
|
|
PVOID SmpHeap;
|
|
ULONG SmBaseTag;
|
|
HANDLE SmpDebugPort, SmpDosDevicesObjectDirectory;
|
|
PWCHAR SmpDefaultEnvironment, SmpDefaultLibPathBuffer;
|
|
UNICODE_STRING SmpKnownDllPath, SmpDefaultLibPath;
|
|
ULONG SmpCalledConfigEnv;
|
|
|
|
ULONG SmpInitProgressByLine;
|
|
NTSTATUS SmpInitReturnStatus;
|
|
PVOID SmpInitLastCall;
|
|
|
|
SECURITY_DESCRIPTOR SmpPrimarySDBody, SmpLiberalSDBody, SmpKnownDllsSDBody;
|
|
SECURITY_DESCRIPTOR SmpApiPortSDBody;
|
|
PISECURITY_DESCRIPTOR SmpPrimarySecurityDescriptor, SmpLiberalSecurityDescriptor;
|
|
PISECURITY_DESCRIPTOR SmpKnownDllsSecurityDescriptor, SmpApiPortSecurityDescriptor;
|
|
|
|
ULONG SmpAllowProtectedRenames, SmpProtectionMode = 1;
|
|
BOOLEAN MiniNTBoot = FALSE;
|
|
|
|
#define SMSS_CHECKPOINT(x, y) \
|
|
{ \
|
|
SmpInitProgressByLine = __LINE__; \
|
|
SmpInitReturnStatus = (y); \
|
|
SmpInitLastCall = (x); \
|
|
}
|
|
|
|
/* REGISTRY CONFIGURATION *****************************************************/
|
|
|
|
NTSTATUS
|
|
NTAPI
|
|
SmpSaveRegistryValue(IN PLIST_ENTRY ListAddress,
|
|
IN PWSTR Name,
|
|
IN PWCHAR Value,
|
|
IN BOOLEAN Flags)
|
|
{
|
|
PSMP_REGISTRY_VALUE RegEntry;
|
|
UNICODE_STRING NameString, ValueString;
|
|
ANSI_STRING AnsiValueString;
|
|
PLIST_ENTRY NextEntry;
|
|
|
|
/* Convert to unicode strings */
|
|
RtlInitUnicodeString(&NameString, Name);
|
|
RtlInitUnicodeString(&ValueString, Value);
|
|
|
|
/* In case this is the first value, initialize a new list/structure */
|
|
RegEntry = NULL;
|
|
|
|
/* Check if we should do a duplicate check */
|
|
if (Flags)
|
|
{
|
|
/* Loop the current list */
|
|
NextEntry = ListAddress->Flink;
|
|
while (NextEntry != ListAddress)
|
|
{
|
|
/* Get each entry */
|
|
RegEntry = CONTAINING_RECORD(NextEntry, SMP_REGISTRY_VALUE, Entry);
|
|
|
|
/* Check if the value name matches */
|
|
if (!RtlCompareUnicodeString(&RegEntry->Name, &NameString, TRUE))
|
|
{
|
|
/* Check if the value is the exact same thing */
|
|
if (!RtlCompareUnicodeString(&RegEntry->Value, &ValueString, TRUE))
|
|
{
|
|
/* Fail -- the same setting is being set twice */
|
|
return STATUS_OBJECT_NAME_EXISTS;
|
|
}
|
|
|
|
/* We found the list, and this isn't a duplicate value */
|
|
break;
|
|
}
|
|
|
|
/* This wasn't a match, keep going */
|
|
NextEntry = NextEntry->Flink;
|
|
RegEntry = NULL;
|
|
}
|
|
}
|
|
|
|
/* Are we adding on, or creating a new entry */
|
|
if (!RegEntry)
|
|
{
|
|
/* A new entry -- allocate it */
|
|
RegEntry = RtlAllocateHeap(RtlGetProcessHeap(),
|
|
SmBaseTag,
|
|
sizeof(SMP_REGISTRY_VALUE) +
|
|
NameString.MaximumLength);
|
|
if (!RegEntry) return STATUS_NO_MEMORY;
|
|
|
|
/* Initialize the list and set all values to NULL */
|
|
InitializeListHead(&RegEntry->Entry);
|
|
RegEntry->AnsiValue = NULL;
|
|
RegEntry->Value.Buffer = NULL;
|
|
|
|
/* Copy and initialize the value name */
|
|
RegEntry->Name.Buffer = (PWCHAR)(RegEntry + 1);
|
|
RegEntry->Name.Length = NameString.Length;
|
|
RegEntry->Name.MaximumLength = NameString.MaximumLength;
|
|
RtlCopyMemory(RegEntry->Name.Buffer,
|
|
NameString.Buffer,
|
|
NameString.MaximumLength);
|
|
|
|
/* Add this entry into the list */
|
|
InsertTailList(ListAddress, &RegEntry->Entry);
|
|
}
|
|
|
|
/* Did we have an old value buffer? */
|
|
if (RegEntry->Value.Buffer)
|
|
{
|
|
/* Free it */
|
|
ASSERT(RegEntry->Value.Length != 0);
|
|
RtlFreeHeap(RtlGetProcessHeap(), 0, RegEntry->Value.Buffer);
|
|
}
|
|
|
|
/* Is there no value associated? */
|
|
if (!Value)
|
|
{
|
|
/* We're done here */
|
|
RtlInitUnicodeString(&RegEntry->Value, NULL);
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
/* There is a value, so allocate a buffer for it */
|
|
RegEntry->Value.Buffer = RtlAllocateHeap(RtlGetProcessHeap(),
|
|
SmBaseTag,
|
|
ValueString.MaximumLength);
|
|
if (!RegEntry->Value.Buffer)
|
|
{
|
|
/* Out of memory, undo */
|
|
RemoveEntryList(&RegEntry->Entry);
|
|
RtlFreeHeap(RtlGetProcessHeap(), 0, RegEntry);
|
|
return STATUS_NO_MEMORY;
|
|
}
|
|
|
|
/* Copy the value into the entry */
|
|
RegEntry->Value.Length = ValueString.Length;
|
|
RegEntry->Value.MaximumLength = ValueString.MaximumLength;
|
|
RtlCopyMemory(RegEntry->Value.Buffer,
|
|
ValueString.Buffer,
|
|
ValueString.MaximumLength);
|
|
|
|
/* Now allocate memory for an ANSI copy of it */
|
|
RegEntry->AnsiValue = RtlAllocateHeap(RtlGetProcessHeap(),
|
|
SmBaseTag,
|
|
(ValueString.Length / sizeof(WCHAR)) +
|
|
sizeof(ANSI_NULL));
|
|
if (!RegEntry->AnsiValue)
|
|
{
|
|
/* Out of memory, undo */
|
|
RtlFreeHeap(RtlGetProcessHeap(), 0, RegEntry->Value.Buffer);
|
|
RemoveEntryList(&RegEntry->Entry);
|
|
RtlFreeHeap(RtlGetProcessHeap(), 0, RegEntry);
|
|
return STATUS_NO_MEMORY;
|
|
}
|
|
|
|
/* Convert the Unicode value string and return success */
|
|
RtlInitEmptyAnsiString(&AnsiValueString,
|
|
RegEntry->AnsiValue,
|
|
(ValueString.Length / sizeof(WCHAR)) +
|
|
sizeof(ANSI_NULL));
|
|
RtlUnicodeStringToAnsiString(&AnsiValueString, &ValueString, FALSE);
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
PSMP_REGISTRY_VALUE
|
|
NTAPI
|
|
SmpFindRegistryValue(IN PLIST_ENTRY List,
|
|
IN PWSTR ValueName)
|
|
{
|
|
PSMP_REGISTRY_VALUE RegEntry;
|
|
UNICODE_STRING ValueString;
|
|
PLIST_ENTRY NextEntry;
|
|
|
|
/* Initialize the value name sting */
|
|
RtlInitUnicodeString(&ValueString, ValueName);
|
|
|
|
/* Loop the list */
|
|
NextEntry = List->Flink;
|
|
while (NextEntry != List)
|
|
{
|
|
/* Get each entry */
|
|
RegEntry = CONTAINING_RECORD(NextEntry, SMP_REGISTRY_VALUE, Entry);
|
|
|
|
/* Check if the value name matches */
|
|
if (!RtlCompareUnicodeString(&RegEntry->Name, &ValueString, TRUE)) break;
|
|
|
|
/* It doesn't, move on */
|
|
NextEntry = NextEntry->Flink;
|
|
}
|
|
|
|
/* If we looped back, return NULL, otherwise return the entry we found */
|
|
if (NextEntry == List) RegEntry = NULL;
|
|
return RegEntry;
|
|
}
|
|
|
|
NTSTATUS
|
|
NTAPI
|
|
SmpConfigureProtectionMode(IN PWSTR ValueName,
|
|
IN ULONG ValueType,
|
|
IN PVOID ValueData,
|
|
IN ULONG ValueLength,
|
|
IN PVOID Context,
|
|
IN PVOID EntryContext)
|
|
{
|
|
/* Make sure the value is valid */
|
|
if (ValueLength == sizeof(ULONG))
|
|
{
|
|
/* Read it */
|
|
SmpProtectionMode = *(PULONG)ValueData;
|
|
}
|
|
else
|
|
{
|
|
/* Default is to protect stuff */
|
|
SmpProtectionMode = 1;
|
|
}
|
|
|
|
/* Recreate the security descriptors to take into account security mode */
|
|
SmpCreateSecurityDescriptors(FALSE);
|
|
DPRINT("SmpProtectionMode: %lu\n", SmpProtectionMode);
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
NTSTATUS
|
|
NTAPI
|
|
SmpConfigureAllowProtectedRenames(IN PWSTR ValueName,
|
|
IN ULONG ValueType,
|
|
IN PVOID ValueData,
|
|
IN ULONG ValueLength,
|
|
IN PVOID Context,
|
|
IN PVOID EntryContext)
|
|
{
|
|
/* Make sure the value is valid */
|
|
if (ValueLength == sizeof(ULONG))
|
|
{
|
|
/* Read it */
|
|
SmpAllowProtectedRenames = *(PULONG)ValueData;
|
|
}
|
|
else
|
|
{
|
|
/* Default is to not allow protected renames */
|
|
SmpAllowProtectedRenames = 0;
|
|
}
|
|
|
|
DPRINT("SmpAllowProtectedRenames: %lu\n", SmpAllowProtectedRenames);
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
NTSTATUS
|
|
NTAPI
|
|
SmpConfigureObjectDirectories(IN PWSTR ValueName,
|
|
IN ULONG ValueType,
|
|
IN PVOID ValueData,
|
|
IN ULONG ValueLength,
|
|
IN PVOID Context,
|
|
IN PVOID EntryContext)
|
|
{
|
|
PISECURITY_DESCRIPTOR SecDescriptor;
|
|
NTSTATUS Status;
|
|
OBJECT_ATTRIBUTES ObjectAttributes;
|
|
HANDLE DirHandle;
|
|
UNICODE_STRING RpcString, WindowsString, SearchString;
|
|
PWCHAR SourceString = ValueData;
|
|
|
|
/* Initialize the two strings we will be looking for */
|
|
RtlInitUnicodeString(&RpcString, L"\\RPC Control");
|
|
RtlInitUnicodeString(&WindowsString, L"\\Windows");
|
|
|
|
/* Loop the registry data we received */
|
|
while (*SourceString)
|
|
{
|
|
/* Assume primary SD for most objects */
|
|
RtlInitUnicodeString(&SearchString, SourceString);
|
|
SecDescriptor = SmpPrimarySecurityDescriptor;
|
|
|
|
/* But for these two always set the liberal descriptor */
|
|
if ((RtlEqualUnicodeString(&SearchString, &RpcString, TRUE)) ||
|
|
(RtlEqualUnicodeString(&SearchString, &WindowsString, TRUE)))
|
|
{
|
|
SecDescriptor = SmpLiberalSecurityDescriptor;
|
|
}
|
|
|
|
/* Create the requested directory with the requested descriptor */
|
|
InitializeObjectAttributes(&ObjectAttributes,
|
|
&SearchString,
|
|
OBJ_CASE_INSENSITIVE |
|
|
OBJ_OPENIF |
|
|
OBJ_PERMANENT,
|
|
NULL,
|
|
SecDescriptor);
|
|
DPRINT("Creating: %wZ directory\n", &SearchString);
|
|
Status = NtCreateDirectoryObject(&DirHandle,
|
|
DIRECTORY_ALL_ACCESS,
|
|
&ObjectAttributes);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
/* Failure case */
|
|
DPRINT1("SMSS: Unable to create %wZ object directory - Status == %lx\n",
|
|
&SearchString, Status);
|
|
}
|
|
else
|
|
{
|
|
/* It worked, now close the handle */
|
|
NtClose(DirHandle);
|
|
}
|
|
|
|
/* Move to the next requested object */
|
|
SourceString += wcslen(SourceString) + 1;
|
|
}
|
|
|
|
/* All done */
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
NTSTATUS
|
|
NTAPI
|
|
SmpConfigureMemoryMgmt(IN PWSTR ValueName,
|
|
IN ULONG ValueType,
|
|
IN PVOID ValueData,
|
|
IN ULONG ValueLength,
|
|
IN PVOID Context,
|
|
IN PVOID EntryContext)
|
|
{
|
|
/* Save this is into a list */
|
|
return SmpSaveRegistryValue(EntryContext, ValueData, NULL, TRUE);
|
|
}
|
|
|
|
NTSTATUS
|
|
NTAPI
|
|
SmpConfigureFileRenames(IN PWSTR ValueName,
|
|
IN ULONG ValueType,
|
|
IN PVOID ValueData,
|
|
IN ULONG ValueLength,
|
|
IN PVOID Context,
|
|
IN PVOID EntryContext)
|
|
{
|
|
NTSTATUS Status;
|
|
static PWCHAR Canary = NULL;
|
|
|
|
/* Check if this is the second call */
|
|
if (Canary)
|
|
{
|
|
/* Save the data into the list */
|
|
DPRINT("Renamed file: '%S' - '%S'\n", Canary, ValueData);
|
|
Status = SmpSaveRegistryValue(EntryContext, Canary, ValueData, FALSE);
|
|
Canary = NULL;
|
|
}
|
|
else
|
|
{
|
|
/* This it the first call, do nothing until we get the second call */
|
|
Canary = ValueData;
|
|
Status = STATUS_SUCCESS;
|
|
}
|
|
|
|
/* Return the status */
|
|
return Status;
|
|
}
|
|
|
|
NTSTATUS
|
|
NTAPI
|
|
SmpConfigureExcludeKnownDlls(IN PWSTR ValueName,
|
|
IN ULONG ValueType,
|
|
IN PVOID ValueData,
|
|
IN ULONG ValueLength,
|
|
IN PVOID Context,
|
|
IN PVOID EntryContext)
|
|
{
|
|
PWCHAR DllName;
|
|
NTSTATUS Status;
|
|
|
|
/* Make sure the value type is valid */
|
|
if ((ValueType == REG_MULTI_SZ) || (ValueType == REG_SZ))
|
|
{
|
|
/* Keep going for each DLL in the list */
|
|
DllName = ValueData;
|
|
while (*DllName)
|
|
{
|
|
/* Add this to the linked list */
|
|
DPRINT("Excluded DLL: %S\n", DllName);
|
|
Status = SmpSaveRegistryValue(EntryContext, DllName, NULL, TRUE);
|
|
|
|
/* Bail out on failure or if only one DLL name was present */
|
|
if (!(NT_SUCCESS(Status)) || (ValueType == REG_SZ)) return Status;
|
|
|
|
/* Otherwise, move to the next DLL name */
|
|
DllName += wcslen(DllName) + 1;
|
|
}
|
|
}
|
|
|
|
/* All done */
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
NTSTATUS
|
|
NTAPI
|
|
SmpConfigureDosDevices(IN PWSTR ValueName,
|
|
IN ULONG ValueType,
|
|
IN PVOID ValueData,
|
|
IN ULONG ValueLength,
|
|
IN PVOID Context,
|
|
IN PVOID EntryContext)
|
|
{
|
|
/* Save into linked list */
|
|
return SmpSaveRegistryValue(EntryContext, ValueName, ValueData, TRUE);
|
|
}
|
|
|
|
NTSTATUS
|
|
NTAPI
|
|
SmpInitializeKnownDllPath(IN PUNICODE_STRING DllPath,
|
|
IN PWCHAR Buffer,
|
|
IN ULONG Length)
|
|
{
|
|
NTSTATUS Status;
|
|
|
|
/* Allocate the buffer */
|
|
DllPath->Buffer = RtlAllocateHeap(RtlGetProcessHeap(), SmBaseTag, Length);
|
|
if (DllPath->Buffer)
|
|
{
|
|
/* Fill out the rest of the string */
|
|
DllPath->MaximumLength = (USHORT)Length;
|
|
DllPath->Length = (USHORT)Length - sizeof(UNICODE_NULL);
|
|
|
|
/* Copy the actual path and return success */
|
|
RtlCopyMemory(DllPath->Buffer, Buffer, Length);
|
|
Status = STATUS_SUCCESS;
|
|
}
|
|
else
|
|
{
|
|
/* Fail with out of memory code */
|
|
Status = STATUS_NO_MEMORY;
|
|
}
|
|
|
|
/* Return result */
|
|
return Status;
|
|
}
|
|
|
|
NTSTATUS
|
|
NTAPI
|
|
SmpConfigureKnownDlls(IN PWSTR ValueName,
|
|
IN ULONG ValueType,
|
|
IN PVOID ValueData,
|
|
IN ULONG ValueLength,
|
|
IN PVOID Context,
|
|
IN PVOID EntryContext)
|
|
{
|
|
/* Check which value is being set */
|
|
if (_wcsicmp(ValueName, L"DllDirectory") == 0)
|
|
{
|
|
/* This is the directory, initialize it */
|
|
DPRINT("KnownDll Path: %S\n", ValueData);
|
|
return SmpInitializeKnownDllPath(&SmpKnownDllPath, ValueData, ValueLength);
|
|
}
|
|
else
|
|
{
|
|
/* Add to the linked list -- this is a file */
|
|
return SmpSaveRegistryValue(EntryContext, ValueName, ValueData, TRUE);
|
|
}
|
|
}
|
|
|
|
NTSTATUS
|
|
NTAPI
|
|
SmpConfigureEnvironment(IN PWSTR ValueName,
|
|
IN ULONG ValueType,
|
|
IN PVOID ValueData,
|
|
IN ULONG ValueLength,
|
|
IN PVOID Context,
|
|
IN PVOID EntryContext)
|
|
{
|
|
NTSTATUS Status;
|
|
UNICODE_STRING ValueString, DataString;
|
|
|
|
/* Convert the strings into UNICODE_STRING and set the variable defined */
|
|
RtlInitUnicodeString(&ValueString, ValueName);
|
|
RtlInitUnicodeString(&DataString, ValueData);
|
|
DPRINT("Setting %wZ = %wZ\n", &ValueString, &DataString);
|
|
Status = RtlSetEnvironmentVariable(0, &ValueString, &DataString);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
DPRINT1("SMSS: 'SET %wZ = %wZ' failed - Status == %lx\n",
|
|
&ValueString, &DataString, Status);
|
|
return Status;
|
|
}
|
|
|
|
/* Check if the path is being set, and wait for the second instantiation */
|
|
if ((_wcsicmp(ValueName, L"Path") == 0) && (++SmpCalledConfigEnv == 2))
|
|
{
|
|
/* Allocate the path buffer */
|
|
SmpDefaultLibPathBuffer = RtlAllocateHeap(RtlGetProcessHeap(),
|
|
SmBaseTag,
|
|
ValueLength);
|
|
if (!SmpDefaultLibPathBuffer) return STATUS_NO_MEMORY;
|
|
|
|
/* Copy the data into it and create the UNICODE_STRING to hold it */
|
|
RtlCopyMemory(SmpDefaultLibPathBuffer, ValueData, ValueLength);
|
|
RtlInitUnicodeString(&SmpDefaultLibPath, SmpDefaultLibPathBuffer);
|
|
}
|
|
|
|
/* All good */
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
NTSTATUS
|
|
NTAPI
|
|
SmpConfigureSubSystems(IN PWSTR ValueName,
|
|
IN ULONG ValueType,
|
|
IN PVOID ValueData,
|
|
IN ULONG ValueLength,
|
|
IN PVOID Context,
|
|
IN PVOID EntryContext)
|
|
{
|
|
PSMP_REGISTRY_VALUE RegEntry;
|
|
PWCHAR SubsystemName;
|
|
|
|
/* Is this a required or optional subsystem? */
|
|
if ((_wcsicmp(ValueName, L"Required") != 0) &&
|
|
(_wcsicmp(ValueName, L"Optional") != 0))
|
|
{
|
|
/* It isn't, is this the PSI flag? */
|
|
if ((_wcsicmp(ValueName, L"PosixSingleInstance") != 0) ||
|
|
(ValueType != REG_DWORD))
|
|
{
|
|
/* It isn't, must be a subsystem entry, add it to the list */
|
|
DPRINT("Subsystem entry: %S-%S\n", ValueName, ValueData);
|
|
return SmpSaveRegistryValue(EntryContext, ValueName, ValueData, TRUE);
|
|
}
|
|
|
|
/* This was the PSI flag, save it and exit */
|
|
RegPosixSingleInstance = TRUE;
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
/* This should be one of the required/optional lists. Is the type valid? */
|
|
if (ValueType == REG_MULTI_SZ)
|
|
{
|
|
/* It is, get the first subsystem */
|
|
SubsystemName = ValueData;
|
|
while (*SubsystemName)
|
|
{
|
|
/* We should have already put it into the list when we found it */
|
|
DPRINT("Found subsystem: %S\n", SubsystemName);
|
|
RegEntry = SmpFindRegistryValue(EntryContext, SubsystemName);
|
|
if (!RegEntry)
|
|
{
|
|
/* This subsystem doesn't exist, so skip it */
|
|
DPRINT1("SMSS: Invalid subsystem name - %ws\n", SubsystemName);
|
|
}
|
|
else
|
|
{
|
|
/* Found it -- remove it from the main list */
|
|
RemoveEntryList(&RegEntry->Entry);
|
|
|
|
/* Figure out which list to put it in */
|
|
if (_wcsicmp(ValueName, L"Required") == 0)
|
|
{
|
|
/* Put it into the required list */
|
|
DPRINT("Required\n");
|
|
InsertTailList(&SmpSubSystemsToLoad, &RegEntry->Entry);
|
|
}
|
|
else
|
|
{
|
|
/* Put it into the optional list */
|
|
DPRINT("Optional\n");
|
|
InsertTailList(&SmpSubSystemsToDefer, &RegEntry->Entry);
|
|
}
|
|
}
|
|
|
|
/* Move to the next name */
|
|
SubsystemName += wcslen(SubsystemName) + 1;
|
|
}
|
|
}
|
|
|
|
/* All done! */
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
RTL_QUERY_REGISTRY_TABLE
|
|
SmpRegistryConfigurationTable[] =
|
|
{
|
|
{
|
|
SmpConfigureProtectionMode,
|
|
0,
|
|
L"ProtectionMode",
|
|
NULL,
|
|
REG_DWORD,
|
|
NULL,
|
|
0
|
|
},
|
|
|
|
{
|
|
SmpConfigureAllowProtectedRenames,
|
|
RTL_QUERY_REGISTRY_DELETE,
|
|
L"AllowProtectedRenames",
|
|
NULL,
|
|
REG_DWORD,
|
|
NULL,
|
|
0
|
|
},
|
|
|
|
{
|
|
SmpConfigureObjectDirectories,
|
|
0,
|
|
L"ObjectDirectories",
|
|
NULL,
|
|
REG_MULTI_SZ,
|
|
L"\\Windows\0\\RPC Control\0",
|
|
0
|
|
},
|
|
|
|
{
|
|
SmpConfigureMemoryMgmt,
|
|
0,
|
|
L"BootExecute",
|
|
&SmpBootExecuteList,
|
|
REG_MULTI_SZ,
|
|
L"autocheck AutoChk.exe *\0",
|
|
0
|
|
},
|
|
|
|
{
|
|
SmpConfigureMemoryMgmt,
|
|
RTL_QUERY_REGISTRY_TOPKEY,
|
|
L"SetupExecute",
|
|
&SmpSetupExecuteList,
|
|
REG_NONE,
|
|
NULL,
|
|
0
|
|
},
|
|
|
|
{
|
|
SmpConfigureFileRenames,
|
|
RTL_QUERY_REGISTRY_DELETE,
|
|
L"PendingFileRenameOperations",
|
|
&SmpFileRenameList,
|
|
REG_NONE,
|
|
NULL,
|
|
0
|
|
},
|
|
|
|
{
|
|
SmpConfigureFileRenames,
|
|
RTL_QUERY_REGISTRY_DELETE,
|
|
L"PendingFileRenameOperations2",
|
|
&SmpFileRenameList,
|
|
REG_NONE,
|
|
NULL,
|
|
0
|
|
},
|
|
|
|
{
|
|
SmpConfigureExcludeKnownDlls,
|
|
0,
|
|
L"ExcludeFromKnownDlls",
|
|
&SmpExcludeKnownDllsList,
|
|
REG_MULTI_SZ,
|
|
L"\0",
|
|
0
|
|
},
|
|
|
|
{
|
|
NULL,
|
|
RTL_QUERY_REGISTRY_SUBKEY,
|
|
L"Memory Management",
|
|
NULL,
|
|
REG_NONE,
|
|
NULL,
|
|
0
|
|
},
|
|
|
|
{
|
|
SmpConfigureMemoryMgmt,
|
|
0,
|
|
L"PagingFiles",
|
|
&SmpPagingFileList,
|
|
REG_MULTI_SZ,
|
|
L"?:\\pagefile.sys\0",
|
|
0
|
|
},
|
|
|
|
{
|
|
SmpConfigureDosDevices,
|
|
RTL_QUERY_REGISTRY_SUBKEY,
|
|
L"DOS Devices",
|
|
&SmpDosDevicesList,
|
|
REG_NONE,
|
|
NULL,
|
|
0
|
|
},
|
|
|
|
{
|
|
SmpConfigureKnownDlls,
|
|
RTL_QUERY_REGISTRY_SUBKEY,
|
|
L"KnownDlls",
|
|
&SmpKnownDllsList,
|
|
REG_NONE,
|
|
NULL,
|
|
0
|
|
},
|
|
|
|
{
|
|
SmpConfigureEnvironment,
|
|
RTL_QUERY_REGISTRY_SUBKEY,
|
|
L"Environment",
|
|
NULL,
|
|
REG_NONE,
|
|
NULL,
|
|
0
|
|
},
|
|
|
|
{
|
|
SmpConfigureSubSystems,
|
|
RTL_QUERY_REGISTRY_SUBKEY,
|
|
L"SubSystems",
|
|
&SmpSubSystemList,
|
|
REG_NONE,
|
|
NULL,
|
|
0
|
|
},
|
|
|
|
{
|
|
SmpConfigureSubSystems,
|
|
RTL_QUERY_REGISTRY_NOEXPAND,
|
|
L"Required",
|
|
&SmpSubSystemList,
|
|
REG_MULTI_SZ,
|
|
L"Debug\0Windows\0",
|
|
0
|
|
},
|
|
|
|
{
|
|
SmpConfigureSubSystems,
|
|
RTL_QUERY_REGISTRY_NOEXPAND,
|
|
L"Optional",
|
|
&SmpSubSystemList,
|
|
REG_NONE,
|
|
NULL,
|
|
0
|
|
},
|
|
|
|
{
|
|
SmpConfigureSubSystems,
|
|
0,
|
|
L"Kmode",
|
|
&SmpSubSystemList,
|
|
REG_NONE,
|
|
NULL,
|
|
0
|
|
},
|
|
|
|
{
|
|
SmpConfigureMemoryMgmt,
|
|
RTL_QUERY_REGISTRY_TOPKEY,
|
|
L"Execute",
|
|
&SmpExecuteList,
|
|
REG_NONE,
|
|
NULL,
|
|
0
|
|
},
|
|
|
|
{0},
|
|
};
|
|
|
|
/* FUNCTIONS ******************************************************************/
|
|
|
|
VOID
|
|
NTAPI
|
|
SmpTranslateSystemPartitionInformation(VOID)
|
|
{
|
|
NTSTATUS Status;
|
|
UNICODE_STRING UnicodeString, LinkTarget, SearchString, SystemPartition;
|
|
OBJECT_ATTRIBUTES ObjectAttributes;
|
|
HANDLE KeyHandle, LinkHandle;
|
|
ULONG Length, Context;
|
|
size_t StrLength;
|
|
WCHAR LinkBuffer[MAX_PATH];
|
|
CHAR ValueBuffer[sizeof(KEY_VALUE_PARTIAL_INFORMATION) + 512];
|
|
PKEY_VALUE_PARTIAL_INFORMATION PartialInfo = (PVOID)ValueBuffer;
|
|
CHAR DirInfoBuffer[sizeof(OBJECT_DIRECTORY_INFORMATION) + 512];
|
|
POBJECT_DIRECTORY_INFORMATION DirInfo = (PVOID)DirInfoBuffer;
|
|
|
|
/* Open the setup key */
|
|
RtlInitUnicodeString(&UnicodeString, L"\\Registry\\Machine\\System\\Setup");
|
|
InitializeObjectAttributes(&ObjectAttributes,
|
|
&UnicodeString,
|
|
OBJ_CASE_INSENSITIVE,
|
|
NULL,
|
|
NULL);
|
|
Status = NtOpenKey(&KeyHandle, KEY_READ, &ObjectAttributes);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
DPRINT1("SMSS: Cannot open system setup key for reading: 0x%x\n", Status);
|
|
return;
|
|
}
|
|
|
|
/* Query the system partition */
|
|
RtlInitUnicodeString(&UnicodeString, L"SystemPartition");
|
|
Status = NtQueryValueKey(KeyHandle,
|
|
&UnicodeString,
|
|
KeyValuePartialInformation,
|
|
PartialInfo,
|
|
sizeof(ValueBuffer),
|
|
&Length);
|
|
NtClose(KeyHandle);
|
|
if (!NT_SUCCESS(Status) ||
|
|
((PartialInfo->Type != REG_SZ) && (PartialInfo->Type != REG_EXPAND_SZ)))
|
|
{
|
|
DPRINT1("SMSS: Cannot query SystemPartition value (Type %lu, Status 0x%x)\n",
|
|
PartialInfo->Type, Status);
|
|
return;
|
|
}
|
|
|
|
/* Initialize the system partition string */
|
|
RtlInitEmptyUnicodeString(&SystemPartition,
|
|
(PWCHAR)PartialInfo->Data,
|
|
PartialInfo->DataLength);
|
|
RtlStringCbLengthW(SystemPartition.Buffer,
|
|
SystemPartition.MaximumLength,
|
|
&StrLength);
|
|
SystemPartition.Length = (USHORT)StrLength;
|
|
|
|
/* Enumerate the directory looking for the symbolic link string */
|
|
RtlInitUnicodeString(&SearchString, L"SymbolicLink");
|
|
RtlInitEmptyUnicodeString(&LinkTarget, LinkBuffer, sizeof(LinkBuffer));
|
|
Status = NtQueryDirectoryObject(SmpDosDevicesObjectDirectory,
|
|
DirInfo,
|
|
sizeof(DirInfoBuffer),
|
|
TRUE,
|
|
TRUE,
|
|
&Context,
|
|
NULL);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
DPRINT1("SMSS: Cannot find drive letter for system partition\n");
|
|
return;
|
|
}
|
|
|
|
/* Keep searching until we find it */
|
|
do
|
|
{
|
|
/* Is this it? */
|
|
if ((RtlEqualUnicodeString(&DirInfo->TypeName, &SearchString, TRUE)) &&
|
|
(DirInfo->Name.Length == 2 * sizeof(WCHAR)) &&
|
|
(DirInfo->Name.Buffer[1] == L':'))
|
|
{
|
|
/* Looks like we found it, open the link to get its target */
|
|
InitializeObjectAttributes(&ObjectAttributes,
|
|
&DirInfo->Name,
|
|
OBJ_CASE_INSENSITIVE,
|
|
SmpDosDevicesObjectDirectory,
|
|
NULL);
|
|
Status = NtOpenSymbolicLinkObject(&LinkHandle,
|
|
SYMBOLIC_LINK_ALL_ACCESS,
|
|
&ObjectAttributes);
|
|
if (NT_SUCCESS(Status))
|
|
{
|
|
/* Open worked, query the target now */
|
|
Status = NtQuerySymbolicLinkObject(LinkHandle,
|
|
&LinkTarget,
|
|
NULL);
|
|
NtClose(LinkHandle);
|
|
|
|
/* Check if it matches the string we had found earlier */
|
|
if ((NT_SUCCESS(Status)) &&
|
|
((RtlEqualUnicodeString(&SystemPartition,
|
|
&LinkTarget,
|
|
TRUE)) ||
|
|
((RtlPrefixUnicodeString(&SystemPartition,
|
|
&LinkTarget,
|
|
TRUE)) &&
|
|
(LinkTarget.Buffer[SystemPartition.Length / sizeof(WCHAR)] == L'\\'))))
|
|
{
|
|
/* All done */
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Couldn't find it, try again */
|
|
Status = NtQueryDirectoryObject(SmpDosDevicesObjectDirectory,
|
|
DirInfo,
|
|
sizeof(DirInfoBuffer),
|
|
TRUE,
|
|
FALSE,
|
|
&Context,
|
|
NULL);
|
|
} while (NT_SUCCESS(Status));
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
DPRINT1("SMSS: Cannot find drive letter for system partition\n");
|
|
return;
|
|
}
|
|
|
|
/* Open the setup key again, for full access this time */
|
|
RtlInitUnicodeString(&UnicodeString,
|
|
L"\\Registry\\Machine\\Software\\Microsoft\\Windows\\CurrentVersion\\Setup");
|
|
InitializeObjectAttributes(&ObjectAttributes,
|
|
&UnicodeString,
|
|
OBJ_CASE_INSENSITIVE,
|
|
NULL,
|
|
NULL);
|
|
Status = NtOpenKey(&KeyHandle, KEY_ALL_ACCESS, &ObjectAttributes);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
DPRINT1("SMSS: Cannot open software setup key for writing: 0x%x\n",
|
|
Status);
|
|
return;
|
|
}
|
|
|
|
/* Wrap up the end of the link buffer */
|
|
wcsncpy(LinkBuffer, DirInfo->Name.Buffer, 2);
|
|
LinkBuffer[2] = L'\\';
|
|
LinkBuffer[3] = L'\0';
|
|
|
|
/* Now set this as the "BootDir" */
|
|
RtlInitUnicodeString(&UnicodeString, L"BootDir");
|
|
Status = NtSetValueKey(KeyHandle,
|
|
&UnicodeString,
|
|
0,
|
|
REG_SZ,
|
|
LinkBuffer,
|
|
4 * sizeof(WCHAR));
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
DPRINT1("SMSS: couldn't write BootDir value: 0x%x\n", Status);
|
|
}
|
|
NtClose(KeyHandle);
|
|
}
|
|
|
|
NTSTATUS
|
|
NTAPI
|
|
SmpCreateSecurityDescriptors(IN BOOLEAN InitialCall)
|
|
{
|
|
NTSTATUS Status;
|
|
PSID WorldSid = NULL, AdminSid = NULL, SystemSid = NULL;
|
|
PSID RestrictedSid = NULL, OwnerSid = NULL;
|
|
SID_IDENTIFIER_AUTHORITY WorldAuthority = {SECURITY_WORLD_SID_AUTHORITY};
|
|
SID_IDENTIFIER_AUTHORITY NtAuthority = {SECURITY_NT_AUTHORITY};
|
|
SID_IDENTIFIER_AUTHORITY CreatorAuthority = {SECURITY_CREATOR_SID_AUTHORITY};
|
|
ULONG AclLength, SidLength;
|
|
PACL Acl;
|
|
PACE_HEADER Ace;
|
|
BOOLEAN ProtectionRequired = FALSE;
|
|
|
|
/* Check if this is the first call */
|
|
if (InitialCall)
|
|
{
|
|
/* Create and set the primary descriptor */
|
|
SmpPrimarySecurityDescriptor = &SmpPrimarySDBody;
|
|
Status = RtlCreateSecurityDescriptor(SmpPrimarySecurityDescriptor,
|
|
SECURITY_DESCRIPTOR_REVISION);
|
|
ASSERT(NT_SUCCESS(Status));
|
|
Status = RtlSetDaclSecurityDescriptor(SmpPrimarySecurityDescriptor,
|
|
TRUE,
|
|
NULL,
|
|
FALSE);
|
|
ASSERT(NT_SUCCESS(Status));
|
|
|
|
/* Create and set the liberal descriptor */
|
|
SmpLiberalSecurityDescriptor = &SmpLiberalSDBody;
|
|
Status = RtlCreateSecurityDescriptor(SmpLiberalSecurityDescriptor,
|
|
SECURITY_DESCRIPTOR_REVISION);
|
|
ASSERT(NT_SUCCESS(Status));
|
|
Status = RtlSetDaclSecurityDescriptor(SmpLiberalSecurityDescriptor,
|
|
TRUE,
|
|
NULL,
|
|
FALSE);
|
|
ASSERT(NT_SUCCESS(Status));
|
|
|
|
/* Create and set the \KnownDlls descriptor */
|
|
SmpKnownDllsSecurityDescriptor = &SmpKnownDllsSDBody;
|
|
Status = RtlCreateSecurityDescriptor(SmpKnownDllsSecurityDescriptor,
|
|
SECURITY_DESCRIPTOR_REVISION);
|
|
ASSERT(NT_SUCCESS(Status));
|
|
Status = RtlSetDaclSecurityDescriptor(SmpKnownDllsSecurityDescriptor,
|
|
TRUE,
|
|
NULL,
|
|
FALSE);
|
|
ASSERT(NT_SUCCESS(Status));
|
|
|
|
/* Create and Set the \ApiPort descriptor */
|
|
SmpApiPortSecurityDescriptor = &SmpApiPortSDBody;
|
|
Status = RtlCreateSecurityDescriptor(SmpApiPortSecurityDescriptor,
|
|
SECURITY_DESCRIPTOR_REVISION);
|
|
ASSERT(NT_SUCCESS(Status));
|
|
Status = RtlSetDaclSecurityDescriptor(SmpApiPortSecurityDescriptor,
|
|
TRUE,
|
|
NULL,
|
|
FALSE);
|
|
ASSERT(NT_SUCCESS(Status));
|
|
}
|
|
|
|
/* Check if protection was requested in the registry (on by default) */
|
|
if (SmpProtectionMode & 1) ProtectionRequired = TRUE;
|
|
|
|
/* Exit if there's nothing to do */
|
|
if (!(InitialCall || ProtectionRequired)) return STATUS_SUCCESS;
|
|
|
|
/* Build the world SID */
|
|
Status = RtlAllocateAndInitializeSid(&WorldAuthority, 1,
|
|
SECURITY_WORLD_RID,
|
|
0, 0, 0, 0, 0, 0, 0,
|
|
&WorldSid);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
WorldSid = NULL;
|
|
goto Quickie;
|
|
}
|
|
|
|
/* Build the admin SID */
|
|
Status = RtlAllocateAndInitializeSid(&NtAuthority, 2,
|
|
SECURITY_BUILTIN_DOMAIN_RID,
|
|
DOMAIN_ALIAS_RID_ADMINS,
|
|
0, 0, 0, 0, 0, 0,
|
|
&AdminSid);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
AdminSid = NULL;
|
|
goto Quickie;
|
|
}
|
|
|
|
/* Build the owner SID */
|
|
Status = RtlAllocateAndInitializeSid(&CreatorAuthority, 1,
|
|
SECURITY_CREATOR_OWNER_RID,
|
|
0, 0, 0, 0, 0, 0, 0,
|
|
&OwnerSid);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
OwnerSid = NULL;
|
|
goto Quickie;
|
|
}
|
|
|
|
/* Build the restricted SID */
|
|
Status = RtlAllocateAndInitializeSid(&NtAuthority, 1,
|
|
SECURITY_RESTRICTED_CODE_RID,
|
|
0, 0, 0, 0, 0, 0, 0,
|
|
&RestrictedSid);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
RestrictedSid = NULL;
|
|
goto Quickie;
|
|
}
|
|
|
|
/* Build the system SID */
|
|
Status = RtlAllocateAndInitializeSid(&NtAuthority, 1,
|
|
SECURITY_LOCAL_SYSTEM_RID,
|
|
0, 0, 0, 0, 0, 0, 0,
|
|
&SystemSid);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
SystemSid = NULL;
|
|
goto Quickie;
|
|
}
|
|
|
|
/* Now check if we're creating the core descriptors */
|
|
if (!InitialCall)
|
|
{
|
|
/* We're skipping NextAcl so we have to do this here */
|
|
SidLength = RtlLengthSid(WorldSid) + RtlLengthSid(RestrictedSid) + RtlLengthSid(AdminSid);
|
|
SidLength *= 2;
|
|
goto NotInitial;
|
|
}
|
|
|
|
/* Allocate an ACL with two ACEs with two SIDs each */
|
|
SidLength = RtlLengthSid(SystemSid) + RtlLengthSid(AdminSid);
|
|
AclLength = sizeof(ACL) + 2 * sizeof(ACCESS_ALLOWED_ACE) + SidLength;
|
|
Acl = RtlAllocateHeap(RtlGetProcessHeap(), 0, AclLength);
|
|
if (!Acl) Status = STATUS_NO_MEMORY;
|
|
if (!NT_SUCCESS(Status)) goto NextAcl;
|
|
|
|
/* Now build the ACL and add the two ACEs */
|
|
Status = RtlCreateAcl(Acl, AclLength, ACL_REVISION2);
|
|
ASSERT(NT_SUCCESS(Status));
|
|
Status = RtlAddAccessAllowedAce(Acl, ACL_REVISION2, GENERIC_ALL, AdminSid);
|
|
ASSERT(NT_SUCCESS(Status));
|
|
Status = RtlAddAccessAllowedAce(Acl, ACL_REVISION2, GENERIC_ALL, SystemSid);
|
|
ASSERT(NT_SUCCESS(Status));
|
|
|
|
/* Set this as the DACL */
|
|
Status = RtlSetDaclSecurityDescriptor(SmpApiPortSecurityDescriptor,
|
|
TRUE,
|
|
Acl,
|
|
FALSE);
|
|
ASSERT(NT_SUCCESS(Status));
|
|
|
|
NextAcl:
|
|
/* Allocate an ACL with 6 ACEs, two ACEs per SID */
|
|
SidLength = RtlLengthSid(WorldSid) + RtlLengthSid(RestrictedSid) + RtlLengthSid(AdminSid);
|
|
SidLength *= 2;
|
|
AclLength = sizeof(ACL) + 6 * sizeof(ACCESS_ALLOWED_ACE) + SidLength;
|
|
Acl = RtlAllocateHeap(RtlGetProcessHeap(), 0, AclLength);
|
|
if (!Acl) Status = STATUS_NO_MEMORY;
|
|
if (!NT_SUCCESS(Status)) goto NotInitial;
|
|
|
|
/* Now build the ACL and add the six ACEs */
|
|
Status = RtlCreateAcl(Acl, AclLength, ACL_REVISION2);
|
|
ASSERT(NT_SUCCESS(Status));
|
|
Status = RtlAddAccessAllowedAce(Acl, ACL_REVISION2, GENERIC_EXECUTE, WorldSid);
|
|
ASSERT(NT_SUCCESS(Status));
|
|
Status = RtlAddAccessAllowedAce(Acl, ACL_REVISION2, GENERIC_EXECUTE, RestrictedSid);
|
|
ASSERT(NT_SUCCESS(Status));
|
|
Status = RtlAddAccessAllowedAce(Acl, ACL_REVISION2, GENERIC_ALL, AdminSid);
|
|
ASSERT(NT_SUCCESS(Status));
|
|
Status = RtlAddAccessAllowedAce(Acl, ACL_REVISION2, GENERIC_EXECUTE | GENERIC_READ | GENERIC_WRITE, WorldSid);
|
|
ASSERT(NT_SUCCESS(Status));
|
|
Status = RtlAddAccessAllowedAce(Acl, ACL_REVISION2, GENERIC_EXECUTE | GENERIC_READ | GENERIC_WRITE, RestrictedSid);
|
|
ASSERT(NT_SUCCESS(Status));
|
|
Status = RtlAddAccessAllowedAce(Acl, ACL_REVISION2, GENERIC_ALL, AdminSid);
|
|
ASSERT(NT_SUCCESS(Status));
|
|
|
|
/* Now edit the last three ACEs and make them inheritable */
|
|
Status = RtlGetAce(Acl, 3, (PVOID)&Ace);
|
|
ASSERT(NT_SUCCESS(Status));
|
|
Ace->AceFlags = OBJECT_INHERIT_ACE | CONTAINER_INHERIT_ACE | INHERIT_ONLY_ACE;
|
|
Status = RtlGetAce(Acl, 4, (PVOID)&Ace);
|
|
ASSERT(NT_SUCCESS(Status));
|
|
Ace->AceFlags = OBJECT_INHERIT_ACE | CONTAINER_INHERIT_ACE | INHERIT_ONLY_ACE;
|
|
Status = RtlGetAce(Acl, 5, (PVOID)&Ace);
|
|
ASSERT(NT_SUCCESS(Status));
|
|
Ace->AceFlags = OBJECT_INHERIT_ACE | CONTAINER_INHERIT_ACE | INHERIT_ONLY_ACE;
|
|
|
|
/* Set this as the DACL */
|
|
Status = RtlSetDaclSecurityDescriptor(SmpKnownDllsSecurityDescriptor,
|
|
TRUE,
|
|
Acl,
|
|
FALSE);
|
|
ASSERT(NT_SUCCESS(Status));
|
|
|
|
NotInitial:
|
|
/* The initial ACLs have been created, are we also protecting objects? */
|
|
if (!ProtectionRequired) goto Quickie;
|
|
|
|
/* Allocate an ACL with 7 ACEs, two ACEs per SID, and one final owner ACE */
|
|
SidLength += RtlLengthSid(OwnerSid);
|
|
AclLength = sizeof(ACL) + 7 * sizeof (ACCESS_ALLOWED_ACE) + 2 * SidLength;
|
|
Acl = RtlAllocateHeap(RtlGetProcessHeap(), 0, AclLength);
|
|
if (!Acl) Status = STATUS_NO_MEMORY;
|
|
if (!NT_SUCCESS(Status)) goto Quickie;
|
|
|
|
/* Build the ACL and add the seven ACEs */
|
|
Status = RtlCreateAcl(Acl, AclLength, ACL_REVISION2);
|
|
ASSERT(NT_SUCCESS(Status));
|
|
Status = RtlAddAccessAllowedAce(Acl, ACL_REVISION2, GENERIC_EXECUTE | GENERIC_READ, WorldSid);
|
|
ASSERT(NT_SUCCESS(Status));
|
|
Status = RtlAddAccessAllowedAce(Acl, ACL_REVISION2, GENERIC_EXECUTE | GENERIC_READ, RestrictedSid);
|
|
ASSERT(NT_SUCCESS(Status));
|
|
Status = RtlAddAccessAllowedAce(Acl, ACL_REVISION2, GENERIC_ALL, AdminSid);
|
|
ASSERT(NT_SUCCESS(Status));
|
|
Status = RtlAddAccessAllowedAce(Acl, ACL_REVISION2, GENERIC_EXECUTE | GENERIC_READ, WorldSid);
|
|
ASSERT(NT_SUCCESS(Status));
|
|
Status = RtlAddAccessAllowedAce(Acl, ACL_REVISION2, GENERIC_EXECUTE | GENERIC_READ, RestrictedSid);
|
|
ASSERT(NT_SUCCESS(Status));
|
|
Status = RtlAddAccessAllowedAce(Acl, ACL_REVISION2, GENERIC_ALL, AdminSid);
|
|
ASSERT(NT_SUCCESS(Status));
|
|
Status = RtlAddAccessAllowedAce(Acl, ACL_REVISION2, GENERIC_ALL, OwnerSid);
|
|
ASSERT(NT_SUCCESS(Status));
|
|
|
|
/* Edit the last 4 ACEs to make then inheritable */
|
|
Status = RtlGetAce(Acl, 3, (PVOID)&Ace);
|
|
ASSERT(NT_SUCCESS(Status));
|
|
Ace->AceFlags = OBJECT_INHERIT_ACE | CONTAINER_INHERIT_ACE | INHERIT_ONLY_ACE;
|
|
Status = RtlGetAce(Acl, 4, (PVOID)&Ace);
|
|
ASSERT(NT_SUCCESS(Status));
|
|
Ace->AceFlags = OBJECT_INHERIT_ACE | CONTAINER_INHERIT_ACE | INHERIT_ONLY_ACE;
|
|
Status = RtlGetAce(Acl, 5, (PVOID)&Ace);
|
|
ASSERT(NT_SUCCESS(Status));
|
|
Ace->AceFlags = OBJECT_INHERIT_ACE | CONTAINER_INHERIT_ACE | INHERIT_ONLY_ACE;
|
|
Status = RtlGetAce(Acl, 6, (PVOID)&Ace);
|
|
ASSERT(NT_SUCCESS(Status));
|
|
Ace->AceFlags = OBJECT_INHERIT_ACE | CONTAINER_INHERIT_ACE | INHERIT_ONLY_ACE;
|
|
|
|
/* Set this as the DACL for the primary SD */
|
|
Status = RtlSetDaclSecurityDescriptor(SmpPrimarySecurityDescriptor,
|
|
TRUE,
|
|
Acl,
|
|
FALSE);
|
|
ASSERT(NT_SUCCESS(Status));
|
|
|
|
/* Allocate an ACL with 7 ACEs, two ACEs per SID, and one final owner ACE */
|
|
AclLength = sizeof(ACL) + 7 * sizeof (ACCESS_ALLOWED_ACE) + 2 * SidLength;
|
|
Acl = RtlAllocateHeap(RtlGetProcessHeap(), 0, AclLength);
|
|
if (!Acl) Status = STATUS_NO_MEMORY;
|
|
if (!NT_SUCCESS(Status)) goto Quickie;
|
|
|
|
/* Build the ACL and add the seven ACEs */
|
|
Status = RtlCreateAcl(Acl, AclLength, ACL_REVISION2);
|
|
ASSERT(NT_SUCCESS(Status));
|
|
Status = RtlAddAccessAllowedAce(Acl, ACL_REVISION2, GENERIC_EXECUTE | GENERIC_READ | GENERIC_WRITE, WorldSid);
|
|
ASSERT(NT_SUCCESS(Status));
|
|
Status = RtlAddAccessAllowedAce(Acl, ACL_REVISION2, GENERIC_EXECUTE | GENERIC_READ | GENERIC_WRITE, RestrictedSid);
|
|
ASSERT(NT_SUCCESS(Status));
|
|
Status = RtlAddAccessAllowedAce(Acl, ACL_REVISION2, GENERIC_ALL, AdminSid);
|
|
ASSERT(NT_SUCCESS(Status));
|
|
Status = RtlAddAccessAllowedAce(Acl, ACL_REVISION2, GENERIC_EXECUTE | GENERIC_READ | GENERIC_WRITE, WorldSid);
|
|
ASSERT(NT_SUCCESS(Status));
|
|
Status = RtlAddAccessAllowedAce(Acl, ACL_REVISION2, GENERIC_EXECUTE | GENERIC_READ | GENERIC_WRITE, RestrictedSid);
|
|
ASSERT(NT_SUCCESS(Status));
|
|
Status = RtlAddAccessAllowedAce(Acl, ACL_REVISION2, GENERIC_ALL, AdminSid);
|
|
ASSERT(NT_SUCCESS(Status));
|
|
Status = RtlAddAccessAllowedAce(Acl, ACL_REVISION2, GENERIC_ALL, OwnerSid);
|
|
ASSERT(NT_SUCCESS(Status));
|
|
|
|
/* Edit the last 4 ACEs to make then inheritable */
|
|
Status = RtlGetAce(Acl, 3, (PVOID)&Ace);
|
|
ASSERT(NT_SUCCESS(Status));
|
|
Ace->AceFlags = OBJECT_INHERIT_ACE | CONTAINER_INHERIT_ACE | INHERIT_ONLY_ACE;
|
|
Status = RtlGetAce(Acl, 4, (PVOID)&Ace);
|
|
ASSERT(NT_SUCCESS(Status));
|
|
Ace->AceFlags = OBJECT_INHERIT_ACE | CONTAINER_INHERIT_ACE | INHERIT_ONLY_ACE;
|
|
Status = RtlGetAce(Acl, 5, (PVOID)&Ace);
|
|
ASSERT(NT_SUCCESS(Status));
|
|
Ace->AceFlags = OBJECT_INHERIT_ACE | CONTAINER_INHERIT_ACE | INHERIT_ONLY_ACE;
|
|
Status = RtlGetAce(Acl, 6, (PVOID)&Ace);
|
|
ASSERT(NT_SUCCESS(Status));
|
|
Ace->AceFlags = OBJECT_INHERIT_ACE | CONTAINER_INHERIT_ACE | INHERIT_ONLY_ACE;
|
|
|
|
/* Now set this as the DACL for the liberal SD */
|
|
Status = RtlSetDaclSecurityDescriptor(SmpLiberalSecurityDescriptor,
|
|
TRUE,
|
|
Acl,
|
|
FALSE);
|
|
ASSERT(NT_SUCCESS(Status));
|
|
|
|
Quickie:
|
|
/* Cleanup the SIDs */
|
|
if (OwnerSid) RtlFreeHeap(RtlGetProcessHeap(), 0, OwnerSid);
|
|
if (AdminSid) RtlFreeHeap(RtlGetProcessHeap(), 0, AdminSid);
|
|
if (WorldSid) RtlFreeHeap(RtlGetProcessHeap(), 0, WorldSid);
|
|
if (SystemSid) RtlFreeHeap(RtlGetProcessHeap(), 0, SystemSid);
|
|
if (RestrictedSid) RtlFreeHeap(RtlGetProcessHeap(), 0, RestrictedSid);
|
|
return Status;
|
|
}
|
|
|
|
NTSTATUS
|
|
NTAPI
|
|
SmpInitializeDosDevices(VOID)
|
|
{
|
|
NTSTATUS Status;
|
|
PSMP_REGISTRY_VALUE RegEntry;
|
|
SECURITY_DESCRIPTOR_CONTROL OldFlag = 0;
|
|
OBJECT_ATTRIBUTES ObjectAttributes;
|
|
UNICODE_STRING DestinationString;
|
|
HANDLE DirHandle;
|
|
PLIST_ENTRY NextEntry, Head;
|
|
|
|
/* Open the GLOBAL?? directory */
|
|
RtlInitUnicodeString(&DestinationString, L"\\??");
|
|
InitializeObjectAttributes(&ObjectAttributes,
|
|
&DestinationString,
|
|
OBJ_CASE_INSENSITIVE | OBJ_OPENIF | OBJ_PERMANENT,
|
|
NULL,
|
|
NULL);
|
|
Status = NtOpenDirectoryObject(&SmpDosDevicesObjectDirectory,
|
|
DIRECTORY_ALL_ACCESS,
|
|
&ObjectAttributes);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
DPRINT1("SMSS: Unable to open %wZ directory - Status == %lx\n",
|
|
&DestinationString, Status);
|
|
return Status;
|
|
}
|
|
|
|
/* Loop the DOS devices */
|
|
Head = &SmpDosDevicesList;
|
|
while (!IsListEmpty(Head))
|
|
{
|
|
/* Get the entry and remove it */
|
|
NextEntry = RemoveHeadList(Head);
|
|
RegEntry = CONTAINING_RECORD(NextEntry, SMP_REGISTRY_VALUE, Entry);
|
|
|
|
/* Initialize the attributes, and see which descriptor is being used */
|
|
InitializeObjectAttributes(&ObjectAttributes,
|
|
&RegEntry->Name,
|
|
OBJ_CASE_INSENSITIVE | OBJ_OPENIF | OBJ_PERMANENT,
|
|
SmpDosDevicesObjectDirectory,
|
|
SmpPrimarySecurityDescriptor);
|
|
if (SmpPrimarySecurityDescriptor)
|
|
{
|
|
/* Save the old flag and set it while we create this link */
|
|
OldFlag = SmpPrimarySecurityDescriptor->Control;
|
|
SmpPrimarySecurityDescriptor->Control |= SE_DACL_DEFAULTED;
|
|
}
|
|
|
|
/* Create the symbolic link */
|
|
DPRINT("Creating symlink for %wZ to %wZ\n", &RegEntry->Name, &RegEntry->Value);
|
|
Status = NtCreateSymbolicLinkObject(&DirHandle,
|
|
SYMBOLIC_LINK_ALL_ACCESS,
|
|
&ObjectAttributes,
|
|
&RegEntry->Value);
|
|
if (Status == STATUS_OBJECT_NAME_EXISTS)
|
|
{
|
|
/* Make it temporary and get rid of the handle */
|
|
NtMakeTemporaryObject(DirHandle);
|
|
NtClose(DirHandle);
|
|
|
|
/* Treat this as success, and see if we got a name back */
|
|
Status = STATUS_SUCCESS;
|
|
if (RegEntry->Value.Length)
|
|
{
|
|
/* Create it now with this name */
|
|
ObjectAttributes.Attributes &= ~OBJ_OPENIF;
|
|
Status = NtCreateSymbolicLinkObject(&DirHandle,
|
|
SYMBOLIC_LINK_ALL_ACCESS,
|
|
&ObjectAttributes,
|
|
&RegEntry->Value);
|
|
}
|
|
}
|
|
|
|
/* If we were using a security descriptor, restore the non-defaulted flag */
|
|
if (ObjectAttributes.SecurityDescriptor)
|
|
{
|
|
SmpPrimarySecurityDescriptor->Control = OldFlag;
|
|
}
|
|
|
|
/* Print a failure if we failed to create the symbolic link */
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
DPRINT1("SMSS: Unable to create %wZ => %wZ symbolic link object - Status == 0x%lx\n",
|
|
&RegEntry->Name,
|
|
&RegEntry->Value,
|
|
Status);
|
|
break;
|
|
}
|
|
|
|
/* Close the handle */
|
|
NtClose(DirHandle);
|
|
|
|
/* Free this entry */
|
|
if (RegEntry->AnsiValue) RtlFreeHeap(RtlGetProcessHeap(), 0, RegEntry->AnsiValue);
|
|
if (RegEntry->Value.Buffer) RtlFreeHeap(RtlGetProcessHeap(), 0, RegEntry->Value.Buffer);
|
|
RtlFreeHeap(RtlGetProcessHeap(), 0, RegEntry);
|
|
}
|
|
|
|
/* Return the status */
|
|
return Status;
|
|
}
|
|
|
|
VOID
|
|
NTAPI
|
|
SmpProcessModuleImports(IN PVOID Unused,
|
|
IN PCHAR ImportName)
|
|
{
|
|
ULONG Length = 0;
|
|
WCHAR Buffer[MAX_PATH];
|
|
PWCHAR DllName, DllValue;
|
|
ANSI_STRING ImportString;
|
|
UNICODE_STRING ImportUnicodeString;
|
|
NTSTATUS Status;
|
|
|
|
/* Skip NTDLL since it's already always mapped */
|
|
if (!_stricmp(ImportName, "ntdll.dll")) return;
|
|
|
|
/* Initialize our strings */
|
|
RtlInitAnsiString(&ImportString, ImportName);
|
|
RtlInitEmptyUnicodeString(&ImportUnicodeString, Buffer, sizeof(Buffer));
|
|
Status = RtlAnsiStringToUnicodeString(&ImportUnicodeString, &ImportString, FALSE);
|
|
if (!NT_SUCCESS(Status)) return;
|
|
|
|
/* Loop to find the DLL file extension */
|
|
while (Length < ImportUnicodeString.Length)
|
|
{
|
|
if (ImportUnicodeString.Buffer[Length / sizeof(WCHAR)] == L'.') break;
|
|
Length += sizeof(WCHAR);
|
|
}
|
|
|
|
/*
|
|
* Break up the values as needed; the buffer acquires the form:
|
|
* "dll_name.dll\0dll_name\0"
|
|
*/
|
|
DllValue = ImportUnicodeString.Buffer;
|
|
DllName = &ImportUnicodeString.Buffer[(ImportUnicodeString.Length + sizeof(UNICODE_NULL)) / sizeof(WCHAR)];
|
|
RtlStringCbCopyNW(DllName,
|
|
ImportUnicodeString.MaximumLength - (ImportUnicodeString.Length + sizeof(UNICODE_NULL)),
|
|
ImportUnicodeString.Buffer, Length);
|
|
|
|
/* Add the DLL to the list */
|
|
SmpSaveRegistryValue(&SmpKnownDllsList, DllName, DllValue, TRUE);
|
|
}
|
|
|
|
NTSTATUS
|
|
NTAPI
|
|
SmpInitializeKnownDllsInternal(IN PUNICODE_STRING Directory,
|
|
IN PUNICODE_STRING Path)
|
|
{
|
|
HANDLE DirFileHandle, DirHandle, SectionHandle, FileHandle, LinkHandle;
|
|
UNICODE_STRING NtPath, DestinationString;
|
|
OBJECT_ATTRIBUTES ObjectAttributes;
|
|
NTSTATUS Status, Status1;
|
|
PLIST_ENTRY NextEntry;
|
|
PSMP_REGISTRY_VALUE RegEntry;
|
|
ULONG_PTR ErrorParameters[3];
|
|
UNICODE_STRING ErrorResponse;
|
|
IO_STATUS_BLOCK IoStatusBlock;
|
|
SECURITY_DESCRIPTOR_CONTROL OldFlag = 0;
|
|
USHORT ImageCharacteristics;
|
|
|
|
/* Initialize to NULL */
|
|
DirFileHandle = NULL;
|
|
DirHandle = NULL;
|
|
NtPath.Buffer = NULL;
|
|
|
|
/* Create the \KnownDLLs directory */
|
|
InitializeObjectAttributes(&ObjectAttributes,
|
|
Directory,
|
|
OBJ_CASE_INSENSITIVE | OBJ_OPENIF | OBJ_PERMANENT,
|
|
NULL,
|
|
SmpKnownDllsSecurityDescriptor);
|
|
Status = NtCreateDirectoryObject(&DirHandle,
|
|
DIRECTORY_ALL_ACCESS,
|
|
&ObjectAttributes);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
/* Handle failure */
|
|
DPRINT1("SMSS: Unable to create %wZ directory - Status == %lx\n",
|
|
Directory, Status);
|
|
return Status;
|
|
}
|
|
|
|
/* Convert the path to native format */
|
|
if (!RtlDosPathNameToNtPathName_U(Path->Buffer, &NtPath, NULL, NULL))
|
|
{
|
|
/* Fail if this didn't work */
|
|
DPRINT1("SMSS: Unable to to convert %wZ to an Nt path\n", Path);
|
|
Status = STATUS_OBJECT_NAME_INVALID;
|
|
goto Quickie;
|
|
}
|
|
|
|
/* Open the path that was specified, which should be a directory */
|
|
InitializeObjectAttributes(&ObjectAttributes,
|
|
&NtPath,
|
|
OBJ_CASE_INSENSITIVE,
|
|
NULL,
|
|
NULL);
|
|
Status = NtOpenFile(&DirFileHandle,
|
|
FILE_LIST_DIRECTORY | SYNCHRONIZE,
|
|
&ObjectAttributes,
|
|
&IoStatusBlock,
|
|
FILE_SHARE_READ | FILE_SHARE_WRITE,
|
|
FILE_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
/* Fail if we couldn't open it */
|
|
DPRINT1("SMSS: Unable to open a handle to the KnownDll directory (%wZ)"
|
|
"- Status == %lx\n",
|
|
Path,
|
|
Status);
|
|
FileHandle = NULL;
|
|
goto Quickie;
|
|
}
|
|
|
|
/* Temporarily hack the SD to use a default DACL for this symbolic link */
|
|
if (SmpPrimarySecurityDescriptor)
|
|
{
|
|
OldFlag = SmpPrimarySecurityDescriptor->Control;
|
|
SmpPrimarySecurityDescriptor->Control |= SE_DACL_DEFAULTED;
|
|
}
|
|
|
|
/* Create a symbolic link to the directory in the object manager */
|
|
RtlInitUnicodeString(&DestinationString, L"KnownDllPath");
|
|
InitializeObjectAttributes(&ObjectAttributes,
|
|
&DestinationString,
|
|
OBJ_CASE_INSENSITIVE | OBJ_OPENIF | OBJ_PERMANENT,
|
|
DirHandle,
|
|
SmpPrimarySecurityDescriptor);
|
|
Status = NtCreateSymbolicLinkObject(&LinkHandle,
|
|
SYMBOLIC_LINK_ALL_ACCESS,
|
|
&ObjectAttributes,
|
|
Path);
|
|
|
|
/* Undo the hack */
|
|
if (SmpPrimarySecurityDescriptor) SmpPrimarySecurityDescriptor->Control = OldFlag;
|
|
|
|
/* Check if the symlink was created */
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
/* It wasn't, so bail out since the OS needs it to exist */
|
|
DPRINT1("SMSS: Unable to create %wZ symbolic link - Status == %lx\n",
|
|
&DestinationString, Status);
|
|
LinkHandle = NULL;
|
|
goto Quickie;
|
|
}
|
|
|
|
/* We created it permanent, we can go ahead and close the handle now */
|
|
Status1 = NtClose(LinkHandle);
|
|
ASSERT(NT_SUCCESS(Status1));
|
|
|
|
/* Now loop the known DLLs */
|
|
NextEntry = SmpKnownDllsList.Flink;
|
|
while (NextEntry != &SmpKnownDllsList)
|
|
{
|
|
/* Get the entry and move on */
|
|
RegEntry = CONTAINING_RECORD(NextEntry, SMP_REGISTRY_VALUE, Entry);
|
|
NextEntry = NextEntry->Flink;
|
|
|
|
DPRINT("Processing known DLL: %wZ-%wZ\n", &RegEntry->Name, &RegEntry->Value);
|
|
|
|
/* Skip the entry if it's in the excluded list */
|
|
if ((SmpFindRegistryValue(&SmpExcludeKnownDllsList,
|
|
RegEntry->Name.Buffer)) ||
|
|
(SmpFindRegistryValue(&SmpExcludeKnownDllsList,
|
|
RegEntry->Value.Buffer)))
|
|
{
|
|
continue;
|
|
}
|
|
|
|
/* Open the actual file */
|
|
InitializeObjectAttributes(&ObjectAttributes,
|
|
&RegEntry->Value,
|
|
OBJ_CASE_INSENSITIVE,
|
|
DirFileHandle,
|
|
NULL);
|
|
Status1 = NtOpenFile(&FileHandle,
|
|
SYNCHRONIZE | FILE_EXECUTE,
|
|
&ObjectAttributes,
|
|
&IoStatusBlock,
|
|
FILE_SHARE_READ | FILE_SHARE_DELETE,
|
|
FILE_NON_DIRECTORY_FILE |
|
|
FILE_SYNCHRONOUS_IO_NONALERT);
|
|
/* If we failed, skip it */
|
|
if (!NT_SUCCESS(Status1)) continue;
|
|
|
|
/* Checksum it */
|
|
Status = LdrVerifyImageMatchesChecksum((HANDLE)((ULONG_PTR)FileHandle | 1),
|
|
SmpProcessModuleImports,
|
|
RegEntry,
|
|
&ImageCharacteristics);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
/* Checksum failed, so don't even try going further -- kill SMSS */
|
|
RtlInitUnicodeString(&ErrorResponse,
|
|
L"Verification of a KnownDLL failed.");
|
|
ErrorParameters[0] = (ULONG_PTR)&ErrorResponse;
|
|
ErrorParameters[1] = Status;
|
|
ErrorParameters[2] = (ULONG_PTR)&RegEntry->Value;
|
|
SmpTerminate(ErrorParameters, 5, RTL_NUMBER_OF(ErrorParameters));
|
|
}
|
|
else if (!(ImageCharacteristics & IMAGE_FILE_DLL))
|
|
{
|
|
/* An invalid known DLL entry will also kill SMSS */
|
|
RtlInitUnicodeString(&ErrorResponse,
|
|
L"Non-DLL file included in KnownDLL list.");
|
|
ErrorParameters[0] = (ULONG_PTR)&ErrorResponse;
|
|
ErrorParameters[1] = STATUS_INVALID_IMPORT_OF_NON_DLL;
|
|
ErrorParameters[2] = (ULONG_PTR)&RegEntry->Value;
|
|
SmpTerminate(ErrorParameters, 5, RTL_NUMBER_OF(ErrorParameters));
|
|
}
|
|
|
|
/* Temporarily hack the SD to use a default DACL for this section */
|
|
if (SmpLiberalSecurityDescriptor)
|
|
{
|
|
OldFlag = SmpLiberalSecurityDescriptor->Control;
|
|
SmpLiberalSecurityDescriptor->Control |= SE_DACL_DEFAULTED;
|
|
}
|
|
|
|
/* Create the section for this known DLL */
|
|
InitializeObjectAttributes(&ObjectAttributes,
|
|
&RegEntry->Value,
|
|
OBJ_PERMANENT,
|
|
DirHandle,
|
|
SmpLiberalSecurityDescriptor)
|
|
Status = NtCreateSection(&SectionHandle,
|
|
SECTION_ALL_ACCESS,
|
|
&ObjectAttributes,
|
|
0,
|
|
PAGE_EXECUTE,
|
|
SEC_IMAGE,
|
|
FileHandle);
|
|
|
|
/* Undo the hack */
|
|
if (SmpLiberalSecurityDescriptor) SmpLiberalSecurityDescriptor->Control = OldFlag;
|
|
|
|
/* Check if we created the section okay */
|
|
if (NT_SUCCESS(Status))
|
|
{
|
|
/* We can close it now, since it's marked permanent */
|
|
Status1 = NtClose(SectionHandle);
|
|
ASSERT(NT_SUCCESS(Status1));
|
|
}
|
|
else
|
|
{
|
|
/* If we couldn't make it "known", that's fine and keep going */
|
|
DPRINT1("SMSS: CreateSection for KnownDll %wZ failed - Status == %lx\n",
|
|
&RegEntry->Value, Status);
|
|
}
|
|
|
|
/* Close the file since we can move on to the next one */
|
|
Status1 = NtClose(FileHandle);
|
|
ASSERT(NT_SUCCESS(Status1));
|
|
}
|
|
|
|
Quickie:
|
|
/* Close both handles and free the NT path buffer */
|
|
if (DirHandle)
|
|
{
|
|
Status1 = NtClose(DirHandle);
|
|
ASSERT(NT_SUCCESS(Status1));
|
|
}
|
|
if (DirFileHandle)
|
|
{
|
|
Status1 = NtClose(DirFileHandle);
|
|
ASSERT(NT_SUCCESS(Status1));
|
|
}
|
|
if (NtPath.Buffer) RtlFreeHeap(RtlGetProcessHeap(), 0, NtPath.Buffer);
|
|
return Status;
|
|
}
|
|
|
|
NTSTATUS
|
|
NTAPI
|
|
SmpInitializeKnownDlls(VOID)
|
|
{
|
|
NTSTATUS Status;
|
|
PSMP_REGISTRY_VALUE RegEntry;
|
|
UNICODE_STRING DestinationString;
|
|
PLIST_ENTRY Head, NextEntry;
|
|
|
|
/* Call the internal function */
|
|
RtlInitUnicodeString(&DestinationString, L"\\KnownDlls");
|
|
Status = SmpInitializeKnownDllsInternal(&DestinationString, &SmpKnownDllPath);
|
|
|
|
/* Wipe out the list regardless of success */
|
|
Head = &SmpKnownDllsList;
|
|
while (!IsListEmpty(Head))
|
|
{
|
|
/* Remove this entry */
|
|
NextEntry = RemoveHeadList(Head);
|
|
|
|
/* Free it */
|
|
RegEntry = CONTAINING_RECORD(NextEntry, SMP_REGISTRY_VALUE, Entry);
|
|
RtlFreeHeap(RtlGetProcessHeap(), 0, RegEntry->AnsiValue);
|
|
RtlFreeHeap(RtlGetProcessHeap(), 0, RegEntry->Value.Buffer);
|
|
RtlFreeHeap(RtlGetProcessHeap(), 0, RegEntry);
|
|
}
|
|
|
|
/* All done */
|
|
return Status;
|
|
}
|
|
|
|
NTSTATUS
|
|
NTAPI
|
|
SmpCreateDynamicEnvironmentVariables(VOID)
|
|
{
|
|
NTSTATUS Status;
|
|
SYSTEM_BASIC_INFORMATION BasicInfo;
|
|
SYSTEM_PROCESSOR_INFORMATION ProcessorInfo;
|
|
OBJECT_ATTRIBUTES ObjectAttributes;
|
|
UNICODE_STRING ValueName, DestinationString;
|
|
HANDLE KeyHandle, KeyHandle2;
|
|
PWCHAR ValueData;
|
|
ULONG ResultLength;
|
|
size_t StrLength;
|
|
WCHAR ValueBuffer[sizeof(KEY_VALUE_PARTIAL_INFORMATION) + 512];
|
|
WCHAR ValueBuffer2[sizeof(KEY_VALUE_PARTIAL_INFORMATION) + 512];
|
|
PKEY_VALUE_PARTIAL_INFORMATION PartialInfo = (PVOID)ValueBuffer;
|
|
PKEY_VALUE_PARTIAL_INFORMATION PartialInfo2 = (PVOID)ValueBuffer2;
|
|
|
|
/* Get system basic information -- we'll need the CPU count */
|
|
Status = NtQuerySystemInformation(SystemBasicInformation,
|
|
&BasicInfo,
|
|
sizeof(BasicInfo),
|
|
NULL);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
/* Bail out on failure */
|
|
DPRINT1("SMSS: Unable to query system basic information - %x\n", Status);
|
|
return Status;
|
|
}
|
|
|
|
/* Get the processor information, we'll query a bunch of revision info */
|
|
Status = NtQuerySystemInformation(SystemProcessorInformation,
|
|
&ProcessorInfo,
|
|
sizeof(ProcessorInfo),
|
|
NULL);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
/* Bail out on failure */
|
|
DPRINT1("SMSS: Unable to query system processor information - %x\n", Status);
|
|
return Status;
|
|
}
|
|
|
|
/* We'll be writing all these environment variables over here */
|
|
RtlInitUnicodeString(&DestinationString,
|
|
L"\\Registry\\Machine\\System\\CurrentControlSet\\"
|
|
L"Control\\Session Manager\\Environment");
|
|
InitializeObjectAttributes(&ObjectAttributes,
|
|
&DestinationString,
|
|
OBJ_CASE_INSENSITIVE,
|
|
NULL,
|
|
NULL);
|
|
Status = NtOpenKey(&KeyHandle, GENERIC_WRITE, &ObjectAttributes);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
/* Bail out on failure */
|
|
DPRINT1("SMSS: Unable to open %wZ - %x\n", &DestinationString, Status);
|
|
return Status;
|
|
}
|
|
|
|
/* First let's write the OS variable */
|
|
RtlInitUnicodeString(&ValueName, L"OS");
|
|
ValueData = L"Windows_NT";
|
|
DPRINT("Setting %wZ to %S\n", &ValueName, ValueData);
|
|
Status = NtSetValueKey(KeyHandle,
|
|
&ValueName,
|
|
0,
|
|
REG_SZ,
|
|
ValueData,
|
|
(wcslen(ValueData) + 1) * sizeof(WCHAR));
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
DPRINT1("SMSS: Failed writing %wZ environment variable - %x\n",
|
|
&ValueName, Status);
|
|
NtClose(KeyHandle);
|
|
return Status;
|
|
}
|
|
|
|
/* Next, let's write the CPU architecture variable */
|
|
RtlInitUnicodeString(&ValueName, L"PROCESSOR_ARCHITECTURE");
|
|
switch (ProcessorInfo.ProcessorArchitecture)
|
|
{
|
|
/* Pick the correct string that matches the architecture */
|
|
case PROCESSOR_ARCHITECTURE_INTEL:
|
|
ValueData = L"x86";
|
|
break;
|
|
|
|
case PROCESSOR_ARCHITECTURE_AMD64:
|
|
ValueData = L"AMD64";
|
|
break;
|
|
|
|
case PROCESSOR_ARCHITECTURE_IA64:
|
|
ValueData = L"IA64";
|
|
break;
|
|
|
|
default:
|
|
ValueData = L"Unknown";
|
|
break;
|
|
}
|
|
|
|
/* Set it */
|
|
DPRINT("Setting %wZ to %S\n", &ValueName, ValueData);
|
|
Status = NtSetValueKey(KeyHandle,
|
|
&ValueName,
|
|
0,
|
|
REG_SZ,
|
|
ValueData,
|
|
(wcslen(ValueData) + 1) * sizeof(WCHAR));
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
DPRINT1("SMSS: Failed writing %wZ environment variable - %x\n",
|
|
&ValueName, Status);
|
|
NtClose(KeyHandle);
|
|
return Status;
|
|
}
|
|
|
|
/* And now let's write the processor level */
|
|
RtlInitUnicodeString(&ValueName, L"PROCESSOR_LEVEL");
|
|
swprintf(ValueBuffer, L"%u", ProcessorInfo.ProcessorLevel);
|
|
DPRINT("Setting %wZ to %S\n", &ValueName, ValueBuffer);
|
|
Status = NtSetValueKey(KeyHandle,
|
|
&ValueName,
|
|
0,
|
|
REG_SZ,
|
|
ValueBuffer,
|
|
(wcslen(ValueBuffer) + 1) * sizeof(WCHAR));
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
DPRINT1("SMSS: Failed writing %wZ environment variable - %x\n",
|
|
&ValueName, Status);
|
|
NtClose(KeyHandle);
|
|
return Status;
|
|
}
|
|
|
|
/* Now open the hardware CPU key */
|
|
RtlInitUnicodeString(&DestinationString,
|
|
L"\\Registry\\Machine\\Hardware\\Description\\System\\"
|
|
L"CentralProcessor\\0");
|
|
InitializeObjectAttributes(&ObjectAttributes,
|
|
&DestinationString,
|
|
OBJ_CASE_INSENSITIVE,
|
|
NULL,
|
|
NULL);
|
|
Status = NtOpenKey(&KeyHandle2, KEY_READ, &ObjectAttributes);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
DPRINT1("SMSS: Unable to open %wZ - %x\n", &DestinationString, Status);
|
|
NtClose(KeyHandle);
|
|
return Status;
|
|
}
|
|
|
|
/* So that we can read the identifier out of it... */
|
|
RtlInitUnicodeString(&ValueName, L"Identifier");
|
|
Status = NtQueryValueKey(KeyHandle2,
|
|
&ValueName,
|
|
KeyValuePartialInformation,
|
|
PartialInfo,
|
|
sizeof(ValueBuffer),
|
|
&ResultLength);
|
|
if (!NT_SUCCESS(Status) ||
|
|
((PartialInfo->Type != REG_SZ) && (PartialInfo->Type != REG_EXPAND_SZ)))
|
|
{
|
|
NtClose(KeyHandle2);
|
|
NtClose(KeyHandle);
|
|
DPRINT1("SMSS: Unable to read %wZ\\%wZ (Type %lu, Status 0x%x)\n",
|
|
&DestinationString, &ValueName, PartialInfo->Type, Status);
|
|
return Status;
|
|
}
|
|
|
|
/* Initialize the string so that it can be large enough
|
|
* to contain both the identifier and the vendor strings. */
|
|
RtlInitEmptyUnicodeString(&DestinationString,
|
|
(PWCHAR)PartialInfo->Data,
|
|
sizeof(ValueBuffer) -
|
|
FIELD_OFFSET(KEY_VALUE_PARTIAL_INFORMATION, Data));
|
|
RtlStringCbLengthW(DestinationString.Buffer,
|
|
PartialInfo->DataLength,
|
|
&StrLength);
|
|
DestinationString.Length = (USHORT)StrLength;
|
|
|
|
/* As well as the vendor... */
|
|
RtlInitUnicodeString(&ValueName, L"VendorIdentifier");
|
|
Status = NtQueryValueKey(KeyHandle2,
|
|
&ValueName,
|
|
KeyValuePartialInformation,
|
|
PartialInfo2,
|
|
sizeof(ValueBuffer2),
|
|
&ResultLength);
|
|
NtClose(KeyHandle2);
|
|
if (NT_SUCCESS(Status) &&
|
|
((PartialInfo2->Type == REG_SZ) || (PartialInfo2->Type == REG_EXPAND_SZ)))
|
|
{
|
|
/* To combine it into a single string */
|
|
RtlStringCbPrintfW(DestinationString.Buffer + DestinationString.Length / sizeof(WCHAR),
|
|
DestinationString.MaximumLength - DestinationString.Length,
|
|
L", %.*s",
|
|
PartialInfo2->DataLength / sizeof(WCHAR),
|
|
(PWCHAR)PartialInfo2->Data);
|
|
DestinationString.Length = (USHORT)(wcslen(DestinationString.Buffer) * sizeof(WCHAR));
|
|
}
|
|
|
|
/* So that we can set this as the PROCESSOR_IDENTIFIER variable */
|
|
RtlInitUnicodeString(&ValueName, L"PROCESSOR_IDENTIFIER");
|
|
DPRINT("Setting %wZ to %wZ\n", &ValueName, &DestinationString);
|
|
Status = NtSetValueKey(KeyHandle,
|
|
&ValueName,
|
|
0,
|
|
REG_SZ,
|
|
DestinationString.Buffer,
|
|
DestinationString.Length + sizeof(UNICODE_NULL));
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
DPRINT1("SMSS: Failed writing %wZ environment variable - %x\n",
|
|
&ValueName, Status);
|
|
NtClose(KeyHandle);
|
|
return Status;
|
|
}
|
|
|
|
/* Now let's get the processor architecture */
|
|
RtlInitUnicodeString(&ValueName, L"PROCESSOR_REVISION");
|
|
switch (ProcessorInfo.ProcessorArchitecture)
|
|
{
|
|
/* Check if this is an older Intel CPU */
|
|
case PROCESSOR_ARCHITECTURE_INTEL:
|
|
if ((ProcessorInfo.ProcessorRevision >> 8) == 0xFF)
|
|
{
|
|
/* These guys used a revision + stepping, so get the rev only */
|
|
swprintf(ValueBuffer, L"%02x", ProcessorInfo.ProcessorRevision & 0xFF);
|
|
_wcsupr(ValueBuffer);
|
|
break;
|
|
}
|
|
|
|
/* Modern Intel, as well as 64-bit CPUs use a revision without stepping */
|
|
case PROCESSOR_ARCHITECTURE_IA64:
|
|
case PROCESSOR_ARCHITECTURE_AMD64:
|
|
swprintf(ValueBuffer, L"%04x", ProcessorInfo.ProcessorRevision);
|
|
break;
|
|
|
|
/* And anything else we'll just read the whole revision identifier */
|
|
default:
|
|
swprintf(ValueBuffer, L"%u", ProcessorInfo.ProcessorRevision);
|
|
break;
|
|
}
|
|
|
|
/* Write the revision to the registry */
|
|
DPRINT("Setting %wZ to %S\n", &ValueName, ValueBuffer);
|
|
Status = NtSetValueKey(KeyHandle,
|
|
&ValueName,
|
|
0,
|
|
REG_SZ,
|
|
ValueBuffer,
|
|
(wcslen(ValueBuffer) + 1) * sizeof(WCHAR));
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
DPRINT1("SMSS: Failed writing %wZ environment variable - %x\n",
|
|
&ValueName, Status);
|
|
NtClose(KeyHandle);
|
|
return Status;
|
|
}
|
|
|
|
/* And finally, write the number of CPUs */
|
|
RtlInitUnicodeString(&ValueName, L"NUMBER_OF_PROCESSORS");
|
|
swprintf(ValueBuffer, L"%d", BasicInfo.NumberOfProcessors);
|
|
DPRINT("Setting %wZ to %S\n", &ValueName, ValueBuffer);
|
|
Status = NtSetValueKey(KeyHandle,
|
|
&ValueName,
|
|
0,
|
|
REG_SZ,
|
|
ValueBuffer,
|
|
(wcslen(ValueBuffer) + 1) * sizeof(WCHAR));
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
DPRINT1("SMSS: Failed writing %wZ environment variable - %x\n",
|
|
&ValueName, Status);
|
|
NtClose(KeyHandle);
|
|
return Status;
|
|
}
|
|
|
|
/* Now we need to write the safeboot option key in a different format */
|
|
RtlInitUnicodeString(&DestinationString,
|
|
L"\\Registry\\Machine\\System\\CurrentControlSet\\"
|
|
L"Control\\Safeboot\\Option");
|
|
InitializeObjectAttributes(&ObjectAttributes,
|
|
&DestinationString,
|
|
OBJ_CASE_INSENSITIVE,
|
|
NULL,
|
|
NULL);
|
|
Status = NtOpenKey(&KeyHandle2, KEY_ALL_ACCESS, &ObjectAttributes);
|
|
if (NT_SUCCESS(Status))
|
|
{
|
|
/* This was indeed a safeboot, so check what kind of safeboot it was */
|
|
RtlInitUnicodeString(&ValueName, L"OptionValue");
|
|
Status = NtQueryValueKey(KeyHandle2,
|
|
&ValueName,
|
|
KeyValuePartialInformation,
|
|
PartialInfo,
|
|
sizeof(ValueBuffer),
|
|
&ResultLength);
|
|
NtClose(KeyHandle2);
|
|
if (NT_SUCCESS(Status) &&
|
|
(PartialInfo->Type == REG_DWORD) &&
|
|
(PartialInfo->DataLength >= sizeof(ULONG)))
|
|
{
|
|
/* Convert from the integer value to the correct specifier */
|
|
RtlInitUnicodeString(&ValueName, L"SAFEBOOT_OPTION");
|
|
switch (*(PULONG)PartialInfo->Data)
|
|
{
|
|
case 1:
|
|
wcscpy(ValueBuffer, L"MINIMAL");
|
|
break;
|
|
case 2:
|
|
wcscpy(ValueBuffer, L"NETWORK");
|
|
break;
|
|
case 3:
|
|
wcscpy(ValueBuffer, L"DSREPAIR");
|
|
break;
|
|
}
|
|
|
|
/* And write it in the environment! */
|
|
DPRINT("Setting %wZ to %S\n", &ValueName, ValueBuffer);
|
|
Status = NtSetValueKey(KeyHandle,
|
|
&ValueName,
|
|
0,
|
|
REG_SZ,
|
|
ValueBuffer,
|
|
(wcslen(ValueBuffer) + 1) * sizeof(WCHAR));
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
DPRINT1("SMSS: Failed writing %wZ environment variable - %x\n",
|
|
&ValueName, Status);
|
|
NtClose(KeyHandle);
|
|
return Status;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
DPRINT1("SMSS: Failed to query SAFEBOOT option (Type %lu, Status 0x%x)\n",
|
|
PartialInfo->Type, Status);
|
|
}
|
|
}
|
|
|
|
/* We are all done now */
|
|
NtClose(KeyHandle);
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
NTSTATUS
|
|
NTAPI
|
|
SmpProcessFileRenames(VOID)
|
|
{
|
|
BOOLEAN OldState, HavePrivilege = FALSE;
|
|
NTSTATUS Status;
|
|
HANDLE FileHandle, OtherFileHandle;
|
|
FILE_INFORMATION_CLASS InformationClass;
|
|
OBJECT_ATTRIBUTES ObjectAttributes;
|
|
IO_STATUS_BLOCK IoStatusBlock;
|
|
UNICODE_STRING FileString;
|
|
FILE_BASIC_INFORMATION BasicInfo;
|
|
FILE_DISPOSITION_INFORMATION DeleteInformation;
|
|
PFILE_RENAME_INFORMATION Buffer;
|
|
PLIST_ENTRY Head, NextEntry;
|
|
PSMP_REGISTRY_VALUE RegEntry;
|
|
PWCHAR FileName;
|
|
ULONG ValueLength, Length;
|
|
|
|
/* Give us access to restore any files we want */
|
|
Status = RtlAdjustPrivilege(SE_RESTORE_PRIVILEGE, TRUE, FALSE, &OldState);
|
|
if (NT_SUCCESS(Status)) HavePrivilege = TRUE;
|
|
|
|
// FIXME: Handle SFC-protected file renames!
|
|
if (SmpAllowProtectedRenames)
|
|
DPRINT1("SMSS: FIXME: Handle SFC-protected file renames!\n");
|
|
|
|
/* Process pending files to rename */
|
|
Head = &SmpFileRenameList;
|
|
while (!IsListEmpty(Head))
|
|
{
|
|
/* Get this entry */
|
|
NextEntry = RemoveHeadList(Head);
|
|
RegEntry = CONTAINING_RECORD(NextEntry, SMP_REGISTRY_VALUE, Entry);
|
|
DPRINT("Processing PFRO: '%wZ' / '%wZ'\n", &RegEntry->Value, &RegEntry->Name);
|
|
|
|
/* Skip past the '@' marker */
|
|
if (!(RegEntry->Value.Length) && (*RegEntry->Name.Buffer == L'@'))
|
|
{
|
|
RegEntry->Name.Length -= sizeof(UNICODE_NULL);
|
|
RegEntry->Name.Buffer++;
|
|
}
|
|
|
|
/* Open the file for delete access */
|
|
InitializeObjectAttributes(&ObjectAttributes,
|
|
&RegEntry->Name,
|
|
OBJ_CASE_INSENSITIVE,
|
|
NULL,
|
|
NULL);
|
|
Status = NtOpenFile(&OtherFileHandle,
|
|
DELETE | SYNCHRONIZE,
|
|
&ObjectAttributes,
|
|
&IoStatusBlock,
|
|
FILE_SHARE_READ | FILE_SHARE_WRITE,
|
|
FILE_SYNCHRONOUS_IO_NONALERT);
|
|
if (!NT_SUCCESS(Status)) goto Quickie;
|
|
|
|
/* Check if it's a rename or just a delete */
|
|
ValueLength = RegEntry->Value.Length;
|
|
if (!ValueLength)
|
|
{
|
|
/* Just a delete, set up the class, length and buffer */
|
|
InformationClass = FileDispositionInformation;
|
|
Length = sizeof(DeleteInformation);
|
|
Buffer = (PFILE_RENAME_INFORMATION)&DeleteInformation;
|
|
|
|
/* Set the delete disposition */
|
|
DeleteInformation.DeleteFile = TRUE;
|
|
}
|
|
else
|
|
{
|
|
/* This is a rename, setup the class and length */
|
|
InformationClass = FileRenameInformation;
|
|
Length = ValueLength + sizeof(FILE_RENAME_INFORMATION);
|
|
|
|
/* Skip past the special markers */
|
|
FileName = RegEntry->Value.Buffer;
|
|
if ((*FileName == L'!') || (*FileName == L'@'))
|
|
{
|
|
FileName++;
|
|
Length -= sizeof(UNICODE_NULL);
|
|
}
|
|
|
|
/* Now allocate the buffer for the rename information */
|
|
Buffer = RtlAllocateHeap(RtlGetProcessHeap(), SmBaseTag, Length);
|
|
if (Buffer)
|
|
{
|
|
/* Setup the buffer to point to the filename, and copy it */
|
|
Buffer->RootDirectory = NULL;
|
|
Buffer->FileNameLength = Length - sizeof(FILE_RENAME_INFORMATION);
|
|
Buffer->ReplaceIfExists = FileName != RegEntry->Value.Buffer;
|
|
RtlCopyMemory(Buffer->FileName, FileName, Buffer->FileNameLength);
|
|
}
|
|
else
|
|
{
|
|
/* Fail */
|
|
Status = STATUS_NO_MEMORY;
|
|
}
|
|
}
|
|
|
|
/* Check if everything is okay till here */
|
|
if (NT_SUCCESS(Status))
|
|
{
|
|
/* Now either rename or delete the file as requested */
|
|
Status = NtSetInformationFile(OtherFileHandle,
|
|
&IoStatusBlock,
|
|
Buffer,
|
|
Length,
|
|
InformationClass);
|
|
|
|
/* Check if we seem to have failed because the file was readonly */
|
|
if (!NT_SUCCESS(Status) &&
|
|
(InformationClass == FileRenameInformation) &&
|
|
(Status == STATUS_OBJECT_NAME_COLLISION) &&
|
|
Buffer->ReplaceIfExists)
|
|
{
|
|
/* Open the file for write attribute access this time... */
|
|
DPRINT("\nSMSS: '%wZ' => '%wZ' failed - Status == %x, Possible readonly target\n",
|
|
&RegEntry->Name,
|
|
&RegEntry->Value,
|
|
STATUS_OBJECT_NAME_COLLISION);
|
|
FileString.Length = RegEntry->Value.Length - sizeof(WCHAR);
|
|
FileString.MaximumLength = RegEntry->Value.MaximumLength - sizeof(WCHAR);
|
|
FileString.Buffer = FileName;
|
|
InitializeObjectAttributes(&ObjectAttributes,
|
|
&FileString,
|
|
OBJ_CASE_INSENSITIVE,
|
|
NULL,
|
|
NULL);
|
|
Status = NtOpenFile(&FileHandle,
|
|
FILE_WRITE_ATTRIBUTES | SYNCHRONIZE,
|
|
&ObjectAttributes,
|
|
&IoStatusBlock,
|
|
FILE_SHARE_READ | FILE_SHARE_WRITE,
|
|
FILE_SYNCHRONOUS_IO_NONALERT);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
/* That didn't work, so bail out */
|
|
DPRINT1(" SMSS: Open Existing file Failed - Status == %x\n",
|
|
Status);
|
|
}
|
|
else
|
|
{
|
|
/* Now remove the read-only attribute from the file */
|
|
DPRINT(" SMSS: Open Existing Success\n");
|
|
RtlZeroMemory(&BasicInfo, sizeof(BasicInfo));
|
|
BasicInfo.FileAttributes = FILE_ATTRIBUTE_NORMAL;
|
|
Status = NtSetInformationFile(FileHandle,
|
|
&IoStatusBlock,
|
|
&BasicInfo,
|
|
sizeof(BasicInfo),
|
|
FileBasicInformation);
|
|
NtClose(FileHandle);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
/* That didn't work, bail out */
|
|
DPRINT1(" SMSS: Set To NORMAL Failed - Status == %x\n",
|
|
Status);
|
|
}
|
|
else
|
|
{
|
|
/* Now that the file is no longer read-only, delete! */
|
|
DPRINT(" SMSS: Set To NORMAL OK\n");
|
|
Status = NtSetInformationFile(OtherFileHandle,
|
|
&IoStatusBlock,
|
|
Buffer,
|
|
Length,
|
|
FileRenameInformation);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
/* That failed too! */
|
|
DPRINT1(" SMSS: Re-Rename Failed - Status == %x\n",
|
|
Status);
|
|
}
|
|
else
|
|
{
|
|
/* Everything ok */
|
|
DPRINT(" SMSS: Re-Rename Worked OK\n");
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Close the file handle and check the operation result */
|
|
NtClose(OtherFileHandle);
|
|
Quickie:
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
/* We totally failed */
|
|
DPRINT1("SMSS: '%wZ' => '%wZ' failed - Status == %x\n",
|
|
&RegEntry->Name, &RegEntry->Value, Status);
|
|
}
|
|
else if (RegEntry->Value.Length)
|
|
{
|
|
/* We succeed with a rename */
|
|
DPRINT("SMSS: '%wZ' (renamed to) '%wZ'\n", &RegEntry->Name, &RegEntry->Value);
|
|
}
|
|
else
|
|
{
|
|
/* We succeeded with a delete */
|
|
DPRINT("SMSS: '%wZ' (deleted)\n", &RegEntry->Name);
|
|
}
|
|
|
|
/* Now free this entry and keep going */
|
|
if (RegEntry->AnsiValue) RtlFreeHeap(RtlGetProcessHeap(), 0, RegEntry->AnsiValue);
|
|
if (RegEntry->Value.Buffer) RtlFreeHeap(RtlGetProcessHeap(), 0, RegEntry->Value.Buffer);
|
|
RtlFreeHeap(RtlGetProcessHeap(), 0, RegEntry);
|
|
}
|
|
|
|
/* Put back the restore privilege if we had requested it, and return */
|
|
if (HavePrivilege) RtlAdjustPrivilege(SE_RESTORE_PRIVILEGE, FALSE, FALSE, &OldState);
|
|
return Status;
|
|
}
|
|
|
|
NTSTATUS
|
|
NTAPI
|
|
SmpLoadDataFromRegistry(OUT PUNICODE_STRING InitialCommand)
|
|
{
|
|
NTSTATUS Status;
|
|
PLIST_ENTRY Head, NextEntry;
|
|
PSMP_REGISTRY_VALUE RegEntry;
|
|
PVOID OriginalEnvironment;
|
|
ULONG MuSessionId = 0;
|
|
OBJECT_ATTRIBUTES ObjectAttributes;
|
|
HANDLE KeyHandle;
|
|
UNICODE_STRING DestinationString;
|
|
|
|
/* Initialize the keywords we'll be looking for */
|
|
RtlInitUnicodeString(&SmpDebugKeyword, L"debug");
|
|
RtlInitUnicodeString(&SmpASyncKeyword, L"async");
|
|
RtlInitUnicodeString(&SmpAutoChkKeyword, L"autocheck");
|
|
|
|
/* Initialize all the registry-associated list heads */
|
|
InitializeListHead(&SmpBootExecuteList);
|
|
InitializeListHead(&SmpSetupExecuteList);
|
|
InitializeListHead(&SmpPagingFileList);
|
|
InitializeListHead(&SmpDosDevicesList);
|
|
InitializeListHead(&SmpFileRenameList);
|
|
InitializeListHead(&SmpKnownDllsList);
|
|
InitializeListHead(&SmpExcludeKnownDllsList);
|
|
InitializeListHead(&SmpSubSystemList);
|
|
InitializeListHead(&SmpSubSystemsToLoad);
|
|
InitializeListHead(&SmpSubSystemsToDefer);
|
|
InitializeListHead(&SmpExecuteList);
|
|
SmpPagingFileInitialize();
|
|
|
|
/* Initialize the SMSS environment */
|
|
Status = RtlCreateEnvironment(TRUE, &SmpDefaultEnvironment);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
/* Fail if there was a problem */
|
|
DPRINT1("SMSS: Unable to allocate default environment - Status == %X\n",
|
|
Status);
|
|
SMSS_CHECKPOINT(RtlCreateEnvironment, Status);
|
|
return Status;
|
|
}
|
|
|
|
/* Check if we were booted in PE mode (LiveCD should have this) */
|
|
RtlInitUnicodeString(&DestinationString,
|
|
L"\\Registry\\Machine\\System\\CurrentControlSet\\"
|
|
L"Control\\MiniNT");
|
|
InitializeObjectAttributes(&ObjectAttributes,
|
|
&DestinationString,
|
|
OBJ_CASE_INSENSITIVE,
|
|
NULL,
|
|
NULL);
|
|
Status = NtOpenKey(&KeyHandle, KEY_ALL_ACCESS, &ObjectAttributes);
|
|
if (NT_SUCCESS(Status))
|
|
{
|
|
/* If the key exists, we were */
|
|
NtClose(KeyHandle);
|
|
MiniNTBoot = TRUE;
|
|
}
|
|
|
|
/* Print out if this is the case */
|
|
if (MiniNTBoot) DPRINT("SMSS: !!! MiniNT Boot !!!\n");
|
|
|
|
/* Open the environment key to see if we are booted in safe mode */
|
|
RtlInitUnicodeString(&DestinationString,
|
|
L"\\Registry\\Machine\\System\\CurrentControlSet\\"
|
|
L"Control\\Session Manager\\Environment");
|
|
InitializeObjectAttributes(&ObjectAttributes,
|
|
&DestinationString,
|
|
OBJ_CASE_INSENSITIVE,
|
|
NULL,
|
|
NULL);
|
|
Status = NtOpenKey(&KeyHandle, KEY_ALL_ACCESS, &ObjectAttributes);
|
|
if (NT_SUCCESS(Status))
|
|
{
|
|
/* Delete the value if we found it */
|
|
RtlInitUnicodeString(&DestinationString, L"SAFEBOOT_OPTION");
|
|
NtDeleteValueKey(KeyHandle, &DestinationString);
|
|
NtClose(KeyHandle);
|
|
}
|
|
|
|
/* Switch environments, then query the registry for all needed settings */
|
|
OriginalEnvironment = NtCurrentPeb()->ProcessParameters->Environment;
|
|
NtCurrentPeb()->ProcessParameters->Environment = SmpDefaultEnvironment;
|
|
Status = RtlQueryRegistryValues(RTL_REGISTRY_CONTROL,
|
|
L"Session Manager",
|
|
SmpRegistryConfigurationTable,
|
|
NULL,
|
|
NULL);
|
|
SmpDefaultEnvironment = NtCurrentPeb()->ProcessParameters->Environment;
|
|
NtCurrentPeb()->ProcessParameters->Environment = OriginalEnvironment;
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
/* We failed somewhere in registry initialization, which is bad... */
|
|
DPRINT1("SMSS: RtlQueryRegistryValues failed - Status == %lx\n", Status);
|
|
SMSS_CHECKPOINT(RtlQueryRegistryValues, Status);
|
|
return Status;
|
|
}
|
|
|
|
/* Now we can start acting on the registry settings. First to DOS devices */
|
|
Status = SmpInitializeDosDevices();
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
/* Failed */
|
|
DPRINT1("SMSS: Unable to initialize DosDevices configuration - Status == %lx\n",
|
|
Status);
|
|
SMSS_CHECKPOINT(SmpInitializeDosDevices, Status);
|
|
return Status;
|
|
}
|
|
|
|
/* Next create the session directory... */
|
|
RtlInitUnicodeString(&DestinationString, L"\\Sessions");
|
|
InitializeObjectAttributes(&ObjectAttributes,
|
|
&DestinationString,
|
|
OBJ_CASE_INSENSITIVE | OBJ_OPENIF | OBJ_PERMANENT,
|
|
NULL,
|
|
SmpPrimarySecurityDescriptor);
|
|
Status = NtCreateDirectoryObject(&SmpSessionsObjectDirectory,
|
|
DIRECTORY_ALL_ACCESS,
|
|
&ObjectAttributes);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
/* Fail */
|
|
DPRINT1("SMSS: Unable to create %wZ object directory - Status == %lx\n",
|
|
&DestinationString, Status);
|
|
SMSS_CHECKPOINT(NtCreateDirectoryObject, Status);
|
|
return Status;
|
|
}
|
|
|
|
/* Next loop all the boot execute binaries */
|
|
Head = &SmpBootExecuteList;
|
|
while (!IsListEmpty(Head))
|
|
{
|
|
/* Remove each one from the list */
|
|
NextEntry = RemoveHeadList(Head);
|
|
|
|
/* Execute it */
|
|
RegEntry = CONTAINING_RECORD(NextEntry, SMP_REGISTRY_VALUE, Entry);
|
|
SmpExecuteCommand(&RegEntry->Name, 0, NULL, 0);
|
|
|
|
/* And free it */
|
|
if (RegEntry->AnsiValue) RtlFreeHeap(RtlGetProcessHeap(), 0, RegEntry->AnsiValue);
|
|
if (RegEntry->Value.Buffer) RtlFreeHeap(RtlGetProcessHeap(), 0, RegEntry->Value.Buffer);
|
|
RtlFreeHeap(RtlGetProcessHeap(), 0, RegEntry);
|
|
}
|
|
|
|
/* Now do any pending file rename operations... */
|
|
if (!MiniNTBoot) SmpProcessFileRenames();
|
|
|
|
/* And initialize known DLLs... */
|
|
Status = SmpInitializeKnownDlls();
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
/* Fail if that didn't work */
|
|
DPRINT1("SMSS: Unable to initialize KnownDll configuration - Status == %lx\n",
|
|
Status);
|
|
SMSS_CHECKPOINT(SmpInitializeKnownDlls, Status);
|
|
return Status;
|
|
}
|
|
|
|
/* Create the needed page files */
|
|
if (!MiniNTBoot)
|
|
{
|
|
/* Loop every page file */
|
|
Head = &SmpPagingFileList;
|
|
while (!IsListEmpty(Head))
|
|
{
|
|
/* Remove each one from the list */
|
|
NextEntry = RemoveHeadList(Head);
|
|
|
|
/* Create the descriptor for it */
|
|
RegEntry = CONTAINING_RECORD(NextEntry, SMP_REGISTRY_VALUE, Entry);
|
|
SmpCreatePagingFileDescriptor(&RegEntry->Name);
|
|
|
|
/* And free it */
|
|
if (RegEntry->AnsiValue) RtlFreeHeap(RtlGetProcessHeap(), 0, RegEntry->AnsiValue);
|
|
if (RegEntry->Value.Buffer) RtlFreeHeap(RtlGetProcessHeap(), 0, RegEntry->Value.Buffer);
|
|
RtlFreeHeap(RtlGetProcessHeap(), 0, RegEntry);
|
|
}
|
|
|
|
/* Now create all the paging files for the descriptors that we have */
|
|
SmpCreatePagingFiles();
|
|
}
|
|
|
|
/* Tell Cm it's now safe to fully enable write access to the registry */
|
|
NtInitializeRegistry(CM_BOOT_FLAG_SMSS);
|
|
|
|
/* Create all the system-based environment variables for later inheriting */
|
|
Status = SmpCreateDynamicEnvironmentVariables();
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
/* Handle failure */
|
|
SMSS_CHECKPOINT(SmpCreateDynamicEnvironmentVariables, Status);
|
|
return Status;
|
|
}
|
|
|
|
/* And finally load all the subsystems for our first session! */
|
|
Status = SmpLoadSubSystemsForMuSession(&MuSessionId,
|
|
&SmpWindowsSubSysProcessId,
|
|
InitialCommand);
|
|
ASSERT(MuSessionId == 0);
|
|
if (!NT_SUCCESS(Status)) SMSS_CHECKPOINT(SmpLoadSubSystemsForMuSession, Status);
|
|
return Status;
|
|
}
|
|
|
|
NTSTATUS
|
|
NTAPI
|
|
SmpInit(IN PUNICODE_STRING InitialCommand,
|
|
OUT PHANDLE ProcessHandle)
|
|
{
|
|
NTSTATUS Status, Status2;
|
|
OBJECT_ATTRIBUTES ObjectAttributes;
|
|
UNICODE_STRING PortName, EventName;
|
|
HANDLE EventHandle, PortHandle;
|
|
ULONG HardErrorMode;
|
|
|
|
/* Create the SMSS Heap */
|
|
SmBaseTag = RtlCreateTagHeap(RtlGetProcessHeap(),
|
|
0,
|
|
L"SMSS!",
|
|
L"INIT");
|
|
SmpHeap = RtlGetProcessHeap();
|
|
|
|
/* Enable hard errors */
|
|
HardErrorMode = TRUE;
|
|
NtSetInformationProcess(NtCurrentProcess(),
|
|
ProcessDefaultHardErrorMode,
|
|
&HardErrorMode,
|
|
sizeof(HardErrorMode));
|
|
|
|
/* Initialize the subsystem list and the session list, plus their locks */
|
|
RtlInitializeCriticalSection(&SmpKnownSubSysLock);
|
|
InitializeListHead(&SmpKnownSubSysHead);
|
|
RtlInitializeCriticalSection(&SmpSessionListLock);
|
|
InitializeListHead(&SmpSessionListHead);
|
|
|
|
/* Initialize the process list */
|
|
InitializeListHead(&NativeProcessList);
|
|
|
|
/* Initialize session parameters */
|
|
SmpNextSessionId = 1;
|
|
SmpNextSessionIdScanMode = 0;
|
|
SmpDbgSsLoaded = FALSE;
|
|
|
|
/* Create the initial security descriptors */
|
|
Status = SmpCreateSecurityDescriptors(TRUE);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
/* Fail */
|
|
SMSS_CHECKPOINT(SmpCreateSecurityDescriptors, Status);
|
|
return Status;
|
|
}
|
|
|
|
/* Initialize subsystem names */
|
|
RtlInitUnicodeString(&SmpSubsystemName, L"NT-Session Manager");
|
|
RtlInitUnicodeString(&PosixName, L"POSIX");
|
|
RtlInitUnicodeString(&Os2Name, L"OS2");
|
|
|
|
/* Create the SM API Port */
|
|
RtlInitUnicodeString(&PortName, L"\\SmApiPort");
|
|
InitializeObjectAttributes(&ObjectAttributes, &PortName, 0, NULL, SmpApiPortSecurityDescriptor);
|
|
Status = NtCreatePort(&PortHandle,
|
|
&ObjectAttributes,
|
|
sizeof(SB_CONNECTION_INFO),
|
|
sizeof(SM_API_MSG),
|
|
sizeof(SB_API_MSG) * 32);
|
|
ASSERT(NT_SUCCESS(Status));
|
|
SmpDebugPort = PortHandle;
|
|
|
|
/* Create two SM API threads */
|
|
Status = RtlCreateUserThread(NtCurrentProcess(),
|
|
NULL,
|
|
FALSE,
|
|
0,
|
|
0,
|
|
0,
|
|
SmpApiLoop,
|
|
PortHandle,
|
|
NULL,
|
|
NULL);
|
|
ASSERT(NT_SUCCESS(Status));
|
|
Status = RtlCreateUserThread(NtCurrentProcess(),
|
|
NULL,
|
|
FALSE,
|
|
0,
|
|
0,
|
|
0,
|
|
SmpApiLoop,
|
|
PortHandle,
|
|
NULL,
|
|
NULL);
|
|
ASSERT(NT_SUCCESS(Status));
|
|
|
|
/* Create the write event that autochk can set after running */
|
|
RtlInitUnicodeString(&EventName, L"\\Device\\VolumesSafeForWriteAccess");
|
|
InitializeObjectAttributes(&ObjectAttributes,
|
|
&EventName,
|
|
OBJ_PERMANENT,
|
|
NULL,
|
|
NULL);
|
|
Status2 = NtCreateEvent(&EventHandle,
|
|
EVENT_ALL_ACCESS,
|
|
&ObjectAttributes,
|
|
0,
|
|
0);
|
|
if (!NT_SUCCESS(Status2))
|
|
{
|
|
/* Should never really fail */
|
|
DPRINT1("SMSS: Unable to create %wZ event - Status == %lx\n",
|
|
&EventName, Status2);
|
|
ASSERT(NT_SUCCESS(Status2));
|
|
}
|
|
|
|
/* Now initialize everything else based on the registry parameters */
|
|
Status = SmpLoadDataFromRegistry(InitialCommand);
|
|
if (NT_SUCCESS(Status))
|
|
{
|
|
/* Autochk should've run now. Set the event and save the CSRSS handle */
|
|
*ProcessHandle = SmpWindowsSubSysProcess;
|
|
NtSetEvent(EventHandle, 0);
|
|
NtClose(EventHandle);
|
|
}
|
|
|
|
/* All done */
|
|
return Status;
|
|
}
|