/*++ Copyright (c) 1989 Microsoft Corporation Module Name: sminit.c Abstract: Session Manager Initialization Author: Mark Lucovsky (markl) 04-Oct-1989 Revision History: --*/ #include "smsrvp.h" #include #include void SmpDisplayString( char *s ); // #define SMP_SHOW_REGISTRY_DATA 1 #define MAX_PAGING_FILES 16 // // Protection mode flags // #define SMP_NO_PROTECTION (0x0) #define SMP_STANDARD_PROTECTION (0x1) #define SMP_PROTECTION_REQUIRED \ (SMP_STANDARD_PROTECTION) ULONG CountPageFiles; LONG PageFileMinSizes[ MAX_PAGING_FILES ]; LONG PageFileMaxSizes[ MAX_PAGING_FILES ]; UNICODE_STRING PageFileSpecs[ MAX_PAGING_FILES ]; PSECURITY_DESCRIPTOR SmpPrimarySecurityDescriptor; SECURITY_DESCRIPTOR SmpPrimarySDBody; PSECURITY_DESCRIPTOR SmpLiberalSecurityDescriptor; SECURITY_DESCRIPTOR SmpLiberalSDBody; PSECURITY_DESCRIPTOR SmpKnownDllsSecurityDescriptor; SECURITY_DESCRIPTOR SmpKnownDllsSDBody; PSECURITY_DESCRIPTOR SmpApiPortSecurityDescriptor; SECURITY_DESCRIPTOR SmpApiPortSDBody; ULONG SmpProtectionMode = 0; #if DBG BOOLEAN SmpEnableDots = FALSE; #else BOOLEAN SmpEnableDots = TRUE; #endif WCHAR InitialCommandBuffer[ 256 ]; UNICODE_STRING SmpDebugKeyword; UNICODE_STRING SmpASyncKeyword; UNICODE_STRING SmpAutoChkKeyword; UNICODE_STRING SmpKnownDllPath; HANDLE SmpWindowsSubSysProcess; typedef struct _SMP_REGISTRY_VALUE { LIST_ENTRY Entry; UNICODE_STRING Name; UNICODE_STRING Value; LPSTR AnsiValue; } SMP_REGISTRY_VALUE, *PSMP_REGISTRY_VALUE; LIST_ENTRY SmpBootExecuteList; LIST_ENTRY SmpPagingFileList; LIST_ENTRY SmpDosDevicesList; LIST_ENTRY SmpFileRenameList; LIST_ENTRY SmpKnownDllsList; LIST_ENTRY SmpExcludeKnownDllsList; LIST_ENTRY SmpSubSystemList; LIST_ENTRY SmpSubSystemsToLoad; LIST_ENTRY SmpSubSystemsToDefer; LIST_ENTRY SmpExecuteList; NTSTATUS SmpCreateSecurityDescriptors( IN BOOLEAN InitialCall ); NTSTATUS SmpLoadDataFromRegistry( OUT PUNICODE_STRING InitialCommand ); NTSTATUS SmpCreateDynamicEnvironmentVariables( VOID ); PSMP_REGISTRY_VALUE SmpFindRegistryValue( IN PLIST_ENTRY ListHead, IN PWSTR Name ); NTSTATUS SmpSaveRegistryValue( IN OUT PLIST_ENTRY ListHead, IN PWSTR Name, IN PWSTR Value OPTIONAL, IN BOOLEAN CheckForDuplicate ); #ifdef SMP_SHOW_REGISTRY_DATA VOID SmpDumpQuery( IN PCHAR RoutineName, IN PWSTR ValueName, IN ULONG ValueType, IN PVOID ValueData, IN ULONG ValueLength ); #endif NTSTATUS SmpConfigureProtectionMode( IN PWSTR ValueName, IN ULONG ValueType, IN PVOID ValueData, IN ULONG ValueLength, IN PVOID Context, IN PVOID EntryContext ); NTSTATUS SmpConfigureObjectDirectories( IN PWSTR ValueName, IN ULONG ValueType, IN PVOID ValueData, IN ULONG ValueLength, IN PVOID Context, IN PVOID EntryContext ); NTSTATUS SmpConfigureExecute( IN PWSTR ValueName, IN ULONG ValueType, IN PVOID ValueData, IN ULONG ValueLength, IN PVOID Context, IN PVOID EntryContext ); NTSTATUS SmpConfigureFileRenames( IN PWSTR ValueName, IN ULONG ValueType, IN PVOID ValueData, IN ULONG ValueLength, IN PVOID Context, IN PVOID EntryContext ); NTSTATUS SmpConfigureMemoryMgmt( IN PWSTR ValueName, IN ULONG ValueType, IN PVOID ValueData, IN ULONG ValueLength, IN PVOID Context, IN PVOID EntryContext ); NTSTATUS SmpConfigureDosDevices( IN PWSTR ValueName, IN ULONG ValueType, IN PVOID ValueData, IN ULONG ValueLength, IN PVOID Context, IN PVOID EntryContext ); NTSTATUS SmpConfigureKnownDlls( IN PWSTR ValueName, IN ULONG ValueType, IN PVOID ValueData, IN ULONG ValueLength, IN PVOID Context, IN PVOID EntryContext ); NTSTATUS SmpConfigureExcludeKnownDlls( IN PWSTR ValueName, IN ULONG ValueType, IN PVOID ValueData, IN ULONG ValueLength, IN PVOID Context, IN PVOID EntryContext ); NTSTATUS SmpConfigureSubSystems( IN PWSTR ValueName, IN ULONG ValueType, IN PVOID ValueData, IN ULONG ValueLength, IN PVOID Context, IN PVOID EntryContext ); NTSTATUS SmpConfigureEnvironment( IN PWSTR ValueName, IN ULONG ValueType, IN PVOID ValueData, IN ULONG ValueLength, IN PVOID Context, IN PVOID EntryContext ); RTL_QUERY_REGISTRY_TABLE SmpRegistryConfigurationTable[] = { // // Note that the SmpConfigureProtectionMode entry should preceed others // to ensure we set up the right protection for use by the others. // {SmpConfigureProtectionMode, 0, L"ProtectionMode", NULL, REG_DWORD, (PVOID)0, 0}, {SmpConfigureObjectDirectories, 0, L"ObjectDirectories", NULL, REG_MULTI_SZ, (PVOID)L"\\Windows\0\\RPC Control\0", 0}, {SmpConfigureExecute, 0, L"BootExecute", &SmpBootExecuteList, REG_MULTI_SZ, L"autocheck \\SystemRoot\\Windows\\System32\\AutoChk.exe *\0", 0}, {SmpConfigureFileRenames, RTL_QUERY_REGISTRY_DELETE, L"PendingFileRenameOperations", &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, "?:\\pagefile.sys 10 60\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}, {SmpConfigureExecute, RTL_QUERY_REGISTRY_TOPKEY, L"Execute", &SmpExecuteList, REG_NONE, NULL, 0}, {NULL, 0, NULL, NULL, REG_NONE, NULL, 0} }; NTSTATUS SmpInvokeAutoChk( IN PUNICODE_STRING ImageFileName, IN PUNICODE_STRING CurrentDirectory, IN PUNICODE_STRING Arguments, IN ULONG Flags ); NTSTATUS SmpLoadSubSystem( IN PUNICODE_STRING ImageFileName, IN PUNICODE_STRING CurrentDirectory, IN PUNICODE_STRING CommandLine, IN ULONG Flags ); NTSTATUS SmpExecuteCommand( IN PUNICODE_STRING CommandLine, IN ULONG Flags ); NTSTATUS SmpInitializeDosDevices( VOID ); NTSTATUS SmpInitializeKnownDlls( VOID ); VOID SmpProcessFileRenames( VOID ); NTSTATUS SmpParseToken( IN PUNICODE_STRING Source, IN BOOLEAN RemainderOfSource, OUT PUNICODE_STRING Token ); NTSTATUS SmpParseCommandLine( IN PUNICODE_STRING CommandLine, OUT PULONG Flags, OUT PUNICODE_STRING ImageFileName, OUT PUNICODE_STRING ImageFileDirectory, OUT PUNICODE_STRING Arguments ); #define SMP_DEBUG_FLAG 0x00000001 #define SMP_ASYNC_FLAG 0x00000002 #define SMP_AUTOCHK_FLAG 0x00000004 #define SMP_SUBSYSTEM_FLAG 0x00000008 #define SMP_IMAGE_NOT_FOUND 0x00000010 #define SMP_DONT_START 0x00000020 ULONG SmpConvertInteger( IN PWSTR String ); NTSTATUS SmpAddPagingFile( IN PUNICODE_STRING PagingFileSpec ); NTSTATUS SmpCreatePagingFile( PUNICODE_STRING PagingFileSpec, LARGE_INTEGER MinPagingFileSize, LARGE_INTEGER MaxPagingFileSize ); NTSTATUS SmpCreatePagingFiles( VOID ); VOID SmpTranslateSystemPartitionInformation( VOID ); #define VALUE_BUFFER_SIZE (sizeof(KEY_VALUE_PARTIAL_INFORMATION) + 256 * sizeof(WCHAR)) // // local Macros // // // VOID // SmpSetDaclDefaulted( // IN POBJECT_ATTRIBUTES ObjectAttributes, // OUT PSECURITY_DESCRIPTOR_CONTROL CurrentSdControl // ) // // Description: // // This routine will set the DaclDefaulted flag of the DACL passed // via the ObjectAttributes parameter. If the ObjectAttributes do // not include a SecurityDescriptor, then no action is taken. // // Parameters: // // ObjectAttributes - The object attributes whose security descriptor is // to have its DaclDefaulted flag set. // // CurrentSdControl - Receives the current value of the security descriptor's // control flags. This may be used in a subsequent call to // SmpRestoreDaclDefaulted() to restore the flag to its original state. // #define SmpSetDaclDefaulted( OA, SDC ) \ if( (OA)->SecurityDescriptor != NULL) { \ (*SDC) = ((PISECURITY_DESCRIPTOR)((OA)->SecurityDescriptor))->Control & \ SE_DACL_DEFAULTED; \ ((PISECURITY_DESCRIPTOR)((OA)->SecurityDescriptor))->Control |= \ SE_DACL_DEFAULTED; \ } // // VOID // SmpRestoreDaclDefaulted( // IN POBJECT_ATTRIBUTES ObjectAttributes, // IN SECURITY_DESCRIPTOR_CONTROL OriginalSdControl // ) // // Description: // // This routine will set the DaclDefaulted flag of the DACL back to // a prior state (indicated by the value in OriginalSdControl). // // Parameters: // // ObjectAttributes - The object attributes whose security descriptor is // to have its DaclDefaulted flag restored. If the object attributes // have no security descriptor, then no action is taken. // // OriginalSdControl - The original value of the security descriptor's // control flags. This typically is obtained via a prior call to // SmpSetDaclDefaulted(). // #define SmpRestoreDaclDefaulted( OA, SDC ) \ if( (OA)->SecurityDescriptor != NULL) { \ ((PISECURITY_DESCRIPTOR)((OA)->SecurityDescriptor))->Control = \ (((PISECURITY_DESCRIPTOR)((OA)->SecurityDescriptor))->Control & \ ~SE_DACL_DEFAULTED) | \ (SDC & SE_DACL_DEFAULTED); \ } // // routines // BOOLEAN SmpQueryRegistrySosOption( VOID ) /*++ Routine Description: This function queries the registry to determine if the loadoptions boot environment variable contains the string "SOS". HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control:SystemStartOptions Arguments: None. Return Value: TRUE if "SOS" was set. Otherwise FALSE. --*/ { NTSTATUS Status; UNICODE_STRING KeyName; UNICODE_STRING ValueName; OBJECT_ATTRIBUTES ObjectAttributes; HANDLE Key; WCHAR ValueBuffer[VALUE_BUFFER_SIZE]; PKEY_VALUE_PARTIAL_INFORMATION KeyValueInfo; ULONG ValueLength; // // Open the registry key. // KeyValueInfo = (PKEY_VALUE_PARTIAL_INFORMATION)ValueBuffer; RtlInitUnicodeString(&KeyName, L"\\Registry\\Machine\\System\\CurrentControlSet\\Control"); InitializeObjectAttributes(&ObjectAttributes, &KeyName, OBJ_CASE_INSENSITIVE, NULL, NULL); Status = NtOpenKey(&Key, KEY_READ, &ObjectAttributes); if (!NT_SUCCESS(Status)) { KdPrint(("SMSS: can't open control key: 0x%x\n", Status)); return FALSE; } // // Query the key value. // RtlInitUnicodeString(&ValueName, L"SystemStartOptions"); Status = NtQueryValueKey(Key, &ValueName, KeyValuePartialInformation, (PVOID)KeyValueInfo, VALUE_BUFFER_SIZE, &ValueLength); ASSERT(ValueLength < VALUE_BUFFER_SIZE); NtClose(Key); if (!NT_SUCCESS(Status)) { KdPrint(("SMSS: can't query value key: 0x%x\n", Status)); return FALSE; } // // Check is "sos" or "SOS" ois specified. // if (NULL != wcsstr((PWCHAR)&KeyValueInfo->Data, L"SOS") || NULL != wcsstr((PWCHAR)&KeyValueInfo->Data, L"sos")) { return TRUE; } return FALSE; } NTSTATUS SmpSaveRegistryValue( IN OUT PLIST_ENTRY ListHead, IN PWSTR Name, IN PWSTR Value OPTIONAL, IN BOOLEAN CheckForDuplicate ) { PLIST_ENTRY Next; PSMP_REGISTRY_VALUE p; UNICODE_STRING UnicodeName; UNICODE_STRING UnicodeValue; ANSI_STRING AnsiString; RtlInitUnicodeString( &UnicodeName, Name ); RtlInitUnicodeString( &UnicodeValue, Value ); if (CheckForDuplicate) { Next = ListHead->Flink; p = NULL; while ( Next != ListHead ) { p = CONTAINING_RECORD( Next, SMP_REGISTRY_VALUE, Entry ); if (!RtlCompareUnicodeString( &p->Name, &UnicodeName, TRUE )) { if ((!ARGUMENT_PRESENT( Value ) && p->Value.Buffer == NULL) || (ARGUMENT_PRESENT( Value ) && !RtlCompareUnicodeString( &p->Value, &UnicodeValue, TRUE ) ) ) { return( STATUS_OBJECT_NAME_EXISTS ); } break; } Next = Next->Flink; p = NULL; } } else { p = NULL; } if (p == NULL) { p = RtlAllocateHeap( RtlProcessHeap(), MAKE_TAG( INIT_TAG ), sizeof( *p ) + UnicodeName.MaximumLength ); if (p == NULL) { return( STATUS_NO_MEMORY ); } InitializeListHead( &p->Entry ); p->Name.Buffer = (PWSTR)(p+1); p->Name.Length = UnicodeName.Length; p->Name.MaximumLength = UnicodeName.MaximumLength; RtlMoveMemory( p->Name.Buffer, UnicodeName.Buffer, UnicodeName.MaximumLength ); p->Value.Buffer = NULL; InsertTailList( ListHead, &p->Entry ); } if (p->Value.Buffer != NULL) { RtlFreeHeap( RtlProcessHeap(), 0, p->Value.Buffer ); } if (ARGUMENT_PRESENT( Value )) { p->Value.Buffer = (PWSTR)RtlAllocateHeap( RtlProcessHeap(), MAKE_TAG( INIT_TAG ), UnicodeValue.MaximumLength ); if (p->Value.Buffer == NULL) { RemoveEntryList( &p->Entry ); RtlFreeHeap( RtlProcessHeap(), 0, p ); return( STATUS_NO_MEMORY ); } p->Value.Length = UnicodeValue.Length; p->Value.MaximumLength = UnicodeValue.MaximumLength; RtlMoveMemory( p->Value.Buffer, UnicodeValue.Buffer, UnicodeValue.MaximumLength ); p->AnsiValue = (LPSTR)RtlAllocateHeap( RtlProcessHeap(), MAKE_TAG( INIT_TAG ), (UnicodeValue.Length / sizeof( WCHAR )) + 1 ); if (p->AnsiValue == NULL) { RtlFreeHeap( RtlProcessHeap(), 0, p->Value.Buffer ); RemoveEntryList( &p->Entry ); RtlFreeHeap( RtlProcessHeap(), 0, p ); return( STATUS_NO_MEMORY ); } AnsiString.Buffer = p->AnsiValue; AnsiString.Length = 0; AnsiString.MaximumLength = (UnicodeValue.Length / sizeof( WCHAR )) + 1; RtlUnicodeStringToAnsiString( &AnsiString, &UnicodeValue, FALSE ); } else { RtlInitUnicodeString( &p->Value, NULL ); } return( STATUS_SUCCESS ); } PSMP_REGISTRY_VALUE SmpFindRegistryValue( IN PLIST_ENTRY ListHead, IN PWSTR Name ) { PLIST_ENTRY Next; PSMP_REGISTRY_VALUE p; UNICODE_STRING UnicodeName; RtlInitUnicodeString( &UnicodeName, Name ); Next = ListHead->Flink; while ( Next != ListHead ) { p = CONTAINING_RECORD( Next, SMP_REGISTRY_VALUE, Entry ); if (!RtlCompareUnicodeString( &p->Name, &UnicodeName, TRUE )) { return( p ); } Next = Next->Flink; } return( NULL ); } NTSTATUS SmpInit( OUT PUNICODE_STRING InitialCommand, OUT PHANDLE WindowsSubSystem ) { NTSTATUS st; OBJECT_ATTRIBUTES ObjA; HANDLE SmpApiConnectionPort; UNICODE_STRING Unicode; NTSTATUS Status; ULONG HardErrorMode; SmBaseTag = RtlCreateTagHeap( RtlProcessHeap(), 0, L"SMSS!", L"INIT\0" L"DBG\0" L"SM\0" ); // // Make sure we specify hard error popups // HardErrorMode = 1; NtSetInformationProcess( NtCurrentProcess(), ProcessDefaultHardErrorMode, (PVOID) &HardErrorMode, sizeof( HardErrorMode ) ); RtlInitUnicodeString( &SmpSubsystemName, L"NT-Session Manager" ); RtlInitializeCriticalSection(&SmpKnownSubSysLock); InitializeListHead(&SmpKnownSubSysHead); RtlInitializeCriticalSection(&SmpSessionListLock); InitializeListHead(&SmpSessionListHead); SmpNextSessionId = 1; SmpNextSessionIdScanMode = FALSE; SmpDbgSsLoaded = FALSE; // // Initialize security descriptors to grant wide access // (protection mode not yet read in from registry). // st = SmpCreateSecurityDescriptors( TRUE ); if ( !NT_SUCCESS(st) ) { return(st); } InitializeListHead(&NativeProcessList); SmpHeap = RtlProcessHeap(); RtlInitUnicodeString( &Unicode, L"\\SmApiPort" ); InitializeObjectAttributes( &ObjA, &Unicode, 0, NULL, SmpApiPortSecurityDescriptor); st = NtCreatePort( &SmpApiConnectionPort, &ObjA, sizeof(SBCONNECTINFO), sizeof(SMMESSAGE_SIZE), sizeof(SBAPIMSG) * 32 ); ASSERT( NT_SUCCESS(st) ); SmpDebugPort = SmpApiConnectionPort; st = RtlCreateUserThread( NtCurrentProcess(), NULL, FALSE, 0L, 0L, 0L, SmpApiLoop, (PVOID) SmpApiConnectionPort, NULL, NULL ); ASSERT( NT_SUCCESS(st) ); st = RtlCreateUserThread( NtCurrentProcess(), NULL, FALSE, 0L, 0L, 0L, SmpApiLoop, (PVOID) SmpApiConnectionPort, NULL, NULL ); ASSERT( NT_SUCCESS(st) ); // // Configure the system // Status = SmpLoadDataFromRegistry( InitialCommand ); if (NT_SUCCESS( Status )) { *WindowsSubSystem = SmpWindowsSubSysProcess; } return( Status ); } typedef struct _SMP_ACQUIRE_STATE { HANDLE Token; PTOKEN_PRIVILEGES OldPrivileges; PTOKEN_PRIVILEGES NewPrivileges; UCHAR OldPrivBuffer[ 1024 ]; } SMP_ACQUIRE_STATE, *PSMP_ACQUIRE_STATE; NTSTATUS SmpAcquirePrivilege( ULONG Privilege, PVOID *ReturnedState ) { PSMP_ACQUIRE_STATE State; ULONG cbNeeded; LUID LuidPrivilege; NTSTATUS Status; // // Make sure we have access to adjust and to get the old token privileges // *ReturnedState = NULL; State = RtlAllocateHeap( RtlProcessHeap(), MAKE_TAG( INIT_TAG ), sizeof(SMP_ACQUIRE_STATE) + sizeof(TOKEN_PRIVILEGES) + (1 - ANYSIZE_ARRAY) * sizeof(LUID_AND_ATTRIBUTES) ); if (State == NULL) { return STATUS_NO_MEMORY; } Status = NtOpenProcessToken( NtCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &State->Token ); if ( !NT_SUCCESS( Status )) { RtlFreeHeap( RtlProcessHeap(), 0, State ); return Status; } State->NewPrivileges = (PTOKEN_PRIVILEGES)(State+1); State->OldPrivileges = (PTOKEN_PRIVILEGES)(State->OldPrivBuffer); // // Initialize the privilege adjustment structure // LuidPrivilege = RtlConvertUlongToLuid(Privilege); State->NewPrivileges->PrivilegeCount = 1; State->NewPrivileges->Privileges[0].Luid = LuidPrivilege; State->NewPrivileges->Privileges[0].Attributes = SE_PRIVILEGE_ENABLED; // // Enable the privilege // cbNeeded = sizeof( State->OldPrivBuffer ); Status = NtAdjustPrivilegesToken( State->Token, FALSE, State->NewPrivileges, cbNeeded, State->OldPrivileges, &cbNeeded ); if (Status == STATUS_BUFFER_TOO_SMALL) { State->OldPrivileges = RtlAllocateHeap( RtlProcessHeap(), MAKE_TAG( INIT_TAG ), cbNeeded ); if (State->OldPrivileges == NULL) { Status = STATUS_NO_MEMORY; } else { Status = NtAdjustPrivilegesToken( State->Token, FALSE, State->NewPrivileges, cbNeeded, State->OldPrivileges, &cbNeeded ); } } // // STATUS_NOT_ALL_ASSIGNED means that the privilege isn't // in the token, so we can't proceed. // // This is a warning level status, so map it to an error status. // if (Status == STATUS_NOT_ALL_ASSIGNED) { Status = STATUS_PRIVILEGE_NOT_HELD; } if (!NT_SUCCESS( Status )) { if (State->OldPrivileges != (PTOKEN_PRIVILEGES)(State->OldPrivBuffer)) { RtlFreeHeap( RtlProcessHeap(), 0, State->OldPrivileges ); } NtClose( State->Token ); RtlFreeHeap( RtlProcessHeap(), 0, State ); return Status; } *ReturnedState = State; return STATUS_SUCCESS; } VOID SmpReleasePrivilege( PVOID StatePointer ) { PSMP_ACQUIRE_STATE State = (PSMP_ACQUIRE_STATE)StatePointer; NtAdjustPrivilegesToken( State->Token, FALSE, State->OldPrivileges, 0, NULL, NULL ); if (State->OldPrivileges != (PTOKEN_PRIVILEGES)(State->OldPrivBuffer)) { RtlFreeHeap( RtlProcessHeap(), 0, State->OldPrivileges ); } NtClose( State->Token ); RtlFreeHeap( RtlProcessHeap(), 0, State ); return; } NTSTATUS SmpLoadDataFromRegistry( OUT PUNICODE_STRING InitialCommand ) /*++ Routine Description: This function loads all of the configurable data for the NT Session Manager from the registry. Arguments: None Return Value: Status of operation --*/ { NTSTATUS Status; PLIST_ENTRY Head, Next; PSMP_REGISTRY_VALUE p; PVOID OriginalEnvironment; RtlInitUnicodeString( &SmpDebugKeyword, L"debug" ); RtlInitUnicodeString( &SmpASyncKeyword, L"async" ); RtlInitUnicodeString( &SmpAutoChkKeyword, L"autocheck" ); InitializeListHead( &SmpBootExecuteList ); InitializeListHead( &SmpPagingFileList ); InitializeListHead( &SmpDosDevicesList ); InitializeListHead( &SmpFileRenameList ); InitializeListHead( &SmpKnownDllsList ); InitializeListHead( &SmpExcludeKnownDllsList ); InitializeListHead( &SmpSubSystemList ); InitializeListHead( &SmpSubSystemsToLoad ); InitializeListHead( &SmpSubSystemsToDefer ); InitializeListHead( &SmpExecuteList ); Status = RtlCreateEnvironment( TRUE, &SmpDefaultEnvironment ); if (!NT_SUCCESS( Status )) { KdPrint(("SMSS: Unable to allocate default environment - Status == %X\n", Status )); return( Status ); } // // In order to track growth in smpdefaultenvironment, make it sm's environment // while doing the registry groveling and then restore it // 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 )) { KdPrint(( "SMSS: RtlQueryRegistryValues failed - Status == %lx\n", Status )); return( Status ); } Status = SmpInitializeDosDevices(); if (!NT_SUCCESS( Status )) { KdPrint(( "SMSS: Unable to initialize DosDevices configuration - Status == %lx\n", Status )); return( Status ); } Head = &SmpBootExecuteList; while (!IsListEmpty( Head )) { Next = RemoveHeadList( Head ); p = CONTAINING_RECORD( Next, SMP_REGISTRY_VALUE, Entry ); #ifdef SMP_SHOW_REGISTRY_DATA DbgPrint( "SMSS: BootExecute( %wZ )\n", &p->Name ); #endif SmpExecuteCommand( &p->Name, 0 ); RtlFreeHeap( RtlProcessHeap(), 0, p ); } SmpProcessFileRenames(); Status = SmpInitializeKnownDlls(); if (!NT_SUCCESS( Status )) { KdPrint(( "SMSS: Unable to initialize KnownDll configuration - Status == %lx\n", Status )); return( Status ); } // // Process the list of paging files. // Head = &SmpPagingFileList; while (!IsListEmpty( Head )) { Next = RemoveHeadList( Head ); p = CONTAINING_RECORD( Next, SMP_REGISTRY_VALUE, Entry ); #ifdef SMP_SHOW_REGISTRY_DATA DbgPrint( "SMSS: PagingFile( %wZ )\n", &p->Name ); #endif SmpAddPagingFile( &p->Name ); RtlFreeHeap( RtlProcessHeap(), 0, p ); } // // Create any paging files specified in NT section(s) // SmpCreatePagingFiles(); // // Finish registry initialization // NtInitializeRegistry(FALSE); Status = SmpCreateDynamicEnvironmentVariables( ); if (!NT_SUCCESS( Status )) { return Status; } // // Translate the system partition information stored during IoInitSystem into // a DOS path and store in Win32-standard location. // SmpTranslateSystemPartitionInformation(); Head = &SmpSubSystemList; while (!IsListEmpty( Head )) { Next = RemoveHeadList( Head ); p = CONTAINING_RECORD( Next, SMP_REGISTRY_VALUE, Entry ); if ( !_wcsicmp( p->Name.Buffer, L"Kmode" )) { BOOLEAN TranslationStatus; UNICODE_STRING FileName; TranslationStatus = RtlDosPathNameToNtPathName_U( p->Value.Buffer, &FileName, NULL, NULL ); if ( TranslationStatus ) { PVOID State; Status = SmpAcquirePrivilege( SE_LOAD_DRIVER_PRIVILEGE, &State ); if (NT_SUCCESS( Status )) { Status = NtSetSystemInformation( SystemExtendServiceTableInformation, (PVOID)&FileName, sizeof(FileName) ); RtlFreeHeap(RtlProcessHeap(), 0, FileName.Buffer); SmpReleasePrivilege( State ); if ( !NT_SUCCESS(Status) ) { Status = STATUS_SUCCESS; } } } else { Status = STATUS_OBJECT_PATH_SYNTAX_BAD; } } #ifdef SMP_SHOW_REGISTRY_DATA DbgPrint( "SMSS: Unused SubSystem( %wZ = %wZ )\n", &p->Name, &p->Value ); #endif RtlFreeHeap( RtlProcessHeap(), 0, p ); } Head = &SmpSubSystemsToLoad; while (!IsListEmpty( Head )) { Next = RemoveHeadList( Head ); p = CONTAINING_RECORD( Next, SMP_REGISTRY_VALUE, Entry ); #ifdef SMP_SHOW_REGISTRY_DATA DbgPrint( "SMSS: Loaded SubSystem( %wZ = %wZ )\n", &p->Name, &p->Value ); #endif if (!_wcsicmp( p->Name.Buffer, L"debug" )) { SmpExecuteCommand( &p->Value, SMP_SUBSYSTEM_FLAG | SMP_DEBUG_FLAG ); } else { SmpExecuteCommand( &p->Value, SMP_SUBSYSTEM_FLAG ); } RtlFreeHeap( RtlProcessHeap(), 0, p ); } Head = &SmpExecuteList; if (!IsListEmpty( Head )) { Next = Head->Blink; p = CONTAINING_RECORD( Next, SMP_REGISTRY_VALUE, Entry ); RemoveEntryList( &p->Entry ); *InitialCommand = p->Name; // // This path is only taken when people want to run ntsd -p -1 winlogon // // This is nearly impossible to do in a race free manner. In some // cases, we can get in a state where we can not properly fail // a debug API. This is due to the subsystem switch that occurs // when ntsd is invoked on csr. If csr is relatively idle, this // does not occur. If it is active when you attach, then we can get // into a potential race. The slimy fix is to do a 5 second delay // if the command line is anything other that the default. // { LARGE_INTEGER DelayTime; DelayTime.QuadPart = Int32x32To64( 5000, -10000 ); NtDelayExecution( FALSE, &DelayTime ); } } else { RtlInitUnicodeString( InitialCommand, L"winlogon.exe" ); InitialCommandBuffer[ 0 ] = UNICODE_NULL; LdrQueryImageFileExecutionOptions( InitialCommand, L"Debugger", REG_SZ, InitialCommandBuffer, sizeof( InitialCommandBuffer ), NULL ); if (InitialCommandBuffer[ 0 ] != UNICODE_NULL) { wcscat( InitialCommandBuffer, L" " ); wcscat( InitialCommandBuffer, InitialCommand->Buffer ); RtlInitUnicodeString( InitialCommand, InitialCommandBuffer ); KdPrint(( "SMSS: InitialCommand == '%wZ'\n", InitialCommand )); } } while (!IsListEmpty( Head )) { Next = RemoveHeadList( Head ); p = CONTAINING_RECORD( Next, SMP_REGISTRY_VALUE, Entry ); #ifdef SMP_SHOW_REGISTRY_DATA DbgPrint( "SMSS: Execute( %wZ )\n", &p->Name ); #endif SmpExecuteCommand( &p->Name, 0 ); RtlFreeHeap( RtlProcessHeap(), 0, p ); } #ifdef SMP_SHOW_REGISTRY_DATA DbgPrint( "SMSS: InitialCommand( %wZ )\n", InitialCommand ); #endif return( Status ); } NTSTATUS SmpCreateDynamicEnvironmentVariables( VOID ) { NTSTATUS Status; SYSTEM_BASIC_INFORMATION SystemInfo; SYSTEM_PROCESSOR_INFORMATION ProcessorInfo; OBJECT_ATTRIBUTES ObjectAttributes; UNICODE_STRING KeyName; UNICODE_STRING ValueName; PWSTR ValueData; WCHAR ValueBuffer[ 256 ]; WCHAR ValueBuffer1[ 256 ]; PKEY_VALUE_PARTIAL_INFORMATION KeyValueInfo; ULONG ValueLength; HANDLE Key, Key1; Status = NtQuerySystemInformation( SystemBasicInformation, &SystemInfo, sizeof( SystemInfo ), NULL ); if (!NT_SUCCESS( Status )) { KdPrint(( "SMSS: Unable to query system basic information - %x\n", Status )); return Status; } Status = NtQuerySystemInformation( SystemProcessorInformation, &ProcessorInfo, sizeof( ProcessorInfo ), NULL ); if (!NT_SUCCESS( Status )) { KdPrint(( "SMSS: Unable to query system processor information - %x\n", Status )); return Status; } RtlInitUnicodeString( &KeyName, L"\\Registry\\Machine\\System\\CurrentControlSet\\Control\\Session Manager\\Environment" ); InitializeObjectAttributes( &ObjectAttributes, &KeyName, OBJ_CASE_INSENSITIVE, NULL, NULL ); Status = NtOpenKey( &Key, GENERIC_WRITE, &ObjectAttributes ); if (!NT_SUCCESS( Status )) { KdPrint(( "SMSS: Unable to open %wZ - %x\n", &KeyName, Status )); return Status; } RtlInitUnicodeString( &ValueName, L"OS" ); ValueData = L"Windows_NT"; Status = NtSetValueKey( Key, &ValueName, 0, REG_SZ, ValueData, (wcslen( ValueData ) + 1) * sizeof( WCHAR ) ); if (!NT_SUCCESS( Status )) { KdPrint(( "SMSS: Failed writing %wZ environment variable - %x\n", &ValueName, Status )); goto failexit; } RtlInitUnicodeString( &ValueName, L"PROCESSOR_ARCHITECTURE" ); switch( ProcessorInfo.ProcessorArchitecture ) { case PROCESSOR_ARCHITECTURE_INTEL: ValueData = L"x86"; break; case PROCESSOR_ARCHITECTURE_MIPS: ValueData = L"MIPS"; break; case PROCESSOR_ARCHITECTURE_ALPHA: ValueData = L"ALPHA"; break; case PROCESSOR_ARCHITECTURE_PPC: ValueData = L"PPC"; break; default: ValueData = L"Unknown"; break; } Status = NtSetValueKey( Key, &ValueName, 0, REG_SZ, ValueData, (wcslen( ValueData ) + 1) * sizeof( WCHAR ) ); if (!NT_SUCCESS( Status )) { KdPrint(( "SMSS: Failed writing %wZ environment variable - %x\n", &ValueName, Status )); goto failexit; } RtlInitUnicodeString( &ValueName, L"PROCESSOR_LEVEL" ); switch( ProcessorInfo.ProcessorArchitecture ) { case PROCESSOR_ARCHITECTURE_MIPS: // // Multiple MIPS level by 1000 so 4 becomes 4000 // swprintf( ValueBuffer, L"%u", ProcessorInfo.ProcessorLevel * 1000 ); break; case PROCESSOR_ARCHITECTURE_PPC: // // Just output the ProcessorLevel in decimal. // swprintf( ValueBuffer, L"%u", ProcessorInfo.ProcessorLevel ); break; case PROCESSOR_ARCHITECTURE_INTEL: case PROCESSOR_ARCHITECTURE_ALPHA: default: // // All others use a single level number // swprintf( ValueBuffer, L"%u", ProcessorInfo.ProcessorLevel ); break; } Status = NtSetValueKey( Key, &ValueName, 0, REG_SZ, ValueBuffer, (wcslen( ValueBuffer ) + 1) * sizeof( WCHAR ) ); if (!NT_SUCCESS( Status )) { KdPrint(( "SMSS: Failed writing %wZ environment variable - %x\n", &ValueName, Status )); goto failexit; } RtlInitUnicodeString( &KeyName, L"\\Registry\\Machine\\Hardware\\Description\\System\\CentralProcessor\\0" ); InitializeObjectAttributes( &ObjectAttributes, &KeyName, OBJ_CASE_INSENSITIVE, NULL, NULL ); Status = NtOpenKey( &Key1, KEY_READ, &ObjectAttributes ); if (!NT_SUCCESS( Status )) { KdPrint(( "SMSS: Unable to open %wZ - %x\n", &KeyName, Status )); goto failexit; } RtlInitUnicodeString( &ValueName, L"Identifier" ); KeyValueInfo = (PKEY_VALUE_PARTIAL_INFORMATION)ValueBuffer; Status = NtQueryValueKey( Key1, &ValueName, KeyValuePartialInformation, (PVOID)KeyValueInfo, sizeof( ValueBuffer ), &ValueLength ); if (!NT_SUCCESS( Status )) { NtClose( Key1 ); KdPrint(( "SMSS: Unable to read %wZ\\%wZ - %x\n", &KeyName, &ValueName, Status )); goto failexit; } ValueData = (PWSTR)KeyValueInfo->Data; RtlInitUnicodeString( &ValueName, L"VendorIdentifier" ); KeyValueInfo = (PKEY_VALUE_PARTIAL_INFORMATION)ValueBuffer1; Status = NtQueryValueKey( Key1, &ValueName, KeyValuePartialInformation, (PVOID)KeyValueInfo, sizeof( ValueBuffer1 ), &ValueLength ); NtClose( Key1 ); if (NT_SUCCESS( Status )) { swprintf( ValueData + wcslen( ValueData ), L", %ws", (PWSTR)KeyValueInfo->Data ); } RtlInitUnicodeString( &ValueName, L"PROCESSOR_IDENTIFIER" ); Status = NtSetValueKey( Key, &ValueName, 0, REG_SZ, ValueData, (wcslen( ValueData ) + 1) * sizeof( WCHAR ) ); if (!NT_SUCCESS( Status )) { KdPrint(( "SMSS: Failed writing %wZ environment variable - %x\n", &ValueName, Status )); goto failexit; } RtlInitUnicodeString( &ValueName, L"PROCESSOR_REVISION" ); switch( ProcessorInfo.ProcessorArchitecture ) { case PROCESSOR_ARCHITECTURE_INTEL: if ((ProcessorInfo.ProcessorRevision >> 8) == 0xFF) { // // Intel 386/486 are An stepping format // swprintf( ValueBuffer, L"%02x", ProcessorInfo.ProcessorRevision & 0xFF ); _wcsupr( ValueBuffer ); break; } // Fall through for Cyrix/NextGen 486 and Pentium processors. case PROCESSOR_ARCHITECTURE_PPC: // // Intel and PowerPC use fixed point binary number // Output is 4 hex digits, no formatting. // swprintf( ValueBuffer, L"%04x", ProcessorInfo.ProcessorRevision ); break; case PROCESSOR_ARCHITECTURE_ALPHA: swprintf( ValueBuffer, L"Model %c, Pass %u", 'A' + (ProcessorInfo.ProcessorRevision >> 8), ProcessorInfo.ProcessorRevision & 0xFF ); swprintf( ValueBuffer, L"%u", ProcessorInfo.ProcessorRevision ); break; case PROCESSOR_ARCHITECTURE_MIPS: default: // // All others use a single revision number // swprintf( ValueBuffer, L"%u", ProcessorInfo.ProcessorRevision ); break; } Status = NtSetValueKey( Key, &ValueName, 0, REG_SZ, ValueBuffer, (wcslen( ValueBuffer ) + 1) * sizeof( WCHAR ) ); if (!NT_SUCCESS( Status )) { KdPrint(( "SMSS: Failed writing %wZ environment variable - %x\n", &ValueName, Status )); goto failexit; } RtlInitUnicodeString( &ValueName, L"NUMBER_OF_PROCESSORS" ); swprintf( ValueBuffer, L"%u", SystemInfo.NumberOfProcessors ); Status = NtSetValueKey( Key, &ValueName, 0, REG_SZ, ValueBuffer, (wcslen( ValueBuffer ) + 1) * sizeof( WCHAR ) ); if (!NT_SUCCESS( Status )) { KdPrint(( "SMSS: Failed writing %wZ environment variable - %x\n", &ValueName, Status )); goto failexit; } failexit: NtClose( Key ); return Status; } NTSTATUS SmpInitializeDosDevices( VOID ) { NTSTATUS Status; PLIST_ENTRY Head, Next; PSMP_REGISTRY_VALUE p; UNICODE_STRING UnicodeString; OBJECT_ATTRIBUTES ObjectAttributes; HANDLE LinkHandle; SECURITY_DESCRIPTOR_CONTROL OriginalSdControl; // // Do DosDevices initialization - the directory object is created in I/O init // RtlInitUnicodeString( &UnicodeString, L"\\??" ); InitializeObjectAttributes( &ObjectAttributes, &UnicodeString, OBJ_CASE_INSENSITIVE | OBJ_OPENIF | OBJ_PERMANENT, NULL, NULL ); Status = NtOpenDirectoryObject( &SmpDosDevicesObjectDirectory, DIRECTORY_ALL_ACCESS, &ObjectAttributes ); if (!NT_SUCCESS( Status )) { KdPrint(( "SMSS: Unable to open %wZ directory - Status == %lx\n", &UnicodeString, Status )); return( Status ); } // // Process the list of defined DOS devices and create their // associated symbolic links in the \DosDevices object directory. // Head = &SmpDosDevicesList; while (!IsListEmpty( Head )) { Next = RemoveHeadList( Head ); p = CONTAINING_RECORD( Next, SMP_REGISTRY_VALUE, Entry ); #ifdef SMP_SHOW_REGISTRY_DATA DbgPrint( "SMSS: DosDevices( %wZ = %wZ )\n", &p->Name, &p->Value ); #endif InitializeObjectAttributes( &ObjectAttributes, &p->Name, OBJ_CASE_INSENSITIVE | OBJ_PERMANENT | OBJ_OPENIF, SmpDosDevicesObjectDirectory, SmpPrimarySecurityDescriptor ); SmpSetDaclDefaulted( &ObjectAttributes, &OriginalSdControl ); //Use inheritable protection if available Status = NtCreateSymbolicLinkObject( &LinkHandle, SYMBOLIC_LINK_ALL_ACCESS, &ObjectAttributes, &p->Value ); if (Status == STATUS_OBJECT_NAME_EXISTS) { NtMakeTemporaryObject( LinkHandle ); NtClose( LinkHandle ); if (p->Value.Length != 0) { ObjectAttributes.Attributes &= ~OBJ_OPENIF; Status = NtCreateSymbolicLinkObject( &LinkHandle, SYMBOLIC_LINK_ALL_ACCESS, &ObjectAttributes, &p->Value ); } else { Status = STATUS_SUCCESS; } } SmpRestoreDaclDefaulted( &ObjectAttributes, OriginalSdControl ); if (!NT_SUCCESS( Status )) { KdPrint(( "SMSS: Unable to create %wZ => %wZ symbolic link object - Status == 0x%lx\n", &p->Name, &p->Value, Status )); return( Status ); } NtClose( LinkHandle ); RtlFreeHeap( RtlProcessHeap(), 0, p ); } return( Status ); } VOID SmpProcessModuleImports( IN PVOID Parameter, IN PCHAR ModuleName ) { NTSTATUS Status; WCHAR NameBuffer[ DOS_MAX_PATH_LENGTH ]; UNICODE_STRING UnicodeString; ANSI_STRING AnsiString; PWSTR Name, Value; ULONG n; PWSTR s; PSMP_REGISTRY_VALUE p; // // Skip NTDLL.DLL as it is implicitly added to KnownDll list by kernel // before SMSS.EXE is started. // if (!_stricmp( ModuleName, "ntdll.dll" )) { return; } RtlInitAnsiString( &AnsiString, ModuleName ); UnicodeString.Buffer = NameBuffer; UnicodeString.Length = 0; UnicodeString.MaximumLength = sizeof( NameBuffer ); Status = RtlAnsiStringToUnicodeString( &UnicodeString, &AnsiString, FALSE ); if (!NT_SUCCESS( Status )) { return; } UnicodeString.MaximumLength = (USHORT)(UnicodeString.Length + sizeof( UNICODE_NULL )); s = UnicodeString.Buffer; n = 0; while (n < UnicodeString.Length) { if (*s == L'.') { break; } else { n += sizeof( WCHAR ); s += 1; } } Value = UnicodeString.Buffer; Name = UnicodeString.Buffer + (UnicodeString.MaximumLength / sizeof( WCHAR )); n = n / sizeof( WCHAR ); wcsncpy( Name, Value, n ); Name[ n ] = UNICODE_NULL; Status = SmpSaveRegistryValue( (PLIST_ENTRY)&SmpKnownDllsList, Name, Value, TRUE ); if (Status == STATUS_OBJECT_NAME_EXISTS || !NT_SUCCESS( Status )) { return; } p = CONTAINING_RECORD( (PLIST_ENTRY)Parameter, SMP_REGISTRY_VALUE, Entry ); KdPrint(( "SMSS: %wZ added %ws to KnownDlls\n", &p->Value, Value )); return; } NTSTATUS SmpInitializeKnownDlls( VOID ) { NTSTATUS Status; PLIST_ENTRY Head, Next; PSMP_REGISTRY_VALUE p; PSMP_REGISTRY_VALUE pExclude; UNICODE_STRING UnicodeString; OBJECT_ATTRIBUTES ObjectAttributes; HANDLE LinkHandle, FileHandle, SectionHandle; IO_STATUS_BLOCK IoStatusBlock; UNICODE_STRING FileName; SECURITY_DESCRIPTOR_CONTROL OriginalSdControl; USHORT ImageCharacteristics; // // Create \KnownDlls object directory // RtlInitUnicodeString( &UnicodeString, L"\\KnownDlls" ); InitializeObjectAttributes( &ObjectAttributes, &UnicodeString, OBJ_CASE_INSENSITIVE | OBJ_OPENIF | OBJ_PERMANENT, NULL, SmpKnownDllsSecurityDescriptor ); Status = NtCreateDirectoryObject( &SmpKnownDllObjectDirectory, DIRECTORY_ALL_ACCESS, &ObjectAttributes ); if (!NT_SUCCESS( Status )) { KdPrint(( "SMSS: Unable to create %wZ directory - Status == %lx\n", &UnicodeString, Status )); return( Status ); } // // Open a handle to the file system directory that contains all the // known DLL files so we can do relative opens. // if (!RtlDosPathNameToNtPathName_U( SmpKnownDllPath.Buffer, &FileName, NULL, NULL ) ) { KdPrint(( "SMSS: Unable to to convert %wZ to an Nt path\n", &SmpKnownDllPath )); return( STATUS_OBJECT_NAME_INVALID ); } InitializeObjectAttributes( &ObjectAttributes, &FileName, OBJ_CASE_INSENSITIVE, NULL, NULL ); // // Open a handle to the known dll file directory. Don't allow // deletes of the directory. // Status = NtOpenFile( &SmpKnownDllFileDirectory, FILE_LIST_DIRECTORY | SYNCHRONIZE, &ObjectAttributes, &IoStatusBlock, FILE_SHARE_READ | FILE_SHARE_WRITE, FILE_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT ); if (!NT_SUCCESS( Status )) { KdPrint(( "SMSS: Unable to open a handle to the KnownDll directory (%wZ) - Status == %lx\n", &SmpKnownDllPath, Status )); return Status; } RtlInitUnicodeString( &UnicodeString, L"KnownDllPath" ); InitializeObjectAttributes( &ObjectAttributes, &UnicodeString, OBJ_CASE_INSENSITIVE | OBJ_OPENIF | OBJ_PERMANENT, SmpKnownDllObjectDirectory, SmpPrimarySecurityDescriptor ); SmpSetDaclDefaulted( &ObjectAttributes, &OriginalSdControl ); //Use inheritable protection if available Status = NtCreateSymbolicLinkObject( &LinkHandle, SYMBOLIC_LINK_ALL_ACCESS, &ObjectAttributes, &SmpKnownDllPath ); SmpRestoreDaclDefaulted( &ObjectAttributes, OriginalSdControl ); if (!NT_SUCCESS( Status )) { KdPrint(( "SMSS: Unable to create %wZ symbolic link - Status == %lx\n", &UnicodeString, Status )); return( Status ); } Head = &SmpKnownDllsList; Next = Head->Flink; while (Next != Head) { HANDLE ObjectDirectory; ObjectDirectory = NULL; p = CONTAINING_RECORD( Next, SMP_REGISTRY_VALUE, Entry ); pExclude = SmpFindRegistryValue( &SmpExcludeKnownDllsList, p->Name.Buffer ); if (pExclude == NULL) { pExclude = SmpFindRegistryValue( &SmpExcludeKnownDllsList, p->Value.Buffer ); } if (pExclude != NULL) { KdPrint(( "Excluding %wZ from KnownDlls\n", &p->Value )); Status = STATUS_OBJECT_NAME_NOT_FOUND; } else { #ifdef SMP_SHOW_REGISTRY_DATA DbgPrint( "SMSS: KnownDll( %wZ = %wZ )\n", &p->Name, &p->Value ); #endif InitializeObjectAttributes( &ObjectAttributes, &p->Value, OBJ_CASE_INSENSITIVE, SmpKnownDllFileDirectory, NULL ); Status = NtOpenFile( &FileHandle, SYNCHRONIZE | FILE_EXECUTE, &ObjectAttributes, &IoStatusBlock, FILE_SHARE_READ | FILE_SHARE_DELETE, FILE_NON_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT ); } if (NT_SUCCESS( Status )) { ObjectDirectory = SmpKnownDllObjectDirectory; Status = LdrVerifyImageMatchesChecksum(FileHandle, SmpProcessModuleImports, Next, &ImageCharacteristics ); if ( Status == STATUS_IMAGE_CHECKSUM_MISMATCH ) { ULONG ErrorParameters; ULONG ErrorResponse; // // Hard error time. One of the know DLL's is corrupt ! // ErrorParameters = (ULONG)(&p->Value); NtRaiseHardError( Status, 1, 1, &ErrorParameters, OptionOk, &ErrorResponse ); } else if (ImageCharacteristics & IMAGE_FILE_DLL) { InitializeObjectAttributes( &ObjectAttributes, &p->Value, OBJ_CASE_INSENSITIVE | OBJ_PERMANENT, ObjectDirectory, SmpLiberalSecurityDescriptor ); SmpSetDaclDefaulted( &ObjectAttributes, &OriginalSdControl ); //use inheritable protection if available Status = NtCreateSection( &SectionHandle, SECTION_ALL_ACCESS, &ObjectAttributes, NULL, PAGE_EXECUTE, SEC_IMAGE, FileHandle ); SmpRestoreDaclDefaulted( &ObjectAttributes, OriginalSdControl ); if (!NT_SUCCESS( Status )) { KdPrint(("SMSS: CreateSection for KnownDll %wZ failed - Status == %lx\n", &p->Value, Status )); } else { NtClose(SectionHandle); } } else { KdPrint(( "SMSS: Ignoring %wZ as KnownDll since it is not a DLL\n", &p->Value )); } NtClose( FileHandle ); } Next = Next->Flink; // // Note that section remains open. This will keep it around. // Maybe this should be a permenent section ? // } Head = &SmpKnownDllsList; Next = Head->Flink; while (Next != Head) { p = CONTAINING_RECORD( Next, SMP_REGISTRY_VALUE, Entry ); Next = Next->Flink; RtlFreeHeap( RtlProcessHeap(), 0, p ); } } VOID SmpProcessFileRenames( VOID ) { NTSTATUS Status; PLIST_ENTRY Head, Next; PSMP_REGISTRY_VALUE p; OBJECT_ATTRIBUTES ObjectAttributes; IO_STATUS_BLOCK IoStatusBlock; HANDLE OldFileHandle; PFILE_RENAME_INFORMATION RenameInformation; FILE_DISPOSITION_INFORMATION DeleteInformation; FILE_INFORMATION_CLASS SetInfoClass; ULONG SetInfoLength; PVOID SetInfoBuffer; PWSTR s; BOOLEAN WasEnabled; Status = RtlAdjustPrivilege( SE_RESTORE_PRIVILEGE, TRUE, FALSE, &WasEnabled ); if (!NT_SUCCESS( Status )) { WasEnabled = TRUE; } // // Process the list of file rename operations. // Head = &SmpFileRenameList; while (!IsListEmpty( Head )) { Next = RemoveHeadList( Head ); p = CONTAINING_RECORD( Next, SMP_REGISTRY_VALUE, Entry ); #ifdef SMP_SHOW_REGISTRY_DATA DbgPrint( "SMSS: FileRename( %wZ => %wZ )\n", &p->Name, &p->Value ); #endif InitializeObjectAttributes( &ObjectAttributes, &p->Name, OBJ_CASE_INSENSITIVE, NULL, NULL ); // // Open the file for delete access // Status = NtOpenFile( &OldFileHandle, (ACCESS_MASK)DELETE | SYNCHRONIZE, &ObjectAttributes, &IoStatusBlock, FILE_SHARE_READ | FILE_SHARE_WRITE, FILE_SYNCHRONOUS_IO_NONALERT ); if (NT_SUCCESS( Status )) { if (p->Value.Length == 0) { SetInfoClass = FileDispositionInformation; SetInfoLength = sizeof( DeleteInformation ); SetInfoBuffer = &DeleteInformation; DeleteInformation.DeleteFile = TRUE; RenameInformation = NULL; } else { SetInfoClass = FileRenameInformation; SetInfoLength = p->Value.Length + sizeof( *RenameInformation ); s = p->Value.Buffer; if (*s == L'!') { s++; SetInfoLength -= sizeof( UNICODE_NULL ); } SetInfoBuffer = RtlAllocateHeap( RtlProcessHeap(), MAKE_TAG( INIT_TAG ), SetInfoLength ); if (SetInfoBuffer != NULL) { RenameInformation = SetInfoBuffer; RenameInformation->ReplaceIfExists = (BOOLEAN)(s != p->Value.Buffer); RenameInformation->RootDirectory = NULL; RenameInformation->FileNameLength = SetInfoLength - sizeof( *RenameInformation ); RtlMoveMemory( RenameInformation->FileName, s, RenameInformation->FileNameLength ); } else { Status = STATUS_NO_MEMORY; } } if (NT_SUCCESS( Status )) { Status = NtSetInformationFile( OldFileHandle, &IoStatusBlock, SetInfoBuffer, SetInfoLength, SetInfoClass ); } NtClose( OldFileHandle ); } #if DBG if (!NT_SUCCESS( Status )) { DbgPrint( "SM: %wZ => %wZ failed - Status == %x\n", &p->Name, &p->Value, Status ); } else if (p->Value.Length == 0) { DbgPrint( "SM: %wZ (deleted)\n", &p->Name ); } else { DbgPrint( "SM: %wZ (renamed to) %wZ\n", &p->Name, &p->Value ); } #endif RtlFreeHeap( RtlProcessHeap(), 0, p ); } if (!WasEnabled) { Status = RtlAdjustPrivilege( SE_RESTORE_PRIVILEGE, FALSE, FALSE, &WasEnabled ); } return; } #ifdef SMP_SHOW_REGISTRY_DATA VOID SmpDumpQuery( IN PCHAR RoutineName, IN PWSTR ValueName, IN ULONG ValueType, IN PVOID ValueData, IN ULONG ValueLength ) { PWSTR s; if (ValueName == NULL) { DbgPrint( "SM: SmpConfigure%s( %ws )\n", RoutineName ); return; } if (ValueData == NULL) { DbgPrint( "SM: SmpConfigure%s( %ws, %ws NULL ValueData )\n", RoutineName, ValueName ); return; } s = (PWSTR)ValueData; DbgPrint( "SM: SmpConfigure%s( %ws, %u, (%u) ", RoutineName, ValueName, ValueType, ValueLength ); if (ValueType == REG_SZ || ValueType == REG_EXPAND_SZ || ValueType == REG_MULTI_SZ) { while (*s) { if (s != (PWSTR)ValueData) { DbgPrint( ", " ); } DbgPrint( "'%ws'", s ); while(*s++) { } if (ValueType != REG_MULTI_SZ) { break; } } } else { DbgPrint( "*** non-string data (%08lx)", *(PULONG)ValueData ); } DbgPrint( "\n" ); } #endif NTSTATUS SmpConfigureObjectDirectories( IN PWSTR ValueName, IN ULONG ValueType, IN PVOID ValueData, IN ULONG ValueLength, IN PVOID Context, IN PVOID EntryContext ) { PWSTR s; UNICODE_STRING UnicodeString; UNICODE_STRING RpcControl; UNICODE_STRING Windows; NTSTATUS Status; OBJECT_ATTRIBUTES ObjectAttributes; HANDLE DirectoryHandle; PSECURITY_DESCRIPTOR SecurityDescriptor; UNREFERENCED_PARAMETER( Context ); RtlInitUnicodeString( &RpcControl, L"\\RPC Control"); RtlInitUnicodeString( &Windows, L"\\Windows"); #ifdef SMP_SHOW_REGISTRY_DATA SmpDumpQuery( "ObjectDirectories", ValueName, ValueType, ValueData, ValueLength ); #else UNREFERENCED_PARAMETER( ValueName ); UNREFERENCED_PARAMETER( ValueType ); UNREFERENCED_PARAMETER( ValueLength ); #endif s = (PWSTR)ValueData; while (*s) { RtlInitUnicodeString( &UnicodeString, s ); // // This is NOT how I would choose to do this if starting from // scratch, but we are very close to shipping Daytona and I // needed to get the right protection on these objects. // SecurityDescriptor = SmpPrimarySecurityDescriptor; if (RtlEqualString( (PSTRING)&UnicodeString, (PSTRING)&RpcControl, TRUE ) || RtlEqualString( (PSTRING)&UnicodeString, (PSTRING)&Windows, TRUE) ) { SecurityDescriptor = SmpLiberalSecurityDescriptor; } InitializeObjectAttributes( &ObjectAttributes, &UnicodeString, OBJ_CASE_INSENSITIVE | OBJ_OPENIF | OBJ_PERMANENT, NULL, SecurityDescriptor ); Status = NtCreateDirectoryObject( &DirectoryHandle, DIRECTORY_ALL_ACCESS, &ObjectAttributes ); if (!NT_SUCCESS( Status )) { KdPrint(( "SMSS: Unable to create %wZ object directory - Status == %lx\n", &UnicodeString, Status )); } else { NtClose( DirectoryHandle ); } while (*s++) { } } // // We dont care if the creates failed. // return( STATUS_SUCCESS ); } NTSTATUS SmpConfigureExecute( IN PWSTR ValueName, IN ULONG ValueType, IN PVOID ValueData, IN ULONG ValueLength, IN PVOID Context, IN PVOID EntryContext ) { UNREFERENCED_PARAMETER( Context ); #ifdef SMP_SHOW_REGISTRY_DATA SmpDumpQuery( "Execute", ValueName, ValueType, ValueData, ValueLength ); #else UNREFERENCED_PARAMETER( ValueName ); UNREFERENCED_PARAMETER( ValueType ); UNREFERENCED_PARAMETER( ValueLength ); #endif return (SmpSaveRegistryValue( (PLIST_ENTRY)EntryContext, ValueData, NULL, TRUE ) ); } NTSTATUS SmpConfigureMemoryMgmt( IN PWSTR ValueName, IN ULONG ValueType, IN PVOID ValueData, IN ULONG ValueLength, IN PVOID Context, IN PVOID EntryContext ) { NTSTATUS Status = STATUS_SUCCESS; UNREFERENCED_PARAMETER( Context ); #ifdef SMP_SHOW_REGISTRY_DATA SmpDumpQuery( "MemoryMgmt", ValueName, ValueType, ValueData, ValueLength ); #else UNREFERENCED_PARAMETER( ValueName ); UNREFERENCED_PARAMETER( ValueType ); UNREFERENCED_PARAMETER( ValueLength ); #endif return (SmpSaveRegistryValue( (PLIST_ENTRY)EntryContext, ValueData, NULL, TRUE ) ); } NTSTATUS SmpConfigureFileRenames( IN PWSTR ValueName, IN ULONG ValueType, IN PVOID ValueData, IN ULONG ValueLength, IN PVOID Context, IN PVOID EntryContext ) { NTSTATUS Status; static PWSTR OldName = NULL; UNREFERENCED_PARAMETER( Context ); #ifdef SMP_SHOW_REGISTRY_DATA SmpDumpQuery( "FileRenameOperation", ValueName, ValueType, ValueData, ValueLength ); #else UNREFERENCED_PARAMETER( ValueType ); #endif // // This routine gets called for each string in the MULTI_SZ. The // first string we get is the old name, the next string is the new name. // if (OldName == NULL) { // // Save a pointer to the old name, we'll need it on the next // callback. // OldName = ValueData; return(STATUS_SUCCESS); } else { Status = SmpSaveRegistryValue((PLIST_ENTRY)EntryContext, OldName, ValueData, FALSE); if (!NT_SUCCESS(Status)) { #ifdef SMP_SHOW_REGISTRY_DATA DbgPrint("SMSS: SmpSaveRegistryValue returned %08lx for FileRenameOperation\n", Status); DbgPrint("SMSS: %ws %ws\n", OldName, ValueData); #endif } OldName = NULL; return(Status); } } NTSTATUS SmpConfigureDosDevices( IN PWSTR ValueName, IN ULONG ValueType, IN PVOID ValueData, IN ULONG ValueLength, IN PVOID Context, IN PVOID EntryContext ) { UNREFERENCED_PARAMETER( Context ); #ifdef SMP_SHOW_REGISTRY_DATA SmpDumpQuery( "DosDevices", ValueName, ValueType, ValueData, ValueLength ); #else UNREFERENCED_PARAMETER( ValueType ); UNREFERENCED_PARAMETER( ValueLength ); #endif return (SmpSaveRegistryValue( (PLIST_ENTRY)EntryContext, ValueName, ValueData, TRUE ) ); } NTSTATUS SmpConfigureKnownDlls( IN PWSTR ValueName, IN ULONG ValueType, IN PVOID ValueData, IN ULONG ValueLength, IN PVOID Context, IN PVOID EntryContext ) { UNREFERENCED_PARAMETER( Context ); #ifdef SMP_SHOW_REGISTRY_DATA SmpDumpQuery( "KnownDlls", ValueName, ValueType, ValueData, ValueLength ); #else UNREFERENCED_PARAMETER( ValueType ); #endif if (!_wcsicmp( ValueName, L"DllDirectory" )) { SmpKnownDllPath.Buffer = RtlAllocateHeap( RtlProcessHeap(), MAKE_TAG( INIT_TAG ), ValueLength ); if (SmpKnownDllPath.Buffer == NULL) { return( STATUS_NO_MEMORY ); } SmpKnownDllPath.Length = (USHORT)(ValueLength - sizeof( UNICODE_NULL ) ); SmpKnownDllPath.MaximumLength = (USHORT)ValueLength; RtlMoveMemory( SmpKnownDllPath.Buffer, ValueData, ValueLength ); return( STATUS_SUCCESS ); } else { return (SmpSaveRegistryValue( (PLIST_ENTRY)EntryContext, ValueName, ValueData, TRUE ) ); } } NTSTATUS SmpConfigureExcludeKnownDlls( IN PWSTR ValueName, IN ULONG ValueType, IN PVOID ValueData, IN ULONG ValueLength, IN PVOID Context, IN PVOID EntryContext ) { NTSTATUS Status; UNREFERENCED_PARAMETER( Context ); #ifdef SMP_SHOW_REGISTRY_DATA SmpDumpQuery( "ExcludeKnownDlls", ValueName, ValueType, ValueData, ValueLength ); #else UNREFERENCED_PARAMETER( ValueType ); #endif if (ValueType == REG_MULTI_SZ || ValueType == REG_SZ) { PWSTR s; s = (PWSTR)ValueData; while (*s != UNICODE_NULL) { Status = SmpSaveRegistryValue( (PLIST_ENTRY)EntryContext, s, NULL, TRUE ); if (!NT_SUCCESS( Status ) || ValueType == REG_SZ) { return Status; } while (*s++ != UNICODE_NULL) { } } } return( STATUS_SUCCESS ); } NTSTATUS SmpConfigureEnvironment( IN PWSTR ValueName, IN ULONG ValueType, IN PVOID ValueData, IN ULONG ValueLength, IN PVOID Context, IN PVOID EntryContext ) { NTSTATUS Status; UNICODE_STRING Name, Value; UNREFERENCED_PARAMETER( Context ); UNREFERENCED_PARAMETER( EntryContext ); #ifdef SMP_SHOW_REGISTRY_DATA SmpDumpQuery( "Environment", ValueName, ValueType, ValueData, ValueLength ); #else UNREFERENCED_PARAMETER( ValueType ); #endif RtlInitUnicodeString( &Name, ValueName ); RtlInitUnicodeString( &Value, ValueData ); Status = RtlSetEnvironmentVariable( NULL, &Name, &Value ); if (!NT_SUCCESS( Status )) { KdPrint(( "SMSS: 'SET %wZ = %wZ' failed - Status == %lx\n", &Name, &Value, Status )); return( Status ); } if (!_wcsicmp( ValueName, L"Path" )) { SmpDefaultLibPathBuffer = RtlAllocateHeap( RtlProcessHeap(), MAKE_TAG( INIT_TAG ), ValueLength ); if ( !SmpDefaultLibPathBuffer ) { return ( STATUS_NO_MEMORY ); } RtlMoveMemory( SmpDefaultLibPathBuffer, ValueData, ValueLength ); RtlInitUnicodeString( &SmpDefaultLibPath, SmpDefaultLibPathBuffer ); } return( STATUS_SUCCESS ); } NTSTATUS SmpConfigureSubSystems( IN PWSTR ValueName, IN ULONG ValueType, IN PVOID ValueData, IN ULONG ValueLength, IN PVOID Context, IN PVOID EntryContext ) { UNREFERENCED_PARAMETER( Context ); #ifdef SMP_SHOW_REGISTRY_DATA SmpDumpQuery( "SubSystems", ValueName, ValueType, ValueData, ValueLength ); #else UNREFERENCED_PARAMETER( ValueLength ); #endif if (!_wcsicmp( ValueName, L"Required" ) || !_wcsicmp( ValueName, L"Optional" )) { if (ValueType == REG_MULTI_SZ) { // // Here if processing Required= or Optional= values, since they are // the only REG_MULTI_SZ value types under the SubSystem key. // PSMP_REGISTRY_VALUE p; PWSTR s; s = (PWSTR)ValueData; while (*s != UNICODE_NULL) { p = SmpFindRegistryValue( (PLIST_ENTRY)EntryContext, s ); if (p != NULL) { RemoveEntryList( &p->Entry ); // // Required Subsystems are loaded. Optional subsystems are // defered. // if (!_wcsicmp( ValueName, L"Required" ) ) { InsertTailList( &SmpSubSystemsToLoad, &p->Entry ); } else { InsertTailList( &SmpSubSystemsToDefer, &p->Entry ); } } else { KdPrint(( "SMSS: Invalid subsystem name - %ws\n", s )); } while (*s++ != UNICODE_NULL) { } } } return( STATUS_SUCCESS ); } else { return (SmpSaveRegistryValue( (PLIST_ENTRY)EntryContext, ValueName, ValueData, TRUE ) ); } } NTSTATUS SmpParseToken( IN PUNICODE_STRING Source, IN BOOLEAN RemainderOfSource, OUT PUNICODE_STRING Token ) { PWSTR s, s1; ULONG i, cb; RtlInitUnicodeString( Token, NULL ); s = Source->Buffer; if (Source->Length == 0) { return( STATUS_SUCCESS ); } i = 0; while ((USHORT)i < Source->Length && *s <= L' ') { s++; i += 2; } if (RemainderOfSource) { cb = Source->Length - (i * sizeof( WCHAR )); s1 = (PWSTR)((PCHAR)s + cb); i = Source->Length / sizeof( WCHAR ); } else { s1 = s; while ((USHORT)i < Source->Length && *s1 > L' ') { s1++; i += 2; } cb = (PCHAR)s1 - (PCHAR)s; while ((USHORT)i < Source->Length && *s1 <= L' ') { s1++; i += 2; } } if (cb > 0) { Token->Buffer = RtlAllocateHeap( RtlProcessHeap(), MAKE_TAG( INIT_TAG ), cb + sizeof( UNICODE_NULL ) ); if (Token->Buffer == NULL) { return( STATUS_NO_MEMORY ); } Token->Length = (USHORT)cb; Token->MaximumLength = (USHORT)(cb + sizeof( UNICODE_NULL )); RtlMoveMemory( Token->Buffer, s, cb ); Token->Buffer[ cb / sizeof( WCHAR ) ] = UNICODE_NULL; } Source->Length -= (USHORT)((PCHAR)s1 - (PCHAR)Source->Buffer); Source->Buffer = s1; return( STATUS_SUCCESS ); } NTSTATUS SmpParseCommandLine( IN PUNICODE_STRING CommandLine, OUT PULONG Flags OPTIONAL, OUT PUNICODE_STRING ImageFileName, OUT PUNICODE_STRING ImageFileDirectory, OUT PUNICODE_STRING Arguments ) { NTSTATUS Status; UNICODE_STRING Input, Token; UNICODE_STRING PathVariableName; UNICODE_STRING PathVariableValue; PWSTR DosFilePart; WCHAR FullDosPathBuffer[ DOS_MAX_PATH_LENGTH ]; ULONG SpResult; RtlInitUnicodeString( ImageFileName, NULL ); RtlInitUnicodeString( Arguments, NULL ); // // make sure lib path has systemroot\system32. Otherwise, the system will // not boot properly // if ( !SmpSystemRoot.Length ) { UNICODE_STRING NewLibString; RtlInitUnicodeString( &SmpSystemRoot,USER_SHARED_DATA->NtSystemRoot ); NewLibString.Length = 0; NewLibString.MaximumLength = SmpSystemRoot.MaximumLength + 20 + // length of \system32; SmpDefaultLibPath.MaximumLength; NewLibString.Buffer = RtlAllocateHeap( RtlProcessHeap(), MAKE_TAG( INIT_TAG ), NewLibString.MaximumLength ); if ( NewLibString.Buffer ) { RtlAppendUnicodeStringToString(&NewLibString,&SmpSystemRoot ); RtlAppendUnicodeToString(&NewLibString,L"\\system32;"); RtlAppendUnicodeStringToString(&NewLibString,&SmpDefaultLibPath ); RtlFreeHeap(RtlProcessHeap(), 0, SmpDefaultLibPath.Buffer ); SmpDefaultLibPath = NewLibString; } } Input = *CommandLine; while (TRUE) { Status = SmpParseToken( &Input, FALSE, &Token ); if (!NT_SUCCESS( Status ) || Token.Buffer == NULL) { return( STATUS_UNSUCCESSFUL ); } if (ARGUMENT_PRESENT( Flags )) { if (RtlEqualUnicodeString( &Token, &SmpDebugKeyword, TRUE )) { *Flags |= SMP_DEBUG_FLAG; RtlFreeHeap( RtlProcessHeap(), 0, Token.Buffer ); continue; } else if (RtlEqualUnicodeString( &Token, &SmpASyncKeyword, TRUE )) { *Flags |= SMP_ASYNC_FLAG; RtlFreeHeap( RtlProcessHeap(), 0, Token.Buffer ); continue; } else if (RtlEqualUnicodeString( &Token, &SmpAutoChkKeyword, TRUE )) { *Flags |= SMP_AUTOCHK_FLAG; RtlFreeHeap( RtlProcessHeap(), 0, Token.Buffer ); continue; } } SpResult = 0; RtlInitUnicodeString( &PathVariableName, L"Path" ); PathVariableValue.Length = 0; PathVariableValue.MaximumLength = 4096; PathVariableValue.Buffer = RtlAllocateHeap( RtlProcessHeap(), MAKE_TAG( INIT_TAG ), PathVariableValue.MaximumLength ); Status = RtlQueryEnvironmentVariable_U( SmpDefaultEnvironment, &PathVariableName, &PathVariableValue ); if ( Status == STATUS_BUFFER_TOO_SMALL ) { PathVariableValue.MaximumLength = PathVariableValue.Length + 2; PathVariableValue.Length = 0; PathVariableValue.Buffer = RtlAllocateHeap( RtlProcessHeap(), MAKE_TAG( INIT_TAG ), PathVariableValue.MaximumLength ); Status = RtlQueryEnvironmentVariable_U( SmpDefaultEnvironment, &PathVariableName, &PathVariableValue ); } if (!NT_SUCCESS( Status )) { KdPrint(( "SMSS: %wZ environment variable not defined.\n", &PathVariableName )); Status = STATUS_OBJECT_NAME_NOT_FOUND; } else if (!ARGUMENT_PRESENT( Flags ) || !(SpResult = RtlDosSearchPath_U( PathVariableValue.Buffer, Token.Buffer, L".exe", sizeof( FullDosPathBuffer ), FullDosPathBuffer, &DosFilePart )) ) { if (!ARGUMENT_PRESENT( Flags )) { wcscpy( FullDosPathBuffer, Token.Buffer ); } else { if ( !SpResult ) { // // The search path call failed. Now try the call again using // the default lib path. This always has systemroot\system32 // at the front. // SpResult = RtlDosSearchPath_U( SmpDefaultLibPath.Buffer, Token.Buffer, L".exe", sizeof( FullDosPathBuffer ), FullDosPathBuffer, &DosFilePart ); } if ( !SpResult ) { *Flags |= SMP_IMAGE_NOT_FOUND; *ImageFileName = Token; RtlFreeHeap( RtlProcessHeap(), 0, PathVariableValue.Buffer ); return( STATUS_SUCCESS ); } } } RtlFreeHeap( RtlProcessHeap(), 0, PathVariableValue.Buffer ); if (NT_SUCCESS( Status ) && !RtlDosPathNameToNtPathName_U( FullDosPathBuffer, ImageFileName, NULL, NULL ) ) { KdPrint(( "SMSS: Unable to translate %ws into an NT File Name\n", FullDosPathBuffer )); Status = STATUS_OBJECT_PATH_INVALID; } if (!NT_SUCCESS( Status )) { return( Status ); } if (ARGUMENT_PRESENT( ImageFileDirectory )) { if (DosFilePart > FullDosPathBuffer) { *--DosFilePart = UNICODE_NULL; RtlCreateUnicodeString( ImageFileDirectory, FullDosPathBuffer ); } else { RtlInitUnicodeString( ImageFileDirectory, NULL ); } } break; } Status = SmpParseToken( &Input, TRUE, Arguments ); return( Status ); } ULONG SmpConvertInteger( IN PWSTR String ) { NTSTATUS Status; UNICODE_STRING UnicodeString; ULONG Value; RtlInitUnicodeString( &UnicodeString, String ); Status = RtlUnicodeStringToInteger( &UnicodeString, 0, &Value ); if (NT_SUCCESS( Status )) { return( Value ); } else { return( 0 ); } } NTSTATUS SmpAddPagingFile( IN PUNICODE_STRING PagingFileSpec ) /*++ Routine Description: This function is called during configuration to add a paging file to the system. The format of PagingFileSpec is: name-of-paging-file size-of-paging-file(in megabytes) Arguments: PagingFileSpec - Unicode string that specifies the paging file name and size. Return Value: Status of operation --*/ { NTSTATUS Status; UNICODE_STRING PagingFileName; UNICODE_STRING Arguments; ULONG PageFileMinSizeInMb; ULONG PageFileMaxSizeInMb; PWSTR ArgSave, Arg2; if (CountPageFiles == MAX_PAGING_FILES) { KdPrint(( "SMSS: Too many paging files specified - %d\n", CountPageFiles )); return( STATUS_TOO_MANY_PAGING_FILES ); } Status = SmpParseCommandLine( PagingFileSpec, NULL, &PagingFileName, NULL, &Arguments ); if (!NT_SUCCESS( Status )) { KdPrint(( "SMSS: SmpParseCommand( %wZ ) failed - Status == %lx\n", PagingFileSpec, Status )); return( Status ); } PageFileMaxSizeInMb = 0; Status = RtlUnicodeStringToInteger( &Arguments, 0, &PageFileMinSizeInMb ); if (!NT_SUCCESS( Status )) { PageFileMinSizeInMb = 10; } else { ArgSave = Arguments.Buffer; Arg2 = ArgSave; while (*Arg2 != UNICODE_NULL) { if (*Arg2++ == L' ') { Arguments.Length -= (USHORT)((PCHAR)Arg2 - (PCHAR)ArgSave); Arguments.Buffer = Arg2; Status = RtlUnicodeStringToInteger( &Arguments, 0, &PageFileMaxSizeInMb ); if (!NT_SUCCESS( Status )) { PageFileMaxSizeInMb = 0; } Arguments.Buffer = ArgSave; break; } } } if (PageFileMinSizeInMb == 0) { PageFileMinSizeInMb = 10; } if (PageFileMaxSizeInMb == 0) { PageFileMaxSizeInMb = PageFileMinSizeInMb + 50; } else if (PageFileMaxSizeInMb < PageFileMinSizeInMb) { PageFileMaxSizeInMb = PageFileMinSizeInMb; } PageFileSpecs[ CountPageFiles ] = PagingFileName; PageFileMinSizes[ CountPageFiles ] = (LONG)PageFileMinSizeInMb; PageFileMaxSizes[ CountPageFiles ] = (LONG)PageFileMaxSizeInMb; CountPageFiles++; if (Arguments.Buffer) { RtlFreeHeap( RtlProcessHeap(), 0, Arguments.Buffer ); } return STATUS_SUCCESS; } NTSTATUS SmpCreatePagingFile( PUNICODE_STRING PageFileSpec, LARGE_INTEGER MinPagingFileSize, LARGE_INTEGER MaxPagingFileSize ) { NTSTATUS Status, Status1; OBJECT_ATTRIBUTES ObjectAttributes; HANDLE Handle; IO_STATUS_BLOCK IoStatusBlock; BOOLEAN FileSizeInfoValid; FILE_STANDARD_INFORMATION FileSizeInfo; FILE_DISPOSITION_INFORMATION Disposition; FILE_FS_SIZE_INFORMATION SizeInfo; UNICODE_STRING VolumePath; ULONG n; PWSTR s; LARGE_INTEGER AvailableBytes; LARGE_INTEGER MinimumSlop; FileSizeInfoValid = FALSE; InitializeObjectAttributes( &ObjectAttributes, PageFileSpec, OBJ_CASE_INSENSITIVE, NULL, NULL ); Status = NtOpenFile( &Handle, (ACCESS_MASK)FILE_READ_ATTRIBUTES | SYNCHRONIZE, &ObjectAttributes, &IoStatusBlock, FILE_SHARE_READ | FILE_SHARE_WRITE, FILE_SYNCHRONOUS_IO_NONALERT ); if (NT_SUCCESS( Status )) { Status = NtQueryInformationFile( Handle, &IoStatusBlock, &FileSizeInfo, sizeof( FileSizeInfo ), FileStandardInformation ); if (NT_SUCCESS( Status )) { FileSizeInfoValid = TRUE; } NtClose( Handle ); } VolumePath = *PageFileSpec; n = VolumePath.Length; VolumePath.Length = 0; s = VolumePath.Buffer; while (n) { if (*s++ == L':' && *s == OBJ_NAME_PATH_SEPARATOR) { s++; break; } else { n -= sizeof( WCHAR ); } } VolumePath.Length = (USHORT)((PCHAR)s - (PCHAR)VolumePath.Buffer); InitializeObjectAttributes( &ObjectAttributes, &VolumePath, OBJ_CASE_INSENSITIVE, NULL, NULL ); Status = NtOpenFile( &Handle, (ACCESS_MASK)FILE_LIST_DIRECTORY | SYNCHRONIZE, &ObjectAttributes, &IoStatusBlock, FILE_SHARE_READ | FILE_SHARE_WRITE, FILE_SYNCHRONOUS_IO_NONALERT | FILE_DIRECTORY_FILE ); if (!NT_SUCCESS( Status )) { return Status; } // // Determine the size parameters of the volume. // Status = NtQueryVolumeInformationFile( Handle, &IoStatusBlock, &SizeInfo, sizeof( SizeInfo ), FileFsSizeInformation ); NtClose( Handle ); if (!NT_SUCCESS( Status )) { return Status; } // // Deal with 64 bit sizes // AvailableBytes = RtlExtendedIntegerMultiply( SizeInfo.AvailableAllocationUnits, SizeInfo.SectorsPerAllocationUnit ); AvailableBytes = RtlExtendedIntegerMultiply( AvailableBytes, SizeInfo.BytesPerSector ); if (FileSizeInfoValid) { AvailableBytes.QuadPart += FileSizeInfo.AllocationSize.QuadPart; } if ( AvailableBytes.QuadPart <= MinPagingFileSize.QuadPart ) { Status = STATUS_DISK_FULL; } else { AvailableBytes.QuadPart -= ( 2 * 1024 * 1024 ); if ( AvailableBytes.QuadPart <= MinPagingFileSize.QuadPart ) { Status = STATUS_DISK_FULL; } else { Status = STATUS_SUCCESS; } } if (NT_SUCCESS( Status )) { Status = NtCreatePagingFile( PageFileSpec, &MinPagingFileSize, &MaxPagingFileSize, 0 ); } else if (FileSizeInfoValid) { InitializeObjectAttributes( &ObjectAttributes, PageFileSpec, OBJ_CASE_INSENSITIVE, NULL, NULL ); Status1 = NtOpenFile( &Handle, (ACCESS_MASK)DELETE, &ObjectAttributes, &IoStatusBlock, FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE, FILE_NON_DIRECTORY_FILE ); if (NT_SUCCESS( Status1 )) { Disposition.DeleteFile = TRUE; Status1 = NtSetInformationFile( Handle, &IoStatusBlock, &Disposition, sizeof( Disposition ), FileDispositionInformation ); if (NT_SUCCESS( Status1 )) { KdPrint(( "SMSS: Deleted stale paging file - %wZ\n", PageFileSpec )); } NtClose(Handle); } } return Status; } NTSTATUS SmpCreatePagingFiles( VOID ) { LARGE_INTEGER MinPagingFileSize, MaxPagingFileSize; NTSTATUS Status; ULONG i, Pass; PWSTR CurrentDrive; char MessageBuffer[ 128 ]; BOOLEAN CreatedAtLeastOnePagingFile = FALSE; Status = STATUS_SUCCESS; for (Pass=1; Pass<=2; Pass++) { for (i=0; i 2 && (CurrentDrive == NULL || Pass == 2) && Status == STATUS_DISK_FULL ) { PageFileMinSizes[ i ] -= 2; goto retry; } else if (CurrentDrive && Pass == 2 && *CurrentDrive < L'Z' && Status == STATUS_DISK_FULL ) { *CurrentDrive += 1; goto retry; } else if (CurrentDrive && Pass == 2) { *CurrentDrive = L'?'; sprintf( MessageBuffer, "INIT: Failed to find drive with space for %wZ (%u MB)\n", &PageFileSpecs[ i ], PageFileMinSizes[ i ] ); #if DBG SmpDisplayString( MessageBuffer ); #endif } } else { CreatedAtLeastOnePagingFile = TRUE; if (CurrentDrive) { sprintf( MessageBuffer, "INIT: Created paging file: %wZ [%u..%u] MB\n", &PageFileSpecs[ i ], PageFileMinSizes[ i ], PageFileMaxSizes[ i ] ); #if DBG SmpDisplayString( MessageBuffer ); #endif } } if (Pass == 2) { RtlFreeHeap( RtlProcessHeap(), 0, PageFileSpecs[ i ].Buffer ); } } } if (!CreatedAtLeastOnePagingFile) { sprintf( MessageBuffer, "INIT: Unable to create a paging file. Proceeding anyway.\n" ); #if DBG SmpDisplayString( MessageBuffer ); #endif } return( Status ); } NTSTATUS SmpExecuteImage( IN PUNICODE_STRING ImageFileName, IN PUNICODE_STRING CurrentDirectory, IN PUNICODE_STRING CommandLine, IN ULONG Flags, IN OUT PRTL_USER_PROCESS_INFORMATION ProcessInformation OPTIONAL ) /*++ Routine Description: This function creates and starts a process specified by the CommandLine parameter. After starting the process, the procedure will optionally wait for the first thread in the process to terminate. Arguments: ImageFileName - Supplies the full NT path for the image file to execute. Presumably computed or extracted from the first token of the CommandLine. CommandLine - Supplies the command line to execute. The first blank separate token on the command line must be a fully qualified NT Path name of an image file to execute. Flags - Supplies information about how to invoke the command. ProcessInformation - Optional parameter, which if specified, receives information for images invoked with the SMP_ASYNC_FLAG. Ignore if this flag is not set. Return Value: Status of operation --*/ { NTSTATUS Status; RTL_USER_PROCESS_INFORMATION MyProcessInformation; PRTL_USER_PROCESS_PARAMETERS ProcessParameters; if (!ARGUMENT_PRESENT( ProcessInformation )) { ProcessInformation = &MyProcessInformation; } Status = RtlCreateProcessParameters( &ProcessParameters, ImageFileName, (SmpDefaultLibPath.Length == 0 ? NULL : &SmpDefaultLibPath ), CurrentDirectory, CommandLine, SmpDefaultEnvironment, NULL, NULL, NULL, NULL ); ASSERTMSG( "RtlCreateProcessParameters", NT_SUCCESS( Status ) ); if (Flags & SMP_DEBUG_FLAG) { ProcessParameters->DebugFlags = TRUE; } else { ProcessParameters->DebugFlags = SmpDebug; } if ( Flags & SMP_SUBSYSTEM_FLAG ) { ProcessParameters->Flags |= RTL_USER_PROC_RESERVE_1MB; } ProcessInformation->Length = sizeof( RTL_USER_PROCESS_INFORMATION ); Status = RtlCreateUserProcess( ImageFileName, OBJ_CASE_INSENSITIVE, ProcessParameters, NULL, NULL, NULL, FALSE, NULL, NULL, ProcessInformation ); RtlDestroyProcessParameters( ProcessParameters ); if ( !NT_SUCCESS( Status ) ) { KdPrint(( "SMSS: Failed load of %wZ - Status == %lx\n", ImageFileName, Status )); return( Status ); } if (!(Flags & SMP_DONT_START)) { if (ProcessInformation->ImageInformation.SubSystemType != IMAGE_SUBSYSTEM_NATIVE ) { NtTerminateProcess( ProcessInformation->Process, STATUS_INVALID_IMAGE_FORMAT ); NtWaitForSingleObject( ProcessInformation->Thread, FALSE, NULL ); NtClose( ProcessInformation->Thread ); NtClose( ProcessInformation->Process ); KdPrint(( "SMSS: Not an NT image - %wZ\n", ImageFileName )); return( STATUS_INVALID_IMAGE_FORMAT ); } NtResumeThread( ProcessInformation->Thread, NULL ); if (!(Flags & SMP_ASYNC_FLAG)) { NtWaitForSingleObject( ProcessInformation->Thread, FALSE, NULL ); } NtClose( ProcessInformation->Thread ); NtClose( ProcessInformation->Process ); } return( Status ); } NTSTATUS SmpExecuteCommand( IN PUNICODE_STRING CommandLine, IN ULONG Flags ) /*++ Routine Description: This function is called to execute a command. The format of CommandLine is: Nt-Path-To-AutoChk.exe Nt-Path-To-Disk-Partition If the NT path to the disk partition is an asterisk, then invoke the AutoChk.exe utility on all hard disk partitions. Arguments: CommandLine - Supplies the Command line to invoke. Flags - Specifies the type of command and options. Return Value: Status of operation --*/ { NTSTATUS Status; UNICODE_STRING ImageFileName; UNICODE_STRING CurrentDirectory; UNICODE_STRING Arguments; if (Flags & SMP_DEBUG_FLAG) { return( SmpLoadDbgSs( NULL ) ); } Status = SmpParseCommandLine( CommandLine, &Flags, &ImageFileName, &CurrentDirectory, &Arguments ); if (!NT_SUCCESS( Status )) { KdPrint(( "SMSS: SmpParseCommand( %wZ ) failed - Status == %lx\n", CommandLine, Status )); return( Status ); } if (Flags & SMP_AUTOCHK_FLAG) { Status = SmpInvokeAutoChk( &ImageFileName, &CurrentDirectory, &Arguments, Flags ); } else if (Flags & SMP_SUBSYSTEM_FLAG) { Status = SmpLoadSubSystem( &ImageFileName, &CurrentDirectory, CommandLine, Flags ); } else { if (Flags & SMP_IMAGE_NOT_FOUND) { KdPrint(( "SMSS: Image file (%wZ) not found\n", &ImageFileName )); Status = STATUS_OBJECT_NAME_NOT_FOUND; } else { Status = SmpExecuteImage( &ImageFileName, &CurrentDirectory, CommandLine, Flags, NULL ); } } if (ImageFileName.Buffer && !(Flags & SMP_IMAGE_NOT_FOUND)) { RtlFreeHeap( RtlProcessHeap(), 0, ImageFileName.Buffer ); if (CurrentDirectory.Buffer != NULL) { RtlFreeHeap( RtlProcessHeap(), 0, CurrentDirectory.Buffer ); } } if (Arguments.Buffer) { RtlFreeHeap( RtlProcessHeap(), 0, Arguments.Buffer ); } if (!NT_SUCCESS( Status )) { KdPrint(( "SMSS: Command '%wZ' failed - Status == %x\n", CommandLine, Status )); } return( Status ); } NTSTATUS SmpInvokeAutoChk( IN PUNICODE_STRING ImageFileName, IN PUNICODE_STRING CurrentDirectory, IN PUNICODE_STRING Arguments, IN ULONG Flags ) { NTSTATUS Status; OBJECT_ATTRIBUTES ObjectAttributes; HANDLE Handle; POBJECT_DIRECTORY_INFORMATION DirInfo; CHAR DirInfoBuffer[ 256 ]; ULONG Context, Length; BOOLEAN RestartScan; BOOLEAN ForceAutoChk; UNICODE_STRING ArgPrefix; UNICODE_STRING LinkTarget; UNICODE_STRING LinkTypeName; UNICODE_STRING LinkTargetPrefix; WCHAR LinkTargetBuffer[ MAXIMUM_FILENAME_LENGTH ]; CHAR DisplayBuffer[ MAXIMUM_FILENAME_LENGTH ]; ANSI_STRING AnsiDisplayString; UNICODE_STRING DisplayString; UNICODE_STRING CmdLine; WCHAR CmdLineBuffer[ 2 * MAXIMUM_FILENAME_LENGTH ]; UNICODE_STRING NtDllName; PVOID NtDllHandle; PMESSAGE_RESOURCE_ENTRY MessageEntry; PSZ CheckingString = NULL; // // Query the system environment variable "osloadoptions" to determine // if SOS is specified. // if (SmpQueryRegistrySosOption() != FALSE) { SmpEnableDots = FALSE; } RtlInitUnicodeString(&NtDllName, L"ntdll"); Status = LdrGetDllHandle( NULL, NULL, &NtDllName, &NtDllHandle ); if ( NT_SUCCESS(Status) ) { Status = RtlFindMessage( NtDllHandle, 11, #if defined(DBCS) // SmpInvokeAutoChk() // // We have to use ENGLISH resource anytime instead of default resource. Because // We can only display ASCII character onto Blue Screen via HalDisplayString() // MAKELANGID(LANG_ENGLISH,SUBLANG_ENGLISH_US), #else 0, #endif // defined(DBCS) STATUS_CHECKING_FILE_SYSTEM, &MessageEntry ); if ( NT_SUCCESS(Status) ) { CheckingString = MessageEntry->Text; } } if (!CheckingString) { CheckingString = "Checking File System on %wZ\n"; } if (Flags & SMP_IMAGE_NOT_FOUND) { sprintf( DisplayBuffer, "%wZ program not found - skipping AUTOCHECK\n", ImageFileName ); RtlInitAnsiString( &AnsiDisplayString, DisplayBuffer ); Status = RtlAnsiStringToUnicodeString( &DisplayString, &AnsiDisplayString, TRUE ); if (NT_SUCCESS( Status )) { NtDisplayString( &DisplayString ); RtlFreeUnicodeString( &DisplayString ); } return( STATUS_SUCCESS ); } RtlInitUnicodeString( &ArgPrefix, L"/p " ); if (RtlPrefixUnicodeString( &ArgPrefix, Arguments, TRUE )) { Arguments->Length -= 3 * sizeof( WCHAR ); RtlMoveMemory( Arguments->Buffer, Arguments->Buffer + 3, Arguments->Length ); ForceAutoChk = TRUE; } else { ForceAutoChk = FALSE; } CmdLine.Buffer = CmdLineBuffer; CmdLine.MaximumLength = sizeof( CmdLineBuffer ); RtlInitUnicodeString( &LinkTarget, L"*" ); if (!RtlEqualUnicodeString( Arguments, &LinkTarget, TRUE )) { CmdLine.Length = 0; RtlAppendUnicodeStringToString( &CmdLine, ImageFileName ); RtlAppendUnicodeToString( &CmdLine, L" " ); if (ForceAutoChk) { RtlAppendUnicodeToString( &CmdLine, L"/p " ); } RtlAppendUnicodeStringToString( &CmdLine, Arguments ); SmpExecuteImage( ImageFileName, CurrentDirectory, &CmdLine, Flags & ~SMP_AUTOCHK_FLAG, NULL ); } else { LinkTarget.Buffer = LinkTargetBuffer; DirInfo = (POBJECT_DIRECTORY_INFORMATION)&DirInfoBuffer; RestartScan = TRUE; RtlInitUnicodeString( &LinkTypeName, L"SymbolicLink" ); RtlInitUnicodeString( &LinkTargetPrefix, L"\\Device\\Harddisk" ); while (TRUE) { Status = NtQueryDirectoryObject( SmpDosDevicesObjectDirectory, (PVOID)DirInfo, sizeof( DirInfoBuffer ), TRUE, RestartScan, &Context, &Length ); if (!NT_SUCCESS( Status )) { Status = STATUS_SUCCESS; break; } if (RtlEqualUnicodeString( &DirInfo->TypeName, &LinkTypeName, TRUE ) && DirInfo->Name.Buffer[(DirInfo->Name.Length>>1)-1] == L':') { InitializeObjectAttributes( &ObjectAttributes, &DirInfo->Name, OBJ_CASE_INSENSITIVE, SmpDosDevicesObjectDirectory, NULL ); Status = NtOpenSymbolicLinkObject( &Handle, SYMBOLIC_LINK_ALL_ACCESS, &ObjectAttributes ); if (NT_SUCCESS( Status )) { LinkTarget.Length = 0; LinkTarget.MaximumLength = sizeof( LinkTargetBuffer ); Status = NtQuerySymbolicLinkObject( Handle, &LinkTarget, NULL ); NtClose( Handle ); if (NT_SUCCESS( Status ) && RtlPrefixUnicodeString( &LinkTargetPrefix, &LinkTarget, TRUE ) ) { sprintf( DisplayBuffer, CheckingString, &DirInfo->Name ); if (SmpEnableDots != FALSE) { RtlInitAnsiString( &AnsiDisplayString, "." ); } else { RtlInitAnsiString( &AnsiDisplayString, DisplayBuffer ); } Status = RtlAnsiStringToUnicodeString( &DisplayString, &AnsiDisplayString, TRUE ); if (NT_SUCCESS( Status )) { NtDisplayString( &DisplayString ); RtlFreeUnicodeString( &DisplayString ); } CmdLine.Length = 0; RtlAppendUnicodeStringToString( &CmdLine, ImageFileName ); RtlAppendUnicodeToString( &CmdLine, L" " ); if (ForceAutoChk) { RtlAppendUnicodeToString( &CmdLine, L"/p " ); } RtlAppendUnicodeToString( &CmdLine, L" /d" ); RtlAppendUnicodeToString( &CmdLine, DirInfo->Name.Buffer ); RtlAppendUnicodeToString( &CmdLine, L" " ); RtlAppendUnicodeStringToString( &CmdLine, &LinkTarget ); SmpExecuteImage( ImageFileName, CurrentDirectory, &CmdLine, Flags & ~SMP_AUTOCHK_FLAG, NULL ); } } } RestartScan = FALSE; if (!NT_SUCCESS( Status )) { break; } } } return( Status ); } NTSTATUS SmpLoadSubSystem( IN PUNICODE_STRING ImageFileName, IN PUNICODE_STRING CurrentDirectory, IN PUNICODE_STRING CommandLine, IN ULONG Flags ) /*++ Routine Description: This function loads and starts the specified system service emulation subsystem. The system freezes until the loaded subsystem completes the subsystem connection protocol by connecting to SM, and then accepting a connection from SM. Arguments: CommandLine - Supplies the command line to execute the subsystem. Return Value: TBD --*/ { NTSTATUS Status; RTL_USER_PROCESS_INFORMATION ProcessInformation; PSMPKNOWNSUBSYS KnownSubSys; PSMPKNOWNSUBSYS TargetSubSys; if (Flags & SMP_IMAGE_NOT_FOUND) { KdPrint(( "SMSS: Unable to find subsystem - %wZ\n", ImageFileName )); return( STATUS_OBJECT_NAME_NOT_FOUND ); } Flags |= SMP_DONT_START; Status = SmpExecuteImage( ImageFileName, CurrentDirectory, CommandLine, Flags, &ProcessInformation ); if (!NT_SUCCESS( Status )) { return( Status ); } KnownSubSys = RtlAllocateHeap( SmpHeap, MAKE_TAG( INIT_TAG ), sizeof( SMPKNOWNSUBSYS ) ); KnownSubSys->Process = ProcessInformation.Process; KnownSubSys->InitialClientId = ProcessInformation.ClientId; KnownSubSys->ImageType = (ULONG)0xFFFFFFFF; KnownSubSys->SmApiCommunicationPort = (HANDLE) NULL; KnownSubSys->SbApiCommunicationPort = (HANDLE) NULL; Status = NtCreateEvent( &KnownSubSys->Active, EVENT_ALL_ACCESS, NULL, NotificationEvent, FALSE ); // // now that we have the process all set, make sure that the // subsystem is either an NT native app, or an app type of // a previously loaded subsystem // if (ProcessInformation.ImageInformation.SubSystemType != IMAGE_SUBSYSTEM_NATIVE ) { SBAPIMSG SbApiMsg; PSBCREATESESSION args; ULONG SessionId; args = &SbApiMsg.u.CreateSession; args->ProcessInformation = ProcessInformation; args->DebugSession = 0; args->DebugUiClientId.UniqueProcess = NULL; args->DebugUiClientId.UniqueThread = NULL; TargetSubSys = SmpLocateKnownSubSysByType( ProcessInformation.ImageInformation.SubSystemType ); if ( !TargetSubSys ) { return STATUS_NO_SUCH_PACKAGE; } // // Transfer the handles to the subsystem responsible for this // process // Status = NtDuplicateObject( NtCurrentProcess(), ProcessInformation.Process, TargetSubSys->Process, &args->ProcessInformation.Process, PROCESS_ALL_ACCESS, 0, 0 ); if (!NT_SUCCESS( Status )) { return( Status ); } Status = NtDuplicateObject( NtCurrentProcess(), ProcessInformation.Thread, TargetSubSys->Process, &args->ProcessInformation.Thread, THREAD_ALL_ACCESS, 0, 0 ); if (!NT_SUCCESS( Status )) { return( Status ); } SessionId = SmpAllocateSessionId( TargetSubSys, NULL ); args->SessionId = SessionId; SbApiMsg.ApiNumber = SbCreateSessionApi; SbApiMsg.h.u1.s1.DataLength = sizeof(*args) + 8; SbApiMsg.h.u1.s1.TotalLength = sizeof(SbApiMsg); SbApiMsg.h.u2.ZeroInit = 0L; Status = NtRequestWaitReplyPort( TargetSubSys->SbApiCommunicationPort, (PPORT_MESSAGE) &SbApiMsg, (PPORT_MESSAGE) &SbApiMsg ); if (NT_SUCCESS( Status )) { Status = SbApiMsg.ReturnedStatus; } if (!NT_SUCCESS( Status )) { SmpDeleteSession( SessionId, FALSE, Status ); return( Status ); } } else { SmpWindowsSubSysProcess = ProcessInformation.Process; } ASSERTMSG( "NtCreateEvent", NT_SUCCESS( Status ) ); RtlEnterCriticalSection( &SmpKnownSubSysLock ); InsertHeadList( &SmpKnownSubSysHead, &KnownSubSys->Links ); RtlLeaveCriticalSection( &SmpKnownSubSysLock ); NtResumeThread( ProcessInformation.Thread, NULL ); NtWaitForSingleObject( KnownSubSys->Active, FALSE, NULL ); return STATUS_SUCCESS; } NTSTATUS SmpExecuteInitialCommand( IN PUNICODE_STRING InitialCommand, OUT PHANDLE InitialCommandProcess ) { NTSTATUS Status; RTL_USER_PROCESS_INFORMATION ProcessInformation; ULONG Flags; UNICODE_STRING ImageFileName; UNICODE_STRING CurrentDirectory; UNICODE_STRING Arguments; HANDLE SmApiPort; Status = SmConnectToSm( NULL, NULL, 0, &SmApiPort ); if (!NT_SUCCESS( Status )) { KdPrint(( "SMSS: Unable to connect to SM - Status == %lx\n", Status )); return( Status ); } Flags = 0; Status = SmpParseCommandLine( InitialCommand, &Flags, &ImageFileName, &CurrentDirectory, &Arguments ); if (Flags & SMP_IMAGE_NOT_FOUND) { KdPrint(( "SMSS: Initial command image (%wZ) not found\n", &ImageFileName )); return( STATUS_OBJECT_NAME_NOT_FOUND ); } if (!NT_SUCCESS( Status )) { KdPrint(( "SMSS: SmpParseCommand( %wZ ) failed - Status == %lx\n", InitialCommand, Status )); return( Status ); } Status = SmpExecuteImage( &ImageFileName, &CurrentDirectory, InitialCommand, SMP_DONT_START, &ProcessInformation ); if (!NT_SUCCESS( Status )) { return( Status ); } Status = NtDuplicateObject( NtCurrentProcess(), ProcessInformation.Process, NtCurrentProcess(), InitialCommandProcess, PROCESS_ALL_ACCESS, 0, 0 ); if (!NT_SUCCESS(Status) ) { KdPrint(( "SMSS: DupObject Failed. Status == %lx\n", Status )); NtTerminateProcess( ProcessInformation.Process, Status ); NtResumeThread( ProcessInformation.Thread, NULL ); NtClose( ProcessInformation.Thread ); NtClose( ProcessInformation.Process ); return( Status ); } Status = SmExecPgm( SmApiPort, &ProcessInformation, FALSE ); if (!NT_SUCCESS( Status )) { KdPrint(( "SMSS: SmExecPgm Failed. Status == %lx\n", Status )); return( Status ); } return( Status ); } void SmpDisplayString( char *s ) { ANSI_STRING AnsiString; UNICODE_STRING UnicodeString; RtlInitAnsiString( &AnsiString, s ); RtlAnsiStringToUnicodeString( &UnicodeString, &AnsiString, TRUE ); NtDisplayString( &UnicodeString ); RtlFreeUnicodeString( &UnicodeString ); } NTSTATUS SmpLoadDeferedSubsystem( IN PSMAPIMSG SmApiMsg, IN PSMP_CLIENT_CONTEXT CallingClient, IN HANDLE CallPort ) { NTSTATUS Status; PLIST_ENTRY Head, Next; PSMP_REGISTRY_VALUE p; UNICODE_STRING DeferedName; PSMLOADDEFERED args; args = &SmApiMsg->u.LoadDefered; DeferedName.Length = (USHORT)args->SubsystemNameLength; DeferedName.MaximumLength = (USHORT)args->SubsystemNameLength; DeferedName.Buffer = args->SubsystemName; Head = &SmpSubSystemsToDefer; Next = Head->Flink; while (Next != Head ) { p = CONTAINING_RECORD( Next, SMP_REGISTRY_VALUE, Entry ); if ( RtlEqualUnicodeString(&DeferedName,&p->Name,TRUE)) { // // This is it. Load the subsystem... // RemoveEntryList(Next); Status = SmpExecuteCommand( &p->Value, SMP_SUBSYSTEM_FLAG ); RtlFreeHeap( RtlProcessHeap(), 0, p ); return Status; } Next = Next->Flink; } return STATUS_OBJECT_NAME_NOT_FOUND; } NTSTATUS SmpConfigureProtectionMode( IN PWSTR ValueName, IN ULONG ValueType, IN PVOID ValueData, IN ULONG ValueLength, IN PVOID Context, IN PVOID EntryContext ) /*++ Routine Description: This function is a dispatch routine for the QueryRegistry call (see SmpRegistryConfigurationTable[] earlier in this file). The purpose of this routine is to read the Base Object Protection Mode out of the registry. This information is kept in Key Name: \\Hkey_Local_Machine\System\CurrentControlSet\SessionManager Value: ProtectionMode [REG_DWORD] The value is a flag word, with the following flags defined: SMP_NO_PROTECTION - No base object protection SMP_STANDARD_PROTECTION - Apply standard base object protection This information will be placed in the global variable SmpProtectionMode. No value, or an invalid value length or type results in no base object protection being applied. Arguments: None. Return Value: --*/ { #ifdef SMP_SHOW_REGISTRY_DATA SmpDumpQuery( "BaseObjectsProtection", ValueName, ValueType, ValueData, ValueLength ); #else UNREFERENCED_PARAMETER( ValueName ); UNREFERENCED_PARAMETER( ValueType ); #endif if (ValueLength != sizeof(ULONG)) { // // Key value not valid, set to run without base object protection. // This is how we initialized, so no need to set up new // security descriptors. // SmpProtectionMode = 0; } else { SmpProtectionMode = (*((PULONG)(ValueData))); // // Change the security descriptors // (VOID)SmpCreateSecurityDescriptors( FALSE ); } return( STATUS_SUCCESS ); } NTSTATUS SmpCreateSecurityDescriptors( IN BOOLEAN InitialCall ) /*++ Routine Description: This function allocates and initializes security descriptors used in SM. The security descriptors include: SmpPrimarySecurityDescriptor - (global variable) This is used to assign protection to objects created by SM that need to be accessed by others, but not modified. This descriptor grants the following access: Grant: World: Execute | Read (Inherit) Grant: Admin: All Access (Inherit) Grant: Owner: All Access (Inherit Only) SmpLiberalSecurityDescriptor = (globalVariable) This is used to assign protection objects created by SM that need to be modified by others (such as writing to a shared memory section). This descriptor grants the following access: Grant: World: Execute | Read | Write (Inherit) Grant: Admin: All Access (Inherit) Grant: Owner: All Access (Inherit Only) SmpKnownDllsSecurityDescriptor = (globalVariable) This is used to assign protection to the \KnownDlls object directory. This descriptor grants the following access: Grant: World: Execute (No Inherit) Grant: Admin: All Access (Inherit) Grant: World: Execute | Read | Write (Inherit Only) Note that System is an administrator, so granting Admin an access also grants System that access. Arguments: InitialCall - Indicates whether this routine is being called for the first time, or is being called to change the security descriptors as a result of a protection mode change. TRUE - being called for first time. FALSE - being called a subsequent time. (global variables: SmpBaseObjectsUnprotected) Return Value: STATUS_SUCCESS - The security descriptor(s) have been allocated and initialized. STATUS_NO_MEMORY - couldn't allocate memory for a security descriptor. --*/ { NTSTATUS Status; PSID WorldSid, AdminSid, OwnerSid; SID_IDENTIFIER_AUTHORITY WorldAuthority = SECURITY_WORLD_SID_AUTHORITY, NtAuthority = SECURITY_NT_AUTHORITY, CreatorAuthority = SECURITY_CREATOR_SID_AUTHORITY; ACCESS_MASK AdminAccess = (GENERIC_ALL), WorldAccess = (GENERIC_EXECUTE | GENERIC_READ), OwnerAccess = (GENERIC_ALL); UCHAR InheritOnlyFlags = (OBJECT_INHERIT_ACE | CONTAINER_INHERIT_ACE | INHERIT_ONLY_ACE); ULONG AceIndex, AclLength; PACL Acl; PACE_HEADER Ace; BOOLEAN ProtectionRequired = FALSE, WasEnabled; if (InitialCall) { // // Now init the security descriptors for no protection. // If told to, we will change these to have protection. // // Primary SmpPrimarySecurityDescriptor = &SmpPrimarySDBody; Status = RtlCreateSecurityDescriptor ( SmpPrimarySecurityDescriptor, SECURITY_DESCRIPTOR_REVISION ); ASSERT( NT_SUCCESS(Status) ); Status = RtlSetDaclSecurityDescriptor ( SmpPrimarySecurityDescriptor, TRUE, //DaclPresent, NULL, //Dacl (no protection) FALSE //DaclDefaulted OPTIONAL ); ASSERT( NT_SUCCESS(Status) ); // Liberal SmpLiberalSecurityDescriptor = &SmpLiberalSDBody; Status = RtlCreateSecurityDescriptor ( SmpLiberalSecurityDescriptor, SECURITY_DESCRIPTOR_REVISION ); ASSERT( NT_SUCCESS(Status) ); Status = RtlSetDaclSecurityDescriptor ( SmpLiberalSecurityDescriptor, TRUE, //DaclPresent, NULL, //Dacl (no protection) FALSE //DaclDefaulted OPTIONAL ); ASSERT( NT_SUCCESS(Status) ); // KnownDlls SmpKnownDllsSecurityDescriptor = &SmpKnownDllsSDBody; Status = RtlCreateSecurityDescriptor ( SmpKnownDllsSecurityDescriptor, SECURITY_DESCRIPTOR_REVISION ); ASSERT( NT_SUCCESS(Status) ); Status = RtlSetDaclSecurityDescriptor ( SmpKnownDllsSecurityDescriptor, TRUE, //DaclPresent, NULL, //Dacl (no protection) FALSE //DaclDefaulted OPTIONAL ); ASSERT( NT_SUCCESS(Status) ); // ApiPort SmpApiPortSecurityDescriptor = &SmpApiPortSDBody; Status = RtlCreateSecurityDescriptor ( SmpApiPortSecurityDescriptor, SECURITY_DESCRIPTOR_REVISION ); ASSERT( NT_SUCCESS(Status) ); Status = RtlSetDaclSecurityDescriptor ( SmpApiPortSecurityDescriptor, TRUE, //DaclPresent, NULL, //Dacl (no protection) FALSE //DaclDefaulted OPTIONAL ); ASSERT( NT_SUCCESS(Status) ); } if ((SmpProtectionMode & SMP_PROTECTION_REQUIRED) != 0) { ProtectionRequired = TRUE; } if (!InitialCall && !ProtectionRequired) { return(STATUS_SUCCESS); } if (InitialCall || ProtectionRequired) { // // We need to set up the ApiPort protection, and maybe // others. // Status = RtlAllocateAndInitializeSid( &WorldAuthority, 1, SECURITY_WORLD_RID, 0, 0, 0, 0, 0, 0, 0, &WorldSid ); if (NT_SUCCESS( Status )) { Status = RtlAllocateAndInitializeSid( &NtAuthority, 2, SECURITY_BUILTIN_DOMAIN_RID, DOMAIN_ALIAS_RID_ADMINS, 0, 0, 0, 0, 0, 0, &AdminSid ); if (NT_SUCCESS( Status )) { Status = RtlAllocateAndInitializeSid( &CreatorAuthority, 1, SECURITY_CREATOR_OWNER_RID, 0, 0, 0, 0, 0, 0, 0, &OwnerSid ); if (NT_SUCCESS( Status )) { // // Build the ApiPort security descriptor only // if this is the initial call // if (InitialCall) { WorldAccess = GENERIC_EXECUTE | GENERIC_READ | GENERIC_READ; AdminAccess = GENERIC_ALL; AclLength = sizeof( ACL ) + 2 * sizeof( ACCESS_ALLOWED_ACE ) + (RtlLengthSid( WorldSid )) + (RtlLengthSid( AdminSid )); Acl = RtlAllocateHeap( RtlProcessHeap(), MAKE_TAG( INIT_TAG ), AclLength ); if (Acl == NULL) { Status = STATUS_NO_MEMORY; } if (NT_SUCCESS(Status)) { // // Create the ACL, then add each ACE // Status = RtlCreateAcl (Acl, AclLength, ACL_REVISION2 ); ASSERT( NT_SUCCESS(Status) ); // // Only Non-inheritable ACEs in this ACL // World // Admin // Status = RtlAddAccessAllowedAce ( Acl, ACL_REVISION2, WorldAccess, WorldSid ); ASSERT( NT_SUCCESS(Status) ); Status = RtlAddAccessAllowedAce ( Acl, ACL_REVISION2, AdminAccess, AdminSid ); ASSERT( NT_SUCCESS(Status) ); Status = RtlSetDaclSecurityDescriptor ( SmpApiPortSecurityDescriptor, TRUE, //DaclPresent, Acl, //Dacl FALSE //DaclDefaulted OPTIONAL ); ASSERT( NT_SUCCESS(Status) ); } } // // The remaining security descriptors are only // built if we are running with the correct in // protection mode set. Notice that we only // put protection on if standard protection is // also specified. Otherwise, there is no protection // on the objects, and nothing should fail. // if (SmpProtectionMode & SMP_STANDARD_PROTECTION) { // // Build the primary Security descriptor // WorldAccess = GENERIC_EXECUTE | GENERIC_READ; AdminAccess = GENERIC_ALL; OwnerAccess = GENERIC_ALL; AclLength = sizeof( ACL ) + 5 * sizeof( ACCESS_ALLOWED_ACE ) + (2*RtlLengthSid( WorldSid )) + (2*RtlLengthSid( AdminSid )) + RtlLengthSid( OwnerSid ); Acl = RtlAllocateHeap( RtlProcessHeap(), MAKE_TAG( INIT_TAG ), AclLength ); if (Acl == NULL) { Status = STATUS_NO_MEMORY; } if (NT_SUCCESS(Status)) { // // Create the ACL, then add each ACE // Status = RtlCreateAcl (Acl, AclLength, ACL_REVISION2 ); ASSERT( NT_SUCCESS(Status) ); // // Non-inheritable ACEs first // World // Admin // AceIndex = 0; Status = RtlAddAccessAllowedAce ( Acl, ACL_REVISION2, WorldAccess, WorldSid ); ASSERT( NT_SUCCESS(Status) ); AceIndex++; Status = RtlAddAccessAllowedAce ( Acl, ACL_REVISION2, AdminAccess, AdminSid ); ASSERT( NT_SUCCESS(Status) ); // // Inheritable ACEs at end of ACE // World // Admin // Owner AceIndex++; Status = RtlAddAccessAllowedAce ( Acl, ACL_REVISION2, WorldAccess, WorldSid ); ASSERT( NT_SUCCESS(Status) ); Status = RtlGetAce( Acl, AceIndex, (PVOID)&Ace ); ASSERT( NT_SUCCESS(Status) ); Ace->AceFlags = InheritOnlyFlags; AceIndex++; Status = RtlAddAccessAllowedAce ( Acl, ACL_REVISION2, AdminAccess, AdminSid ); ASSERT( NT_SUCCESS(Status) ); Status = RtlGetAce( Acl, AceIndex, (PVOID)&Ace ); ASSERT( NT_SUCCESS(Status) ); Ace->AceFlags = InheritOnlyFlags; AceIndex++; Status = RtlAddAccessAllowedAce ( Acl, ACL_REVISION2, OwnerAccess, OwnerSid ); ASSERT( NT_SUCCESS(Status) ); Status = RtlGetAce( Acl, AceIndex, (PVOID)&Ace ); ASSERT( NT_SUCCESS(Status) ); Ace->AceFlags = InheritOnlyFlags; Status = RtlSetDaclSecurityDescriptor ( SmpPrimarySecurityDescriptor, TRUE, //DaclPresent, Acl, //Dacl FALSE //DaclDefaulted OPTIONAL ); ASSERT( NT_SUCCESS(Status) ); } // // Build the liberal security descriptor // AdminAccess = GENERIC_ALL; WorldAccess = GENERIC_EXECUTE | GENERIC_READ | GENERIC_WRITE; AclLength = sizeof( ACL ) + 5 * sizeof( ACCESS_ALLOWED_ACE ) + (2*RtlLengthSid( WorldSid )) + (2*RtlLengthSid( AdminSid )) + RtlLengthSid( OwnerSid ); Acl = RtlAllocateHeap( RtlProcessHeap(), MAKE_TAG( INIT_TAG ), AclLength ); if (Acl == NULL) { Status = STATUS_NO_MEMORY; } if (NT_SUCCESS(Status)) { // // Create the ACL // Status = RtlCreateAcl (Acl, AclLength, ACL_REVISION2 ); ASSERT( NT_SUCCESS(Status) ); // // Add the non-inheritable ACEs first // World // Admin // AceIndex = 0; Status = RtlAddAccessAllowedAce ( Acl, ACL_REVISION2, WorldAccess, WorldSid ); ASSERT( NT_SUCCESS(Status) ); AceIndex++; Status = RtlAddAccessAllowedAce ( Acl, ACL_REVISION2, AdminAccess, AdminSid ); ASSERT( NT_SUCCESS(Status) ); // // Put the inherit only ACEs at at the end // World // Admin // Owner // AceIndex++; Status = RtlAddAccessAllowedAce ( Acl, ACL_REVISION2, WorldAccess, WorldSid ); ASSERT( NT_SUCCESS(Status) ); Status = RtlGetAce( Acl, AceIndex, (PVOID)&Ace ); ASSERT( NT_SUCCESS(Status) ); Ace->AceFlags = InheritOnlyFlags; AceIndex++; Status = RtlAddAccessAllowedAce ( Acl, ACL_REVISION2, AdminAccess, AdminSid ); ASSERT( NT_SUCCESS(Status) ); Status = RtlGetAce( Acl, AceIndex, (PVOID)&Ace ); ASSERT( NT_SUCCESS(Status) ); Ace->AceFlags = InheritOnlyFlags; AceIndex++; Status = RtlAddAccessAllowedAce ( Acl, ACL_REVISION2, OwnerAccess, OwnerSid ); ASSERT( NT_SUCCESS(Status) ); Status = RtlGetAce( Acl, AceIndex, (PVOID)&Ace ); ASSERT( NT_SUCCESS(Status) ); Ace->AceFlags = InheritOnlyFlags; // // Put the Acl in the security descriptor // Status = RtlSetDaclSecurityDescriptor ( SmpLiberalSecurityDescriptor, TRUE, //DaclPresent, Acl, //Dacl FALSE //DaclDefaulted OPTIONAL ); ASSERT( NT_SUCCESS(Status) ); } // // Build the KnownDlls security descriptor // AdminAccess = GENERIC_ALL; AclLength = sizeof( ACL ) + 4 * sizeof( ACCESS_ALLOWED_ACE ) + (2*RtlLengthSid( WorldSid )) + (2*RtlLengthSid( AdminSid )); Acl = RtlAllocateHeap( RtlProcessHeap(), MAKE_TAG( INIT_TAG ), AclLength ); if (Acl == NULL) { Status = STATUS_NO_MEMORY; } if (NT_SUCCESS(Status)) { // // Create the ACL // Status = RtlCreateAcl (Acl, AclLength, ACL_REVISION2 ); ASSERT( NT_SUCCESS(Status) ); // // Add the non-inheritable ACEs first // World // Admin // AceIndex = 0; WorldAccess = GENERIC_EXECUTE; Status = RtlAddAccessAllowedAce ( Acl, ACL_REVISION2, WorldAccess, WorldSid ); ASSERT( NT_SUCCESS(Status) ); AceIndex++; Status = RtlAddAccessAllowedAce ( Acl, ACL_REVISION2, AdminAccess, AdminSid ); ASSERT( NT_SUCCESS(Status) ); // // Put the inherit only ACEs at at the end // World // Admin // AceIndex++; WorldAccess = GENERIC_EXECUTE | GENERIC_READ | GENERIC_WRITE; Status = RtlAddAccessAllowedAce ( Acl, ACL_REVISION2, WorldAccess, WorldSid ); ASSERT( NT_SUCCESS(Status) ); Status = RtlGetAce( Acl, AceIndex, (PVOID)&Ace ); ASSERT( NT_SUCCESS(Status) ); Ace->AceFlags = InheritOnlyFlags; AceIndex++; Status = RtlAddAccessAllowedAce ( Acl, ACL_REVISION2, AdminAccess, AdminSid ); ASSERT( NT_SUCCESS(Status) ); Status = RtlGetAce( Acl, AceIndex, (PVOID)&Ace ); ASSERT( NT_SUCCESS(Status) ); Ace->AceFlags = InheritOnlyFlags; // // Put the Acl in the security descriptor // Status = RtlSetDaclSecurityDescriptor ( SmpKnownDllsSecurityDescriptor, TRUE, //DaclPresent, Acl, //Dacl FALSE //DaclDefaulted OPTIONAL ); ASSERT( NT_SUCCESS(Status) ); } } // // No more security descriptors to build // RtlFreeHeap( RtlProcessHeap(), 0, OwnerSid ); } RtlFreeHeap( RtlProcessHeap(), 0, AdminSid ); } RtlFreeHeap( RtlProcessHeap(), 0, WorldSid ); } } return( Status ); } VOID SmpTranslateSystemPartitionInformation( VOID ) /*++ Routine Description: This routine translates the NT device path for the system partition (stored during IoInitSystem) into a DOS path, and stores the resulting REG_SZ 'BootDir' value under HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Setup Arguments: None. Return Value: None. --*/ { NTSTATUS Status; UNICODE_STRING UnicodeString; OBJECT_ATTRIBUTES ObjectAttributes; HANDLE Key; UCHAR ValueBuffer[ VALUE_BUFFER_SIZE ]; ULONG ValueLength; UNICODE_STRING SystemPartitionString; POBJECT_DIRECTORY_INFORMATION DirInfo; UCHAR DirInfoBuffer[ sizeof(OBJECT_DIRECTORY_INFORMATION) + (256 + sizeof("SymbolicLink")) * sizeof(WCHAR) ]; UNICODE_STRING LinkTypeName; BOOLEAN RestartScan; ULONG Context; HANDLE SymbolicLinkHandle; WCHAR UnicodeBuffer[ MAXIMUM_FILENAME_LENGTH ]; UNICODE_STRING LinkTarget; // // Retrieve 'SystemPartition' value stored under HKLM\SYSTEM\Setup // RtlInitUnicodeString(&UnicodeString, L"\\Registry\\Machine\\System\\Setup"); InitializeObjectAttributes(&ObjectAttributes, &UnicodeString, OBJ_CASE_INSENSITIVE, NULL, NULL ); Status = NtOpenKey(&Key, KEY_READ, &ObjectAttributes); if (!NT_SUCCESS(Status)) { KdPrint(("SMSS: can't open system setup key for reading: 0x%x\n", Status)); return; } RtlInitUnicodeString(&UnicodeString, L"SystemPartition"); Status = NtQueryValueKey(Key, &UnicodeString, KeyValuePartialInformation, ValueBuffer, sizeof(ValueBuffer), &ValueLength ); NtClose(Key); if (!NT_SUCCESS(Status)) { KdPrint(("SMSS: can't query SystemPartition value: 0x%x\n", Status)); return; } RtlInitUnicodeString(&SystemPartitionString, (PWSTR)(((PKEY_VALUE_PARTIAL_INFORMATION)ValueBuffer)->Data) ); // // Next, examine objects in the DosDevices directory, looking for one that's a symbolic link // to the system partition. // LinkTarget.Buffer = UnicodeBuffer; DirInfo = (POBJECT_DIRECTORY_INFORMATION)DirInfoBuffer; RestartScan = TRUE; RtlInitUnicodeString(&LinkTypeName, L"SymbolicLink"); while (TRUE) { Status = NtQueryDirectoryObject(SmpDosDevicesObjectDirectory, DirInfo, sizeof(DirInfoBuffer), TRUE, RestartScan, &Context, NULL ); if (!NT_SUCCESS(Status)) { break; } if (RtlEqualUnicodeString(&DirInfo->TypeName, &LinkTypeName, TRUE) && (DirInfo->Name.Length == 2 * sizeof(WCHAR)) && (DirInfo->Name.Buffer[1] == L':')) { // // We have a drive letter--check the NT device name it's linked to. // InitializeObjectAttributes(&ObjectAttributes, &DirInfo->Name, OBJ_CASE_INSENSITIVE, SmpDosDevicesObjectDirectory, NULL ); Status = NtOpenSymbolicLinkObject(&SymbolicLinkHandle, SYMBOLIC_LINK_ALL_ACCESS, &ObjectAttributes ); if (NT_SUCCESS(Status)) { LinkTarget.Length = 0; LinkTarget.MaximumLength = sizeof(UnicodeBuffer); Status = NtQuerySymbolicLinkObject(SymbolicLinkHandle, &LinkTarget, NULL ); NtClose(SymbolicLinkHandle); if (NT_SUCCESS(Status) && RtlEqualUnicodeString(&SystemPartitionString, &LinkTarget, TRUE)) { // // We've found the drive letter corresponding to the system partition. // break; } } } RestartScan = FALSE; } if (!NT_SUCCESS(Status)) { KdPrint(("SMSS: can't find drive letter for system partition\n")); return; } // // Now write out the DOS path for the system partition to // HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Setup // RtlInitUnicodeString(&UnicodeString, L"\\Registry\\Machine\\Software\\Microsoft\\Windows\\CurrentVersion\\Setup"); InitializeObjectAttributes(&ObjectAttributes, &UnicodeString, OBJ_CASE_INSENSITIVE, NULL, NULL ); Status = NtOpenKey(&Key, KEY_ALL_ACCESS, &ObjectAttributes); if (!NT_SUCCESS(Status)) { KdPrint(("SMSS: can't open software setup key for writing: 0x%x\n", Status)); return; } wcsncpy(UnicodeBuffer, DirInfo->Name.Buffer, 2); UnicodeBuffer[2] = L'\\'; UnicodeBuffer[3] = L'\0'; RtlInitUnicodeString(&UnicodeString, L"BootDir"); Status = NtSetValueKey(Key, &UnicodeString, 0, REG_SZ, UnicodeBuffer, 4 * sizeof(WCHAR) ); if (!NT_SUCCESS(Status)) { KdPrint(("SMSS: couldn't write BootDir value: 0x%x\n", Status)); } NtClose(Key); }