Source code of Windows XP (NT5)
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.
 
 
 
 
 
 

3004 lines
92 KiB

/*++
Copyright (c) 1990 Microsoft Corporation
Module Name:
srvinit.c
Abstract:
This is the main initialization file for the Windows 32-bit Base API
Server DLL.
Author:
Steve Wood (stevewo) 10-Oct-1990
Revision History:
--*/
#include "basesrv.h"
//
// needed for definitions of structures when broadcasting a message to all
// the windows that have the caller's LUID
//
#include <dbt.h>
//
// TS broadcast support
//
#include <winsta.h>
#define NT_DRIVE_LETTER_PATH_LENGTH 8 // "\??\X:<NULL>" = 7 chars
// Protection mode for named objects
ULONG ProtectionMode = 0;
UNICODE_STRING BaseSrvCSDString;
ULONG BaseSrvCSDNumber;
UNICODE_STRING BaseSrvKernel32DllPath;
UNICODE_STRING BaseSrvSxsDllPath;
UNICODE_STRING UnexpandedSystemRootString = RTL_CONSTANT_STRING(L"%SystemRoot%");
RTL_QUERY_REGISTRY_TABLE BaseServerRegistryConfigurationTable[] = {
{NULL, RTL_QUERY_REGISTRY_DIRECT,
L"CSDVersion", &BaseSrvCSDString,
REG_NONE, NULL, 0},
{NULL, 0,
NULL, NULL,
REG_NONE, NULL, 0}
};
RTL_QUERY_REGISTRY_TABLE BaseServerRegistryConfigurationTable1[] = {
{NULL, RTL_QUERY_REGISTRY_DIRECT,
L"CSDVersion", &BaseSrvCSDNumber,
REG_NONE, NULL, 0},
{NULL, 0,
NULL, NULL,
REG_NONE, NULL, 0}
};
PCSR_API_ROUTINE BaseServerApiDispatchTable[ BasepMaxApiNumber+1 ] = {
BaseSrvCreateProcess,
BaseSrvCreateThread,
BaseSrvGetTempFile,
BaseSrvExitProcess,
BaseSrvDebugProcess,
BaseSrvCheckVDM,
BaseSrvUpdateVDMEntry,
BaseSrvGetNextVDMCommand,
BaseSrvExitVDM,
BaseSrvIsFirstVDM,
BaseSrvGetVDMExitCode,
BaseSrvSetReenterCount,
BaseSrvSetProcessShutdownParam,
BaseSrvGetProcessShutdownParam,
BaseSrvNlsSetUserInfo,
BaseSrvNlsSetMultipleUserInfo,
BaseSrvNlsCreateSection,
BaseSrvSetVDMCurDirs,
BaseSrvGetVDMCurDirs,
BaseSrvBatNotification,
BaseSrvRegisterWowExec,
BaseSrvSoundSentryNotification,
BaseSrvRefreshIniFileMapping,
BaseSrvDefineDosDevice,
BaseSrvSetTermsrvAppInstallMode,
BaseSrvNlsUpdateCacheCount,
BaseSrvSetTermsrvClientTimeZone,
BaseSrvSxsCreateActivationContext,
BaseSrvDebugProcessStop,
BaseSrvRegisterThread,
BaseSrvNlsGetUserInfo, // BaseSrvNlsGetUserInfo
NULL
};
BOOLEAN BaseServerApiServerValidTable[ BasepMaxApiNumber+1 ] = {
TRUE, // SrvCreateProcess,
TRUE, // SrvCreateThread,
TRUE, // SrvGetTempFile,
FALSE, // SrvExitProcess,
FALSE, // SrvDebugProcess,
TRUE, // SrvCheckVDM,
TRUE, // SrvUpdateVDMEntry
TRUE, // SrvGetNextVDMCommand
TRUE, // SrvExitVDM
TRUE, // SrvIsFirstVDM
TRUE, // SrvGetVDMExitCode
TRUE, // SrvSetReenterCount
TRUE, // SrvSetProcessShutdownParam
TRUE, // SrvGetProcessShutdownParam
TRUE, // SrvNlsSetUserInfo
TRUE, // SrvNlsSetMultipleUserInfo
TRUE, // SrvNlsCreateSection
TRUE, // SrvSetVDMCurDirs
TRUE, // SrvGetVDMCurDirs
TRUE, // SrvBatNotification
TRUE, // SrvRegisterWowExec
TRUE, // SrvSoundSentryNotification
TRUE, // SrvRefreshIniFileMapping
TRUE, // SrvDefineDosDevice
TRUE, // SrvSetTermsrvAppInstallMode
TRUE, // SrvNlsUpdateCacheCount,
TRUE, // SrvSetTermsrvClientTimeZone
TRUE, // SrvSxsCreateActivationContext
TRUE, // SrvDebugProcessStop
TRUE, // SrvRegisterThread,
TRUE, // BaseSrvNlsGetUserInfo
FALSE
};
#if DBG
PSZ BaseServerApiNameTable[ BasepMaxApiNumber+1 ] = {
"BaseCreateProcess",
"BaseCreateThread",
"BaseGetTempFile",
"BaseExitProcess",
"BaseDebugProcess",
"BaseCheckVDM",
"BaseUpdateVDMEntry",
"BaseGetNextVDMCommand",
"BaseExitVDM",
"BaseIsFirstVDM",
"BaseGetVDMExitCode",
"BaseSetReenterCount",
"BaseSetProcessShutdownParam",
"BaseGetProcessShutdownParam",
"BaseNlsSetUserInfo",
"BaseNlsSetMultipleUserInfo",
"BaseNlsCreateSection",
"BaseSetVDMCurDirs",
"BaseGetVDMCurDirs",
"BaseBatNotification",
"BaseRegisterWowExec",
"BaseSoundSentryNotification",
"BaseSrvRefreshIniFileMapping"
"BaseDefineDosDevice",
"BaseSrvSetTermsrvAppInstallMode",
"BaseSrvNlsUpdateCacheCount",
"BaseSrvSetTermsrvClientTimeZone",
"BaseSrvSxsCreateActivationContext",
"BaseSrvDebugProcessStop",
"BaseRegisterThread",
"BaseNlsGetUserInfo",
NULL
};
#endif // DBG
HANDLE BaseSrvNamedObjectDirectory;
HANDLE BaseSrvRestrictedObjectDirectory;
RTL_CRITICAL_SECTION BaseSrvDosDeviceCritSec;
BOOLEAN BaseSrvFirstClient = TRUE;
#if defined(_WIN64)
SYSTEM_BASIC_INFORMATION SysInfo;
#endif
//
// With LUID Device Maps,
// Use BroadCastSystemMessageEx to broadcast the message to all the windows
// with the LUID
// Function pointer to BroadCastSystemMessageEx
//
long (WINAPI *PBROADCASTSYSTEMMESSAGEEXW)( DWORD, LPDWORD, UINT, WPARAM, LPARAM, PBSMINFO ) = NULL;
//
// Data structures and functions used for broadcast drive letter
// change to application and the desktop with the caller's LUID
//
typedef struct _DDD_BSM_REQUEST *PDDD_BSM_REQUEST;
typedef struct _DDD_BSM_REQUEST {
PDDD_BSM_REQUEST pNextRequest;
LUID Luid;
DWORD iDrive;
BOOLEAN DeleteRequest;
} DDD_BSM_REQUEST, *PDDD_BSM_REQUEST;
PDDD_BSM_REQUEST BSM_Request_Queue = NULL;
PDDD_BSM_REQUEST BSM_Request_Queue_End = NULL;
RTL_CRITICAL_SECTION BaseSrvDDDBSMCritSec;
LONG BaseSrvpBSMThreadCount = 0;
#define BaseSrvpBSMThreadMax 1
#define PREALLOCATE_EVENT_MASK 0x80000000
//
// TS broadcast support
//
#define DEFAULT_BROADCAST_TIME_OUT 5
typedef LONG (*FP_WINSTABROADCASTSYSTEMMESSAGE)(HANDLE hServer,
BOOL sendToAllWinstations,
ULONG sessionID,
ULONG timeOut,
DWORD dwFlags,
DWORD *lpdwRecipients,
ULONG uiMessage,
WPARAM wParam,
LPARAM lParam,
LONG *pResponse);
NTSTATUS
SendWinStationBSM (
DWORD dwFlags,
LPDWORD lpdwRecipients,
UINT uiMessage,
WPARAM wParam,
LPARAM lParam
);
//
// END: TS broadcast support
//
NTSTATUS
AddBSMRequest(
IN DWORD iDrive,
IN BOOLEAN DeleteRequest,
IN PLUID pLuid
);
NTSTATUS
CreateBSMThread();
NTSTATUS
BaseSrvBSMThread(
PVOID pJunk
);
BOOLEAN
CheckForGlobalSymLink (
PUNICODE_STRING pDeviceName
);
//
// END: broadcast drive letter change
//
WORD
ConvertUnicodeToWord( PWSTR s );
NTSTATUS
CreateBaseAcls( PACL *Dacl, PACL *RestrictedDacl );
NTSTATUS
IsGlobalSymbolicLink(
IN HANDLE hSymLink,
OUT PBOOLEAN pbGlobalSymLink);
NTSTATUS
GetCallerLuid (
OUT PLUID pLuid);
NTSTATUS
BroadcastDriveLetterChange(
IN DWORD iDrive,
IN BOOLEAN DeleteRequest,
IN PLUID pLuid);
WORD
ConvertUnicodeToWord( PWSTR s )
{
NTSTATUS Status;
ULONG Result;
UNICODE_STRING UnicodeString;
while (*s && *s <= L' ') {
s += 1;
}
RtlInitUnicodeString( &UnicodeString, s );
Status = RtlUnicodeStringToInteger( &UnicodeString,
10,
&Result
);
if (!NT_SUCCESS( Status )) {
Result = 0;
}
return (WORD)Result;
}
#if defined(WX86) || defined(_AXP64_)
PKEY_VALUE_PARTIAL_INFORMATION
Wx86QueryValueKey(
HANDLE KeyHandle,
PWCHAR ValueName
)
{
NTSTATUS Status;
ULONG ResultLength;
UNICODE_STRING UnicodeString;
PKEY_VALUE_PARTIAL_INFORMATION KeyValueInformation, RetKeyValueInfo;
BYTE ValueBuffer[MAX_PATH*sizeof(WCHAR) + sizeof(KEY_VALUE_PARTIAL_INFORMATION)];
KeyValueInformation = (PKEY_VALUE_PARTIAL_INFORMATION)ValueBuffer;
RtlInitUnicodeString( &UnicodeString, ValueName);
Status = NtQueryValueKey(KeyHandle,
&UnicodeString,
KeyValuePartialInformation,
KeyValueInformation,
sizeof( ValueBuffer ),
&ResultLength
);
if (NT_SUCCESS(Status)) {
RetKeyValueInfo = RtlAllocateHeap(RtlProcessHeap(), 0, ResultLength);
if (RetKeyValueInfo) {
RtlMoveMemory(RetKeyValueInfo,
KeyValueInformation,
ResultLength
);
}
}
else if (Status == STATUS_BUFFER_OVERFLOW) {
RetKeyValueInfo = RtlAllocateHeap(RtlProcessHeap(), 0, ResultLength);
if (RetKeyValueInfo) {
Status = NtQueryValueKey(KeyHandle,
&UnicodeString,
KeyValuePartialInformation,
RetKeyValueInfo,
ResultLength,
&ResultLength
);
if (!NT_SUCCESS(Status)) {
RtlFreeHeap(RtlProcessHeap(), 0, RetKeyValueInfo);
RetKeyValueInfo = NULL;
}
}
}
else {
RetKeyValueInfo = NULL;
}
return RetKeyValueInfo;
}
/*
* Initializes the volatile registry entries for Wx86.
* \Registry\Machine\HARDWARE\DESCRIPTION\System\Wx86FloatingPointProcessor
*/
void
SetupWx86KeyMapping(void)
{
NTSTATUS Status;
ULONG Processors, ProcessorCount;
HANDLE KeyHandle, ParentKeyHandle;
PKEY_VALUE_PARTIAL_INFORMATION Identifier;
PKEY_VALUE_PARTIAL_INFORMATION ConfigurationData;
PKEY_VALUE_PARTIAL_INFORMATION ComponentInformation;
UNICODE_STRING UnicodeString;
UNICODE_STRING ClassUnicode;
OBJECT_ATTRIBUTES Obja;
WCHAR wcProcessor[64];
//
// If the Wx86\\KeyRemapping\\FloatingPointProcessor does not exist
// don't create the Wx86FloatingPointProcessor. So that x86 apps
// will think there isn't any fpu.
//
RtlInitUnicodeString(&UnicodeString,
L"\\Registry\\Machine\\System\\CurrentControlSet\\Control\\Wx86\\KeyRemapping\\FloatingPointProcessor"
);
InitializeObjectAttributes(&Obja,
&UnicodeString,
OBJ_CASE_INSENSITIVE,
NULL,
NULL
);
Status = NtOpenKey(&KeyHandle, KEY_READ | KEY_WRITE, &Obja);
if (!NT_SUCCESS(Status)) {
return;
}
NtClose(KeyHandle);
//
// Create Wx86FloatingPointProcessor key
//
RtlInitUnicodeString(&ClassUnicode, L"Processor");
RtlInitUnicodeString(
&UnicodeString,
L"\\Registry\\Machine\\Hardware\\Description\\System\\Wx86FloatingPointProcessor"
);
InitializeObjectAttributes(&Obja,
&UnicodeString,
OBJ_CASE_INSENSITIVE,
NULL,
NULL
);
Status = NtCreateKey(&ParentKeyHandle,
KEY_READ | KEY_WRITE | KEY_CREATE_SUB_KEY,
&Obja,
0, // TitleIndex ?
&ClassUnicode,
REG_OPTION_VOLATILE,
NULL
);
if (!NT_SUCCESS(Status)) {
return;
}
RtlInitUnicodeString(&UnicodeString,
L"\\Registry\\Machine\\Hardware\\Description\\System\\CentralProcessor\\0"
);
InitializeObjectAttributes(&Obja,
&UnicodeString,
OBJ_CASE_INSENSITIVE,
NULL,
NULL
);
Status = NtOpenKey(&KeyHandle, KEY_READ, &Obja);
if (!NT_SUCCESS(Status)) {
NtClose(ParentKeyHandle);
return;
}
Identifier = Wx86QueryValueKey(KeyHandle, L"Identifier");
ConfigurationData = Wx86QueryValueKey(KeyHandle, L"Configuration Data");
ComponentInformation = Wx86QueryValueKey(KeyHandle, L"Component Information");
NtClose(KeyHandle);
KeyHandle = NULL;
#if defined(_WIN64)
Processors = SysInfo.NumberOfProcessors;
#else
Processors = BaseSrvpStaticServerData->SysInfo.NumberOfProcessors;
#endif
ProcessorCount = 0;
while (Processors--) {
swprintf(wcProcessor, L"%d", ProcessorCount++);
RtlInitUnicodeString(&UnicodeString, wcProcessor);
InitializeObjectAttributes(&Obja,
&UnicodeString,
OBJ_CASE_INSENSITIVE,
ParentKeyHandle,
NULL
);
Status = NtCreateKey(&KeyHandle,
KEY_READ | KEY_WRITE,
&Obja,
0,
NULL,
REG_OPTION_VOLATILE,
NULL
);
if (!NT_SUCCESS(Status)) {
KeyHandle = NULL;
goto SWMCleanup;
}
if (ComponentInformation) {
RtlInitUnicodeString(&UnicodeString, L"Component Information");
Status = NtSetValueKey(KeyHandle,
&UnicodeString,
0,
ComponentInformation->Type,
ComponentInformation->Data,
ComponentInformation->DataLength
);
if (!NT_SUCCESS(Status)) {
goto SWMCleanup;
}
}
if (ConfigurationData) {
RtlInitUnicodeString(&UnicodeString, L"Configuration Data");
Status = NtSetValueKey(KeyHandle,
&UnicodeString,
0,
ConfigurationData->Type,
ConfigurationData->Data,
ConfigurationData->DataLength
);
if (!NT_SUCCESS(Status)) {
goto SWMCleanup;
}
}
if (Identifier) {
RtlInitUnicodeString(&UnicodeString, L"Identifier");
Status = NtSetValueKey(KeyHandle,
&UnicodeString,
0,
Identifier->Type,
Identifier->Data,
Identifier->DataLength
);
if (!NT_SUCCESS(Status)) {
goto SWMCleanup;
}
}
NtClose(KeyHandle);
KeyHandle = NULL;
}
SWMCleanup:
if (ConfigurationData) {
RtlFreeHeap( RtlProcessHeap(), 0, ConfigurationData);
}
if (ComponentInformation) {
RtlFreeHeap( RtlProcessHeap(), 0, ComponentInformation);
}
if (Identifier) {
RtlFreeHeap( RtlProcessHeap(), 0, Identifier);
}
if (ParentKeyHandle) {
NtClose(ParentKeyHandle);
}
if (KeyHandle) {
NtClose(KeyHandle);
}
return;
}
#endif
NTSTATUS
ServerDllInitialization(
PCSR_SERVER_DLL LoadedServerDll
)
{
NTSTATUS Status;
UNICODE_STRING UnicodeString;
OBJECT_ATTRIBUTES Obja;
PSECURITY_DESCRIPTOR PrimarySecurityDescriptor;
PKEY_VALUE_PARTIAL_INFORMATION KeyValueInformation;
ULONG ResultLength;
PVOID p;
WCHAR ValueBuffer[ 400 ];
UNICODE_STRING NameString, ValueString;
HANDLE KeyHandle;
PWSTR s, s1;
PACL Dacl, RestrictedDacl;
WCHAR szObjectDirectory[MAX_SESSION_PATH];
HANDLE SymbolicLinkHandle;
UNICODE_STRING LinkTarget;
ULONG attributes = OBJ_CASE_INSENSITIVE | OBJ_OPENIF;
ULONG LUIDDeviceMapsEnabled;
//
// Id of the Terminal Server Session to which this CSRSS belongs.
// SessionID == 0 is always console session (Standard NT).
//
SessionId = NtCurrentPeb()->SessionId;
//
// Object directories are only permanent for the console session
//
if (SessionId == 0) {
attributes |= OBJ_PERMANENT;
}
BaseSrvHeap = RtlProcessHeap();
BaseSrvTag = RtlCreateTagHeap( BaseSrvHeap,
0,
L"BASESRV!",
L"TMP\0"
L"VDM\0"
L"SXS\0"
);
BaseSrvSharedHeap = LoadedServerDll->SharedStaticServerData;
BaseSrvSharedTag = RtlCreateTagHeap( BaseSrvSharedHeap,
0,
L"BASESHR!",
L"INIT\0"
L"INI\0"
);
LoadedServerDll->ApiNumberBase = BASESRV_FIRST_API_NUMBER;
LoadedServerDll->MaxApiNumber = BasepMaxApiNumber;
LoadedServerDll->ApiDispatchTable = BaseServerApiDispatchTable;
LoadedServerDll->ApiServerValidTable = BaseServerApiServerValidTable;
#if DBG
LoadedServerDll->ApiNameTable = BaseServerApiNameTable;
#else
LoadedServerDll->ApiNameTable = NULL;
#endif
LoadedServerDll->PerProcessDataLength = 0;
LoadedServerDll->ConnectRoutine = BaseClientConnectRoutine;
LoadedServerDll->DisconnectRoutine = BaseClientDisconnectRoutine;
Status = RtlInitializeCriticalSection (&BaseSrvDosDeviceCritSec);
if (!NT_SUCCESS (Status)) {
return Status;
}
ValueString.Buffer = ValueBuffer;
ValueString.MaximumLength = sizeof( ValueBuffer );
Status = RtlExpandEnvironmentStrings_U( NULL,
&UnexpandedSystemRootString,
&ValueString,
NULL
);
//
// RtlCreateUnicodeString includes a terminal nul.
// It makes a heap allocated copy.
// These strings are never freed.
//
ASSERT( NT_SUCCESS( Status ) );
ValueBuffer[ ValueString.Length / sizeof( WCHAR ) ] = UNICODE_NULL;
if (!RtlCreateUnicodeString( &BaseSrvWindowsDirectory, ValueBuffer ))
goto OutOfMemory;
wcscat(ValueBuffer, L"\\System32" );
if (!RtlCreateUnicodeString( &BaseSrvWindowsSystemDirectory, ValueBuffer ))
goto OutOfMemory;
wcscat(ValueBuffer, L"\\kernel32.dll" );
if (!RtlCreateUnicodeString( &BaseSrvKernel32DllPath, ValueBuffer ))
goto OutOfMemory;
wcscpy(ValueBuffer, BaseSrvWindowsSystemDirectory.Buffer);
wcscat(ValueBuffer, L"\\sxs.dll");
if (!RtlCreateUnicodeString( &BaseSrvSxsDllPath, ValueBuffer ))
goto OutOfMemory;
#ifdef WX86
wcscpy(ValueBuffer, BaseSrvWindowsDirectory.Buffer);
wcscat(ValueBuffer, L"\\Sys32x86" );
if (!RtlCreateUnicodeString( &BaseSrvWindowsSys32x86Directory, ValueBuffer))
goto OutOfMemory;
#endif
//
// need to synch this w/ user's desktop concept
//
if (SessionId == 0) {
//
// Console Session
//
wcscpy(szObjectDirectory,L"\\BaseNamedObjects" );
} else {
swprintf(szObjectDirectory,L"%ws\\%ld\\BaseNamedObjects",
SESSION_ROOT,SessionId);
}
RtlInitUnicodeString(&UnicodeString,szObjectDirectory);
//
// initialize base static server data
//
BaseSrvpStaticServerData = RtlAllocateHeap( BaseSrvSharedHeap,
MAKE_SHARED_TAG( INIT_TAG ),
sizeof( BASE_STATIC_SERVER_DATA )
);
if ( !BaseSrvpStaticServerData ) {
return STATUS_NO_MEMORY;
}
LoadedServerDll->SharedStaticServerData = (PVOID)BaseSrvpStaticServerData;
BaseSrvpStaticServerData->TermsrvClientTimeZoneId=TIME_ZONE_ID_INVALID;
Status = NtQuerySystemInformation(
SystemTimeOfDayInformation,
(PVOID)&BaseSrvpStaticServerData->TimeOfDay,
sizeof(BaseSrvpStaticServerData->TimeOfDay),
NULL
);
if ( !NT_SUCCESS( Status ) ) {
return Status;
}
//
// windows directory
//
BaseSrvpStaticServerData->WindowsDirectory = BaseSrvWindowsDirectory;
p = RtlAllocateHeap( BaseSrvSharedHeap,
MAKE_SHARED_TAG( INIT_TAG ),
BaseSrvWindowsDirectory.MaximumLength
);
if ( !p ) {
return STATUS_NO_MEMORY;
}
RtlMoveMemory(p,BaseSrvpStaticServerData->WindowsDirectory.Buffer,BaseSrvWindowsDirectory.MaximumLength);
BaseSrvpStaticServerData->WindowsDirectory.Buffer = p;
//
// windows system directory
//
BaseSrvpStaticServerData->WindowsSystemDirectory = BaseSrvWindowsSystemDirectory;
p = RtlAllocateHeap( BaseSrvSharedHeap,
MAKE_SHARED_TAG( INIT_TAG ),
BaseSrvWindowsSystemDirectory.MaximumLength
);
if ( !p ) {
return STATUS_NO_MEMORY;
}
RtlMoveMemory(p,BaseSrvpStaticServerData->WindowsSystemDirectory.Buffer,BaseSrvWindowsSystemDirectory.MaximumLength);
BaseSrvpStaticServerData->WindowsSystemDirectory.Buffer = p;
#ifdef WX86
//
// wx86 system directory
//
BaseSrvpStaticServerData->WindowsSys32x86Directory = BaseSrvWindowsSys32x86Directory;
p = RtlAllocateHeap( BaseSrvSharedHeap,
MAKE_SHARED_TAG( INIT_TAG ),
BaseSrvWindowsSys32x86Directory.MaximumLength
);
if ( !p ) {
return STATUS_NO_MEMORY;
}
RtlMoveMemory(p,BaseSrvpStaticServerData->WindowsSys32x86Directory.Buffer,BaseSrvWindowsSys32x86Directory.MaximumLength);
BaseSrvpStaticServerData->WindowsSys32x86Directory.Buffer = p;
#else
BaseSrvpStaticServerData->WindowsSys32x86Directory.Buffer = NULL;
BaseSrvpStaticServerData->WindowsSys32x86Directory.Length = 0;
BaseSrvpStaticServerData->WindowsSys32x86Directory.MaximumLength = 0;
#endif
//
// named object directory
//
BaseSrvpStaticServerData->NamedObjectDirectory = UnicodeString;
BaseSrvpStaticServerData->NamedObjectDirectory.MaximumLength = UnicodeString.Length+(USHORT)sizeof(UNICODE_NULL);
p = RtlAllocateHeap( BaseSrvSharedHeap,
MAKE_SHARED_TAG( INIT_TAG ),
UnicodeString.Length + sizeof( UNICODE_NULL )
);
if ( !p ) {
return STATUS_NO_MEMORY;
}
RtlMoveMemory(p,BaseSrvpStaticServerData->NamedObjectDirectory.Buffer,BaseSrvpStaticServerData->NamedObjectDirectory.MaximumLength);
BaseSrvpStaticServerData->NamedObjectDirectory.Buffer = p;
//
// Terminal Server: App installation mode is intially turned off
//
BaseSrvpStaticServerData->fTermsrvAppInstallMode = FALSE;
BaseSrvCSDString.Buffer = &ValueBuffer[ 300 ];
BaseSrvCSDString.Length = 0;
BaseSrvCSDString.MaximumLength = 100 * sizeof( WCHAR );
Status = RtlQueryRegistryValues( RTL_REGISTRY_WINDOWS_NT,
L"",
BaseServerRegistryConfigurationTable1,
NULL,
NULL
);
if (NT_SUCCESS( Status )) {
BaseSrvpStaticServerData->CSDNumber = (USHORT)(BaseSrvCSDNumber & 0xFFFF);
BaseSrvpStaticServerData->RCNumber = (USHORT)(BaseSrvCSDNumber >> 16);
}
else {
BaseSrvpStaticServerData->CSDNumber = 0;
BaseSrvpStaticServerData->RCNumber = 0;
}
Status = RtlQueryRegistryValues( RTL_REGISTRY_WINDOWS_NT,
L"",
BaseServerRegistryConfigurationTable,
NULL,
NULL
);
if (NT_SUCCESS( Status )) {
wcsncpy( BaseSrvpStaticServerData->CSDVersion,
BaseSrvCSDString.Buffer,
BaseSrvCSDString.Length
);
BaseSrvpStaticServerData->CSDVersion[ BaseSrvCSDString.Length ] = UNICODE_NULL;
}
else {
BaseSrvpStaticServerData->CSDVersion[ 0 ] = UNICODE_NULL;
}
#if defined(_WIN64)
Status = NtQuerySystemInformation( SystemBasicInformation,
(PVOID)&SysInfo,
sizeof(SYSTEM_BASIC_INFORMATION),
NULL
);
#else
Status = NtQuerySystemInformation( SystemBasicInformation,
(PVOID)&BaseSrvpStaticServerData->SysInfo,
sizeof( BaseSrvpStaticServerData->SysInfo ),
NULL
);
#endif
if (!NT_SUCCESS( Status )) {
return( Status );
}
Status = BaseSrvInitializeIniFileMappings( BaseSrvpStaticServerData );
if ( !NT_SUCCESS(Status) ){
return Status;
}
BaseSrvpStaticServerData->DefaultSeparateVDM = FALSE;
RtlInitUnicodeString( &NameString, L"\\Registry\\Machine\\System\\CurrentControlSet\\Control\\WOW" );
InitializeObjectAttributes( &Obja,
&NameString,
OBJ_CASE_INSENSITIVE,
NULL,
NULL );
Status = NtOpenKey( &KeyHandle,
KEY_READ,
&Obja
);
if (NT_SUCCESS(Status)) {
RtlInitUnicodeString( &NameString, L"DefaultSeparateVDM" );
KeyValueInformation = (PKEY_VALUE_PARTIAL_INFORMATION)ValueBuffer;
Status = NtQueryValueKey( KeyHandle,
&NameString,
KeyValuePartialInformation,
KeyValueInformation,
sizeof( ValueBuffer ),
&ResultLength
);
if (NT_SUCCESS(Status)) {
if (KeyValueInformation->Type == REG_DWORD) {
BaseSrvpStaticServerData->DefaultSeparateVDM = *(PULONG)KeyValueInformation->Data != 0;
}
else
if (KeyValueInformation->Type == REG_SZ) {
if (!_wcsicmp( (PWSTR)KeyValueInformation->Data, L"yes" ) ||
!_wcsicmp( (PWSTR)KeyValueInformation->Data, L"1" )) {
BaseSrvpStaticServerData->DefaultSeparateVDM = TRUE;
}
}
}
NtClose( KeyHandle );
}
BaseSrvpStaticServerData->ForceDos = FALSE;
RtlInitUnicodeString( &NameString, L"\\Registry\\Machine\\System\\CurrentControlSet\\Control\\WOW" );
InitializeObjectAttributes( &Obja,
&NameString,
OBJ_CASE_INSENSITIVE,
NULL,
NULL );
Status = NtOpenKey( &KeyHandle,
KEY_READ,
&Obja
);
if (NT_SUCCESS(Status)) {
RtlInitUnicodeString( &NameString, L"ForceDos" );
KeyValueInformation = (PKEY_VALUE_PARTIAL_INFORMATION)ValueBuffer;
Status = NtQueryValueKey( KeyHandle,
&NameString,
KeyValuePartialInformation,
KeyValueInformation,
sizeof( ValueBuffer ),
&ResultLength
);
if (NT_SUCCESS(Status)) {
if (KeyValueInformation->Type == REG_DWORD) {
BaseSrvpStaticServerData->ForceDos = *(PULONG)KeyValueInformation->Data != 0;
}
else
if (KeyValueInformation->Type == REG_SZ) {
if (!_wcsicmp( (PWSTR)KeyValueInformation->Data, L"yes" ) ||
!_wcsicmp( (PWSTR)KeyValueInformation->Data, L"1" )) {
BaseSrvpStaticServerData->ForceDos = TRUE;
}
}
}
NtClose( KeyHandle );
}
#if defined(WX86) || defined(_AXP64_)
SetupWx86KeyMapping();
#endif
//
// Following code is direct from Jimk. Why is there a 1k constant
//
PrimarySecurityDescriptor = RtlAllocateHeap( BaseSrvHeap, MAKE_TAG( TMP_TAG ), 1024 );
if ( !PrimarySecurityDescriptor ) {
return STATUS_NO_MEMORY;
}
Status = RtlCreateSecurityDescriptor (
PrimarySecurityDescriptor,
SECURITY_DESCRIPTOR_REVISION1
);
if ( !NT_SUCCESS(Status) ){
return Status;
}
//
// Create an ACL that allows full access to System and partial access to world
//
Status = CreateBaseAcls( &Dacl, &RestrictedDacl );
if ( !NT_SUCCESS(Status) ){
return Status;
}
Status = RtlSetDaclSecurityDescriptor (
PrimarySecurityDescriptor,
TRUE, //DaclPresent,
Dacl, //Dacl
FALSE //DaclDefaulted OPTIONAL
);
if ( !NT_SUCCESS(Status) ){
return Status;
}
InitializeObjectAttributes( &Obja,
&UnicodeString,
attributes,
NULL,
PrimarySecurityDescriptor
);
Status = NtCreateDirectoryObject( &BaseSrvNamedObjectDirectory,
DIRECTORY_ALL_ACCESS,
&Obja
);
if ( !NT_SUCCESS(Status) ){
return Status;
}
//
// Check if LUID device maps are enabled
//
Status = NtQueryInformationProcess( NtCurrentProcess(),
ProcessLUIDDeviceMapsEnabled,
&LUIDDeviceMapsEnabled,
sizeof(LUIDDeviceMapsEnabled),
NULL
);
if (NT_SUCCESS(Status)) {
BaseSrvpStaticServerData->LUIDDeviceMapsEnabled = (LUIDDeviceMapsEnabled != 0);
}
else {
BaseSrvpStaticServerData->LUIDDeviceMapsEnabled = FALSE;
}
//
// If LUID device maps are enabled,
// then initialize the critical section for broadcasting a system message
// about a drive letter change
//
if( BaseSrvpStaticServerData->LUIDDeviceMapsEnabled == TRUE ) {
Status = RtlInitializeCriticalSectionAndSpinCount( &BaseSrvDDDBSMCritSec,
PREALLOCATE_EVENT_MASK );
if (!NT_SUCCESS (Status)) {
return Status;
}
}
//
// Create a symbolic link Global pointing to the Global BaseNamedObjects directory
// This symbolic link will be used by proccesses that want to e.g. access a global
// event instead of the session specific. This will be done by prepending
// "Global\" to the object name.
//
RtlInitUnicodeString( &UnicodeString, GLOBAL_SYM_LINK );
RtlInitUnicodeString( &LinkTarget, L"\\BaseNamedObjects" );
InitializeObjectAttributes( &Obja,
&UnicodeString,
attributes,
(HANDLE)BaseSrvNamedObjectDirectory,
PrimarySecurityDescriptor );
Status = NtCreateSymbolicLinkObject( &SymbolicLinkHandle,
SYMBOLIC_LINK_ALL_ACCESS,
&Obja,
&LinkTarget );
if (NT_SUCCESS( Status ) && (SessionId == 0)) {
NtClose( SymbolicLinkHandle );
}
//
// Create a symbolic link Local pointing to the Current Sessions BaseNamedObjects directory
// This symbolic link will be used for backward compatibility with Hydra 4
// naming conventions
RtlInitUnicodeString( &UnicodeString, LOCAL_SYM_LINK );
RtlInitUnicodeString( &LinkTarget, szObjectDirectory );
InitializeObjectAttributes( &Obja,
&UnicodeString,
attributes,
(HANDLE)BaseSrvNamedObjectDirectory,
PrimarySecurityDescriptor );
Status = NtCreateSymbolicLinkObject( &SymbolicLinkHandle,
SYMBOLIC_LINK_ALL_ACCESS,
&Obja,
&LinkTarget );
if (NT_SUCCESS( Status ) && (SessionId == 0)) {
NtClose( SymbolicLinkHandle );
}
//
// Create a symbolic link Session pointing
// to the \Sessions\BNOLINKS directory
// This symbolic link will be used by proccesses that want to e.g. access a
// event in another session. This will be done by using the following
// naming convention : Session\\<sessionid>\\ObjectName
//
RtlInitUnicodeString( &UnicodeString, SESSION_SYM_LINK );
RtlInitUnicodeString( &LinkTarget, L"\\Sessions\\BNOLINKS" );
InitializeObjectAttributes( &Obja,
&UnicodeString,
attributes,
(HANDLE)BaseSrvNamedObjectDirectory,
PrimarySecurityDescriptor );
Status = NtCreateSymbolicLinkObject( &SymbolicLinkHandle,
SYMBOLIC_LINK_ALL_ACCESS,
&Obja,
&LinkTarget );
if (NT_SUCCESS( Status ) && (SessionId == 0)) {
NtClose( SymbolicLinkHandle );
}
RtlInitUnicodeString( &UnicodeString, L"Restricted" );
Status = RtlSetDaclSecurityDescriptor (
PrimarySecurityDescriptor,
TRUE, //DaclPresent,
RestrictedDacl, //Dacl
FALSE //DaclDefaulted OPTIONAL
);
if ( !NT_SUCCESS(Status) ){
return Status;
}
InitializeObjectAttributes( &Obja,
&UnicodeString,
attributes,
BaseSrvNamedObjectDirectory,
PrimarySecurityDescriptor
);
Status = NtCreateDirectoryObject( &BaseSrvRestrictedObjectDirectory,
DIRECTORY_ALL_ACCESS,
&Obja
);
if ( !NT_SUCCESS(Status) ){
return Status;
}
//
// Initialize the Sxs support
//
Status = BaseSrvSxsInit(BaseSrvpStaticServerData);
if (!NT_SUCCESS(Status)) {
return Status;
}
RtlFreeHeap( BaseSrvHeap, 0, Dacl );
RtlFreeHeap( BaseSrvHeap, 0, RestrictedDacl );
RtlFreeHeap( BaseSrvHeap, 0,PrimarySecurityDescriptor );
BaseSrvVDMInit();
//
// Initialize the shared heap for the NLS information.
//
BaseSrvNLSInit(BaseSrvpStaticServerData);
Status = STATUS_SUCCESS;
goto Exit;
OutOfMemory:
Status = STATUS_NO_MEMORY;
goto Exit;
Exit:
return( Status );
}
NTSTATUS
BaseClientConnectRoutine(
IN PCSR_PROCESS Process,
IN OUT PVOID ConnectionInfo,
IN OUT PULONG ConnectionInfoLength
)
{
if (*ConnectionInfoLength != sizeof(HANDLE)) {
return STATUS_INVALID_PARAMETER;
}
return ( BaseSrvNlsConnect( Process,
ConnectionInfo,
ConnectionInfoLength ) );
}
VOID
BaseClientDisconnectRoutine(
IN PCSR_PROCESS Process
)
{
BaseSrvCleanupVDMResources (Process);
}
ULONG
BaseSrvDefineDosDevice(
IN OUT PCSR_API_MSG m,
IN OUT PCSR_REPLY_STATUS ReplyStatus
)
{
NTSTATUS Status;
PBASE_DEFINEDOSDEVICE_MSG a = (PBASE_DEFINEDOSDEVICE_MSG)&m->u.ApiMessageData;
UNICODE_STRING LinkName;
UNICODE_STRING LinkValue;
HANDLE LinkHandle;
OBJECT_ATTRIBUTES ObjectAttributes;
PWSTR Buffer, s, Src, Dst, pchValue;
ULONG cchBuffer, cch;
ULONG cchName, cchValue, cchSrc, cchSrcStr, cchDst;
BOOLEAN QueryNeeded, MatchFound, RevertToSelfNeeded, DeleteRequest;
ULONG ReturnedLength;
SID_IDENTIFIER_AUTHORITY WorldSidAuthority = SECURITY_WORLD_SID_AUTHORITY;
SID_IDENTIFIER_AUTHORITY NtAuthority = SECURITY_NT_AUTHORITY;
PSID RestrictedSid;
PSID WorldSid;
SECURITY_DESCRIPTOR SecurityDescriptor;
CHAR Acl[256]; // 256 is more than big enough
ULONG AclLength=256;
ACCESS_MASK WorldAccess;
ULONG lastIndex;
DWORD iDrive;
LUID callerLuid;
BOOLEAN bsmForLuid = FALSE;
BOOLEAN haveLuid = FALSE;
BOOLEAN bGlobalSymLink = FALSE;
if (!CsrValidateMessageBuffer(m, &a->DeviceName.Buffer, a->DeviceName.Length, sizeof(BYTE)) ||
(a->DeviceName.Length&(sizeof (WCHAR) - 1))) {
return STATUS_INVALID_PARAMETER;
}
if (a->TargetPath.Length == 0) {
cchBuffer = 0;
}
else {
cchBuffer = sizeof (WCHAR);
}
if (!CsrValidateMessageBuffer(m, &a->TargetPath.Buffer, (a->TargetPath.Length + cchBuffer), sizeof(BYTE)) ||
(a->TargetPath.Length&(sizeof (WCHAR) - 1))) {
return STATUS_INVALID_PARAMETER;
}
cchBuffer = 4096;
Buffer = RtlAllocateHeap( BaseSrvHeap,
MAKE_TAG( TMP_TAG ),
cchBuffer * sizeof( WCHAR )
);
if (Buffer == NULL) {
return (ULONG)STATUS_NO_MEMORY;
}
Status = RtlEnterCriticalSection( &BaseSrvDosDeviceCritSec );
if (!NT_SUCCESS( Status )) {
RtlFreeHeap( BaseSrvHeap, 0, Buffer );
return (ULONG)Status;
}
if (a->Flags & DDD_REMOVE_DEFINITION) {
DeleteRequest = TRUE;
}
else {
DeleteRequest = FALSE;
}
LinkHandle = NULL;
try {
//
// Determine if need to broadcast the change to the system, otherwise
// the client portion of DefineDosDevice will broadcast the change
// if needed.
//
// Broadcast to the system when all the conditions are met:
// - LUID device maps are enabled
// - Successfully completed operations of this BaseSrvDefineDosDevice
// - caller did not specify the DDD_NO_BROADCAST_SYSTEM flag
// - symbolic link's name is the "<drive letter>:" format
//
// Broadcasting this change from the server because
// we need to broadcast as Local_System in order to broadcast this
// message to all desktops that have windows with this LUID.
// Effectively, we are broadcasting to all the windows with this LUID.
//
if ((BaseSrvpStaticServerData->LUIDDeviceMapsEnabled == TRUE) &&
(!(a->Flags & DDD_NO_BROADCAST_SYSTEM)) &&
((a->DeviceName).Buffer != NULL) &&
((a->DeviceName).Length == (2 * sizeof( WCHAR ))) &&
((a->DeviceName).Buffer[ 1 ] == L':')) {
WCHAR DriveLetter = a->DeviceName.Buffer[ 0 ];
if ( ((DriveLetter - L'a') < 26) &&
((DriveLetter - L'a') >= 0) ) {
DriveLetter = RtlUpcaseUnicodeChar( DriveLetter );
}
iDrive = DriveLetter - L'A';
if (iDrive < 26) {
bsmForLuid = TRUE;
}
}
if ((a->Flags & DDD_LUID_BROADCAST_DRIVE) &&
(bsmForLuid == FALSE)) {
Status = STATUS_INVALID_PARAMETER;
leave;
}
//
// Each user LUID has a DeviceMap, so we put the link in that directory,
// instead of in the global \??.
//
// We get the LUID device map by impersonating the user
// and requesting \??\ in the beginning of the symbolic link name
// Then, the Object Manager will get the correct device map
// for this user (based on LUID)
//
s = Buffer;
cch = cchBuffer;
cchName = _snwprintf( s,
cch,
L"\\??\\%wZ",
&a->DeviceName
);
s += cchName + 1;
cch -= (cchName + 1);
RtlInitUnicodeString( &LinkName, Buffer );
InitializeObjectAttributes( &ObjectAttributes,
&LinkName,
OBJ_CASE_INSENSITIVE,
(HANDLE) NULL,
(PSECURITY_DESCRIPTOR)NULL
);
QueryNeeded = TRUE;
RevertToSelfNeeded = CsrImpersonateClient( NULL ); // This stacks client contexts
if( RevertToSelfNeeded == FALSE ) {
Status = STATUS_BAD_IMPERSONATION_LEVEL;
leave;
}
if (bsmForLuid == TRUE) {
Status = GetCallerLuid( &(callerLuid) );
if (NT_SUCCESS( Status )) {
//
// obtained the caller's LUID
//
haveLuid = TRUE;
}
}
Status = NtOpenSymbolicLinkObject( &LinkHandle,
SYMBOLIC_LINK_QUERY | DELETE,
&ObjectAttributes
);
if (RevertToSelfNeeded) {
CsrRevertToSelf(); // This unstacks client contexts
}
//
// With LUID device maps Enabled and DDD_LUID_BROADCAST_DRIVE,
// we capture all the information need to perform the broadcast:
// Drive Letter, action, and the caller's LUID.
// if the user had specified a delete action,
// then the drive letter should not exist (status ==
// STATUS_OBJECT_NAME_NOT_FOUND)
// else the drive letter should exist (status == STATUS_SUCCESS)
//
// if DDD_LUID_BROADCAST_DRIVE is set, we always leave this 'try'
// block because the 'finally' block will perform the broadcast
// when (Status == STATUS_SUCCESS).
//
if (a->Flags & DDD_LUID_BROADCAST_DRIVE) {
if (!NT_SUCCESS( Status )) {
LinkHandle = NULL;
}
if (DeleteRequest && (Status == STATUS_OBJECT_NAME_NOT_FOUND)) {
Status = STATUS_SUCCESS;
}
leave;
}
if (Status == STATUS_OBJECT_NAME_NOT_FOUND) {
LinkHandle = NULL;
if (DeleteRequest) {
if (a->TargetPath.Length == 0) {
Status = STATUS_SUCCESS;
}
leave;
}
QueryNeeded = FALSE;
Status = STATUS_SUCCESS;
}
else {
if (!NT_SUCCESS( Status )) {
LinkHandle = NULL;
leave;
}
else {
//
// Symbolic link already exists
//
// With device maps per LUID, we must determine that the
// symlink does not exist in the global device map because
// DefineDosDevice allow the caller to perform the
// mapping operations on a symlink (push/pop/delete)
// mapping for a particular symlink.
//
// The mapping capability is supported by writing
// all mappings (target(s) of a symlink) into the symlink's
// value, where the mappings names are separate by a NULL
// char. The symlink's list of mappings is terminated by
// two NULL characters.
//
// The first mapping, first target name in the symlink's
// value, is the current (top) mapping for the system because
// the system only reads the symlink's value up to the
// first NULL char.
//
// The mapping code works by opening the existing symlink,
// reading the symlink's entire value (name of the target(s)),
// destroy the old symlink, manipulate the symlink's value
// for the mapping operation, and finally create a
// brand-new symlink with the new symlink's value.
//
// If we don't check that the symlink exists in the global
// device map, we might delete a global symlink and
// and recreate the symlink in a user's LUID device map.
// Thus, the new symlink will no longer reside in the global
// map, i.e. other users cannot access the symlink.
//
if( BaseSrvpStaticServerData->LUIDDeviceMapsEnabled == TRUE ) {
Status = IsGlobalSymbolicLink( LinkHandle,
&bGlobalSymLink
);
if( !NT_SUCCESS( Status )) {
leave;
}
if( bGlobalSymLink == TRUE ) {
s = Buffer;
cch = cchBuffer;
cchName = _snwprintf( s,
cch,
L"\\GLOBAL??\\%wZ",
&a->DeviceName
);
s += cchName + 1;
cch -= (cchName + 1);
LinkName.Length = (USHORT)(cchName * sizeof( WCHAR ));
LinkName.MaximumLength = (USHORT)(LinkName.Length + sizeof(UNICODE_NULL));
}
}
}
}
if (a->TargetPath.Length != 0) {
Src = a->TargetPath.Buffer;
Src[a->TargetPath.Length/sizeof (Src[0])] = L'\0';
cchValue = wcslen( Src );
if ((cchValue + 1) >= cch) {
Status = STATUS_TOO_MANY_NAMES;
leave;
}
RtlMoveMemory( s, Src, (cchValue + 1) * sizeof( WCHAR ) );
pchValue = s;
s += cchValue + 1;
cch -= (cchValue + 1);
}
else {
pchValue = NULL;
cchValue = 0;
}
if (QueryNeeded) {
LinkValue.Length = 0;
LinkValue.MaximumLength = (USHORT)(cch * sizeof( WCHAR ));
LinkValue.Buffer = s;
ReturnedLength = 0;
Status = NtQuerySymbolicLinkObject( LinkHandle,
&LinkValue,
&ReturnedLength
);
if (ReturnedLength == (ULONG)LinkValue.MaximumLength) {
Status = STATUS_BUFFER_OVERFLOW;
}
if (!NT_SUCCESS( Status )) {
leave;
}
lastIndex = ReturnedLength / sizeof( WCHAR );
//
// check if the returned string already has the extra NULL at the end
//
if( (lastIndex >= 2) &&
(s[ lastIndex - 2 ] == UNICODE_NULL) &&
(s[ lastIndex - 1 ] == UNICODE_NULL) ) {
LinkValue.MaximumLength = (USHORT)ReturnedLength;
}
else {
//
// add the extra NULL for the DeleteRequest search later
//
s[ lastIndex ] = UNICODE_NULL;
LinkValue.MaximumLength = (USHORT)(ReturnedLength + sizeof( UNICODE_NULL ));
}
}
else {
if (DeleteRequest) {
RtlInitUnicodeString( &LinkValue, NULL );
}
else {
RtlInitUnicodeString( &LinkValue, s - (cchValue + 1) );
}
}
if (LinkHandle != NULL) {
Status = NtMakeTemporaryObject( LinkHandle );
NtClose( LinkHandle );
LinkHandle = NULL;
}
if (!NT_SUCCESS( Status )) {
leave;
}
if (DeleteRequest) {
Src = Dst = LinkValue.Buffer;
cchSrc = LinkValue.MaximumLength / sizeof( WCHAR );
cchDst = 0;
MatchFound = FALSE;
while (*Src) {
cchSrcStr = 0;
s = Src;
while (*Src++) {
cchSrcStr++;
}
if ( (!MatchFound) &&
( (a->Flags & DDD_EXACT_MATCH_ON_REMOVE &&
cchValue == cchSrcStr &&
!_wcsicmp( s, pchValue )
) ||
( !(a->Flags & DDD_EXACT_MATCH_ON_REMOVE) &&
(cchValue == 0 || !_wcsnicmp( s, pchValue, cchValue ))
)
)
) {
MatchFound = TRUE;
}
else {
if (s != Dst) {
RtlMoveMemory( Dst, s, (cchSrcStr + 1) * sizeof( WCHAR ) );
}
Dst += cchSrcStr + 1;
}
}
*Dst++ = UNICODE_NULL;
LinkValue.Length = wcslen( LinkValue.Buffer ) * sizeof( UNICODE_NULL );
if (LinkValue.Length != 0) {
LinkValue.MaximumLength = (USHORT)((PCHAR)Dst - (PCHAR)LinkValue.Buffer);
}
}
else
if (QueryNeeded) {
LinkValue.Buffer -= (cchValue + 1);
LinkValue.Length = (USHORT)(cchValue * sizeof( WCHAR ));
LinkValue.MaximumLength += LinkValue.Length + sizeof( UNICODE_NULL );
}
//
// Create a new value for the link.
//
if (LinkValue.Length != 0) {
//
// Create the new symbolic link object with a security descriptor
// that grants world SYMBOLIC_LINK_QUERY access.
//
Status = RtlAllocateAndInitializeSid( &WorldSidAuthority,
1,
SECURITY_WORLD_RID,
0, 0, 0, 0, 0, 0, 0,
&WorldSid
);
if (!NT_SUCCESS( Status )) {
leave;
}
Status = RtlAllocateAndInitializeSid( &NtAuthority,
1,
SECURITY_RESTRICTED_CODE_RID,
0, 0, 0, 0, 0, 0, 0,
&RestrictedSid
);
if (!NT_SUCCESS( Status )) {
RtlFreeSid( WorldSid );
leave;
}
Status = RtlCreateSecurityDescriptor( &SecurityDescriptor, SECURITY_DESCRIPTOR_REVISION );
ASSERT(NT_SUCCESS(Status));
Status = RtlCreateAcl( (PACL)Acl,
AclLength,
ACL_REVISION2
);
ASSERT(NT_SUCCESS(Status));
if( (SessionId != 0) && (ProtectionMode & 0x00000003) ) {
// Do not allow world cross session delete in WTS
WorldAccess = SYMBOLIC_LINK_QUERY;
}
else {
WorldAccess = SYMBOLIC_LINK_QUERY | DELETE;
}
Status = RtlAddAccessAllowedAce( (PACL)Acl,
ACL_REVISION2,
WorldAccess,
WorldSid
);
ASSERT(NT_SUCCESS(Status));
Status = RtlAddAccessAllowedAce( (PACL)Acl,
ACL_REVISION2,
WorldAccess,
RestrictedSid
);
ASSERT(NT_SUCCESS(Status));
//
// Sids have been copied into the ACL
//
RtlFreeSid( WorldSid );
RtlFreeSid( RestrictedSid );
Status = RtlSetDaclSecurityDescriptor ( &SecurityDescriptor,
TRUE,
(PACL)Acl,
TRUE // Don't over-ride inherited protection
);
ASSERT(NT_SUCCESS(Status));
ObjectAttributes.SecurityDescriptor = &SecurityDescriptor;
//
// Since we impersonate the user to create in the
// correct directory, we cannot request the creation
// of a permanent object. By default, only Local_System
// can request creation of a permanant object.
//
// However, we use a new API, NtMakePermanentObject that
// only Local_System can call to make the object
// permanant after creation
//
if ( BaseSrvpStaticServerData->LUIDDeviceMapsEnabled == TRUE ) {
if ( bGlobalSymLink == FALSE ) {
//
// Do not impersonate if global symbolic link is being
// created, because administrators do not have permission
// to create in the global device map if we impersonate
//
// Administrators have inherited permissions on the
// existing global symbolic links, so we may recreate
// the existing global link that we opened and destroyed.
//
// We had impersonated the caller when opening the symbolic
// link, so we know that the caller has permissions for the
// link that we are creating.
//
//
// Impersonate Client when creating the Symbolic Link
// This impersonation is needed to ensure that the symlink
// is created in the correct directory
//
RevertToSelfNeeded = CsrImpersonateClient( NULL ); // This stacks client contexts
if( RevertToSelfNeeded == FALSE ) {
Status = STATUS_BAD_IMPERSONATION_LEVEL;
leave;
}
}
//
// if a global symlink is being create, don't impersonate &
// don't use the old style of using the OBJ_PERMANENT flag
// directly
//
}
else {
//
// Old style, disabled when separate dev maps are enabled
//
ObjectAttributes.Attributes |= OBJ_PERMANENT;
}
Status = NtCreateSymbolicLinkObject( &LinkHandle,
SYMBOLIC_LINK_ALL_ACCESS,
&ObjectAttributes,
&LinkValue
);
if ((BaseSrvpStaticServerData->LUIDDeviceMapsEnabled == TRUE) &&
(bGlobalSymLink == FALSE)) {
if (RevertToSelfNeeded) {
CsrRevertToSelf(); // This unstacks client contexts
}
}
if (NT_SUCCESS( Status )) {
if ( BaseSrvpStaticServerData->LUIDDeviceMapsEnabled == TRUE ) {
//
// add the OBJ_PERMANENT attribute to the object
// so that the object remains in the namespace
// of the system
//
Status = NtMakePermanentObject( LinkHandle );
}
NtClose( LinkHandle );
if (DeleteRequest && !MatchFound) {
Status = STATUS_OBJECT_NAME_NOT_FOUND;
}
}
LinkHandle = NULL;
}
}
finally {
if (LinkHandle != NULL) {
NtClose( LinkHandle );
}
RtlFreeHeap( BaseSrvHeap, 0, Buffer );
//
// Determine if need to broadcast change to the system, otherwise
// the client portion of DefineDosDevice will broadcast the change
// if needed.
//
// Broadcast to the system when all the conditions are met:
// - LUID device maps are enabled
// - Successfully completed operations of this BaseSrvDefineDosDevice
// - caller did not specify the DDD_NO_BROADCAST_SYSTEM flag
// - symbolic link's name is the "<drive letter>:" format
//
// Can also broadcast when DDD_LUID_BROADCAST_DRIVE is set,
// and drive exists (when not a DeleteRequest) or
// drive does not exist (when a DeleteRequest)
//
// Broadcasting this change from the server because
// we need to broadcast as Local_System in order to broadcast this
// message to all desktops that have windows with this LUID.
// Effectively, we are broadcasting to all the windows with this LUID.
//
if ((bsmForLuid == TRUE) &&
(Status == STATUS_SUCCESS) &&
(haveLuid == TRUE)) {
LUID SystemLuid = SYSTEM_LUID;
if (bGlobalSymLink == TRUE) {
RtlCopyLuid( &callerLuid, &SystemLuid);
}
AddBSMRequest( iDrive,
DeleteRequest,
&callerLuid );
//
// If the user has removed a drive letter from his LUID DosDevices
// and now sees a global drive letter, then generate a broadcast
// about the arrival of the drive letter to the user's view.
//
if ((DeleteRequest == TRUE) &&
(!RtlEqualLuid( &callerLuid, &SystemLuid )) &&
CheckForGlobalSymLink( &(a->DeviceName) )) {
AddBSMRequest( iDrive,
FALSE,
&callerLuid );
}
}
RtlLeaveCriticalSection( &BaseSrvDosDeviceCritSec );
}
return (ULONG)Status;
ReplyStatus; // get rid of unreferenced parameter warning message
}
NTSTATUS
CreateBaseAcls(
PACL *Dacl,
PACL *RestrictedDacl
)
/*++
Routine Description:
Creates the ACL for the BaseNamedObjects directory.
Arguments:
Dacl - Supplies a pointer to a PDACL that will be filled in with
the resultant ACL (allocated out of the process heap). The caller
is responsible for freeing this memory.
Return Value:
STATUS_NO_MEMORY or Success
--*/
{
PSID LocalSystemSid;
PSID WorldSid;
PSID RestrictedSid;
SID_IDENTIFIER_AUTHORITY NtAuthority = SECURITY_NT_AUTHORITY;
SID_IDENTIFIER_AUTHORITY WorldAuthority = SECURITY_WORLD_SID_AUTHORITY;
NTSTATUS Status;
ACCESS_MASK WorldAccess;
ACCESS_MASK SystemAccess;
ACCESS_MASK RestrictedAccess;
ULONG AclLength;
// Get the Protection mode from Session Manager\ProtectionMode
HANDLE KeyHandle;
ULONG ResultLength;
WCHAR ValueBuffer[ 32 ];
UNICODE_STRING NameString;
OBJECT_ATTRIBUTES ObjectAttributes;
PKEY_VALUE_PARTIAL_INFORMATION KeyValueInformation;
RtlInitUnicodeString( &NameString, L"\\Registry\\Machine\\System\\CurrentControlSet\\Control\\Session Manager" );
InitializeObjectAttributes(
&ObjectAttributes,
&NameString,
OBJ_CASE_INSENSITIVE,
NULL,
NULL
);
Status = NtOpenKey(
&KeyHandle,
KEY_READ,
&ObjectAttributes
);
if (NT_SUCCESS(Status)) {
RtlInitUnicodeString( &NameString, L"ProtectionMode" );
KeyValueInformation = (PKEY_VALUE_PARTIAL_INFORMATION)ValueBuffer;
Status = NtQueryValueKey(
KeyHandle,
&NameString,
KeyValuePartialInformation,
KeyValueInformation,
sizeof( ValueBuffer ),
&ResultLength
);
if (NT_SUCCESS(Status)) {
if (KeyValueInformation->Type == REG_DWORD &&
*(PULONG)KeyValueInformation->Data) {
ProtectionMode = *(PULONG)KeyValueInformation->Data;
}
}
NtClose( KeyHandle );
}
Status = RtlAllocateAndInitializeSid(
&NtAuthority,
1,
SECURITY_LOCAL_SYSTEM_RID,
0, 0, 0, 0, 0, 0, 0,
&LocalSystemSid
);
if (!NT_SUCCESS( Status )) {
return( Status );
}
Status = RtlAllocateAndInitializeSid(
&WorldAuthority,
1,
SECURITY_WORLD_RID,
0, 0, 0, 0, 0, 0, 0,
&WorldSid
);
if (!NT_SUCCESS( Status )) {
return( Status );
}
Status = RtlAllocateAndInitializeSid(
&NtAuthority,
1,
SECURITY_RESTRICTED_CODE_RID,
0, 0, 0, 0, 0, 0, 0,
&RestrictedSid
);
if (!NT_SUCCESS( Status )) {
return( Status );
}
WorldAccess = DIRECTORY_ALL_ACCESS & ~(WRITE_OWNER | WRITE_DAC | DELETE );
RestrictedAccess = DIRECTORY_TRAVERSE;
SystemAccess = DIRECTORY_ALL_ACCESS;
AclLength = sizeof( ACL ) +
3 * sizeof( ACCESS_ALLOWED_ACE ) +
RtlLengthSid( LocalSystemSid ) +
RtlLengthSid( RestrictedSid ) +
RtlLengthSid( WorldSid );
*Dacl = RtlAllocateHeap( BaseSrvHeap, MAKE_TAG( TMP_TAG ), AclLength );
if (*Dacl == NULL) {
return( STATUS_NO_MEMORY );
}
Status = RtlCreateAcl (*Dacl, AclLength, ACL_REVISION2 );
if (!NT_SUCCESS( Status )) {
return( Status );
}
Status = RtlAddAccessAllowedAce ( *Dacl, ACL_REVISION2, WorldAccess, WorldSid );
if (NT_SUCCESS( Status )) {
Status = RtlAddAccessAllowedAce ( *Dacl, ACL_REVISION2, SystemAccess, LocalSystemSid );
}
if (NT_SUCCESS( Status )) {
Status = RtlAddAccessAllowedAce ( *Dacl, ACL_REVISION2, RestrictedAccess, RestrictedSid );
}
// now create the DACL for restricted use
if( (SessionId != 0) && (ProtectionMode & 0x00000003) ) {
// Terminal server does not allow world create in other sessions
RestrictedAccess = DIRECTORY_ALL_ACCESS & ~(WRITE_OWNER | WRITE_DAC | DELETE | DIRECTORY_CREATE_OBJECT | DIRECTORY_CREATE_SUBDIRECTORY);
}
else {
RestrictedAccess = DIRECTORY_ALL_ACCESS & ~(WRITE_OWNER | WRITE_DAC | DELETE );
}
AclLength = sizeof( ACL ) +
3 * sizeof( ACCESS_ALLOWED_ACE ) +
RtlLengthSid( LocalSystemSid ) +
RtlLengthSid( RestrictedSid ) +
RtlLengthSid( WorldSid );
*RestrictedDacl = RtlAllocateHeap( BaseSrvHeap, MAKE_TAG( TMP_TAG ), AclLength );
if (*RestrictedDacl == NULL) {
return( STATUS_NO_MEMORY );
}
Status = RtlCreateAcl (*RestrictedDacl, AclLength, ACL_REVISION2 );
if (!NT_SUCCESS( Status )) {
return( Status );
}
Status = RtlAddAccessAllowedAce ( *RestrictedDacl, ACL_REVISION2, WorldAccess, WorldSid );
if (NT_SUCCESS( Status )) {
Status = RtlAddAccessAllowedAce ( *RestrictedDacl, ACL_REVISION2, SystemAccess, LocalSystemSid );
}
if (NT_SUCCESS( Status )) {
Status = RtlAddAccessAllowedAce ( *RestrictedDacl, ACL_REVISION2, RestrictedAccess, RestrictedSid );
}
//
// These have been copied in, free them.
//
RtlFreeHeap( BaseSrvHeap, 0, LocalSystemSid );
RtlFreeHeap( BaseSrvHeap, 0, RestrictedSid );
RtlFreeHeap( BaseSrvHeap, 0, WorldSid );
return( Status );
}
ULONG
BaseSrvSetTermsrvClientTimeZone(
IN PCSR_API_MSG m,
IN OUT PCSR_REPLY_STATUS ReplyStatus
)
/*++
Routine Description:
Sets BaseSrvpStaticServerData->tziTermsrvClientTimeZone
according to received information
Arguments:
IN PCSR_API_MSG m - part of timezone information.
we have to cut it ito two pieces because of
message size restrictions (100 bytes).
IN OUT PCSR_REPLY_STATUS ReplyStatus - not used.
Return Value:
always STATUS_SUCCESS
--*/
{
PBASE_SET_TERMSRVCLIENTTIMEZONE b = (PBASE_SET_TERMSRVCLIENTTIMEZONE)&m->u.ApiMessageData;
if(b->fFirstChunk) {
BaseSrvpStaticServerData->tziTermsrvClientTimeZone.Bias=b->Bias;
RtlMoveMemory(&(BaseSrvpStaticServerData->tziTermsrvClientTimeZone.StandardName),
&(b->Name),sizeof(b->Name));
BaseSrvpStaticServerData->tziTermsrvClientTimeZone.StandardDate=b->Date;
BaseSrvpStaticServerData->tziTermsrvClientTimeZone.StandardBias=b->Bias1;
//only half of data received
//see comment below
BaseSrvpStaticServerData->TermsrvClientTimeZoneId=TIME_ZONE_ID_INVALID;
} else {
RtlMoveMemory(&(BaseSrvpStaticServerData->tziTermsrvClientTimeZone.DaylightName),
&b->Name,sizeof(b->Name));
BaseSrvpStaticServerData->tziTermsrvClientTimeZone.DaylightDate=b->Date;
BaseSrvpStaticServerData->tziTermsrvClientTimeZone.DaylightBias=b->Bias1;
BaseSrvpStaticServerData->ktTermsrvClientBias=b->RealBias;
//Set TimeZoneId only if last chunk of data received
//it indicates whether we have correct information in
//global data or not.
BaseSrvpStaticServerData->TermsrvClientTimeZoneId=b->TimeZoneId;
}
return( STATUS_SUCCESS );
}
NTSTATUS
IsGlobalSymbolicLink(
IN HANDLE hSymLink,
OUT PBOOLEAN pbGlobalSymLink)
/*++
Routine Description:
Check if the Symbolic Link exists in the global device map
Arguments:
hSymLink [IN] - handle to the symbolic link for verification
pbGlobalSymLink [OUT] - result of "Is symbolic link global?"
TRUE - symbolic link is global
FALSE - symbolic link is not global
Return Value:
NTSTATUS code
STATUS_SUCCESS - operations successful, did not encounter any errors,
the result in pbGlobalSymlink is only valid for this
status code
STATUS_INVALID_PARAMETER - pbGlobalSymLink or hSymLink is NULL
STATUS_NO_MEMORY - could not allocate memory to read the symbolic link's
name
STATUS_INFO_LENGTH_MISMATCH - did not allocate enough memory for the
symbolic link's name
STATUS_UNSUCCESSFUL - an unexpected error encountered
--*/
{
UNICODE_STRING ObjectName;
UNICODE_STRING GlobalDeviceMapPrefix;
PWSTR NameBuffer = NULL;
ULONG ReturnedLength;
NTSTATUS Status = STATUS_UNSUCCESSFUL;
if( ( pbGlobalSymLink == NULL ) || ( hSymLink == NULL ) ) {
return( STATUS_INVALID_PARAMETER );
}
try {
ObjectName.Length = 0;
ObjectName.MaximumLength = 0;
ObjectName.Buffer = NULL;
ReturnedLength = 0;
//
// Determine the length of the symbolic link's name
//
Status = NtQueryObject( hSymLink,
ObjectNameInformation,
(PVOID) &ObjectName,
0,
&ReturnedLength
);
if( !NT_SUCCESS( Status ) && (Status != STATUS_INFO_LENGTH_MISMATCH) ) {
leave;
}
//
// allocate memory for the symbolic link's name
//
NameBuffer = RtlAllocateHeap( BaseSrvHeap,
MAKE_TAG( TMP_TAG ),
ReturnedLength
);
if( NameBuffer == NULL ) {
Status = STATUS_NO_MEMORY;
leave;
}
//
// get the full name of the symbolic link
//
Status = NtQueryObject( hSymLink,
ObjectNameInformation,
NameBuffer,
ReturnedLength,
&ReturnedLength
);
if( !NT_SUCCESS( Status )) {
leave;
}
RtlInitUnicodeString ( &GlobalDeviceMapPrefix, L"\\GLOBAL??\\" );
//
// Check if the symlink exists in the global device map
//
*pbGlobalSymLink = RtlPrefixUnicodeString( &GlobalDeviceMapPrefix,
(PUNICODE_STRING)NameBuffer,
FALSE);
Status = STATUS_SUCCESS;
}
finally {
if( NameBuffer != NULL ) {
RtlFreeHeap( BaseSrvHeap, 0, NameBuffer );
NameBuffer = NULL;
}
}
return ( Status );
}
NTSTATUS
GetCallerLuid (
PLUID pLuid
)
/*++
Routine Description:
Retrieves the caller's LUID from the effective access_token
The effective access_token will be the thread's token if
impersonating, else the process' token
Arguments:
pLuid [IN] - pointer to a buffer to hold the LUID
Return Value:
STATUS_SUCCESS - operations successful, did not encounter any errors
STATUS_INVALID_PARAMETER - pLuid is NULL
STATUS_NO_TOKEN - could not find a token for the user
appropriate NTSTATUS code - an unexpected error encountered
--*/
{
TOKEN_STATISTICS TokenStats;
HANDLE hToken = NULL;
DWORD dwLength = 0;
NTSTATUS Status;
if( (pLuid == NULL) || (sizeof(*pLuid) != sizeof(LUID)) ) {
return( STATUS_INVALID_PARAMETER );
}
//
// Get the access token
// Try to get the impersonation token, else the primary token
//
Status = NtOpenThreadToken( NtCurrentThread(), TOKEN_READ, FALSE, &hToken );
if( Status == STATUS_NO_TOKEN ) {
Status = NtOpenProcessToken( NtCurrentProcess(), TOKEN_READ, &hToken );
}
if( NT_SUCCESS(Status) ) {
//
// Query the LUID for the user.
//
Status = NtQueryInformationToken( hToken,
TokenStatistics,
&TokenStats,
sizeof(TokenStats),
&dwLength );
if( NT_SUCCESS(Status) ) {
RtlCopyLuid( pLuid, &(TokenStats.AuthenticationId) );
}
}
if( hToken != NULL ) {
NtClose( hToken );
}
return( Status );
}
NTSTATUS
BroadcastDriveLetterChange(
IN DWORD iDrive,
IN BOOLEAN DeleteRequest,
IN PLUID pLuid
)
/*++
Routine Description:
broadcasting the drive letter change to all the windows with this LUID
Use BroadcastSystemMessageExW and the flags BSF_LUID & BSM_ALLDESKTOPS
to send the message
To broadcast with the BSM_ALLDESKTOPS flag, we need to call
BroadcastSystemMessageExW as Local_System. So this function should be
called as Local_System.
Arguments:
iDrive [IN] - drive letter that is changing, in the form of a number
relative to 'A', used to create a bit mask
DeleteRequest [IN] - denotes whether this change is a delete
TRUE - drive letter was deleted
FALSE - drive letter was added
pLuid [IN] - caller's LUID
Return Value:
STATUS_SUCCESS - operations successful, did not encounter any errors,
appropriate NTSTATUS code
--*/
{
BSMINFO bsmInfo;
DEV_BROADCAST_VOLUME dbv;
DWORD bsmFlags;
DWORD dwRec;
UNICODE_STRING DllName_U;
STRING bsmName;
HANDLE hUser32DllModule;
LUID SystemLuid = SYSTEM_LUID;
NTSTATUS Status = STATUS_SUCCESS;
if( pLuid == NULL ) {
return( STATUS_INVALID_PARAMETER );
}
bsmInfo.cbSize = sizeof(bsmInfo);
bsmInfo.hdesk = NULL;
bsmInfo.hwnd = NULL;
RtlCopyLuid(&(bsmInfo.luid), pLuid);
dbv.dbcv_size = sizeof( dbv );
dbv.dbcv_devicetype = DBT_DEVTYP_VOLUME;
dbv.dbcv_reserved = 0;
dbv.dbcv_unitmask = (1 << iDrive);
dbv.dbcv_flags = DBTF_NET;
bsmFlags = BSF_FORCEIFHUNG |
BSF_NOHANG |
BSF_NOTIMEOUTIFNOTHUNG;
//
// If the LUID is not Local_System, then broadcast only for the LUID
//
if (!RtlEqualLuid( &(bsmInfo.luid), &SystemLuid )) {
bsmFlags |= BSF_LUID;
}
dwRec = BSM_APPLICATIONS | BSM_ALLDESKTOPS;
hUser32DllModule = NULL;
if( PBROADCASTSYSTEMMESSAGEEXW == NULL ) {
RtlInitUnicodeString( &DllName_U, L"user32" );
Status = LdrGetDllHandle(
UNICODE_NULL,
NULL,
&DllName_U,
(PVOID *)&hUser32DllModule
);
if( hUser32DllModule != NULL && NT_SUCCESS( Status ) ) {
//
// get the address of the BroadcastSystemMessageExW function
//
RtlInitString( &bsmName, "CsrBroadcastSystemMessageExW" );
Status = LdrGetProcedureAddress(
hUser32DllModule,
&bsmName,
0L,
(PVOID *)&PBROADCASTSYSTEMMESSAGEEXW
);
if( !NT_SUCCESS( Status ) ) {
PBROADCASTSYSTEMMESSAGEEXW = NULL;
}
}
}
if( PBROADCASTSYSTEMMESSAGEEXW != NULL ) {
//
// Since this thread is a csrss thread, the thread is not a
// GUI thread and does not have a desktop associated with it.
// Must set the thread's desktop to the active desktop in
// order to call BroadcastSystemMessageExW
//
Status = (PBROADCASTSYSTEMMESSAGEEXW)(
bsmFlags,
&dwRec,
WM_DEVICECHANGE,
(WPARAM)((DeleteRequest == TRUE) ?
DBT_DEVICEREMOVECOMPLETE :
DBT_DEVICEARRIVAL
),
(LPARAM)(DEV_BROADCAST_HDR *)&dbv,
(PBSMINFO)&(bsmInfo)
);
}
//
// Send to all the TS CSRSS servers
//
if( !(bsmFlags & BSF_LUID) ) {
Status = SendWinStationBSM(
bsmFlags,
&dwRec,
WM_DEVICECHANGE,
(WPARAM)((DeleteRequest == TRUE) ?
DBT_DEVICEREMOVECOMPLETE :
DBT_DEVICEARRIVAL
),
(LPARAM)(DEV_BROADCAST_HDR *)&dbv);
}
return( Status );
}
NTSTATUS
AddBSMRequest(
IN DWORD iDrive,
IN BOOLEAN DeleteRequest,
IN PLUID pLuid)
/*++
Routine Description:
Add a request for Broadcasting a System Message about a change with
a drive letter.
Must be running as Local_System and LUID device maps must be enabled.
Places the request item in the BSM_Request_Queue.
This mechanism allows the broadcast to occur asynchronously, otherwise
we encounter waiting issues with explorer.exe, in which the user sees
the shell hang for 20 seconds.
Arguments:
iDrive [IN] - drive letter that is changing, in the form of a number
relative to 'A', used to create a bit mask
DeleteRequest [IN] - denotes whether this change is a delete
TRUE - drive letter was deleted
FALSE - drive letter was added
pLuid [IN] - caller's LUID
Return Value:
STATUS_SUCCESS - operations successful, did not encounter any errors,
STATUS_INVALID_PARAMETER - pLuid is a null pointer
STATUS_ACCESS_DENIED - LUID device maps are disabled or the caller
is not running as Local_System
STATUS_NO_MEMORY - could not allocate memory for the DDD_BSM_REQUEST
data structure
appropriate NTSTATUS code
--*/
{
PDDD_BSM_REQUEST pRequest;
LUID CallerLuid;
LUID SystemLuid = SYSTEM_LUID;
BOOLEAN FirstRequest;
NTSTATUS Status;
if( pLuid == NULL ) {
return( STATUS_INVALID_PARAMETER );
}
//
// LUID device maps must be enabled
//
if( BaseSrvpStaticServerData->LUIDDeviceMapsEnabled == FALSE ) {
return( STATUS_ACCESS_DENIED );
}
Status = GetCallerLuid(&CallerLuid);
if( !NT_SUCCESS(Status) ) {
return Status;
}
//
// The caller must be Local_System
//
if( !RtlEqualLuid(&SystemLuid, &CallerLuid) ) {
return( STATUS_ACCESS_DENIED );
}
pRequest = RtlAllocateHeap( BaseSrvHeap,
MAKE_TAG( TMP_TAG ),
sizeof( DDD_BSM_REQUEST ));
if( pRequest == NULL ) {
return( STATUS_NO_MEMORY );
}
pRequest->iDrive = iDrive;
pRequest->DeleteRequest = DeleteRequest;
RtlCopyLuid( &(pRequest->Luid), pLuid );
pRequest->pNextRequest = NULL;
Status = RtlEnterCriticalSection( &BaseSrvDDDBSMCritSec );
if( !NT_SUCCESS(Status) ) {
RtlFreeHeap( BaseSrvHeap, 0, pRequest );
return( Status );
}
//
// Check if we are adding a request to an empty queue
//
FirstRequest = ( BSM_Request_Queue == NULL );
//
// add the work item to the end of the queue
//
if( BSM_Request_Queue_End != NULL ) {
BSM_Request_Queue_End->pNextRequest = pRequest;
}
else {
BSM_Request_Queue = pRequest;
}
BSM_Request_Queue_End = pRequest;
//
// if we added a request to an empty queue,
// then create a new thread to process the request
//
// BaseSrvDDDBSMCritSec guards BaseSrvpBSMThreadCount
//
if( (FirstRequest == TRUE) ||
(BaseSrvpBSMThreadCount < BaseSrvpBSMThreadMax) ) {
RtlLeaveCriticalSection( &BaseSrvDDDBSMCritSec );
Status = CreateBSMThread();
if( NT_SUCCESS(Status) ) {
Status = RtlEnterCriticalSection( &BaseSrvDDDBSMCritSec );
if( NT_SUCCESS(Status) ) {
BaseSrvpBSMThreadCount++;
RtlLeaveCriticalSection( &BaseSrvDDDBSMCritSec );
}
}
}
else {
RtlLeaveCriticalSection( &BaseSrvDDDBSMCritSec );
}
return( Status );
}
NTSTATUS
CreateBSMThread()
/*++
Routine Description:
Creates a dynamic csr thread
This thread will be use to asynchronously broadcast a drive letter
change message to the LUID's applications
The caller must be Local_System and LUID device maps must be
enabled.
Arguments:
None
Return Value:
STATUS_SUCCESS - operations successful, did not encounter any errors,
STATUS_ACCESS_DENIED - caller is not running as Local_System or
LUID device maps are not enabled
appropriate NTSTATUS code
--*/
{
HANDLE hThread = NULL;
NTSTATUS Status;
//
// Luid device maps must be enabled
//
if( BaseSrvpStaticServerData->LUIDDeviceMapsEnabled == FALSE ) {
return( STATUS_ACCESS_DENIED );
}
//
// Create a thread to asynchronously broadcast a drive letter change
//
Status = RtlCreateUserThread(
NtCurrentProcess(),
NULL,
FALSE, // create the new thread as ready
0,
0,
0,
BaseSrvBSMThread,
NULL,
&hThread,
NULL
);
if( NT_SUCCESS(Status) && hThread ) {
NtClose(hThread);
}
return Status;
}
NTSTATUS
BaseSrvBSMThread(
PVOID pJunk
)
/*++
Routine Description:
Remove a work item from the BSM_Request_Queue and broadcast a message
about drive letter change.
The caller must be Local_System and LUID device maps must be
enabled.
Arguments:
pJunk - not used, RtlCreateUserThread needs a PVOID parameter
Return Value:
STATUS_SUCCESS - operations successful, did not encounter any errors,
STATUS_ACCESS_DENIED - caller is not running as Local_System or
LUID device maps are not enabled
appropriate NTSTATUS code
--*/
{
PDDD_BSM_REQUEST pRequest;
NTSTATUS Status, St;
DWORD Error;
UNREFERENCED_PARAMETER(pJunk);
//
// LUID device maps must be enabled
//
if( BaseSrvpStaticServerData->LUIDDeviceMapsEnabled == FALSE ) {
Status = STATUS_ACCESS_DENIED;
goto ExitCleanup;
}
//
// Enter the critical section that protects the BSM_Request_Queue
//
Status = RtlEnterCriticalSection( &BaseSrvDDDBSMCritSec );
if( !NT_SUCCESS(Status) ) {
goto ExitCleanup;
}
while( BSM_Request_Queue != NULL ) {
pRequest = BSM_Request_Queue;
if( pRequest != NULL ) {
//
// Remove the request from the front of BSM_Request_Queue
//
if( BSM_Request_Queue != NULL ) {
BSM_Request_Queue = BSM_Request_Queue->pNextRequest;
}
//
// if the queue is empty,
// then make sure that the queue's end pointer is NULL
//
if( BSM_Request_Queue == NULL ) {
BSM_Request_Queue_End = NULL;
}
RtlLeaveCriticalSection( &BaseSrvDDDBSMCritSec );
//
// Broadcasting can take a long time
// so broadcast outside of the critical section
//
Status = BroadcastDriveLetterChange( pRequest->iDrive,
pRequest->DeleteRequest,
&(pRequest->Luid) );
//
// free the work item's memory
//
pRequest->pNextRequest = NULL;
RtlFreeHeap( BaseSrvHeap, 0, pRequest );
//
// Enter the critical section that protects the BSM_Request_Queue
//
Status = RtlEnterCriticalSection( &BaseSrvDDDBSMCritSec );
if( !NT_SUCCESS(Status) ) {
goto ExitCleanup;
}
}
}
ExitCleanup:
St = Status;
if( !NT_SUCCESS(Status) ) {
St = RtlEnterCriticalSection( &BaseSrvDDDBSMCritSec );
}
if( BaseSrvpBSMThreadCount > 0 ) {
BaseSrvpBSMThreadCount--;
}
if( NT_SUCCESS(St) ) {
RtlLeaveCriticalSection( &BaseSrvDDDBSMCritSec );
}
//
// Since this thread was created with RtlCreateUserThread,
// we must clean up the thread manually
// Set the variable for User Stack cleanup and terminate the thread
// Note: This thread should not be holding a critical section when
// terminating the thread
//
NtCurrentTeb ()->FreeStackOnTermination = TRUE;
NtTerminateThread( NtCurrentThread(), Status );
return( Status );
}
BOOLEAN
CheckForGlobalSymLink (
PUNICODE_STRING pDeviceName
)
/*++
Routine Description:
Checks if the user sees a drive letter symbolic link that exists in the
global DosDevices
Arguments:
pDeviceName - contains the drive letter name in an UNICODE_STRING
Return Value:
TRUE - operations successful && the drive letter does exist in the
global DosDevices
FALSE - error encountered or drive letter does not exist in the
global DosDevices
--*/
{
WCHAR DeviceName[NT_DRIVE_LETTER_PATH_LENGTH];
UNICODE_STRING LinkName;
OBJECT_ATTRIBUTES ObjectAttributes;
HANDLE LinkHandle;
BOOLEAN RevertToSelfNeeded, bGlobalSymbolicLink;
NTSTATUS Status;
if ( pDeviceName == NULL ) {
return FALSE;
}
_snwprintf( DeviceName,
NT_DRIVE_LETTER_PATH_LENGTH,
L"\\??\\%wZ",
pDeviceName
);
RtlInitUnicodeString( &LinkName, DeviceName );
InitializeObjectAttributes( &ObjectAttributes,
&LinkName,
OBJ_CASE_INSENSITIVE,
(HANDLE) NULL,
(PSECURITY_DESCRIPTOR)NULL
);
//
// Impersonating the user to make sure that there is not a LUID DosDevices
// drive letter masking the global DosDevices drive letter
//
RevertToSelfNeeded = CsrImpersonateClient( NULL ); // This stacks client contexts
if( RevertToSelfNeeded == FALSE ) {
Status = STATUS_BAD_IMPERSONATION_LEVEL;
return FALSE;
}
Status = NtOpenSymbolicLinkObject( &LinkHandle,
SYMBOLIC_LINK_QUERY,
&ObjectAttributes
);
if (RevertToSelfNeeded) {
CsrRevertToSelf(); // This unstacks client contexts
}
if (!NT_SUCCESS(Status)) {
return FALSE;
}
Status = IsGlobalSymbolicLink( LinkHandle,
&bGlobalSymbolicLink
);
NtClose( LinkHandle );
if (!NT_SUCCESS(Status)) {
return FALSE;
}
return (bGlobalSymbolicLink);
}
NTSTATUS
SendWinStationBSM (
DWORD dwFlags,
LPDWORD lpdwRecipients,
UINT uiMessage,
WPARAM wParam,
LPARAM lParam
)
{
FP_WINSTABROADCASTSYSTEMMESSAGE fpWinStationBroadcastSystemMessage = NULL;
UNICODE_STRING DllName_U;
STRING bsmName;
HANDLE hWinStaDllModule = NULL;
LONG result = 0;
NTSTATUS Status;
//
// Load the base library that contains the user message dispatch routines
// for Terminal Services.
//
RtlInitUnicodeString( &DllName_U, L"WINSTA.DLL" );
Status = LdrLoadDll(
NULL,
NULL,
&DllName_U,
(PVOID *)&hWinStaDllModule
);
if(!NT_SUCCESS( Status )) {
return Status;
}
//
// get the address of the WinStationBroadcastSystemMessage function
//
RtlInitString( &bsmName, "WinStationBroadcastSystemMessage" );
Status = LdrGetProcedureAddress(
hWinStaDllModule,
&bsmName,
0L,
(PVOID *)&fpWinStationBroadcastSystemMessage
);
if( !NT_SUCCESS( Status ) ) {
fpWinStationBroadcastSystemMessage = NULL;
}
if( fpWinStationBroadcastSystemMessage != NULL ) {
fpWinStationBroadcastSystemMessage(SERVERNAME_CURRENT,
TRUE,
0,
DEFAULT_BROADCAST_TIME_OUT,
dwFlags,
lpdwRecipients,
uiMessage,
wParam,
lParam,
&result);
}
if(hWinStaDllModule != NULL) {
LdrUnloadDll(hWinStaDllModule);
hWinStaDllModule = NULL;
}
return( Status );
}
ULONG BaseSrvKernel32DelayLoadComplete = FALSE; // keep ULONG for atomicity
HANDLE BaseSrvKernel32DllHandle = NULL;
PGET_NLS_SECTION_NAME pGetNlsSectionName = NULL;
PGET_DEFAULT_SORTKEY_SIZE pGetDefaultSortkeySize = NULL;
PGET_LINGUIST_LANG_SIZE pGetLinguistLangSize = NULL;
PVALIDATE_LOCALE pValidateLocale = NULL;
PVALIDATE_LCTYPE pValidateLCType = NULL;
POPEN_DATA_FILE pOpenDataFile = NULL;
PNLS_CONVERT_INTEGER_TO_STRING pNlsConvertIntegerToString = NULL;
PGET_USER_DEFAULT_LANG_ID pGetUserDefaultLangID = NULL;
PGET_CP_FILE_NAME_FROM_REGISTRY pGetCPFileNameFromRegistry = NULL;
PCREATE_NLS_SECURITY_DESCRIPTOR pCreateNlsSecurityDescriptor = NULL;
const static struct KERNEL32_DELAY_LOAD_FUNCTION {
ANSI_STRING Name;
PVOID* Code;
} BaseSrvKernel32DelayLoadFunctions[] = {
{ RTL_CONSTANT_STRING("OpenDataFile"), (PVOID*)(&pOpenDataFile) },
{ RTL_CONSTANT_STRING("GetDefaultSortkeySize"), (PVOID*)(&pGetDefaultSortkeySize) },
{ RTL_CONSTANT_STRING("GetLinguistLangSize"), (PVOID*)(&pGetLinguistLangSize) },
{ RTL_CONSTANT_STRING("NlsConvertIntegerToString"), (PVOID*)(&pNlsConvertIntegerToString) },
{ RTL_CONSTANT_STRING("ValidateLCType"), (PVOID*)(&pValidateLCType) },
{ RTL_CONSTANT_STRING("ValidateLocale"), (PVOID*)(&pValidateLocale) },
{ RTL_CONSTANT_STRING("GetNlsSectionName"), (PVOID*)(&pGetNlsSectionName) },
{ RTL_CONSTANT_STRING("GetUserDefaultLangID"), (PVOID*)(&pGetUserDefaultLangID) },
{ RTL_CONSTANT_STRING("GetCPFileNameFromRegistry"), (PVOID*)(&pGetCPFileNameFromRegistry) },
{ RTL_CONSTANT_STRING("CreateNlsSecurityDescriptor"),(PVOID*)(&pCreateNlsSecurityDescriptor)}
};
NTSTATUS
BaseSrvDelayLoadKernel32(
VOID
)
{
NTSTATUS Status = STATUS_SUCCESS;
HANDLE LocalKernel32DllHandle = BaseSrvKernel32DllHandle;
int i = 0;
ASSERT(BaseSrvKernel32DllPath.Buffer != NULL && BaseSrvKernel32DllPath.Length != 0);
if (BaseSrvKernel32DelayLoadComplete)
return STATUS_SUCCESS;
//
// The structure here is somewhat inverted.
// Usually you load the library, then loop over functions.
// We loop over functions, only loading the library when we find a NULL one.
//
// I (a-JayK) don't remember why we do this, but it was deliberate.
//
for (i = 0 ; i != RTL_NUMBER_OF(BaseSrvKernel32DelayLoadFunctions) ; ++i) {
//
// Due to races, we cannot skip out of the loop upon finding any non NULLs.
//
if (*BaseSrvKernel32DelayLoadFunctions[i].Code == NULL) {
if (LocalKernel32DllHandle == NULL) {
//
// We depend on the loader lock for thread safety.
// In a race we might refcount kernel32.dll more than once.
// This is ok, because we do not ever unload kernel32.dll.
//
Status = LdrLoadDll(NULL, NULL, &BaseSrvKernel32DllPath, &BaseSrvKernel32DllHandle);
ASSERTMSG("Rerun with ShowSnaps to debug.", NT_SUCCESS(Status));
ASSERTMSG("Rerun with ShowSnaps to debug.", BaseSrvKernel32DllHandle != NULL);
if (!NT_SUCCESS(Status))
goto Exit;
LocalKernel32DllHandle = BaseSrvKernel32DllHandle;
}
Status =
LdrGetProcedureAddress(
BaseSrvKernel32DllHandle,
&BaseSrvKernel32DelayLoadFunctions[i].Name,
0,
BaseSrvKernel32DelayLoadFunctions[i].Code
);
ASSERTMSG("Rerun with ShowSnaps to debug.", NT_SUCCESS(Status));
ASSERTMSG("Rerun with ShowSnaps to debug.", *BaseSrvKernel32DelayLoadFunctions[i].Code != NULL);
if (!NT_SUCCESS(Status))
goto Exit;
}
}
BaseSrvKernel32DelayLoadComplete = TRUE;
Exit:
return Status;
}