/* * 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 /* GLOBALS ********************************************************************/ UNICODE_STRING SmpSubsystemName, PosixName, Os2Name; LIST_ENTRY SmpBootExecuteList, SmpSetupExecuteList; LIST_ENTRY SmpPagingFileList, SmpDosDevicesList, SmpFileRenameList; LIST_ENTRY SmpKnownDllsList, SmpExcludeKnownDllsList; LIST_ENTRY SmpSubSystemList, SmpSubSystemsToLoad, SmpSubSystemsToDefer; LIST_ENTRY 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); } } /** * @remark * SmpConfigureEnvironment() should be called twice in order to resolve * forward references to environment variables. * See the two L"Environment" entries in SmpRegistryConfigurationTable[]. **/ 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(NULL, &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 }, /** * @remark * SmpConfigureEnvironment() is expected to be called twice * (see SmpCalledConfigEnv) in order to resolve forward references * to environment variables (e.g. EnvVar1 referring to EnvVar2, * before EnvVar2 is defined). **/ { SmpConfigureEnvironment, RTL_QUERY_REGISTRY_SUBKEY, L"Environment", NULL, 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 GlobalName; HANDLE DirHandle; PLIST_ENTRY NextEntry, Head; /* Open the \GLOBAL?? directory */ RtlInitUnicodeString(&GlobalName, L"\\??"); InitializeObjectAttributes(&ObjectAttributes, &GlobalName, 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", &GlobalName, 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, SymLinkName; 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(&SymLinkName, L"KnownDllPath"); InitializeObjectAttributes(&ObjectAttributes, &SymLinkName, 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", &SymLinkName, 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 KnownDllsName; PLIST_ENTRY Head, NextEntry; /* Call the internal function */ RtlInitUnicodeString(&KnownDllsName, L"\\KnownDlls"); Status = SmpInitializeKnownDllsInternal(&KnownDllsName, &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, (ULONG)(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, (ULONG)(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, (ULONG)(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, (ULONG)(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, (ULONG)(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, (ULONG)(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 = FALSE; 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, NULL); NtClose(EventHandle); } /* All done */ return Status; }