Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

7103 lines
220 KiB

/*++
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 <stdio.h>
#include <string.h>
#include <safeboot.h>
#include <wow64t.h>
#if defined(REMOTE_BOOT)
#include <windows.h>
#ifdef DeleteFile
#undef DeleteFile
#endif
#include <shdcom.h> // 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;
ULONG SmpCalledConfigEnv = 0;
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 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},
//
// NOTICE-2002/05/23-ELi
// SmpConfigureEnvironment assumes that it will be called twice
// If you change the number of times SmpConfigureEnvironment is called,
// then make sure that you do not break SmpConfigureEnvironment.
// see bug 318452 in the old Windows NT Bugs RAID database for the
// reason why SmpConfigureEnvironment is called twice.
//
//
// this needs to happen twice so that forward references to environment
// variables, which may be defined under the Environment registry key, 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;
UCHAR 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,
sizeof (ValueBuffer),
&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 <sessionid> direcotry under "\Sessions". "\Sessions\<sessionid>
// 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 );
if (p->AnsiValue) { RtlFreeHeap( RtlProcessHeap(), 0, p->AnsiValue ); }
if (p->Value.Buffer) { RtlFreeHeap( RtlProcessHeap(), 0, p->Value.Buffer ); }
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);
if (p->AnsiValue) { RtlFreeHeap( RtlProcessHeap(), 0, p->AnsiValue ); }
if (p->Value.Buffer) { RtlFreeHeap( RtlProcessHeap(), 0, p->Value.Buffer ); }
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, Status1;
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) ) {
DbgPrint ("SMSS: Subsystem execute failed (%WZ)\n", &p->Value);
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;
Status1 = LdrQueryImageFileExecutionOptions( InitialCommand,
L"Debugger",
REG_SZ,
InitialCommandBuffer,
sizeof( InitialCommandBuffer ) - InitialCommand->Length - sizeof (WCHAR),
NULL);
if (NT_SUCCESS (Status1) && 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_IA64:
ValueData = L"IA64";
break;
case PROCESSOR_ARCHITECTURE_AMD64:
ValueData = L"AMD64";
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_INTEL:
case PROCESSOR_ARCHITECTURE_IA64:
case PROCESSOR_ARCHITECTURE_AMD64:
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;
}
//
// Post-486 processors will fall through
//
case PROCESSOR_ARCHITECTURE_IA64:
case PROCESSOR_ARCHITECTURE_AMD64:
//
// Modern X86 processors, IA64 and Amd64 use a fixed point
// binary number. Output is 4 hex digits, no formatting.
//
swprintf( ValueBuffer, L"%04x", ProcessorInfo.ProcessorRevision );
break;
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 );
if (p->AnsiValue) { RtlFreeHeap( RtlProcessHeap(), 0, p->AnsiValue ); }
if (p->Value.Buffer) { RtlFreeHeap( RtlProcessHeap(), 0, p->Value.Buffer ); }
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;
//
// 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 );
if ((Name - Value) + n >= DOS_MAX_PATH_LENGTH) {
return;
}
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;
}
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;
while (!IsListEmpty( Head )) {
Next = RemoveHeadList( Head );
p = CONTAINING_RECORD( Next,
SMP_REGISTRY_VALUE,
Entry
);
if (p->AnsiValue) { RtlFreeHeap( RtlProcessHeap(), 0, p->AnsiValue ); }
if (p->Value.Buffer) { RtlFreeHeap( RtlProcessHeap(), 0, p->Value.Buffer ); }
RtlFreeHeap( RtlProcessHeap(), 0, p );
}
return Status;
}
NTSTATUS
SmpInitializeKnownDllsInternal(
IN PUNICODE_STRING ObjectDirectoryName,
IN PUNICODE_STRING KnownDllPath
)
{
NTSTATUS Status, Status1;
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 = NULL;
HANDLE KnownDllObjectDirectory = NULL;
FileName.Buffer = NULL;
//
// 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));
Status = STATUS_OBJECT_NAME_INVALID;
goto exit_and_free;
}
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));
KnownDllFileDirectory = NULL;
goto exit_and_free;
}
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));
LinkHandle = NULL;
goto exit_and_free;
}
Status1 = NtClose (LinkHandle);
ASSERT (NT_SUCCESS (Status1));
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)) {
//
// 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 {
Status1 = NtClose (SectionHandle);
ASSERT (NT_SUCCESS (Status1));
}
} else {
KdPrintEx((DPFLTR_SMSS_ID,
DPFLTR_WARNING_LEVEL,
"SMSS: Ignoring %wZ as KnownDll since it is not a DLL\n",
&p->Value));
}
Status1 = NtClose (FileHandle);
ASSERT (NT_SUCCESS (Status1));
}
Next = Next->Flink;
//
// Note that section remains open. This will keep it around.
// Maybe this should be a permenent section ?
//
}
Status = STATUS_SUCCESS;
exit_and_free:
if (KnownDllObjectDirectory != NULL) {
Status1 = NtClose (KnownDllObjectDirectory);
ASSERT (NT_SUCCESS (Status1));
}
if (KnownDllFileDirectory != NULL) {
Status1 = NtClose (KnownDllFileDirectory);
ASSERT (NT_SUCCESS (Status1));
}
if (FileName.Buffer != NULL) {
RtlFreeHeap (RtlProcessHeap (), 0, FileName.Buffer);
}
return Status;
}
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,
sizeof (ValueBuffer),
&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; i<CountTier2Files; i++) {
//
// if the file name is prefixed by the '@' character
// then we ignore the check and say the file is ok
//
if (p->Name.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 );
RtlCopyMemory( 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 ));
}
if (p->AnsiValue) { RtlFreeHeap( RtlProcessHeap(), 0, p->AnsiValue ); }
if (p->Value.Buffer) { RtlFreeHeap( RtlProcessHeap(), 0, p->Value.Buffer ); }
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;
RtlCopyMemory(
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" )) {
SmpCalledConfigEnv++;
//
// Check if this is the second time this function is invoked
// This routine is called twice to resolve forward references to
// other environment variables defined in this key
//
if( SmpCalledConfigEnv == 2 ) {
SmpDefaultLibPathBuffer = RtlAllocateHeap(
RtlProcessHeap(),
MAKE_TAG( INIT_TAG ),
ValueLength
);
if ( !SmpDefaultLibPathBuffer ) {
return ( STATUS_NO_MEMORY );
}
RtlCopyMemory( 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 ));
RtlCopyMemory( 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 ) ||
#pragma prefast(suppress: 209, "This *does* take a byte count--not a character count")
!(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.
//
#pragma prefast(suppress: 209, "This *does* take a byte count--not a character count")
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 = NULL,
AdminSid = NULL,
LocalSystemSid = NULL,
RestrictedSid = NULL,
OwnerSid = NULL;
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) );
#pragma prefast(suppress: 248, "Protection enabled via registry")
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) );
#pragma prefast(suppress: 248, "Protection enabled via registry")
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) );
#pragma prefast(suppress: 248, "Protection enabled via registry")
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) );
#pragma prefast(suppress: 248, "Protection enabled via registry")
Status = RtlSetDaclSecurityDescriptor (
SmpApiPortSecurityDescriptor,
TRUE, //DaclPresent,
NULL, //Dacl
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)) {
WorldSid = NULL;
goto exit_and_free;
}
Status = RtlAllocateAndInitializeSid (&NtAuthority,
2,
SECURITY_BUILTIN_DOMAIN_RID,
DOMAIN_ALIAS_RID_ADMINS,
0, 0, 0, 0, 0, 0,
&AdminSid);
if (!NT_SUCCESS (Status)) {
AdminSid = NULL;
goto exit_and_free;
}
Status = RtlAllocateAndInitializeSid (&CreatorAuthority,
1,
SECURITY_CREATOR_OWNER_RID,
0, 0, 0, 0, 0, 0, 0,
&OwnerSid);
if (!NT_SUCCESS (Status)) {
OwnerSid = NULL;
goto exit_and_free;
}
Status = RtlAllocateAndInitializeSid (&NtAuthority,
1,
SECURITY_RESTRICTED_CODE_RID,
0, 0, 0, 0, 0, 0, 0,
&RestrictedSid);
if (!NT_SUCCESS (Status)) {
RestrictedSid = NULL;
goto exit_and_free;
}
Status = RtlAllocateAndInitializeSid (&NtAuthority,
1,
SECURITY_LOCAL_SYSTEM_RID,
0, 0, 0, 0, 0, 0, 0,
&LocalSystemSid);
if (!NT_SUCCESS (Status)) {
LocalSystemSid = NULL;
goto exit_and_free;
}
//
// Build the ApiPort security descriptor only
// if this is the initial call
//
if (InitialCall) {
AdminAccess = GENERIC_ALL;
AclLength = sizeof( ACL ) +
2 * sizeof( ACCESS_ALLOWED_ACE ) +
RtlLengthSid( LocalSystemSid ) +
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
// Admin
//
Status = RtlAddAccessAllowedAce ( Acl, ACL_REVISION2, AdminAccess, AdminSid );
ASSERT( NT_SUCCESS(Status) );
Status = RtlAddAccessAllowedAce ( Acl, ACL_REVISION2, AdminAccess, LocalSystemSid );
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) );
}
}
}
exit_and_free:
if (OwnerSid != NULL) {
RtlFreeHeap( RtlProcessHeap(), 0, OwnerSid );
}
if (AdminSid != NULL) {
RtlFreeHeap( RtlProcessHeap(), 0, AdminSid );
}
if (WorldSid != NULL) {
RtlFreeHeap( RtlProcessHeap(), 0, WorldSid );
}
if (LocalSystemSid != NULL) {
RtlFreeHeap( RtlProcessHeap(), 0, LocalSystemSid );
}
if (RestrictedSid != NULL) {
RtlFreeHeap( RtlProcessHeap(), 0, RestrictedSid );
}
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,
sizeof (ValueBuffer),
&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;
}
RtlCopyMemory(&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;
}