/*++ 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 "pagefile.h" #include #include #include #include #if defined(REMOTE_BOOT) #include #ifdef DeleteFile #undef DeleteFile #endif #include // CSC definitions #endif // defined(REMOTE_BOOT) #include "sfcfiles.h" void SmpDisplayString( char *s ); // // Protection mode flags // #define SMP_NO_PROTECTION (0x0) #define SMP_STANDARD_PROTECTION (0x1) #define SMP_PROTECTION_REQUIRED (SMP_STANDARD_PROTECTION) #define REMOTE_BOOT_CFG_FILE L"RemoteBoot.cfg" // // Shows where was SmpInit execution when it returned // with an error code. This aids debugging smss crashes a lot. // ULONG SmpInitProgressByLine; NTSTATUS SmpInitReturnStatus; PVOID SmpInitLastCall; #define SAVE_SMPINIT_STATUS(caller, status) { \ \ SmpInitProgressByLine = __LINE__; \ SmpInitReturnStatus = (status); \ SmpInitLastCall = (PVOID)(caller); \ } 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 = SMP_STANDARD_PROTECTION; UCHAR TmpBuffer[ 1024 + 2 * DOS_MAX_PATH_LENGTH * sizeof(WCHAR)]; ULONG AttachedSessionId = (-1); #if defined(REMOTE_BOOT) WCHAR wszRemoteBootCfgFile[DOS_MAX_PATH_LENGTH]; #endif // defined(REMOTE_BOOT) #if DBG BOOLEAN SmpEnableDots = FALSE; #else BOOLEAN SmpEnableDots = TRUE; #endif WCHAR InitialCommandBuffer[ 256 ]; UNICODE_STRING SmpDebugKeyword; UNICODE_STRING SmpASyncKeyword; UNICODE_STRING SmpAutoChkKeyword; #if defined(REMOTE_BOOT) UNICODE_STRING SmpAutoFmtKeyword; #endif // defined(REMOTE_BOOT) UNICODE_STRING SmpKnownDllPath; #ifdef _WIN64 UNICODE_STRING SmpKnownDllPath32; #endif HANDLE SmpWindowsSubSysProcess; ULONG_PTR SmpWindowsSubSysProcessId; ULONG_PTR SmpInitialCommandProcessId; UNICODE_STRING PosixName; UNICODE_STRING Os2Name; BOOLEAN RegPosixSingleInstance; // Make Softway Work. ULONG SmpAllowProtectedRenames; BOOLEAN MiniNTBoot = FALSE; LIST_ENTRY SmpBootExecuteList; LIST_ENTRY SmpSetupExecuteList; 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 ); NTSTATUS SmpConfigureProtectionMode( IN PWSTR ValueName, IN ULONG ValueType, IN PVOID ValueData, IN ULONG ValueLength, IN PVOID Context, IN PVOID EntryContext ); NTSTATUS SmpConfigureAllowProtectedRenames( 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 ); ULONGLONG SmpGetFileVersion( IN HANDLE FileHandle, IN PUNICODE_STRING FileName ); NTSTATUS SmpCallCsrCreateProcess( IN OUT PSBAPIMSG m, IN size_t ArgLength, IN HANDLE CommunicationPort ); 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}, {SmpConfigureAllowProtectedRenames, RTL_QUERY_REGISTRY_DELETE, L"AllowProtectedRenames", 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}, {SmpConfigureExecute, RTL_QUERY_REGISTRY_TOPKEY, L"SetupExecute", &SmpSetupExecuteList, REG_NONE, NULL, 0}, {SmpConfigureFileRenames, RTL_QUERY_REGISTRY_DELETE, L"PendingFileRenameOperations", &SmpFileRenameList, REG_NONE, NULL, 0}, {SmpConfigureFileRenames, RTL_QUERY_REGISTRY_DELETE, L"PendingFileRenameOperations2", &SmpFileRenameList, REG_NONE, NULL, 0}, {SmpConfigureExcludeKnownDlls, 0, L"ExcludeFromKnownDlls", &SmpExcludeKnownDllsList, REG_MULTI_SZ, L"\0", 0}, {NULL, RTL_QUERY_REGISTRY_SUBKEY, L"Memory Management", NULL, REG_NONE, NULL, 0}, {SmpConfigureMemoryMgmt, 0, L"PagingFiles", &SmpPagingFileList, REG_MULTI_SZ, L"?:\\pagefile.sys\0", 0}, {SmpConfigureDosDevices, RTL_QUERY_REGISTRY_SUBKEY, L"DOS Devices", &SmpDosDevicesList, REG_NONE, NULL, 0}, {SmpConfigureKnownDlls, RTL_QUERY_REGISTRY_SUBKEY, L"KnownDlls", &SmpKnownDllsList, REG_NONE, NULL, 0}, // // this needs to happen twice so that forward references are // properly resolved // {SmpConfigureEnvironment, RTL_QUERY_REGISTRY_SUBKEY, L"Environment", NULL, REG_NONE, NULL, 0}, {SmpConfigureEnvironment, RTL_QUERY_REGISTRY_SUBKEY, L"Environment", NULL, REG_NONE, NULL, 0}, {SmpConfigureSubSystems, RTL_QUERY_REGISTRY_SUBKEY, L"SubSystems", &SmpSubSystemList, REG_NONE, NULL, 0}, {SmpConfigureSubSystems, RTL_QUERY_REGISTRY_NOEXPAND, L"Required", &SmpSubSystemList, REG_MULTI_SZ, L"Debug\0Windows\0", 0}, {SmpConfigureSubSystems, RTL_QUERY_REGISTRY_NOEXPAND, L"Optional", &SmpSubSystemList, REG_NONE, NULL, 0}, {SmpConfigureSubSystems, 0, L"Kmode", &SmpSubSystemList, REG_NONE, NULL, 0}, {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 ); #if defined(REMOTE_BOOT) NTSTATUS SmpInvokeAutoFmt( IN PUNICODE_STRING ImageFileName, IN PUNICODE_STRING CurrentDirectory, IN PUNICODE_STRING Arguments, IN ULONG Flags ); #endif // defined(REMOTE_BOOT) NTSTATUS SmpLoadSubSystem( IN PUNICODE_STRING ImageFileName, IN PUNICODE_STRING CurrentDirectory, IN PUNICODE_STRING CommandLine, IN ULONG MuSessionId, OUT PULONG_PTR pWindowsSubSysProcessId, IN ULONG Flags ); NTSTATUS SmpExecuteCommand( IN PUNICODE_STRING CommandLine, IN ULONG MuSessionId, OUT PULONG_PTR pWindowsSubSysProcessId, IN ULONG Flags ); NTSTATUS SmpInitializeDosDevices( VOID ); NTSTATUS SmpInitializeKnownDlls( VOID ); NTSTATUS SmpInitializeKnownDllPath( IN PUNICODE_STRING KnownDllPath, IN PVOID ValueData, IN ULONG ValueLength); NTSTATUS SmpInitializeKnownDllsInternal( IN PUNICODE_STRING ObjectDirectoryName, IN PUNICODE_STRING KnownDllPath ); #if defined(REMOTE_BOOT) NTSTATUS SmpExecuteCommandLineArguments( VOID ); #endif // defined(REMOTE_BOOT) 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 OPTIONAL, 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 #if defined(REMOTE_BOOT) #define SMP_AUTOFMT_FLAG 0x00000040 #endif // defined(REMOTE_BOOT) #define SMP_POSIX_SI_FLAG 0x00000080 #define SMP_POSIX_FLAG 0x00000100 #define SMP_OS2_FLAG 0x00000200 ULONG SmpConvertInteger( IN PWSTR String ); VOID SmpTranslateSystemPartitionInformation( VOID ); #if defined(REMOTE_BOOT) // // Useful functions for iterating thru directories and files // typedef enum { NormalReturn, // if the whole process completes uninterrupted EnumFileError, // if an error occurs while enumerating files CallbackReturn // if the callback returns FALSE, causing termination } ENUMFILESRESULT; typedef BOOLEAN (*ENUMFILESPROC) ( IN PWSTR, IN PFILE_BOTH_DIR_INFORMATION, OUT PULONG, IN PVOID ); typedef struct { PVOID OptionalPtr; ENUMFILESPROC EnumProc; } RECURSION_DATA, *PRECURSION_DATA; ENUMFILESRESULT SmpEnumFiles( IN PWSTR DirName, IN ENUMFILESPROC EnumFilesProc, OUT PULONG ReturnData, IN PVOID Pointer ); ENUMFILESRESULT SmpEnumFilesRecursive ( IN PWSTR DirName, IN ENUMFILESPROC EnumFilesProc, OUT PULONG ReturnData, IN PVOID Pointer OPTIONAL ); VOID SmpConcatenatePaths( IN OUT LPWSTR Path1, IN LPCWSTR Path2 ); BOOLEAN SmppRecursiveEnumProc ( IN PWSTR DirName, IN PFILE_BOTH_DIR_INFORMATION FileInfo, OUT PULONG ret, IN PVOID Param ); BOOLEAN SmpDelEnumFile( IN PWSTR DirName, IN PFILE_BOTH_DIR_INFORMATION FileInfo, OUT PULONG ret, IN PVOID Pointer ); #endif // defined(REMOTE_BOOT) // // 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)) { KdPrintEx((DPFLTR_SMSS_ID, DPFLTR_WARNING_LEVEL, "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)) { KdPrintEx((DPFLTR_SMSS_ID, DPFLTR_WARNING_LEVEL, "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 SmpInit( OUT PUNICODE_STRING InitialCommand, OUT PHANDLE WindowsSubSystem ) { NTSTATUS st; OBJECT_ATTRIBUTES ObjA; HANDLE SmpApiConnectionPort; UNICODE_STRING Unicode; NTSTATUS Status, Status2; ULONG HardErrorMode; UNICODE_STRING UnicodeString; HANDLE VolumeSafeEvent; 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)) { SAVE_SMPINIT_STATUS (SmpCreateSecurityDescriptors, st); return(st); } InitializeListHead(&NativeProcessList); SmpHeap = RtlProcessHeap(); RtlInitUnicodeString( &PosixName, L"POSIX" ); RtlInitUnicodeString( &Os2Name, L"OS2" ); 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) ); // // Create a event to signal that volume are safe for write access opens. // Call this event 'VolumesSafeForWriteAccess'. This event will be // signalled after AUTOCHK/AUTOCONV/AUTOFMT have done their business. // RtlInitUnicodeString( &UnicodeString, L"\\Device\\VolumesSafeForWriteAccess"); InitializeObjectAttributes( &ObjA, &UnicodeString, OBJ_CASE_INSENSITIVE | OBJ_PERMANENT, NULL, NULL ); Status2 = NtCreateEvent( &VolumeSafeEvent, EVENT_ALL_ACCESS, &ObjA, NotificationEvent, FALSE ); if (!NT_SUCCESS( Status2 )) { KdPrintEx((DPFLTR_SMSS_ID, DPFLTR_WARNING_LEVEL, "SMSS: Unable to create %wZ event - Status == %lx\n", &UnicodeString, Status2)); ASSERT( NT_SUCCESS(Status2) ); } // // Configure the system // Status = SmpLoadDataFromRegistry( InitialCommand ); if (NT_SUCCESS( Status )) { *WindowsSubSystem = SmpWindowsSubSysProcess; } // // AUTOCHK/AUTOCONV/AUTOFMT are finished. // if (NT_SUCCESS(Status2)) { NtSetEvent(VolumeSafeEvent, NULL); NtClose(VolumeSafeEvent); } return( Status ); } 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; ULONG MuSessionId = 0; UNICODE_STRING KeyName; OBJECT_ATTRIBUTES ObjectAttributes; HANDLE Key; UNICODE_STRING SessionDirName; #if defined(REMOTE_BOOT) HANDLE RdrHandle = NULL; IO_STATUS_BLOCK Iosb; SHADOWINFO ShadowInfo; #endif // defined(REMOTE_BOOT) RtlInitUnicodeString( &SmpDebugKeyword, L"debug" ); RtlInitUnicodeString( &SmpASyncKeyword, L"async" ); RtlInitUnicodeString( &SmpAutoChkKeyword, L"autocheck" ); #if defined(REMOTE_BOOT) RtlInitUnicodeString( &SmpAutoFmtKeyword, L"autoformat" ); #endif // defined(REMOTE_BOOT) InitializeListHead( &SmpBootExecuteList ); InitializeListHead( &SmpSetupExecuteList ); InitializeListHead( &SmpPagingFileList ); InitializeListHead( &SmpDosDevicesList ); InitializeListHead( &SmpFileRenameList ); InitializeListHead( &SmpKnownDllsList ); InitializeListHead( &SmpExcludeKnownDllsList ); InitializeListHead( &SmpSubSystemList ); InitializeListHead( &SmpSubSystemsToLoad ); InitializeListHead( &SmpSubSystemsToDefer ); InitializeListHead( &SmpExecuteList ); SmpPagingFileInitialize (); Status = RtlCreateEnvironment( TRUE, &SmpDefaultEnvironment ); if (!NT_SUCCESS( Status )) { KdPrintEx((DPFLTR_SMSS_ID, DPFLTR_WARNING_LEVEL, "SMSS: Unable to allocate default environment - Status == %X\n", Status)); SAVE_SMPINIT_STATUS (RtlCreateEnvironment, Status); return( Status ); } RtlInitUnicodeString( &KeyName, L"\\Registry\\Machine\\System\\CurrentControlSet\\Control\\MiniNT" ); InitializeObjectAttributes( &ObjectAttributes, &KeyName, OBJ_CASE_INSENSITIVE, NULL, NULL ); Status = NtOpenKey( &Key, KEY_ALL_ACCESS, &ObjectAttributes ); if (NT_SUCCESS( Status )) { NtClose( Key ); MiniNTBoot = TRUE; } if (MiniNTBoot) { DbgPrint("SMSS: !!! MiniNT Boot !!!\n"); } // // before the environment is created we MUST delete the // safemode reg value // RtlInitUnicodeString( &KeyName, L"\\Registry\\Machine\\System\\CurrentControlSet\\Control\\Session Manager\\Environment" ); InitializeObjectAttributes( &ObjectAttributes, &KeyName, OBJ_CASE_INSENSITIVE, NULL, NULL ); Status = NtOpenKey( &Key, KEY_ALL_ACCESS, &ObjectAttributes ); if (NT_SUCCESS( Status )) { RtlInitUnicodeString( &KeyName, L"SAFEBOOT_OPTION" ); NtDeleteValueKey( Key, &KeyName ); NtClose( Key ); } // // 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 )) { KdPrintEx((DPFLTR_SMSS_ID, DPFLTR_WARNING_LEVEL, "SMSS: RtlQueryRegistryValues failed - Status == %lx\n", Status)); SAVE_SMPINIT_STATUS (RtlQueryRegistryValues, Status); return( Status ); } Status = SmpInitializeDosDevices(); if (!NT_SUCCESS( Status )) { KdPrintEx((DPFLTR_SMSS_ID, DPFLTR_WARNING_LEVEL, "SMSS: Unable to initialize DosDevices configuration - Status == %lx\n", Status)); SAVE_SMPINIT_STATUS (SmpInitializeDosDevices, Status); return( Status ); } // // Create the root "Sessions Directory". This is the container for all session // specific directories. Each session specific CSRSS during startup will // create a direcotry under "\Sessions". "\Sessions\ // directory will be the container for that session. // RtlInitUnicodeString( &SessionDirName, L"\\Sessions" ); InitializeObjectAttributes( &ObjectAttributes, &SessionDirName, OBJ_CASE_INSENSITIVE | OBJ_OPENIF | OBJ_PERMANENT, NULL, SmpPrimarySecurityDescriptor ); if (!NT_SUCCESS(Status = NtCreateDirectoryObject( &SmpSessionsObjectDirectory, DIRECTORY_ALL_ACCESS, &ObjectAttributes ))) { KdPrintEx((DPFLTR_SMSS_ID, DPFLTR_WARNING_LEVEL, "SMSS: Unable to create %wZ object directory - Status == %lx\n", &SessionDirName, Status)); SAVE_SMPINIT_STATUS (NtCreateDirectoryObject, Status); return Status; } #if defined(REMOTE_BOOT) // // On a remote boot client, the client-side cache is already initialized. // We need to tell CSC not to cache database handles during the next phase // so that autochk can run. // if (SmpNetboot) { OBJECT_ATTRIBUTES ObjectAttributes; UNICODE_STRING RdrNameString; RtlInitUnicodeString( &RdrNameString, L"\\Device\\LanmanRedirector" ); InitializeObjectAttributes( &ObjectAttributes, &RdrNameString, OBJ_CASE_INSENSITIVE, NULL, NULL ); Status = NtCreateFile( &RdrHandle, GENERIC_READ | GENERIC_WRITE | SYNCHRONIZE, &ObjectAttributes, &Iosb, NULL, 0, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, FILE_OPEN, FILE_SYNCHRONOUS_IO_NONALERT, NULL, 0 ); if ( !NT_SUCCESS(Status) ) { KdPrintEx((DPFLTR_SMSS_ID, DPFLTR_WARNING_LEVEL, "SmpLoadDataFromRegistry: Unable to open redirector: %x\n", Status)); RdrHandle = NULL; } else { ShadowInfo.uOp = SHADOW_CHANGE_HANDLE_CACHING_STATE; ShadowInfo.uStatus = FALSE; Status = NtDeviceIoControlFile( RdrHandle, NULL, NULL, NULL, &Iosb, IOCTL_DO_SHADOW_MAINTENANCE, &ShadowInfo, sizeof(ShadowInfo), NULL, 0 ); if ( NT_SUCCESS(Status) ) { Status = Iosb.Status; } if ( !NT_SUCCESS(Status) ) { KdPrintEx((DPFLTR_SMSS_ID, DPFLTR_WARNING_LEVEL, "SmpLoadDataFromRegistry: Unable to IOCTL CSC: %x\n", Status)); } } } Status = SmpExecuteCommandLineArguments(); if (!NT_SUCCESS( Status )) { KdPrintEx((DPFLTR_SMSS_ID, DPFLTR_WARNING_LEVEL, "SMSS: Unable to process command line arguments - Status == %lx\n", Status)); return( Status ); } #endif // defined(REMOTE_BOOT) Head = &SmpBootExecuteList; while (!IsListEmpty( Head )) { Next = RemoveHeadList( Head ); p = CONTAINING_RECORD( Next, SMP_REGISTRY_VALUE, Entry ); #if SMP_SHOW_REGISTRY_DATA DbgPrint( "SMSS: BootExecute( %wZ )\n", &p->Name ); #endif SmpExecuteCommand( &p->Name, 0, NULL, 0 ); RtlFreeHeap( RtlProcessHeap(), 0, p ); } #if defined(REMOTE_BOOT) // // On a remote boot client, we can now reenable CSC handle caching. // if (SmpNetboot && (RdrHandle != NULL)) { ShadowInfo.uOp = SHADOW_CHANGE_HANDLE_CACHING_STATE; ShadowInfo.uStatus = TRUE; Status = NtDeviceIoControlFile( RdrHandle, NULL, NULL, NULL, &Iosb, IOCTL_DO_SHADOW_MAINTENANCE, &ShadowInfo, sizeof(ShadowInfo), NULL, 0 ); if ( NT_SUCCESS(Status) ) { Status = Iosb.Status; } if ( !NT_SUCCESS(Status) ) { KdPrintEx((DPFLTR_SMSS_ID, DPFLTR_WARNING_LEVEL, "SmpLoadDataFromRegistry: Unable to IOCTL CSC (2): %x\n", Status)); } } #endif // defined(REMOTE_BOOT) if (!MiniNTBoot) { SmpProcessFileRenames(); } // // Begin process of verifying system DLL's // Status = SmpInitializeKnownDlls(); if (!NT_SUCCESS( Status )) { KdPrintEx((DPFLTR_SMSS_ID, DPFLTR_WARNING_LEVEL, "SMSS: Unable to initialize KnownDll configuration - Status == %lx\n", Status)); SAVE_SMPINIT_STATUS (SmpInitializeKnownDlls, Status); return( Status ); } // // Create paging files. // if (! MiniNTBoot) { Head = &SmpPagingFileList; try { // // Process the list of paging file descriptors // read from registry. // while (! IsListEmpty (Head)) { Next = RemoveHeadList (Head); p = CONTAINING_RECORD (Next, SMP_REGISTRY_VALUE, Entry); SmpCreatePagingFileDescriptor (&p->Name); RtlFreeHeap (RtlProcessHeap(), 0, p); } // // Create any paging files specified. // SmpCreatePagingFiles(); } except (SmpPagingFileExceptionFilter (_exception_code(), _exception_info())) { // // Nothing. // } } // // Finish registry initialization // NtInitializeRegistry(REG_INIT_BOOT_SM); Status = SmpCreateDynamicEnvironmentVariables( ); if (!NT_SUCCESS( Status )) { SAVE_SMPINIT_STATUS (SmpCreateDynamicEnvironmentVariables, Status); return Status; } // // Load subsystems for the console session. Console always has // MuSessionId = 0 // Status = SmpLoadSubSystemsForMuSession( &MuSessionId, &SmpWindowsSubSysProcessId, InitialCommand ); ASSERT(MuSessionId == 0); if (! NT_SUCCESS(Status)) { SAVE_SMPINIT_STATUS (SmpLoadSubSystemsForMuSession, Status); } return( Status ); } NTSTATUS SmpLoadSubSystemsForMuSession( PULONG pMuSessionId, PULONG_PTR pWindowsSubSysProcessId, PUNICODE_STRING InitialCommand ) /*++ Routine Description: This function starts all of the configured subsystems for the specified Multi-User Session. For regular NT this routine is called once to start CSRSS etc. For Terminal Server, this routine is called every time we want to start a new Multi-User Session to start session specific subsystems Arguments: Return Value: Status of operation --*/ { NTSTATUS Status = 0; PLIST_ENTRY Head, Next; PSMP_REGISTRY_VALUE p; // // Translate the system partition information stored during IoInitSystem into // a DOS path and store in Win32-standard location. // SmpTranslateSystemPartitionInformation(); // // Second pass of execution. // Next = SmpSetupExecuteList.Flink; while( Next != &SmpSetupExecuteList ) { p = CONTAINING_RECORD( Next, SMP_REGISTRY_VALUE, Entry ); #if SMP_SHOW_REGISTRY_DATA DbgPrint( "SMSS: SetupExecute( %wZ )\n", &p->Name ); #endif SmpExecuteCommand( &p->Name, 0, NULL, 0 ); // // Note this function is reentrant and is called every time we start // a new Multi-User Session. // Next = Next->Flink; } Next = SmpSubSystemList.Flink; while ( Next != &SmpSubSystemList ) { p = CONTAINING_RECORD( Next, SMP_REGISTRY_VALUE, Entry ); if ( !_wcsicmp( p->Name.Buffer, L"Kmode" )) { BOOLEAN TranslationStatus; UNICODE_STRING FileName; UNICODE_STRING Win32kFileName; TranslationStatus = RtlDosPathNameToNtPathName_U( p->Value.Buffer, &FileName, NULL, NULL ); if ( TranslationStatus ) { PVOID State; Status = SmpAcquirePrivilege( SE_LOAD_DRIVER_PRIVILEGE, &State ); if (NT_SUCCESS( Status )) { // // Create a session space before loading any extended // service table providers. This call will create a session // space for the Multi-User session. The session mananger // will see the instance of the newly created session space // after this call. Once session manager is done creating // CSRSS and winlogon it will detach itself from this // session space. // ASSERT( AttachedSessionId == -1 ); Status = NtSetSystemInformation( SystemSessionCreate, (PVOID)pMuSessionId, sizeof(*pMuSessionId) ); if ( !NT_SUCCESS(Status) ) { KdPrintEx((DPFLTR_SMSS_ID, DPFLTR_WARNING_LEVEL, "SMSS: Session space creation failed\n")); // // Do not load any subsystems without SessionSpace. // SmpReleasePrivilege( State ); RtlFreeHeap(RtlProcessHeap(), 0, FileName.Buffer); return( Status ); }; AttachedSessionId = *pMuSessionId; RtlInitUnicodeString(&Win32kFileName,L"\\SystemRoot\\System32\\win32k.sys"); Status = NtSetSystemInformation( SystemExtendServiceTableInformation, (PVOID)&Win32kFileName, sizeof(Win32kFileName) ); RtlFreeHeap(RtlProcessHeap(), 0, FileName.Buffer); SmpReleasePrivilege( State ); if ( !NT_SUCCESS(Status) ) { // // Do not load any subsystems without WIN32K! // KdPrintEx((DPFLTR_SMSS_ID, DPFLTR_ERROR_LEVEL, "SMSS: Load of WIN32K failed.\n")); return( Status ); } } else { RtlFreeHeap(RtlProcessHeap(), 0, FileName.Buffer); } } else { Status = STATUS_OBJECT_PATH_SYNTAX_BAD; } } #if SMP_SHOW_REGISTRY_DATA DbgPrint( "SMSS: Unused SubSystem( %wZ = %wZ )\n", &p->Name, &p->Value ); #endif Next = Next->Flink; } Next = SmpSubSystemsToLoad.Flink; while ( Next != &SmpSubSystemsToLoad ) { p = CONTAINING_RECORD( Next, SMP_REGISTRY_VALUE, Entry ); #if SMP_SHOW_REGISTRY_DATA DbgPrint( "SMSS: Loaded SubSystem( %wZ = %wZ )\n", &p->Name, &p->Value ); #endif if (!_wcsicmp( p->Name.Buffer, L"debug" )) { Status = SmpExecuteCommand( &p->Value, *pMuSessionId, pWindowsSubSysProcessId, SMP_SUBSYSTEM_FLAG | SMP_DEBUG_FLAG ); } else { Status = SmpExecuteCommand( &p->Value, *pMuSessionId, pWindowsSubSysProcessId, SMP_SUBSYSTEM_FLAG ); } if ( !NT_SUCCESS(Status) ) { return( Status ); } Next = Next->Flink; } Head = &SmpExecuteList; if ( !IsListEmpty( Head ) ) { Next = Head->Blink; p = CONTAINING_RECORD( Next, SMP_REGISTRY_VALUE, 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 ); } } Next = SmpExecuteList.Flink; while( Next != &SmpExecuteList ) { // // We do not want to execute the last entry. It's // the winlogon initial command. // if( Next == SmpExecuteList.Blink ) { Next = Next->Flink; continue; } p = CONTAINING_RECORD( Next, SMP_REGISTRY_VALUE, Entry ); #if SMP_SHOW_REGISTRY_DATA DbgPrint( "SMSS: Execute( %wZ )\n", &p->Name ); #endif SmpExecuteCommand( &p->Name, *pMuSessionId, NULL, 0 ); Next = Next->Flink; } #if 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 )) { KdPrintEx((DPFLTR_SMSS_ID, DPFLTR_WARNING_LEVEL, "SMSS: Unable to query system basic information - %x\n", Status)); return Status; } Status = NtQuerySystemInformation( SystemProcessorInformation, &ProcessorInfo, sizeof( ProcessorInfo ), NULL ); if (!NT_SUCCESS( Status )) { KdPrintEx((DPFLTR_SMSS_ID, DPFLTR_WARNING_LEVEL, "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, KEY_ALL_ACCESS, &ObjectAttributes ); if (!NT_SUCCESS( Status )) { KdPrintEx((DPFLTR_SMSS_ID, DPFLTR_WARNING_LEVEL, "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 )) { KdPrintEx((DPFLTR_SMSS_ID, DPFLTR_WARNING_LEVEL, "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; case PROCESSOR_ARCHITECTURE_ALPHA64: ValueData = L"ALPHA64"; break; case PROCESSOR_ARCHITECTURE_IA64: ValueData = L"IA64"; break; default: ValueData = L"Unknown"; break; } Status = NtSetValueKey( Key, &ValueName, 0, REG_SZ, ValueData, (wcslen( ValueData ) + 1) * sizeof( WCHAR ) ); if (!NT_SUCCESS( Status )) { KdPrintEx((DPFLTR_SMSS_ID, DPFLTR_WARNING_LEVEL, "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_IA64: 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 )) { KdPrintEx((DPFLTR_SMSS_ID, DPFLTR_WARNING_LEVEL, "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 )) { KdPrintEx((DPFLTR_SMSS_ID, DPFLTR_WARNING_LEVEL, "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 ); KdPrintEx((DPFLTR_SMSS_ID, DPFLTR_WARNING_LEVEL, "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 )) { KdPrintEx((DPFLTR_SMSS_ID, DPFLTR_WARNING_LEVEL, "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: case PROCESSOR_ARCHITECTURE_IA64: 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 )) { KdPrintEx((DPFLTR_SMSS_ID, DPFLTR_WARNING_LEVEL, "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 )) { KdPrintEx((DPFLTR_SMSS_ID, DPFLTR_WARNING_LEVEL, "SMSS: Failed writing %wZ environment variable - %x\n", &ValueName, Status)); goto failexit; } // // get the safeboot option // RtlInitUnicodeString( &KeyName, L"\\Registry\\Machine\\System\\CurrentControlSet\\Control\\Safeboot\\Option" ); InitializeObjectAttributes( &ObjectAttributes, &KeyName, OBJ_CASE_INSENSITIVE, NULL, NULL ); Status = NtOpenKey( &Key1, KEY_ALL_ACCESS, &ObjectAttributes ); if (NT_SUCCESS(Status)) { RtlInitUnicodeString( &ValueName, L"OptionValue" ); KeyValueInfo = (PKEY_VALUE_PARTIAL_INFORMATION)ValueBuffer; Status = NtQueryValueKey( Key1, &ValueName, KeyValuePartialInformation, (PVOID)KeyValueInfo, sizeof(ValueBuffer), &ValueLength ); NtClose( Key1 ); if (NT_SUCCESS(Status)) { RtlInitUnicodeString( &ValueName, L"SAFEBOOT_OPTION" ); switch (*(PULONG)(KeyValueInfo->Data)) { case SAFEBOOT_MINIMAL: wcscpy(ValueBuffer,SAFEBOOT_MINIMAL_STR_W); break; case SAFEBOOT_NETWORK: wcscpy(ValueBuffer,SAFEBOOT_NETWORK_STR_W); break; case SAFEBOOT_DSREPAIR: wcscpy(ValueBuffer,SAFEBOOT_DSREPAIR_STR_W); break; } Status = NtSetValueKey( Key, &ValueName, 0, REG_SZ, ValueBuffer, (wcslen(ValueBuffer)+1) * sizeof( WCHAR ) ); if (!NT_SUCCESS( Status )) { KdPrintEx((DPFLTR_SMSS_ID, DPFLTR_WARNING_LEVEL, "SMSS: Failed writing %wZ environment variable - %x\n", &ValueName, Status)); goto failexit; } } else { KdPrintEx((DPFLTR_SMSS_ID, DPFLTR_WARNING_LEVEL, "SMSS: Failed querying safeboot option = %x\n", Status)); } } Status = STATUS_SUCCESS; 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=0; // // 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 )) { KdPrintEx((DPFLTR_SMSS_ID, DPFLTR_WARNING_LEVEL, "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 ); #if 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 )) { KdPrintEx((DPFLTR_SMSS_ID, DPFLTR_WARNING_LEVEL, "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 ); return; } NTSTATUS SmpInitializeKnownDlls( VOID ) { NTSTATUS Status; UNICODE_STRING DirectoryObjectName; PLIST_ENTRY Head, Next; PSMP_REGISTRY_VALUE p; RtlInitUnicodeString( &DirectoryObjectName, L"\\KnownDlls" ); Status = SmpInitializeKnownDllsInternal( &DirectoryObjectName, &SmpKnownDllPath); #ifdef _WIN64 if (!MiniNTBoot && NT_SUCCESS(Status)) { RtlInitUnicodeString( &DirectoryObjectName, L"\\KnownDlls32" ); Status = SmpInitializeKnownDllsInternal( &DirectoryObjectName, &SmpKnownDllPath32); } #endif Head = &SmpKnownDllsList; Next = Head->Flink; while (Next != Head) { p = CONTAINING_RECORD( Next, SMP_REGISTRY_VALUE, Entry ); Next = Next->Flink; RtlFreeHeap( RtlProcessHeap(), 0, p ); } return Status; } NTSTATUS SmpInitializeKnownDllsInternal( IN PUNICODE_STRING ObjectDirectoryName, IN PUNICODE_STRING KnownDllPath ) { 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; HANDLE KnownDllFileDirectory; HANDLE KnownDllObjectDirectory; // // Create \KnownDllsxx object directory // InitializeObjectAttributes( &ObjectAttributes, ObjectDirectoryName, OBJ_CASE_INSENSITIVE | OBJ_OPENIF | OBJ_PERMANENT, NULL, SmpKnownDllsSecurityDescriptor ); Status = NtCreateDirectoryObject( &KnownDllObjectDirectory, DIRECTORY_ALL_ACCESS, &ObjectAttributes ); if (!NT_SUCCESS( Status )) { KdPrintEx((DPFLTR_SMSS_ID, DPFLTR_WARNING_LEVEL, "SMSS: Unable to create %wZ directory - Status == %lx\n", ObjectDirectoryName, 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( KnownDllPath->Buffer, &FileName, NULL, NULL ) ) { KdPrintEx((DPFLTR_SMSS_ID, DPFLTR_WARNING_LEVEL, "SMSS: Unable to to convert %wZ to an Nt path\n", KnownDllPath)); 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( &KnownDllFileDirectory, FILE_LIST_DIRECTORY | SYNCHRONIZE, &ObjectAttributes, &IoStatusBlock, FILE_SHARE_READ | FILE_SHARE_WRITE, FILE_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT ); if (!NT_SUCCESS( Status )) { KdPrintEx((DPFLTR_SMSS_ID, DPFLTR_WARNING_LEVEL, "SMSS: Unable to open a handle to the KnownDll directory (%wZ) - Status == %lx\n", KnownDllPath, Status)); return Status; } RtlInitUnicodeString( &UnicodeString, L"KnownDllPath" ); InitializeObjectAttributes( &ObjectAttributes, &UnicodeString, OBJ_CASE_INSENSITIVE | OBJ_OPENIF | OBJ_PERMANENT, KnownDllObjectDirectory, SmpPrimarySecurityDescriptor ); SmpSetDaclDefaulted( &ObjectAttributes, &OriginalSdControl ); //Use inheritable protection if available Status = NtCreateSymbolicLinkObject( &LinkHandle, SYMBOLIC_LINK_ALL_ACCESS, &ObjectAttributes, KnownDllPath ); SmpRestoreDaclDefaulted( &ObjectAttributes, OriginalSdControl ); if (!NT_SUCCESS( Status )) { KdPrintEx((DPFLTR_SMSS_ID, DPFLTR_WARNING_LEVEL, "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) { Status = STATUS_OBJECT_NAME_NOT_FOUND; } else { #if SMP_SHOW_REGISTRY_DATA DbgPrint( "SMSS: KnownDll( %wZ = %wZ )\n", &p->Name, &p->Value ); #endif InitializeObjectAttributes( &ObjectAttributes, &p->Value, OBJ_CASE_INSENSITIVE, KnownDllFileDirectory, 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 )) { // // good old stevewo... We want the side effects of this call (import // callout, but don't want to checksum anymore, so supress with // handle tag bit // ObjectDirectory = KnownDllObjectDirectory; Status = LdrVerifyImageMatchesChecksum((HANDLE)((UINT_PTR)FileHandle|1), SmpProcessModuleImports, Next, &ImageCharacteristics ); if ( Status == STATUS_IMAGE_CHECKSUM_MISMATCH ) { ULONG_PTR ErrorParameters; ULONG ErrorResponse; // // Hard error time. One of the know DLL's is corrupt ! // ErrorParameters = (ULONG_PTR)(&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 )) { KdPrintEx((DPFLTR_SMSS_ID, DPFLTR_WARNING_LEVEL, "SMSS: CreateSection for KnownDll %wZ failed - Status == %lx\n", &p->Value, Status)); } else { NtClose(SectionHandle); } } else { KdPrintEx((DPFLTR_SMSS_ID, DPFLTR_WARNING_LEVEL, "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 ? // } return STATUS_SUCCESS; } NTSTATUS SmpSetProtectedFilesEnvVars( IN BOOLEAN SetEnvVar ) /*++ Routine Description: This function sets some environment variables that are not part of the default environment. (These environment variables are normally set by winlogon.) The environment variables need to be set for us to resolve all the environment variables in our protected files list. Note that SFC mirrors the data into the location below since smss can't get at the actual variable location The variables are: ProgramFiles CommonProgramFiles ProgramFiles(x86) CommonProgramFiles(x86) Arguments: SetEnvVar - if TRUE, we should query the registry for this variables and set them. if FALSE, we should clear the environment variables Return Value: Status of operation --*/ { NTSTATUS Status; UNICODE_STRING KeyName; UNICODE_STRING ValueName; UNICODE_STRING EnvVar; UNICODE_STRING EnvVarValue; OBJECT_ATTRIBUTES ObjectAttributes; HANDLE Key; WCHAR ValueBuffer[VALUE_BUFFER_SIZE]; PKEY_VALUE_PARTIAL_INFORMATION KeyValueInfo; ULONG ValueLength; ULONG Count; PCWSTR RegistryValues[] = { L"ProgramFilesDir" , L"CommonFilesDir" #ifdef WX86 , L"ProgramFilesDir(x86)" , L"CommonFilesDir(x86)" #endif }; PCWSTR EnvVars[] = { L"ProgramFiles" , L"CommonProgramFiles" #ifdef WX86 , L"ProgramFiles(x86)" , L"CommonProgramFiles(x86)" #endif }; #define EnvVarCount sizeof(RegistryValues)/sizeof(PCWSTR) if (SetEnvVar) { // // Open the registry key. // KeyValueInfo = (PKEY_VALUE_PARTIAL_INFORMATION)ValueBuffer; RtlInitUnicodeString(&KeyName, L"\\Registry\\Machine\\System\\CurrentControlSet\\Control\\Session Manager\\SFC"); InitializeObjectAttributes(&ObjectAttributes, &KeyName, OBJ_CASE_INSENSITIVE, NULL, NULL); Status = NtOpenKey(&Key, KEY_READ, &ObjectAttributes); if (!NT_SUCCESS(Status)) { KdPrintEx((DPFLTR_SMSS_ID, DPFLTR_WARNING_LEVEL, "SMSS: can't open control key: 0x%x\n", Status)); return Status; } // // Query the key values. // for (Count = 0; Count < EnvVarCount; Count++) { RtlInitUnicodeString(&ValueName, RegistryValues[Count]); Status = NtQueryValueKey(Key, &ValueName, KeyValuePartialInformation, (PVOID)KeyValueInfo, VALUE_BUFFER_SIZE, &ValueLength); ASSERT(ValueLength < VALUE_BUFFER_SIZE); if (!NT_SUCCESS(Status)) { KdPrintEx((DPFLTR_SMSS_ID, DPFLTR_WARNING_LEVEL, "SMSS: can't query value key %ws: 0x%x\n", RegistryValues[Count], Status)); } else { ASSERT(KeyValueInfo->Type == REG_SZ); RtlInitUnicodeString(&EnvVar, EnvVars[Count]); EnvVarValue.Length = (USHORT)KeyValueInfo->DataLength; EnvVarValue.MaximumLength = (USHORT)KeyValueInfo->DataLength; EnvVarValue.Buffer = (PWSTR)KeyValueInfo->Data; Status = RtlSetEnvironmentVariable( NULL, &EnvVar, &EnvVarValue ); if (!NT_SUCCESS(Status)) { KdPrintEx((DPFLTR_SMSS_ID, DPFLTR_WARNING_LEVEL, "SMSS: can't set environment variable %ws: 0x%x\n", EnvVars[Count], Status)); } } } NtClose(Key); } else { // // clear out the variables // for (Count = 0; Count < EnvVarCount; Count++) { RtlInitUnicodeString(&EnvVar, EnvVars[Count]); RtlInitUnicodeString(&EnvVarValue, NULL); Status = RtlSetEnvironmentVariable( NULL, &EnvVar, &EnvVarValue ); if (!NT_SUCCESS(Status)) { KdPrintEx((DPFLTR_SMSS_ID, DPFLTR_WARNING_LEVEL, "SMSS: can't clear environment variable %ws: 0x%x\n", EnvVars[Count], Status)); } } } return Status; } NTSTATUS SmpGetProtectedFiles( OUT PPROTECT_FILE_ENTRY *Files, OUT PULONG FileCount, OUT PVOID *hModule ) { NTSTATUS Status; UNICODE_STRING DllName; STRING ProcedureName; PSFCGETFILES pSfcGetFiles; ASSERT(hModule != NULL); *hModule = NULL; RtlInitUnicodeString( &DllName, L"sfcfiles.dll" ); Status = LdrLoadDll( NULL, NULL, &DllName, hModule ); if (!NT_SUCCESS( Status )) { KdPrintEx((DPFLTR_SMSS_ID, DPFLTR_WARNING_LEVEL, "SMSS: LdrLoadDll failed for %ws, ec=%lx\n", DllName.Buffer, Status)); return Status; } RtlInitString( &ProcedureName, "SfcGetFiles" ); Status = LdrGetProcedureAddress( *hModule, &ProcedureName, 0, (PVOID*)&pSfcGetFiles ); if (NT_SUCCESS(Status)) { #if SMP_SHOW_REGISTRY_DATA DbgPrint( "SMSS: sfcfile.dll loaded successfully, address=%08x\n", *hModule ); #endif Status = pSfcGetFiles( Files, FileCount ); } else { KdPrintEx((DPFLTR_SMSS_ID, DPFLTR_WARNING_LEVEL, "SMSS: LdrGetProcedureAddress failed for %ws, ec=%lx\n", ProcedureName.Buffer, Status)); LdrUnloadDll(*hModule); *hModule = NULL; } return Status; } LONG SpecialStringCompare( PUNICODE_STRING s1, PUNICODE_STRING s2 ) { UNICODE_STRING tmp; if (s1->Buffer[0] != L'!') { return RtlCompareUnicodeString( s1, s2, TRUE ); } tmp.Length = s1->Length - sizeof(WCHAR); tmp.MaximumLength = s1->MaximumLength - sizeof(WCHAR); tmp.Buffer = s1->Buffer + 1; return RtlCompareUnicodeString( &tmp, s2, TRUE ); } VOID SmpProcessFileRenames( VOID ) { NTSTATUS Status,OpenStatus; PLIST_ENTRY Head, Next; PSMP_REGISTRY_VALUE p; OBJECT_ATTRIBUTES ObjectAttributes; IO_STATUS_BLOCK IoStatusBlock; HANDLE OldFileHandle,SetAttributesHandle; PFILE_RENAME_INFORMATION RenameInformation; FILE_DISPOSITION_INFORMATION DeleteInformation; FILE_INFORMATION_CLASS SetInfoClass; FILE_BASIC_INFORMATION BasicInfo; ULONG SetInfoLength; PVOID SetInfoBuffer; PWSTR s; BOOLEAN WasEnabled; UNICODE_STRING NewName; ULONG i; UNICODE_STRING ProtFileName = {0}; UNICODE_STRING Tier2Name; UNICODE_STRING ProtName; PPROTECT_FILE_ENTRY Tier2Files; ULONG CountTier2Files; PVOID hModule = NULL; BOOLEAN EnvVarSet; Status = RtlAdjustPrivilege( SE_RESTORE_PRIVILEGE, TRUE, FALSE, &WasEnabled ); if (!NT_SUCCESS( Status )) { WasEnabled = TRUE; } if (SmpAllowProtectedRenames == 0) { Status = SmpGetProtectedFiles( &Tier2Files, &CountTier2Files, &hModule ); if (!NT_SUCCESS( Status )) { KdPrintEx((DPFLTR_SMSS_ID, DPFLTR_WARNING_LEVEL, "SMSS: SmpGetProtectedFiles failed, ec=%08x\n", Status)); SmpAllowProtectedRenames = 1; } } // // our list of protected files includes environment variables that are not // in the default environment, they are normally set by winlogon. Set // those environment variables temporarily until we process the file rename // section, then we can clear them out again. // EnvVarSet = TRUE; Status = SmpSetProtectedFilesEnvVars( TRUE ); if (!NT_SUCCESS( Status )) { KdPrintEx((DPFLTR_SMSS_ID, DPFLTR_WARNING_LEVEL, "SMSS: SmpSetProtectedFilesEnvVars failed, ec=%08x\n", Status)); EnvVarSet = FALSE; } // // Process the list of file rename operations. // Head = &SmpFileRenameList; while (!IsListEmpty( Head )) { Next = RemoveHeadList( Head ); p = CONTAINING_RECORD( Next, SMP_REGISTRY_VALUE, Entry ); #if SMP_SHOW_REGISTRY_DATA DbgPrint( "SMSS: FileRename( [%wZ] => [%wZ] )\n", &p->Name, &p->Value ); #endif // // ignore any file that is protected // if (SmpAllowProtectedRenames == 0) { ProtName.MaximumLength = 256 * sizeof(WCHAR); ProtName.Buffer = (PWSTR) RtlAllocateHeap( RtlProcessHeap(), MAKE_TAG( INIT_TAG ), ProtName.MaximumLength ); if (ProtName.Buffer) { for (i=0; iName.Buffer[0] == '@' || p->Value.Buffer[0] == L'@') { break; } // // convert the tier2 file name to an nt style file name // RtlInitUnicodeString(&Tier2Name,Tier2Files[i].FileName); ProtName.Length = 0; RtlZeroMemory( ProtName.Buffer, ProtName.MaximumLength ); if (ProtName.Buffer == NULL) { continue; } Status = RtlExpandEnvironmentStrings_U( NULL, &Tier2Name, &ProtName, NULL ); if (!NT_SUCCESS(Status)) { continue; } if (!RtlDosPathNameToNtPathName_U( ProtName.Buffer, &ProtFileName, NULL, NULL )) { continue; } // // check for matches against both file names // if (SpecialStringCompare( &p->Name, &ProtFileName ) == 0 || SpecialStringCompare( &p->Value, &ProtFileName ) == 0) { break; } RtlFreeUnicodeString(&ProtFileName); ProtFileName.Buffer = NULL; } RtlFreeHeap( RtlProcessHeap(), 0, ProtName.Buffer ); if (i < CountTier2Files) { if (p->Name.Buffer[0] == L'@' || p->Value.Buffer[0] == L'@') { } else { #if SMP_SHOW_REGISTRY_DATA DbgPrint( "SMSS: Skipping rename because it is protected\n" ); #endif // // delete the source file so we don't leave any turds // if (p->Value.Length > 0 && ProtFileName.Buffer && SpecialStringCompare( &p->Name, &ProtFileName ) != 0) { InitializeObjectAttributes( &ObjectAttributes, &p->Name, OBJ_CASE_INSENSITIVE, NULL, NULL ); Status = NtOpenFile( &OldFileHandle, (ACCESS_MASK)DELETE | SYNCHRONIZE, &ObjectAttributes, &IoStatusBlock, FILE_SHARE_READ | FILE_SHARE_WRITE, FILE_SYNCHRONOUS_IO_NONALERT ); if (NT_SUCCESS( Status )) { SetInfoClass = FileDispositionInformation; SetInfoLength = sizeof( DeleteInformation ); SetInfoBuffer = &DeleteInformation; DeleteInformation.DeleteFile = TRUE; Status = NtSetInformationFile( OldFileHandle, &IoStatusBlock, SetInfoBuffer, SetInfoLength, SetInfoClass ); NtClose( OldFileHandle ); } } RtlFreeHeap( RtlProcessHeap(), 0, p ); RtlFreeUnicodeString(&ProtFileName); ProtFileName.Buffer = NULL; continue; } } else { #if SMP_SHOW_REGISTRY_DATA DbgPrint( "SMSS: File is not in the protected list\n" ); #endif } if (ProtFileName.Buffer) { RtlFreeUnicodeString(&ProtFileName); ProtFileName.Buffer = NULL; } } } // // Open the file for delete access // if (p->Value.Length == 0 && p->Name.Buffer[0] == '@') { p->Name.Buffer += 1; p->Name.Length -= sizeof(WCHAR); } InitializeObjectAttributes( &ObjectAttributes, &p->Name, OBJ_CASE_INSENSITIVE, NULL, NULL ); 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 == 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 ); if (!NT_SUCCESS( Status ) && SetInfoClass == FileRenameInformation && Status == STATUS_OBJECT_NAME_COLLISION && RenameInformation->ReplaceIfExists ) { KdPrintEx((DPFLTR_SMSS_ID, DPFLTR_WARNING_LEVEL, "\nSMSS: %wZ => %wZ failed - Status == %x, Possible readonly target\n", &p->Name, &p->Value, Status)); // // A rename was attempted, but the source existing file is readonly. // this is a problem because folks that use movefileex to do delayed // renames expect this to work and can leave a machine unbootable if // the rename fails // // // Open the file for Write Attributes access // NewName.Length = p->Value.Length - sizeof(L'!'); NewName.MaximumLength = p->Value.MaximumLength - sizeof(L'!'); NewName.Buffer = s;; InitializeObjectAttributes( &ObjectAttributes, &NewName, OBJ_CASE_INSENSITIVE, NULL, NULL ); OpenStatus = NtOpenFile( &SetAttributesHandle, (ACCESS_MASK)FILE_WRITE_ATTRIBUTES | SYNCHRONIZE, &ObjectAttributes, &IoStatusBlock, FILE_SHARE_READ | FILE_SHARE_WRITE, FILE_SYNCHRONOUS_IO_NONALERT ); if (NT_SUCCESS( OpenStatus )) { KdPrintEx((DPFLTR_SMSS_ID, DPFLTR_INFO_LEVEL, " SMSS: Open Existing Success\n")); RtlZeroMemory(&BasicInfo,sizeof(BasicInfo)); BasicInfo.FileAttributes = FILE_ATTRIBUTE_NORMAL; OpenStatus = NtSetInformationFile( SetAttributesHandle, &IoStatusBlock, &BasicInfo, sizeof(BasicInfo), FileBasicInformation ); NtClose( SetAttributesHandle ); if ( NT_SUCCESS(OpenStatus) ) { KdPrintEx((DPFLTR_SMSS_ID, DPFLTR_INFO_LEVEL, " SMSS: Set To NORMAL OK\n")); Status = NtSetInformationFile( OldFileHandle, &IoStatusBlock, SetInfoBuffer, SetInfoLength, SetInfoClass ); if ( NT_SUCCESS(Status) ) { KdPrintEx((DPFLTR_SMSS_ID, DPFLTR_INFO_LEVEL, " SMSS: Re-Rename Worked OK\n")); } else { KdPrintEx((DPFLTR_SMSS_ID, DPFLTR_WARNING_LEVEL, " SMSS: Re-Rename Failed - Status == %x\n", Status)); } } else { KdPrintEx((DPFLTR_SMSS_ID, DPFLTR_WARNING_LEVEL, " SMSS: Set To NORMAL Failed - Status == %x\n", OpenStatus)); } } else { KdPrintEx((DPFLTR_SMSS_ID, DPFLTR_WARNING_LEVEL, " SMSS: Open Existing file Failed - Status == %x\n", OpenStatus)); } } } NtClose( OldFileHandle ); } if (!NT_SUCCESS( Status )) { KdPrintEx((DPFLTR_SMSS_ID, DPFLTR_WARNING_LEVEL, "SMSS: %wZ => %wZ failed - Status == %x\n", &p->Name, &p->Value, Status)); } else if (p->Value.Length == 0) { KdPrintEx((DPFLTR_SMSS_ID, DPFLTR_INFO_LEVEL, "SMSS: %wZ (deleted)\n", &p->Name )); } else { KdPrintEx((DPFLTR_SMSS_ID, DPFLTR_INFO_LEVEL, "SMSS: %wZ (renamed to) %wZ\n", &p->Name, &p->Value )); } RtlFreeHeap( RtlProcessHeap(), 0, p ); } if (EnvVarSet) { SmpSetProtectedFilesEnvVars( FALSE ); } if (!WasEnabled) { Status = RtlAdjustPrivilege( SE_RESTORE_PRIVILEGE, FALSE, FALSE, &WasEnabled ); } if (hModule) { LdrUnloadDll( hModule ); } return; } 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"); #if SMP_SHOW_REGISTRY_DATA SmpDumpQuery( L"SMSS", "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 )) { KdPrintEx((DPFLTR_SMSS_ID, DPFLTR_WARNING_LEVEL, "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 ); #if SMP_SHOW_REGISTRY_DATA SmpDumpQuery( L"SMSS", "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 ) { UNREFERENCED_PARAMETER( Context ); #if SMP_SHOW_REGISTRY_DATA SmpDumpQuery( L"SMSS", "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 ); #if SMP_SHOW_REGISTRY_DATA SmpDumpQuery( L"SMSS", "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)) { #if 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 ); #if SMP_SHOW_REGISTRY_DATA SmpDumpQuery( L"SMSS", "DosDevices", ValueName, ValueType, ValueData, ValueLength ); #else UNREFERENCED_PARAMETER( ValueType ); UNREFERENCED_PARAMETER( ValueLength ); #endif return (SmpSaveRegistryValue( (PLIST_ENTRY)EntryContext, ValueName, ValueData, TRUE ) ); } NTSTATUS SmpInitializeKnownDllPath( IN PUNICODE_STRING KnownDllPath, IN PVOID ValueData, IN ULONG ValueLength) { KnownDllPath->Buffer = RtlAllocateHeap( RtlProcessHeap(), MAKE_TAG( INIT_TAG ), ValueLength); if (KnownDllPath->Buffer == NULL) return STATUS_NO_MEMORY; KnownDllPath->Length = (USHORT)( ValueLength - sizeof( UNICODE_NULL ) ); KnownDllPath->MaximumLength = (USHORT)ValueLength; RtlMoveMemory( KnownDllPath->Buffer, ValueData, ValueLength); return STATUS_SUCCESS; } NTSTATUS SmpConfigureKnownDlls( IN PWSTR ValueName, IN ULONG ValueType, IN PVOID ValueData, IN ULONG ValueLength, IN PVOID Context, IN PVOID EntryContext ) { UNREFERENCED_PARAMETER( Context ); #if SMP_SHOW_REGISTRY_DATA SmpDumpQuery( L"SMSS", "KnownDlls", ValueName, ValueType, ValueData, ValueLength ); #else UNREFERENCED_PARAMETER( ValueType ); #endif if (!_wcsicmp( ValueName, L"DllDirectory" )) { return SmpInitializeKnownDllPath( &SmpKnownDllPath, ValueData, ValueLength ); } #ifdef _WIN64 if (!MiniNTBoot && !_wcsicmp( ValueName, L"DllDirectory32" )) { return SmpInitializeKnownDllPath( &SmpKnownDllPath32, ValueData, ValueLength ); } #endif 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 ); #if SMP_SHOW_REGISTRY_DATA SmpDumpQuery( L"SMSS", "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 ); #if SMP_SHOW_REGISTRY_DATA SmpDumpQuery( L"SMSS", "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 )) { KdPrintEx((DPFLTR_SMSS_ID, DPFLTR_WARNING_LEVEL, "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 ); #if SMP_SHOW_REGISTRY_DATA SmpDumpQuery( L"SMSS", "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 { KdPrintEx((DPFLTR_SMSS_ID, DPFLTR_WARNING_LEVEL, "SMSS: Invalid subsystem name - %ws\n", s)); } while (*s++ != UNICODE_NULL) { } } } return( STATUS_SUCCESS ); } else if (!_wcsicmp( ValueName, L"PosixSingleInstance" ) && (ValueType == REG_DWORD)) { RegPosixSingleInstance = TRUE; #if 0 KdPrintEx((DPFLTR_SMSS_ID, DPFLTR_INFO_LEVEL, "SMSS: Single Instance Posix Subsystem Configured\n")); #endif 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 = (ULONG)((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 ); if (ARGUMENT_PRESENT( ImageFileDirectory )) { RtlInitUnicodeString( ImageFileDirectory, 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; } #if defined(REMOTE_BOOT) else if (RtlEqualUnicodeString( &Token, &SmpAutoFmtKeyword, TRUE )) { *Flags |= SMP_AUTOFMT_FLAG; RtlFreeHeap( RtlProcessHeap(), 0, Token.Buffer ); continue; } #endif // defined(REMOTE_BOOT) } SpResult = 0; RtlInitUnicodeString( &PathVariableName, L"Path" ); PathVariableValue.Length = 0; PathVariableValue.MaximumLength = 4096; PathVariableValue.Buffer = RtlAllocateHeap( RtlProcessHeap(), MAKE_TAG( INIT_TAG ), PathVariableValue.MaximumLength ); if (PathVariableValue.Buffer == NULL) { RtlFreeHeap( RtlProcessHeap(), 0, Token.Buffer ); return STATUS_INSUFFICIENT_RESOURCES; } Status = RtlQueryEnvironmentVariable_U( SmpDefaultEnvironment, &PathVariableName, &PathVariableValue ); if ( Status == STATUS_BUFFER_TOO_SMALL ) { RtlFreeHeap( RtlProcessHeap(), 0, PathVariableValue.Buffer ); PathVariableValue.MaximumLength = PathVariableValue.Length + 2; PathVariableValue.Length = 0; PathVariableValue.Buffer = RtlAllocateHeap( RtlProcessHeap(), MAKE_TAG( INIT_TAG ), PathVariableValue.MaximumLength ); if (PathVariableValue.Buffer == NULL) { RtlFreeHeap( RtlProcessHeap(), 0, Token.Buffer ); return STATUS_INSUFFICIENT_RESOURCES; } Status = RtlQueryEnvironmentVariable_U( SmpDefaultEnvironment, &PathVariableName, &PathVariableValue ); } if (!NT_SUCCESS( Status )) { KdPrintEx((DPFLTR_SMSS_ID, DPFLTR_WARNING_LEVEL, "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, Token.Buffer ); RtlFreeHeap( RtlProcessHeap(), 0, PathVariableValue.Buffer ); if (NT_SUCCESS( Status ) && !RtlDosPathNameToNtPathName_U( FullDosPathBuffer, ImageFileName, NULL, NULL ) ) { KdPrintEx((DPFLTR_SMSS_ID, DPFLTR_WARNING_LEVEL, "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 SmpExecuteImage( IN PUNICODE_STRING ImageFileName, IN PUNICODE_STRING CurrentDirectory, IN PUNICODE_STRING CommandLine, IN ULONG MuSessionId, 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 0 BOOLEAN ImageWhacked; #endif if (!ARGUMENT_PRESENT( ProcessInformation )) { ProcessInformation = &MyProcessInformation; } #if 0 // // this seems to break setup's sense of what SystemRoot is ImageWhacked = FALSE; if ( ImageFileName && ImageFileName->Length > 8 ) { if ( ImageFileName->Buffer[0] == L'\\' && ImageFileName->Buffer[1] == L'?' && ImageFileName->Buffer[2] == L'?' && ImageFileName->Buffer[3] == L'\\' ) { ImageWhacked = TRUE; ImageFileName->Buffer[1] = L'\\'; } } #endif Status = RtlCreateProcessParameters( &ProcessParameters, ImageFileName, (SmpDefaultLibPath.Length == 0 ? NULL : &SmpDefaultLibPath ), CurrentDirectory, CommandLine, SmpDefaultEnvironment, NULL, NULL, NULL, NULL ); #if 0 if ( ImageWhacked ) { ImageFileName->Buffer[1] = L'?'; } #endif ASSERTMSG( "RtlCreateProcessParameters", NT_SUCCESS( Status ) ); if ( !NT_SUCCESS( Status ) ) { KdPrintEx((DPFLTR_SMSS_ID, DPFLTR_WARNING_LEVEL, "SMSS: RtlCreateProcessParameters failed for %wZ - Status == %lx\n", ImageFileName, Status)); return( 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 ) ) { KdPrintEx((DPFLTR_SMSS_ID, DPFLTR_WARNING_LEVEL, "SMSS: Failed load of %wZ - Status == %lx\n", ImageFileName, Status)); return( Status ); } // // Set the MuSessionId in the PEB of the new process. // Status = SmpSetProcessMuSessionId( ProcessInformation->Process, MuSessionId ); 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 ); KdPrintEx((DPFLTR_SMSS_ID, DPFLTR_WARNING_LEVEL, "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 MuSessionId, OUT PULONG_PTR pWindowsSubSysProcessId, 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. #if defined(REMOTE_BOOT) -or- Nt-Path-To-AutoFmt.exe Nt-Path-To-Disk-Partition #endif // defined(REMOTE_BOOT) 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( STATUS_SUCCESS ); } Status = SmpParseCommandLine( CommandLine, &Flags, &ImageFileName, &CurrentDirectory, &Arguments ); if (!NT_SUCCESS( Status )) { KdPrintEx((DPFLTR_SMSS_ID, DPFLTR_WARNING_LEVEL, "SMSS: SmpParseCommand( %wZ ) failed - Status == %lx\n", CommandLine, Status)); return( Status ); } if (Flags & SMP_AUTOCHK_FLAG) { Status = SmpInvokeAutoChk( &ImageFileName, &CurrentDirectory, &Arguments, Flags ); } #if defined(REMOTE_BOOT) else if (Flags & SMP_AUTOFMT_FLAG) { Status = SmpInvokeAutoFmt( &ImageFileName, &CurrentDirectory, &Arguments, Flags ); } #endif // defined(REMOTE_BOOT) else if (Flags & SMP_SUBSYSTEM_FLAG) { Status = SmpLoadSubSystem( &ImageFileName, &CurrentDirectory, CommandLine, MuSessionId, pWindowsSubSysProcessId, Flags ); } else { if (Flags & SMP_IMAGE_NOT_FOUND) { KdPrintEx((DPFLTR_SMSS_ID, DPFLTR_WARNING_LEVEL, "SMSS: Image file (%wZ) not found\n", &ImageFileName)); Status = STATUS_OBJECT_NAME_NOT_FOUND; } else { Status = SmpExecuteImage( &ImageFileName, &CurrentDirectory, CommandLine, MuSessionId, Flags, NULL ); } } // ImageFileName may be returned even // when SMP_IMAGE_NOT_FOUND flag is set if (ImageFileName.Buffer) { 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 )) { KdPrintEx((DPFLTR_SMSS_ID, DPFLTR_WARNING_LEVEL, "SMSS: Command '%wZ' failed - Status == %x\n", CommandLine, Status)); } return( Status ); } BOOLEAN SmpSaveAndClearBootStatusData( OUT PBOOLEAN BootOkay, OUT PBOOLEAN ShutdownOkay ) /*++ Routine Description: This routine saves the boot status flags in the boot status data and then sets the data file to indicate a successful boot and shutdown. This is used to avoid triggering auto-recovery in the loader if autoconvert or autochk reboots the machine as part of its run. The caller is responsible for calling SmpRestoreBootStatusData if the auto* program allows boot to continue. Arguments: BootOkay - the status of the boot ShutdownOkay - the status of the shutdown Return Value: TRUE if the values were saved and should be restored. FALSE if an error occurred and no values were saved. --*/ { NTSTATUS Status; PVOID BootStatusDataHandle; *BootOkay = FALSE; *ShutdownOkay = FALSE; Status = RtlLockBootStatusData(&BootStatusDataHandle); if(NT_SUCCESS(Status)) { BOOLEAN t = TRUE; RtlGetSetBootStatusData(BootStatusDataHandle, TRUE, RtlBsdItemBootGood, BootOkay, sizeof(BOOLEAN), NULL); RtlGetSetBootStatusData(BootStatusDataHandle, TRUE, RtlBsdItemBootShutdown, ShutdownOkay, sizeof(BOOLEAN), NULL); RtlGetSetBootStatusData(BootStatusDataHandle, FALSE, RtlBsdItemBootGood, &t, sizeof(BOOLEAN), NULL); RtlGetSetBootStatusData(BootStatusDataHandle, FALSE, RtlBsdItemBootShutdown, &t, sizeof(BOOLEAN), NULL); RtlUnlockBootStatusData(BootStatusDataHandle); return TRUE; } return FALSE; } VOID SmpRestoreBootStatusData( IN BOOLEAN BootOkay, IN BOOLEAN ShutdownOkay ) /*++ Routine Description: This routine restores the boot status flags in the boot status data to the provided values. Arguments: BootOkay - the status of the boot ShutdownOkay - the status of the shutdown Return Value: none. --*/ { NTSTATUS Status; PVOID BootStatusDataHandle; Status = RtlLockBootStatusData(&BootStatusDataHandle); if(NT_SUCCESS(Status)) { RtlGetSetBootStatusData(BootStatusDataHandle, FALSE, RtlBsdItemBootGood, &BootOkay, sizeof(BOOLEAN), NULL); RtlGetSetBootStatusData(BootStatusDataHandle, FALSE, RtlBsdItemBootShutdown, &ShutdownOkay, sizeof(BOOLEAN), NULL); RtlUnlockBootStatusData(BootStatusDataHandle); } return; } NTSTATUS SmpInvokeAutoChk( IN PUNICODE_STRING ImageFileName, IN PUNICODE_STRING CurrentDirectory, IN PUNICODE_STRING Arguments, IN ULONG Flags ) { NTSTATUS Status; CHAR DisplayBuffer[ MAXIMUM_FILENAME_LENGTH ]; ANSI_STRING AnsiDisplayString; UNICODE_STRING DisplayString; UNICODE_STRING CmdLine; WCHAR CmdLineBuffer[ 2 * MAXIMUM_FILENAME_LENGTH ]; BOOLEAN BootStatusDataSaved = FALSE; BOOLEAN BootOkay; BOOLEAN ShutdownOkay; // // Query the system environment variable "osloadoptions" to determine // if SOS is specified. What for though? No one is using it. // if (SmpQueryRegistrySosOption() != FALSE) { SmpEnableDots = FALSE; } 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 ); } // // Save away the boot & shutdown status flags in the boot status data // and restore them after autochk returns. This way if autochk forces // a reboot the loader won't put up the autorecovery menu. // BootStatusDataSaved = SmpSaveAndClearBootStatusData(&BootOkay, &ShutdownOkay); CmdLine.Buffer = CmdLineBuffer; CmdLine.MaximumLength = sizeof( CmdLineBuffer ); CmdLine.Length = 0; RtlAppendUnicodeStringToString( &CmdLine, ImageFileName ); RtlAppendUnicodeToString( &CmdLine, L" " ); RtlAppendUnicodeStringToString( &CmdLine, Arguments ); SmpExecuteImage( ImageFileName, CurrentDirectory, &CmdLine, 0, // MuSessionId Flags & ~SMP_AUTOCHK_FLAG, NULL ); // // If autochk doesn't shut us down then we end up back here. Restore the // values we saved. // if(BootStatusDataSaved) { SmpRestoreBootStatusData(BootOkay, ShutdownOkay); } return( STATUS_SUCCESS ); } #if defined(REMOTE_BOOT) NTSTATUS SmpInvokeAutoFmt( IN PUNICODE_STRING ImageFileName, IN PUNICODE_STRING CurrentDirectory, IN PUNICODE_STRING Arguments, IN ULONG Flags ) { NTSTATUS Status; CHAR DisplayBuffer[ MAXIMUM_FILENAME_LENGTH ]; ANSI_STRING AnsiDisplayString; UNICODE_STRING DisplayString; UNICODE_STRING CmdLine; WCHAR CmdLineBuffer[ 2 * MAXIMUM_FILENAME_LENGTH ]; BOOLEAN BootStatusDataSaved; BOOLEAN BootOkay; BOOLEAN ShutdownOkay; // // Query the system environment variable "osloadoptions" to determine // if SOS is specified. // if (SmpQueryRegistrySosOption() != FALSE) { SmpEnableDots = FALSE; } if (Flags & SMP_IMAGE_NOT_FOUND) { sprintf( DisplayBuffer, "%wZ program not found - skipping AUTOFMT\n", ImageFileName ); RtlInitAnsiString( &AnsiDisplayString, DisplayBuffer ); Status = RtlAnsiStringToUnicodeString( &DisplayString, &AnsiDisplayString, TRUE ); if (NT_SUCCESS( Status )) { NtDisplayString( &DisplayString ); RtlFreeUnicodeString( &DisplayString ); } return( STATUS_SUCCESS ); } BootStatusDataSaved = SmpSaveAndClearBootStatusData(&BootOkay, &ShutdownOkay); CmdLine.Buffer = CmdLineBuffer; CmdLine.MaximumLength = sizeof( CmdLineBuffer ); CmdLine.Length = 0; RtlAppendUnicodeStringToString( &CmdLine, ImageFileName ); RtlAppendUnicodeToString( &CmdLine, L" " ); RtlAppendUnicodeStringToString( &CmdLine, Arguments ); SmpExecuteImage( ImageFileName, CurrentDirectory, &CmdLine, 0, //Console MuSessionId Flags & ~SMP_AUTOFMT_FLAG, NULL ); if(BootStatusDataSaved) { SmpRestoreBootStatusData(BootOkay, ShutdownOkay); } return( STATUS_SUCCESS ); } #endif // defined(REMOTE_BOOT) NTSTATUS SmpLoadSubSystem( IN PUNICODE_STRING ImageFileName, IN PUNICODE_STRING CurrentDirectory, IN PUNICODE_STRING CommandLine, IN ULONG MuSessionId, OUT PULONG_PTR pWindowsSubSysProcessId, 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. For terminal server, the subsystem is started by csrss so that the correct session is used. Arguments: CommandLine - Supplies the command line to execute the subsystem. Return Value: TBD --*/ { NTSTATUS Status; RTL_USER_PROCESS_INFORMATION ProcessInformation; PSMPKNOWNSUBSYS KnownSubSys = NULL; PSMPKNOWNSUBSYS TargetSubSys = NULL; PSMPKNOWNSUBSYS CreatorSubSys = NULL; LARGE_INTEGER Timeout; ULONG SubsysMuSessionId = MuSessionId; PVOID State; NTSTATUS AcquirePrivilegeStatus = STATUS_SUCCESS; if (Flags & SMP_IMAGE_NOT_FOUND) { KdPrintEx((DPFLTR_SMSS_ID, DPFLTR_WARNING_LEVEL, "SMSS: Unable to find subsystem - %wZ\n", ImageFileName)); return( STATUS_OBJECT_NAME_NOT_FOUND ); } // // Check for single instance POSIX subsystem // if (Flags & SMP_POSIX_SI_FLAG) { // Run only one copy using the console Logon Id SubsysMuSessionId = 0; } // // There is a race condition on hydra loading subsystems. Two // requests can be received from the same MuSessionId to load // the same subsystem. There is a window in hydra since the // ImageType == 0xFFFFFFFF until the newly started subsystem connects // back. Non-hydra didn't have this problem since the optional // subsystem entry was destroyed once started. Hydra does not do this // since multiple sessions may want to start an optional subsystem. // // To close this window, on hydra this value is looked up based on // the MuSessionId to see if any subsystems are starting. // If so, we wait for the Active event. We can then // run our checks for the subsystem already being loaded. This has // the effect of serializing the startup of a subsystem on hydra // per MuSessionId, but not across the system. IE: Posix and // OS2 can't start at the same time, but will wait until // the other has started on a MuSessionId basis. // // We also use the SmpKnownSubSysLock to handle existing // race conditions since we have multiple SmpApiLoop() threads. // RtlEnterCriticalSection( &SmpKnownSubSysLock ); do { TargetSubSys = SmpLocateKnownSubSysByType( SubsysMuSessionId, 0xFFFFFFFF ); if( TargetSubSys ) { HANDLE hEvent = TargetSubSys->Active; RtlLeaveCriticalSection( &SmpKnownSubSysLock ); Status = NtWaitForSingleObject( hEvent, FALSE, NULL ); RtlEnterCriticalSection( &SmpKnownSubSysLock ); SmpDeferenceKnownSubSys(TargetSubSys); } } while ( TargetSubSys != NULL ); if (Flags & SMP_POSIX_FLAG) { TargetSubSys = SmpLocateKnownSubSysByType( SubsysMuSessionId, IMAGE_SUBSYSTEM_POSIX_CUI ); if( TargetSubSys ) { SmpDeferenceKnownSubSys(TargetSubSys); RtlLeaveCriticalSection( &SmpKnownSubSysLock ); return( STATUS_SUCCESS ); } } if (Flags & SMP_OS2_FLAG) { TargetSubSys = SmpLocateKnownSubSysByType( SubsysMuSessionId, IMAGE_SUBSYSTEM_OS2_CUI ); if( TargetSubSys ) { SmpDeferenceKnownSubSys(TargetSubSys); RtlLeaveCriticalSection( &SmpKnownSubSysLock ); return( STATUS_SUCCESS ); } } // // Create and register KnownSubSys entry before releasing the lock // so that other threads will see that we are starting a subsystem // on this MuSessionId. // KnownSubSys = RtlAllocateHeap( SmpHeap, MAKE_TAG( INIT_TAG ), sizeof( SMPKNOWNSUBSYS ) ); if ( KnownSubSys == NULL ) { RtlLeaveCriticalSection( &SmpKnownSubSysLock ); return( STATUS_NO_MEMORY ); } KnownSubSys->Deleting = FALSE; KnownSubSys->Process = NULL; KnownSubSys->Active = NULL; KnownSubSys->MuSessionId = SubsysMuSessionId; KnownSubSys->ImageType = (ULONG)0xFFFFFFFF; KnownSubSys->SmApiCommunicationPort = (HANDLE) NULL; KnownSubSys->SbApiCommunicationPort = (HANDLE) NULL; KnownSubSys->RefCount = 1; Status = NtCreateEvent( &KnownSubSys->Active, EVENT_ALL_ACCESS, NULL, NotificationEvent, FALSE ); if( !NT_SUCCESS(Status) ) { RtlFreeHeap( SmpHeap, 0, KnownSubSys ); RtlLeaveCriticalSection( &SmpKnownSubSysLock ); return( STATUS_NO_MEMORY ); } InsertHeadList( &SmpKnownSubSysHead, &KnownSubSys->Links ); RtlLeaveCriticalSection( &SmpKnownSubSysLock ); Flags |= SMP_DONT_START; if (((Flags & SMP_OS2_FLAG) || (Flags & SMP_POSIX_FLAG))) { SBAPIMSG m; PSBCREATEPROCESS args = &m.u.CreateProcess; // // Create it in csrss instead. // CreatorSubSys = SmpLocateKnownSubSysByType(SubsysMuSessionId, IMAGE_SUBSYSTEM_WINDOWS_GUI); // // CSRSS must have been started. // if (CreatorSubSys == NULL) { KdPrintEx((DPFLTR_SMSS_ID, DPFLTR_WARNING_LEVEL, "SMSS: SmpLoadSubSystem - SmpLocateKnownSubSysByType Failed\n")); goto cleanup2; } args->i.ImageFileName = ImageFileName; args->i.CurrentDirectory = CurrentDirectory; args->i.CommandLine = CommandLine; args->i.DefaultLibPath = (SmpDefaultLibPath.Length == 0 ? NULL : &SmpDefaultLibPath ); args->i.Flags = Flags; args->i.DefaultDebugFlags = SmpDebug; Status = SmpCallCsrCreateProcess(&m, sizeof(*args), CreatorSubSys->SbApiCommunicationPort ); if (!NT_SUCCESS( Status )) { KdPrintEx((DPFLTR_SMSS_ID, DPFLTR_WARNING_LEVEL, "SMSS: SmpLoadSubSystem - SmpCallCsrCreateProcess Failed with Status %lx\n", Status)); goto cleanup2; } // // Copy the output parameters to where smss expects them. // ProcessInformation.Process = args->o.Process; ProcessInformation.Thread = args->o.Thread; ProcessInformation.ClientId.UniqueProcess = args->o.ClientId.UniqueProcess; ProcessInformation.ClientId.UniqueThread = args->o.ClientId.UniqueThread; ProcessInformation.ImageInformation.SubSystemType = args->o.SubSystemType; } else { Status = SmpExecuteImage( ImageFileName, CurrentDirectory, CommandLine, SubsysMuSessionId, Flags, &ProcessInformation ); if (!NT_SUCCESS( Status )) { KdPrintEx((DPFLTR_SMSS_ID, DPFLTR_WARNING_LEVEL, "SMSS: SmpLoadSubSystem - SmpExecuteImage Failed with Status %lx\n", Status)); goto cleanup2; } } KnownSubSys->Process = ProcessInformation.Process; KnownSubSys->InitialClientId = ProcessInformation.ClientId; // // 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( MuSessionId, ProcessInformation.ImageInformation.SubSystemType ); if ( !TargetSubSys ) { Status = STATUS_NO_SUCH_PACKAGE; KdPrintEx((DPFLTR_SMSS_ID, DPFLTR_WARNING_LEVEL, "SMSS: SmpLoadSubSystem - SmpLocateKnownSubSysByType Failed with Status %lx for sessionid %ld\n", Status, MuSessionId)); goto cleanup; } // // 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 )) { KdPrintEx((DPFLTR_SETUP_ID, DPFLTR_WARNING_LEVEL, "SMSS: SmpLoadSubSystem - NtDuplicateObject Failed with Status %lx for sessionid %ld\n", Status, MuSessionId)); goto cleanup; } Status = NtDuplicateObject( NtCurrentProcess(), ProcessInformation.Thread, TargetSubSys->Process, &args->ProcessInformation.Thread, THREAD_ALL_ACCESS, 0, 0 ); if (!NT_SUCCESS( Status )) { KdPrintEx((DPFLTR_SETUP_ID, DPFLTR_WARNING_LEVEL, "SMSS: SmpLoadSubSystem - NtDuplicateObject Failed with Status %lx for sessionid %ld\n", Status, MuSessionId)); goto cleanup; } 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); KdPrintEx((DPFLTR_SMSS_ID, DPFLTR_WARNING_LEVEL, "SMSS: SmpLoadSubSystem - NtRequestWaitReplyPort Failed with Status %lx for sessionid %ld\n", Status, MuSessionId)); goto cleanup; } } else { if ( pWindowsSubSysProcessId ) { if ( *pWindowsSubSysProcessId == (ULONG_PTR)NULL ) { *pWindowsSubSysProcessId = (ULONG_PTR) ProcessInformation.ClientId.UniqueProcess; } } if ( !MuSessionId ) { // Only for console SmpWindowsSubSysProcessId = (ULONG_PTR) ProcessInformation.ClientId.UniqueProcess; SmpWindowsSubSysProcess = ProcessInformation.Process; } } ASSERTMSG( "NtCreateEvent", NT_SUCCESS( Status ) ); Status = NtResumeThread( ProcessInformation.Thread, NULL ); if (!NT_SUCCESS(Status)) { KdPrintEx((DPFLTR_SMSS_ID, DPFLTR_WARNING_LEVEL, "SMSS: SmpLoadSubSystem - NtResumeThread failed Status %lx\n", Status)); goto cleanup; } if(MuSessionId != 0) { // // Wait a max of 60 seconds for the subsystem to connect. // Timeout = RtlEnlargedIntegerMultiply( 60000, -10000 ); Status = NtWaitForSingleObject( KnownSubSys->Active, FALSE, &Timeout ); if ( !SmpCheckDuplicateMuSessionId( MuSessionId ) ) { KdPrintEx((DPFLTR_SMSS_ID, DPFLTR_INFO_LEVEL, "SMSS: SmpLoadSubSystem - session deleted\n")); return( STATUS_DELETE_PENDING ); } if (Status != STATUS_SUCCESS) { KdPrintEx((DPFLTR_SMSS_ID, DPFLTR_WARNING_LEVEL, "SMSS: SmpLoadSubSystem - Timeout waiting for subsystem connect with Status %lx for sessionid %ld \n", Status, MuSessionId)); goto cleanup; } } else { NtWaitForSingleObject( KnownSubSys->Active, FALSE, NULL ); } // Close this now since we never need it again NtClose( ProcessInformation.Thread ); RtlEnterCriticalSection( &SmpKnownSubSysLock ); if (KnownSubSys) { SmpDeferenceKnownSubSys(KnownSubSys); } if (TargetSubSys) { SmpDeferenceKnownSubSys(TargetSubSys); } if (CreatorSubSys) { SmpDeferenceKnownSubSys(CreatorSubSys); } RtlLeaveCriticalSection( &SmpKnownSubSysLock ); return STATUS_SUCCESS; cleanup: if ((AttachedSessionId != (-1)) && !(Flags & SMP_POSIX_FLAG) && !(Flags & SMP_OS2_FLAG) && NT_SUCCESS(AcquirePrivilegeStatus = SmpAcquirePrivilege( SE_LOAD_DRIVER_PRIVILEGE, &State ))) { NTSTATUS St; // // If we are attached to a session space, leave it // so we can create a new one // if (NT_SUCCESS(St = NtSetSystemInformation( SystemSessionDetach, (PVOID)&AttachedSessionId, sizeof(MuSessionId) ))) { AttachedSessionId = (-1); } else { // // This has to succeed otherwise we will bugcheck while trying to // create another session // KdPrintEx((DPFLTR_SMSS_ID, DPFLTR_WARNING_LEVEL, "SMSS: SmpStartCsr, Couldn't Detach from Session Space. Status=%x\n", St)); ASSERT(NT_SUCCESS(St)); } SmpReleasePrivilege( State ); } else { KdPrintEx((DPFLTR_SMSS_ID, DPFLTR_INFO_LEVEL, "SMSS: Did not detach from Session Space: SessionId=%x Flags=%x Status=%x\n", AttachedSessionId, Flags, AcquirePrivilegeStatus)); } // There is a lot of cleanup that must be done here NtTerminateProcess( ProcessInformation.Process, Status ); NtClose( ProcessInformation.Thread ); cleanup2: RtlEnterCriticalSection( &SmpKnownSubSysLock ); if (TargetSubSys) { SmpDeferenceKnownSubSys(TargetSubSys); } if (CreatorSubSys) { SmpDeferenceKnownSubSys(CreatorSubSys); } RemoveEntryList( &KnownSubSys->Links ); NtSetEvent( KnownSubSys->Active, NULL ); KnownSubSys->Deleting = TRUE; SmpDeferenceKnownSubSys(KnownSubSys); RtlLeaveCriticalSection( &SmpKnownSubSysLock ); return( Status ); } NTSTATUS SmpExecuteInitialCommand( IN ULONG MuSessionId, IN PUNICODE_STRING InitialCommand, OUT PHANDLE InitialCommandProcess, OUT PULONG_PTR InitialCommandProcessId ) { NTSTATUS Status; RTL_USER_PROCESS_INFORMATION ProcessInformation; ULONG Flags; UNICODE_STRING ImageFileName; UNICODE_STRING CurrentDirectory; UNICODE_STRING Arguments; static HANDLE SmApiPort = NULL; if ( SmApiPort == NULL ) { Status = SmConnectToSm( NULL, NULL, 0, &SmApiPort ); if (!NT_SUCCESS( Status )) { KdPrintEx((DPFLTR_SMSS_ID, DPFLTR_WARNING_LEVEL, "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) { KdPrintEx((DPFLTR_SMSS_ID, DPFLTR_WARNING_LEVEL, "SMSS: Initial command image (%wZ) not found\n", &ImageFileName)); if (ImageFileName.Buffer) RtlFreeHeap( RtlProcessHeap(), 0, ImageFileName.Buffer ); return( STATUS_OBJECT_NAME_NOT_FOUND ); } if (!NT_SUCCESS( Status )) { KdPrintEx((DPFLTR_SMSS_ID, DPFLTR_WARNING_LEVEL, "SMSS: SmpParseCommand( %wZ ) failed - Status == %lx\n", InitialCommand, Status)); return( Status ); } Status = SmpExecuteImage( &ImageFileName, &CurrentDirectory, InitialCommand, MuSessionId, SMP_DONT_START, &ProcessInformation ); if (ImageFileName.Buffer) { 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 )) { return( Status ); } Status = NtDuplicateObject( NtCurrentProcess(), ProcessInformation.Process, NtCurrentProcess(), InitialCommandProcess, PROCESS_ALL_ACCESS, 0, 0 ); if (!NT_SUCCESS(Status) ) { KdPrintEx((DPFLTR_SMSS_ID, DPFLTR_WARNING_LEVEL, "SMSS: DupObject Failed. Status == %lx\n", Status)); NtTerminateProcess( ProcessInformation.Process, Status ); NtResumeThread( ProcessInformation.Thread, NULL ); NtClose( ProcessInformation.Thread ); NtClose( ProcessInformation.Process ); return( Status ); } if ( InitialCommandProcessId != NULL ) *InitialCommandProcessId = (ULONG_PTR)ProcessInformation.ClientId.UniqueProcess; if ( !MuSessionId ) SmpInitialCommandProcessId = (ULONG_PTR)ProcessInformation.ClientId.UniqueProcess; Status = SmExecPgm( SmApiPort, &ProcessInformation, FALSE ); if (!NT_SUCCESS( Status )) { KdPrintEx((DPFLTR_SMSS_ID, DPFLTR_WARNING_LEVEL, "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; ULONG MuSessionId; ULONG Flags; args = &SmApiMsg->u.LoadDefered; DeferedName.Length = (USHORT)args->SubsystemNameLength; DeferedName.MaximumLength = (USHORT)args->SubsystemNameLength; DeferedName.Buffer = args->SubsystemName; Head = &SmpSubSystemsToDefer; // // Get the pointer to the client process's Terminal Server session. // SmpGetProcessMuSessionId( CallingClient->ClientProcessHandle, &MuSessionId ); if ( !SmpCheckDuplicateMuSessionId( MuSessionId ) ) { KdPrintEx((DPFLTR_SMSS_ID, DPFLTR_WARNING_LEVEL, "SMSS: Defered subsystem load ( %wZ ) for MuSessionId %u, status=0x%x\n", &DeferedName, MuSessionId, STATUS_OBJECT_NAME_NOT_FOUND)); return( STATUS_OBJECT_NAME_NOT_FOUND ); } 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... // // To keep from loading multiple subsystems, we must // flag the type so we can see if its running. // This is only a problem with "Optional" subsystems. // Other optional subsystems can still be added, but // they may have startup race conditions. // Flags = SMP_SUBSYSTEM_FLAG; if ( RtlEqualUnicodeString(&DeferedName,&PosixName,TRUE)) { Flags |= SMP_POSIX_FLAG; } if ( RtlEqualUnicodeString(&DeferedName,&Os2Name,TRUE)) { Flags |= SMP_OS2_FLAG; } if (RegPosixSingleInstance && RtlEqualUnicodeString(&DeferedName,&PosixName,TRUE)) { Flags |= SMP_POSIX_SI_FLAG; } Status = SmpExecuteCommand( &p->Value, MuSessionId, NULL, Flags ); 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: --*/ { #if SMP_SHOW_REGISTRY_DATA SmpDumpQuery( L"SMSS", "BaseObjectsProtection", ValueName, ValueType, ValueData, ValueLength ); #else UNREFERENCED_PARAMETER( ValueName ); UNREFERENCED_PARAMETER( ValueType ); #endif if (ValueLength != sizeof(ULONG)) { // // Key value not valid. Run protected // SmpProtectionMode = SMP_STANDARD_PROTECTION; } else { SmpProtectionMode = (*((PULONG)(ValueData))); // // Change the security descriptors // } (VOID)SmpCreateSecurityDescriptors( FALSE ); return( STATUS_SUCCESS ); } NTSTATUS SmpConfigureAllowProtectedRenames( IN PWSTR ValueName, IN ULONG ValueType, IN PVOID ValueData, IN ULONG ValueLength, IN PVOID Context, IN PVOID EntryContext ) { if (ValueLength != sizeof(ULONG)) { SmpAllowProtectedRenames = 0; } else { SmpAllowProtectedRenames = (*((PULONG)(ValueData))); } 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: Restricted: 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: Restricted: 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: Restricted: Execute (No Inherit) Grant: Admin: All Access (Inherit) Grant: World: Execute | Read | Write (Inherit Only) Grant: Restricted: 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, RestrictedSid, 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), RestrictedAccess = (GENERIC_EXECUTE | GENERIC_READ); UCHAR InheritOnlyFlags = (OBJECT_INHERIT_ACE | CONTAINER_INHERIT_ACE | INHERIT_ONLY_ACE); ULONG AceIndex, AclLength; PACL Acl; PACE_HEADER Ace; BOOLEAN ProtectionRequired = FALSE; 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 )) { Status = RtlAllocateAndInitializeSid( &NtAuthority, 1, SECURITY_RESTRICTED_CODE_RID, 0, 0, 0, 0, 0, 0, 0, &RestrictedSid ); 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; RestrictedAccess = GENERIC_EXECUTE | GENERIC_READ | GENERIC_READ; AdminAccess = GENERIC_ALL; AclLength = sizeof( ACL ) + 3 * sizeof( ACCESS_ALLOWED_ACE ) + (RtlLengthSid( WorldSid )) + (RtlLengthSid( RestrictedSid )) + (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 // Restricted // Admin // Status = RtlAddAccessAllowedAce ( Acl, ACL_REVISION2, WorldAccess, WorldSid ); ASSERT( NT_SUCCESS(Status) ); Status = RtlAddAccessAllowedAce ( Acl, ACL_REVISION2, RestrictedAccess, RestrictedSid ); 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) ); } // // Build the KnownDlls security descriptor // AdminAccess = GENERIC_ALL; AclLength = sizeof( ACL ) + 6 * sizeof( ACCESS_ALLOWED_ACE ) + (2*RtlLengthSid( WorldSid )) + (2*RtlLengthSid( RestrictedSid ))+ (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 // Restricted // Admin // AceIndex = 0; WorldAccess = GENERIC_EXECUTE; RestrictedAccess = GENERIC_EXECUTE; Status = RtlAddAccessAllowedAce ( Acl, ACL_REVISION2, WorldAccess, WorldSid ); ASSERT( NT_SUCCESS(Status) ); AceIndex++; Status = RtlAddAccessAllowedAce ( Acl, ACL_REVISION2, RestrictedAccess, RestrictedSid ); 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 // Restricted // Admin // AceIndex++; WorldAccess = GENERIC_EXECUTE | GENERIC_READ | GENERIC_WRITE; RestrictedAccess = 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, RestrictedAccess, RestrictedSid ); 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) ); } } // // 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; RestrictedAccess = GENERIC_EXECUTE | GENERIC_READ; AdminAccess = GENERIC_ALL; OwnerAccess = GENERIC_ALL; AclLength = sizeof( ACL ) + 7 * sizeof( ACCESS_ALLOWED_ACE ) + (2*RtlLengthSid( WorldSid )) + (2*RtlLengthSid( RestrictedSid )) + (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 // Restricted // Admin // AceIndex = 0; Status = RtlAddAccessAllowedAce ( Acl, ACL_REVISION2, WorldAccess, WorldSid ); ASSERT( NT_SUCCESS(Status) ); AceIndex++; Status = RtlAddAccessAllowedAce ( Acl, ACL_REVISION2, RestrictedAccess, RestrictedSid ); ASSERT( NT_SUCCESS(Status) ); AceIndex++; Status = RtlAddAccessAllowedAce ( Acl, ACL_REVISION2, AdminAccess, AdminSid ); ASSERT( NT_SUCCESS(Status) ); // // Inheritable ACEs at end of ACE // World // Restricted // 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, RestrictedAccess, RestrictedSid ); 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; RestrictedAccess = GENERIC_EXECUTE | GENERIC_READ | GENERIC_WRITE; AclLength = sizeof( ACL ) + 7 * sizeof( ACCESS_ALLOWED_ACE ) + (2*RtlLengthSid( WorldSid )) + (2*RtlLengthSid( RestrictedSid ))+ (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 // Restricted // Admin // AceIndex = 0; Status = RtlAddAccessAllowedAce ( Acl, ACL_REVISION2, WorldAccess, WorldSid ); ASSERT( NT_SUCCESS(Status) ); AceIndex++; Status = RtlAddAccessAllowedAce ( Acl, ACL_REVISION2, RestrictedAccess, RestrictedSid ); 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 // Restricted // 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, RestrictedAccess, RestrictedSid ); 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) ); } // // No more security descriptors to build // } RtlFreeHeap( RtlProcessHeap(), 0, RestrictedSid ); } 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)) { KdPrintEx((DPFLTR_SMSS_ID, DPFLTR_WARNING_LEVEL, "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)) { KdPrintEx((DPFLTR_SMSS_ID, DPFLTR_WARNING_LEVEL, "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); // // The last part of the test below handles the remote boot case, // where the system partition is on a redirected drive. // if (NT_SUCCESS(Status) && ( RtlEqualUnicodeString(&SystemPartitionString, &LinkTarget, TRUE) || (RtlPrefixUnicodeString(&SystemPartitionString, &LinkTarget, TRUE) && (LinkTarget.Buffer[SystemPartitionString.Length / sizeof(WCHAR)] == L'\\')) ) ) { // // We've found the drive letter corresponding to the system partition. // break; } } } RestartScan = FALSE; } if (!NT_SUCCESS(Status)) { #if defined (_WIN64) if (Status == STATUS_NO_MORE_ENTRIES) { DirInfo->Name.Buffer = (PWCHAR)(DirInfo+1); DirInfo->Name.Buffer[0] = USER_SHARED_DATA->NtSystemRoot[0]; DirInfo->Name.Buffer[1] = USER_SHARED_DATA->NtSystemRoot[1]; } else { #endif KdPrintEx((DPFLTR_SMSS_ID, DPFLTR_WARNING_LEVEL, "SMSS: can't find drive letter for system partition\n")); return; #if defined (_WIN64) } #endif } // // 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)) { KdPrintEx((DPFLTR_SMSS_ID, DPFLTR_WARNING_LEVEL, "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)) { KdPrintEx((DPFLTR_SMSS_ID, DPFLTR_WARNING_LEVEL, "SMSS: couldn't write BootDir value: 0x%x\n", Status)); } NtClose(Key); } #if defined(REMOTE_BOOT) NTSTATUS SmpExecuteCommandLineArguments( VOID ) /*++ Routine Description: This routine processes any command line arguments that were passed to SMSS.exe. Currently the only valid ones are netboot commands. Arguments: None. Return Value: Success or not. --*/ { UNICODE_STRING CfgFileName; UNICODE_STRING MbrName; UNICODE_STRING AutoFmtCmd; UNICODE_STRING UnicodeString; OBJECT_ATTRIBUTES ObjectAttributes; OBJECT_ATTRIBUTES KeyObjectAttributes; IO_STATUS_BLOCK IoStatusBlock; HANDLE FileHandle; HANDLE SourceHandle; HANDLE TargetHandle; NTSTATUS Status; ULONG BootSerialNumber; ULONG DiskSignature; ULONG CmdFlags; ULONG Length; LARGE_INTEGER ByteOffset; PUCHAR AlignedBuffer; ON_DISK_MBR OnDiskMbr; BOOLEAN WasEnabled; HANDLE Key; WCHAR ValueBuffer[VALUE_BUFFER_SIZE]; PKEY_VALUE_PARTIAL_INFORMATION KeyValueInfo; ULONG ValueLength; ULONG Repartition; ULONG Disk; ULONG Partition; ULONG CSCPartition; PWCHAR pWchar; if (!SmpNetboot || SmpNetbootDisconnected) { return STATUS_SUCCESS; } // // Open the remoteboot.cfg file // RtlInitUnicodeString(&UnicodeString, L"\\SystemRoot"); InitializeObjectAttributes( &ObjectAttributes, &UnicodeString, OBJ_CASE_INSENSITIVE, NULL, NULL ); Status = NtOpenSymbolicLinkObject(&FileHandle, (ACCESS_MASK)SYMBOLIC_LINK_QUERY, &ObjectAttributes ); if (!NT_SUCCESS(Status)) { KdPrintEx((DPFLTR_SMSS_ID, DPFLTR_WARNING_LEVEL, "SMSS: Could not open symbolic link (Status 0x%x) -- quitting.\n", Status)); Status = STATUS_SUCCESS; goto CleanUp; } UnicodeString.Length = 0; UnicodeString.MaximumLength = sizeof(TmpBuffer); UnicodeString.Buffer = (PWCHAR)TmpBuffer; Status = NtQuerySymbolicLinkObject(FileHandle, &UnicodeString, NULL ); NtClose(FileHandle); FileHandle = NULL; if (!NT_SUCCESS(Status)) { KdPrintEx((DPFLTR_SMSS_ID, DPFLTR_WARNING_LEVEL, "SMSS: Could not get symbolic link name (Status 0x%x) -- quitting.\n", Status)); Status = STATUS_SUCCESS; goto CleanUp; } ASSERT(((wcslen((PWCHAR)TmpBuffer) * sizeof(WCHAR)) - sizeof(L"BootDrive")) < (sizeof(wszRemoteBootCfgFile) - sizeof(REMOTE_BOOT_CFG_FILE)) ); wcscpy(wszRemoteBootCfgFile, (PWCHAR)TmpBuffer); pWchar = wcsstr(wszRemoteBootCfgFile, L"BootDrive"); ASSERT(pWchar != NULL); *pWchar = UNICODE_NULL; wcscat(wszRemoteBootCfgFile, REMOTE_BOOT_CFG_FILE); CfgFileName.Length = wcslen(wszRemoteBootCfgFile) * sizeof(WCHAR); CfgFileName.MaximumLength = sizeof(wszRemoteBootCfgFile); CfgFileName.Buffer = wszRemoteBootCfgFile; InitializeObjectAttributes( &ObjectAttributes, &CfgFileName, OBJ_CASE_INSENSITIVE, NULL, NULL ); Status = NtOpenFile( &FileHandle, GENERIC_WRITE | GENERIC_READ | SYNCHRONIZE, &ObjectAttributes, &IoStatusBlock, FILE_SHARE_READ | FILE_SHARE_WRITE, FILE_SYNCHRONOUS_IO_NONALERT | FILE_RANDOM_ACCESS ); // // If it does not exist, then set the flags for reformatting and repinning. // if (!NT_SUCCESS(Status)) { KdPrintEx((DPFLTR_SMSS_ID, DPFLTR_WARNING_LEVEL, "SMSS: Could not open file (status 0x%x) -- creating it.\n", Status)); // // Create the remoteboot.cfg in the system32\config directory if it does not exist. // CreateFile: Status = NtCreateFile( &FileHandle, GENERIC_WRITE | GENERIC_READ | SYNCHRONIZE, &ObjectAttributes, &IoStatusBlock, NULL, FILE_ATTRIBUTE_HIDDEN, FILE_SHARE_READ | FILE_SHARE_WRITE, FILE_OVERWRITE_IF, FILE_SYNCHRONOUS_IO_NONALERT | FILE_RANDOM_ACCESS, NULL, 0 ); if (!NT_SUCCESS(Status)) { // // Something is really wrong, we will just exit and hope all is good. // DEADISSUE, HISTORICAL CODE ONLY: This OK? KdPrintEx((DPFLTR_SMSS_ID, DPFLTR_WARNING_LEVEL, "SMSS: Could not create file (Status 0x%x) -- quitting.\n", Status)); Status = STATUS_SUCCESS; goto CleanUp; } SmpAutoFormat = TRUE; BootSerialNumber = 1; } else { Status = NtReadFile( FileHandle, NULL, NULL, NULL, &IoStatusBlock, &BootSerialNumber, sizeof(ULONG), NULL, NULL ); if (!NT_SUCCESS(Status)) { KdPrintEx((DPFLTR_SMSS_ID, DPFLTR_WARNING_LEVEL, "SMSS: Could not read file (Status 0x%x) -- creating it.\n", Status)); NtClose( FileHandle ); goto CreateFile; } BootSerialNumber++; } // // Process each command // if (SmpAutoFormat) { // // Read from the registry if it is ok to reformat, or just leave the disk alone. // Repartition = 1; KeyValueInfo = (PKEY_VALUE_PARTIAL_INFORMATION)ValueBuffer; RtlInitUnicodeString(&UnicodeString, L"\\Registry\\Machine\\System\\CurrentControlSet\\Control\\RemoteBoot"); InitializeObjectAttributes(&KeyObjectAttributes, &UnicodeString, OBJ_CASE_INSENSITIVE, NULL, NULL); Status = NtOpenKey(&Key, KEY_READ, &KeyObjectAttributes); if (NT_SUCCESS(Status)) { // // Query the key value. // RtlInitUnicodeString(&UnicodeString, L"Repartition"); Status = NtQueryValueKey(Key, &UnicodeString, KeyValuePartialInformation, (PVOID)KeyValueInfo, VALUE_BUFFER_SIZE, &ValueLength); if (NT_SUCCESS(Status)) { ASSERT(ValueLength <= VALUE_BUFFER_SIZE); Repartition = *((PULONG)KeyValueInfo->Data); } NtClose(Key); } SmpGetHarddiskBootPartition(&Disk, &Partition); if (Repartition) { KdPrintEx((DPFLTR_SMSS_ID, DPFLTR_INFO_LEVEL, "SMSS: Autoformatting local disk.\n")); NtClose(FileHandle); // // Repartition the disk. // SmpPartitionDisk(Disk, &Partition); // // Call autoformat on the partition // swprintf((PWSTR)TmpBuffer, L"autoformat autofmt \\Device\\Harddisk%d\\Partition%d /Q /fs:ntfs", Disk, Partition ); AutoFmtCmd.Buffer = (PWSTR)TmpBuffer; AutoFmtCmd.MaximumLength = sizeof(TmpBuffer); AutoFmtCmd.Length = wcslen((PWSTR)TmpBuffer) * sizeof(WCHAR); CmdFlags = 0; Status = SmpExecuteCommand(&AutoFmtCmd, 0, NULL, CmdFlags); if (!NT_SUCCESS(Status)) { // // Big Trouble.... // CSC is disabled if we get here, so just keep on booting. // Status = STATUS_SUCCESS; goto CleanUp; } } else { SmpFindCSCPartition(Disk, &CSCPartition); if (CSCPartition != 0) { // // Just blow away the CSC directory so we can refresh it // swprintf((PWSTR)TmpBuffer, L"\\Device\\Harddisk%d\\Partition%d%ws", Disk, CSCPartition, REMOTE_BOOT_IMIRROR_PATH_W REMOTE_BOOT_CSC_SUBDIR_W ); SmpEnumFilesRecursive( (PWSTR)TmpBuffer, SmpDelEnumFile, &Status, NULL ); if (!NT_SUCCESS(Status)) { // // Ignore this error, and hope that the next boot will fix. Just keep booting this // time and hope. // Status = STATUS_SUCCESS; goto CleanUp; } } } // // Copy the NtLdr to the local disk // SourceHandle = SmpOpenDir( TRUE, TRUE, L"\\" ); if (SourceHandle == NULL) { Status = STATUS_SUCCESS; goto CleanUp; } swprintf((PWSTR)TmpBuffer, L"\\Device\\Harddisk%d\\Partition%d", Disk, Partition ); RtlInitUnicodeString(&UnicodeString, (PWSTR)TmpBuffer); InitializeObjectAttributes( &ObjectAttributes, &UnicodeString, OBJ_CASE_INSENSITIVE, NULL, NULL ); Status = NtCreateFile( &TargetHandle, GENERIC_READ | GENERIC_WRITE | SYNCHRONIZE, &ObjectAttributes, &IoStatusBlock, NULL, FILE_ATTRIBUTE_NORMAL, FILE_SHARE_READ | FILE_SHARE_WRITE, FILE_OPEN, FILE_SYNCHRONOUS_IO_NONALERT, NULL, 0 ); if (!NT_SUCCESS(Status)) { KdPrintEx((DPFLTR_SMSS_ID, DPFLTR_WARNING_LEVEL, "SMSS: Unable to open a handle to the (%ws) directory - Status == %lx\n", UnicodeString.Buffer, Status)); Status = STATUS_SUCCESS; NtClose(SourceHandle); goto CleanUp; } // // If any of the copies fail, there is nothing we can really do. // RtlInitUnicodeString(&UnicodeString, L"ntldr"); Status = SmpCopyFile(SourceHandle, TargetHandle, &UnicodeString); RtlInitUnicodeString(&UnicodeString, L"boot.ini"); Status = SmpCopyFile(SourceHandle, TargetHandle, &UnicodeString); RtlInitUnicodeString(&UnicodeString, L"bootfont.bin"); Status = SmpCopyFile(SourceHandle, TargetHandle, &UnicodeString); RtlInitUnicodeString(&UnicodeString, L"ntdetect.com"); Status = SmpCopyFile(SourceHandle, TargetHandle, &UnicodeString); NtClose(SourceHandle); NtClose(TargetHandle); // // Read Master Boot Record and get disk serial number. // swprintf((PWSTR)TmpBuffer, L"\\Device\\Harddisk%d\\Partition0", Disk ); MbrName.Buffer = (PWSTR)TmpBuffer; MbrName.MaximumLength = (wcslen((PWSTR)TmpBuffer) + 1) * sizeof(WCHAR); MbrName.Length = MbrName.MaximumLength - sizeof(WCHAR); InitializeObjectAttributes( &ObjectAttributes, &MbrName, OBJ_CASE_INSENSITIVE, NULL, NULL ); Status = NtCreateFile( &FileHandle, GENERIC_READ | SYNCHRONIZE, &ObjectAttributes, &IoStatusBlock, NULL, FILE_ATTRIBUTE_NORMAL, FILE_SHARE_READ, FILE_OPEN, FILE_SYNCHRONOUS_IO_NONALERT, NULL, 0 ); if (!NT_SUCCESS(Status)) { // // Something iswrong, but we are running w/o caching, so it should be ok. // KdPrintEx((DPFLTR_SMSS_ID, DPFLTR_WARNING_LEVEL, "SMSS: Could not create file (Status 0x%x).\n", Status)); Status = STATUS_SUCCESS; goto CleanUp; } ASSERT(sizeof(ON_DISK_MBR) == 512); AlignedBuffer = ALIGN(TmpBuffer, 512); Status = NtReadFile( FileHandle, NULL, NULL, NULL, &IoStatusBlock, AlignedBuffer, sizeof(ON_DISK_MBR), NULL, NULL ); NtClose( FileHandle ); if (!NT_SUCCESS(Status)) { KdPrintEx((DPFLTR_SMSS_ID, DPFLTR_WARNING_LEVEL, "SMSS: Could not read file (Status 0x%x) -- creating it.\n", Status)); goto CreateFile; } RtlMoveMemory(&OnDiskMbr,AlignedBuffer,sizeof(ON_DISK_MBR)); ASSERT(U_USHORT(OnDiskMbr.AA55Signature) == 0xAA55); DiskSignature = U_ULONG(OnDiskMbr.NTFTSignature); InitializeObjectAttributes( &ObjectAttributes, &CfgFileName, OBJ_CASE_INSENSITIVE, NULL, NULL ); Status = NtOpenFile( &FileHandle, GENERIC_WRITE | GENERIC_READ | SYNCHRONIZE, &ObjectAttributes, &IoStatusBlock, FILE_SHARE_READ | FILE_SHARE_WRITE, FILE_SYNCHRONOUS_IO_NONALERT | FILE_RANDOM_ACCESS ); if (!NT_SUCCESS(Status)) { // // Big Trouble.... // CSC is disabled if we get here, so just keep on booting. // Status = STATUS_SUCCESS; goto CleanUp; } } // // Update the information // ByteOffset.LowPart = 0; ByteOffset.HighPart = 0; NtWriteFile( FileHandle, NULL, NULL, NULL, &IoStatusBlock, &BootSerialNumber, sizeof(ULONG), &ByteOffset, NULL ); if (SmpAutoFormat) { ByteOffset.LowPart = sizeof(ULONG); NtWriteFile( FileHandle, NULL, NULL, NULL, &IoStatusBlock, &DiskSignature, sizeof(DiskSignature), &ByteOffset, NULL ); } ByteOffset.LowPart = sizeof(ULONG) + sizeof(ULONG); NtWriteFile( FileHandle, NULL, NULL, NULL, &IoStatusBlock, SmpHalName, sizeof(SmpHalName), &ByteOffset, NULL ); NtClose(FileHandle); if (SmpAutoFormat) { // // Reboot the machine to start CSC // Status = RtlAdjustPrivilege( SE_SHUTDOWN_PRIVILEGE, (BOOLEAN)TRUE, TRUE, &WasEnabled ); if (Status == STATUS_NO_TOKEN) { // // No thread token, use the process token // Status = RtlAdjustPrivilege( SE_SHUTDOWN_PRIVILEGE, (BOOLEAN)TRUE, FALSE, &WasEnabled ); } NtShutdownSystem(ShutdownReboot); } Status = STATUS_SUCCESS; CleanUp: return Status; } ENUMFILESRESULT SmpEnumFilesRecursive ( IN PWSTR DirName, IN ENUMFILESPROC EnumFilesProc, OUT PULONG ReturnData, IN PVOID p1 OPTIONAL ) { RECURSION_DATA RecursionData; RecursionData.OptionalPtr = p1; RecursionData.EnumProc = EnumFilesProc; return SmpEnumFiles( DirName, SmppRecursiveEnumProc, ReturnData, &RecursionData ); } BOOLEAN SmppRecursiveEnumProc ( IN PWSTR DirName, IN PFILE_BOTH_DIR_INFORMATION FileInfo, OUT PULONG ret, IN PVOID Param ) { PWSTR FullPath; PWSTR temp; ULONG Len; NTSTATUS Status; ULONG ReturnData; ENUMFILESRESULT EnumResult; BOOLEAN b = FALSE; PRECURSION_DATA RecursionData; RecursionData = (PRECURSION_DATA) Param; // // Build the full file or dir path // temp = (PWSTR)(TmpBuffer + (sizeof(TmpBuffer)/2)); Len = FileInfo->FileNameLength/sizeof(WCHAR); wcsncpy(temp, FileInfo->FileName, Len); temp[Len] = 0; wcscpy((PWSTR)TmpBuffer, DirName); SmpConcatenatePaths((PWSTR)TmpBuffer, temp); // // For directories, recurse // if(FileInfo->FileAttributes & FILE_ATTRIBUTE_DIRECTORY) { if( (wcscmp( temp, L"." ) == 0) || (wcscmp( temp, L".." ) == 0) ) { // // Skip past . and .. directories // b = TRUE; } else { // // Recurse through subdirectory // FullPath = RtlAllocateHeap(RtlProcessHeap(), MAKE_TAG( INIT_TAG ), (wcslen((PWSTR)TmpBuffer)+1) * sizeof(WCHAR) ); if (FullPath == NULL) { *ret = EnumFileError; return FALSE; } wcscpy(FullPath, (PWSTR)TmpBuffer); EnumResult = SmpEnumFilesRecursive ( FullPath, RecursionData->EnumProc, &ReturnData, RecursionData->OptionalPtr ); RtlFreeHeap( RtlProcessHeap(), 0, FullPath ); if (EnumResult != NormalReturn) { *ret = EnumResult; return FALSE; } } } // // Call normal enum proc for file or dir (except . or .. dirs) // if (!b) { b = RecursionData->EnumProc ( DirName, FileInfo, ret, RecursionData->OptionalPtr ); } return b; } VOID SmpConcatenatePaths( IN OUT LPWSTR Path1, IN LPCWSTR Path2 ) { BOOLEAN NeedBackslash = TRUE; ULONG l = wcslen(Path1); // // Determine whether we need to stick a backslash // between the components. // if(l && (Path1[l-1] == L'\\')) { NeedBackslash = FALSE; } if(*Path2 == L'\\') { if(NeedBackslash) { NeedBackslash = FALSE; } else { // // Not only do we not need a backslash, but we // need to eliminate one before concatenating. // Path2++; } } if(NeedBackslash) { wcscat(Path1,L"\\"); } wcscat(Path1,Path2); } BOOLEAN SmpDelEnumFile( IN PWSTR DirName, IN PFILE_BOTH_DIR_INFORMATION FileInfo, OUT PULONG ret, IN PVOID Pointer ) { PWSTR FileName; PWSTR p; UNICODE_STRING UnicodeString; // // Ignore subdirectories // if(FileInfo->FileAttributes & FILE_ATTRIBUTE_DIRECTORY) { return TRUE; // continue processing } // // We have to make a copy of the filename, because the info struct // we get isn't NULL-terminated. // FileName = RtlAllocateHeap(RtlProcessHeap(), MAKE_TAG( INIT_TAG ), FileInfo->FileNameLength + sizeof(WCHAR) ); if (FileName == NULL) { *ret = EnumFileError; return TRUE; } wcsncpy(FileName, FileInfo->FileName, FileInfo->FileNameLength); FileName[FileInfo->FileNameLength / sizeof(WCHAR)] = UNICODE_NULL; // // Point to temporary buffer for pathname. // p = (PWSTR)TmpBuffer; // // Build up the full name of the file to delete. // wcscpy(p,DirName); SmpConcatenatePaths(p,FileName); // // Prepare to open the file. // RtlInitUnicodeString(&UnicodeString, p); // // Ignore return status of delete // SmpDeleteFile(&UnicodeString); RtlFreeHeap( RtlProcessHeap(), 0, FileName ); return TRUE; // continue processing } ENUMFILESRESULT SmpEnumFiles( IN PWSTR DirName, IN ENUMFILESPROC EnumFilesProc, OUT PULONG ReturnData, IN PVOID p1 OPTIONAL ) /*++ Routine Description: This routine processes every file (and subdirectory) in the directory specified by 'DirName'. Each entry is sent to the callback function 'EnumFilesProc' for processing. If the callback returns TRUE, processing continues, otherwise processing terminates. Arguments: DirName - Supplies the directory name containing the files/subdirectories to be processed. EnumFilesProc - Callback function to be called for each file/subdirectory. The function must have the following prototype: BOOLEAN EnumFilesProc( IN PWSTR, IN PFILE_BOTH_DIR_INFORMATION, OUT PULONG ); ReturnData - Pointer to the returned data. The contents stored here depend on the reason for termination (See below). p1 - Optional pointer, to be passed to the callback function. Return Value: This function can return one of three values. The data stored in 'ReturnData' depends upon which value is returned: NormalReturn - if the whole process completes uninterrupted (ReturnData is not used) EnumFileError - if an error occurs while enumerating files (ReturnData contains the error code) CallbackReturn - if the callback returns FALSE, causing termination (ReturnData contains data defined by the callback) --*/ { HANDLE hFindFile; NTSTATUS Status; UNICODE_STRING PathName; OBJECT_ATTRIBUTES Obja; IO_STATUS_BLOCK IoStatusBlock; PFILE_BOTH_DIR_INFORMATION DirectoryInfo; BOOLEAN bStartScan; ENUMFILESRESULT ret; // // Prepare to open the directory // RtlInitUnicodeString(&PathName, DirName); InitializeObjectAttributes( &Obja, &PathName, OBJ_CASE_INSENSITIVE, NULL, NULL ); // // Open the specified directory for list access // Status = NtOpenFile( &hFindFile, FILE_LIST_DIRECTORY | SYNCHRONIZE, &Obja, &IoStatusBlock, FILE_SHARE_READ | FILE_SHARE_WRITE, FILE_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT | FILE_OPEN_FOR_BACKUP_INTENT ); if(!NT_SUCCESS(Status)) { KdPrintEx((DPFLTR_SMSS_ID, DPFLTR_WARNING_LEVEL, "SMSS: Unable to open directory %ws for list (%lx)\n", DirName, Status)); *ReturnData = Status; return EnumFileError; } DirectoryInfo = RtlAllocateHeap(RtlProcessHeap(), MAKE_TAG( INIT_TAG ), DOS_MAX_PATH_LENGTH * 2 + sizeof(FILE_BOTH_DIR_INFORMATION) ); if(!DirectoryInfo) { KdPrintEx((DPFLTR_SMSS_ID, DPFLTR_WARNING_LEVEL, "SMSS: Unable to allocate memory for SpEnumFiles()\n")); *ReturnData = STATUS_INSUFFICIENT_RESOURCES; return EnumFileError; } bStartScan = TRUE; while(TRUE) { Status = NtQueryDirectoryFile( hFindFile, NULL, NULL, NULL, &IoStatusBlock, DirectoryInfo, (DOS_MAX_PATH_LENGTH * 2 + sizeof(FILE_BOTH_DIR_INFORMATION)), FileBothDirectoryInformation, TRUE, NULL, bStartScan ); if(Status == STATUS_NO_MORE_FILES) { ret = NormalReturn; break; } else if(!NT_SUCCESS(Status)) { KdPrintEx((DPFLTR_SMSS_ID, DPFLTR_WARNING_LEVEL, "SMSS: Unable to query directory %ws (%lx)\n", DirName, Status)); *ReturnData = Status; ret = EnumFileError; break; } if(bStartScan) { bStartScan = FALSE; } // // Now pass this entry off to our callback function for processing // if(!EnumFilesProc(DirName, DirectoryInfo, ReturnData, p1)) { ret = CallbackReturn; break; } } RtlFreeHeap( RtlProcessHeap(), 0, DirectoryInfo ); NtClose(hFindFile); return ret; } #endif // defined(REMOTE_BOOT) NTSTATUS SmpDeleteFile( IN PUNICODE_STRING pFile ) { NTSTATUS Status; IO_STATUS_BLOCK IoStatusBlock; OBJECT_ATTRIBUTES Obja; HANDLE Handle; FILE_DISPOSITION_INFORMATION Disposition; FILE_BASIC_INFORMATION BasicInfo; InitializeObjectAttributes( &Obja, pFile, OBJ_CASE_INSENSITIVE, NULL, NULL ); // // Attempt to open the file. // Status = NtOpenFile( &Handle, (ACCESS_MASK)(DELETE | FILE_WRITE_ATTRIBUTES), &Obja, &IoStatusBlock, FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE , FILE_NON_DIRECTORY_FILE | FILE_OPEN_FOR_BACKUP_INTENT ); if(!NT_SUCCESS(Status)) { KdPrintEx((DPFLTR_SMSS_ID, DPFLTR_WARNING_LEVEL, "SMSS: Unable to open %ws for delete (%lx)\n", pFile->Buffer, Status)); return(Status); } // // Change the file attribute to normal // RtlZeroMemory( &BasicInfo, sizeof( FILE_BASIC_INFORMATION ) ); BasicInfo.FileAttributes = FILE_ATTRIBUTE_NORMAL; Status = NtSetInformationFile(Handle, &IoStatusBlock, &BasicInfo, sizeof(BasicInfo), FileBasicInformation ); if(!NT_SUCCESS(Status)) { KdPrintEx((DPFLTR_SMSS_ID, DPFLTR_WARNING_LEVEL, "SMSS: Unable to change attribute of %ls. Status = (%lx)\n", pFile->Buffer, Status)); return(Status); } // // Set up for delete and call worker to do it. // #undef DeleteFile Disposition.DeleteFile = TRUE; Status = NtSetInformationFile(Handle, &IoStatusBlock, &Disposition, sizeof(Disposition), FileDispositionInformation ); // // Clean up and return. // NtClose(Handle); return(Status); } NTSTATUS SmpCallCsrCreateProcess( IN OUT PSBAPIMSG m, IN size_t ArgLength, IN HANDLE CommunicationPort ) /*++ Routine Description: This function sends a message to CSR telling to start a process Arguments: m - message to send ArgLength - length of argument struct inside message CommunicationPort - LPC port to send to Return Value: NTSTATUS --*/ { NTSTATUS Status; m->h.u1.s1.DataLength = ArgLength + 8; m->h.u1.s1.TotalLength = sizeof(SBAPIMSG); m->h.u2.ZeroInit = 0L; m->ApiNumber = SbCreateProcessApi; Status = NtRequestWaitReplyPort(CommunicationPort, (PPORT_MESSAGE) m, (PPORT_MESSAGE) m ); if (NT_SUCCESS( Status )) { Status = m->ReturnedStatus; } return Status; }