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.
2761 lines
88 KiB
2761 lines
88 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}
|
|
};
|
|
|
|
CONST PCSR_API_ROUTINE BaseServerApiDispatchTable[BasepMaxApiNumber + 1] = {
|
|
BaseSrvCreateProcess, // BasepCreateProcess
|
|
BaseSrvCreateThread, // BasepCreateThread
|
|
BaseSrvGetTempFile, // BasepGetTempFile
|
|
BaseSrvExitProcess, // BasepExitProcess
|
|
BaseSrvDebugProcess, // BasepDebugProcess
|
|
BaseSrvCheckVDM, // BasepCheckVDM
|
|
BaseSrvUpdateVDMEntry, // BasepUpdateVDMEntry
|
|
BaseSrvGetNextVDMCommand, // BasepGetNextVDMCommand
|
|
BaseSrvExitVDM, // BasepExitVDM
|
|
BaseSrvIsFirstVDM, // BasepIsFirstVDM
|
|
BaseSrvGetVDMExitCode, // BasepGetVDMExitCode
|
|
BaseSrvSetReenterCount, // BasepSetReenterCount
|
|
BaseSrvSetProcessShutdownParam, // BasepSetProcessShutdownParam
|
|
BaseSrvGetProcessShutdownParam, // BasepGetProcessShutdownParam
|
|
BaseSrvNlsSetUserInfo, // BasepNlsSetUserInfo
|
|
BaseSrvNlsSetMultipleUserInfo, // BasepNlsSetMultipleUserInfo
|
|
BaseSrvNlsCreateSection, // BasepNlsCreateSection
|
|
BaseSrvSetVDMCurDirs, // BasepSetVDMCurDirs
|
|
BaseSrvGetVDMCurDirs, // BasepGetVDMCurDirs
|
|
BaseSrvBatNotification, // BasepBatNotification
|
|
BaseSrvRegisterWowExec, // BasepRegisterWowExec
|
|
BaseSrvSoundSentryNotification, // BasepSoundSentryNotification
|
|
BaseSrvRefreshIniFileMapping, // BasepRefreshIniFileMapping
|
|
BaseSrvDefineDosDevice, // BasepDefineDosDevice
|
|
BaseSrvSetTermsrvAppInstallMode, // BasepSetTermsrvAppInstallMode
|
|
BaseSrvNlsUpdateCacheCount, // BasepNlsUpdateCacheCount
|
|
BaseSrvSetTermsrvClientTimeZone, // BasepSetTermsrvClientTimeZone
|
|
BaseSrvSxsCreateActivationContext, // BasepSxsCreateActivationContext
|
|
BaseSrvDebugProcessStop, // BasepDebugProcessStop
|
|
BaseSrvRegisterThread, // BasepRegisterThread
|
|
BaseSrvCheckApplicationCompatibility, // BasepCheckApplicationCompatibility
|
|
BaseSrvNlsGetUserInfo, // BaseSrvNlsGetUserInfo
|
|
NULL // BasepMaxApiNumber
|
|
};
|
|
|
|
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, // SrvCheckApplicationCompatibility
|
|
TRUE, // BaseSrvNlsGetUserInfo
|
|
FALSE
|
|
};
|
|
|
|
#if DBG
|
|
CONST 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",
|
|
"BaseCheckApplicationCompatibility",
|
|
"BaseNlsGetUserInfo",
|
|
NULL
|
|
};
|
|
#endif // DBG
|
|
|
|
HANDLE BaseSrvNamedObjectDirectory;
|
|
HANDLE BaseSrvRestrictedObjectDirectory;
|
|
RTL_CRITICAL_SECTION BaseSrvDosDeviceCritSec;
|
|
|
|
#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
|
|
CheckForGlobalDriveLetter (
|
|
DWORD iDrive
|
|
);
|
|
//
|
|
// 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;
|
|
}
|
|
|
|
|
|
|
|
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;
|
|
#endif
|
|
LoadedServerDll->PerProcessDataLength = 0;
|
|
LoadedServerDll->ConnectRoutine = BaseClientConnectRoutine;
|
|
LoadedServerDll->DisconnectRoutine = BaseClientDisconnectRoutine;
|
|
|
|
Status = RtlInitializeCriticalSection (&BaseSrvDosDeviceCritSec);
|
|
if (!NT_SUCCESS (Status)) {
|
|
return Status;
|
|
}
|
|
|
|
ValueString.Buffer = ValueBuffer;
|
|
ValueString.Length = 0;
|
|
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;
|
|
}
|
|
|
|
RtlCopyMemory(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;
|
|
}
|
|
RtlCopyMemory(p,BaseSrvpStaticServerData->WindowsSystemDirectory.Buffer,BaseSrvWindowsSystemDirectory.MaximumLength);
|
|
BaseSrvpStaticServerData->WindowsSystemDirectory.Buffer = p;
|
|
|
|
BaseSrvpStaticServerData->WindowsSys32x86Directory.Buffer = NULL;
|
|
BaseSrvpStaticServerData->WindowsSys32x86Directory.Length = 0;
|
|
BaseSrvpStaticServerData->WindowsSys32x86Directory.MaximumLength = 0;
|
|
|
|
//
|
|
// 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;
|
|
}
|
|
|
|
RtlCopyMemory(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;
|
|
}
|
|
|
|
if (SessionId == 0) {
|
|
|
|
Status = NtSetInformationObject( BaseSrvNamedObjectDirectory,
|
|
ObjectSessionInformation,
|
|
NULL,
|
|
0
|
|
);
|
|
|
|
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,
|
|
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,
|
|
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,
|
|
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();
|
|
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;
|
|
|
|
UNREFERENCED_PARAMETER(ReplyStatus);
|
|
|
|
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 STATUS_NO_MEMORY;
|
|
}
|
|
|
|
Status = RtlEnterCriticalSection( &BaseSrvDosDeviceCritSec );
|
|
if (!NT_SUCCESS( Status )) {
|
|
RtlFreeHeap( BaseSrvHeap, 0, Buffer );
|
|
return 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);
|
|
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();
|
|
}
|
|
|
|
//
|
|
// 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();
|
|
}
|
|
}
|
|
|
|
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 )) &&
|
|
CheckForGlobalDriveLetter( iDrive )) {
|
|
AddBSMRequest( iDrive,
|
|
FALSE,
|
|
&callerLuid );
|
|
}
|
|
}
|
|
|
|
RtlLeaveCriticalSection( &BaseSrvDosDeviceCritSec );
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
|
|
|
|
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;
|
|
ULONG ObjectSecurityInformation;
|
|
|
|
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 );
|
|
}
|
|
|
|
if (NtCurrentPeb()->SessionId) {
|
|
|
|
Status = NtQuerySystemInformation( SystemObjectSecurityMode,
|
|
&ObjectSecurityInformation,
|
|
sizeof(ObjectSecurityInformation),
|
|
NULL
|
|
);
|
|
if (!NT_SUCCESS( Status )) {
|
|
|
|
ObjectSecurityInformation = 0;
|
|
}
|
|
|
|
} else {
|
|
|
|
ObjectSecurityInformation = 0;
|
|
}
|
|
|
|
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 );
|
|
}
|
|
|
|
if (ObjectSecurityInformation == 0) {
|
|
|
|
WorldAccess = DIRECTORY_ALL_ACCESS & ~(WRITE_OWNER | WRITE_DAC | DELETE );
|
|
} else {
|
|
|
|
WorldAccess = DIRECTORY_TRAVERSE | DIRECTORY_QUERY;
|
|
}
|
|
|
|
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;
|
|
|
|
//
|
|
// Refresh the system's concept of time
|
|
//
|
|
NtSetSystemTime(NULL,NULL);
|
|
}
|
|
|
|
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;
|
|
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;
|
|
|
|
|
|
RtlEnterCriticalSection( &BaseSrvDDDBSMCritSec );
|
|
|
|
//
|
|
// 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( BaseSrvpBSMThreadCount < BaseSrvpBSMThreadMax ) {
|
|
|
|
RtlLeaveCriticalSection( &BaseSrvDDDBSMCritSec );
|
|
|
|
Status = CreateBSMThread();
|
|
}
|
|
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
|
|
|
|
--*/
|
|
{
|
|
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,
|
|
0,
|
|
0,
|
|
0,
|
|
BaseSrvBSMThread,
|
|
NULL,
|
|
NULL,
|
|
NULL
|
|
);
|
|
|
|
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.
|
|
|
|
Return Value:
|
|
|
|
STATUS_SUCCESS - operations successful, did not encounter any errors,
|
|
|
|
STATUS_ACCESS_DENIED - caller is not running as Local_System.
|
|
|
|
appropriate NTSTATUS code
|
|
|
|
--*/
|
|
{
|
|
PDDD_BSM_REQUEST pRequest;
|
|
NTSTATUS Status = STATUS_SUCCESS;
|
|
DWORD Error;
|
|
|
|
UNREFERENCED_PARAMETER(pJunk);
|
|
|
|
//
|
|
// LUID device maps must be enabled for us to get here.
|
|
//
|
|
ASSERT(BaseSrvpStaticServerData->LUIDDeviceMapsEnabled);
|
|
|
|
//
|
|
// Enter the critical section that protects the BSM_Request_Queue.
|
|
//
|
|
RtlEnterCriticalSection( &BaseSrvDDDBSMCritSec );
|
|
BaseSrvpBSMThreadCount++;
|
|
|
|
while ((pRequest = BSM_Request_Queue) != NULL) {
|
|
//
|
|
// Remove the request from the front of BSM_Request_Queue
|
|
//
|
|
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
|
|
//
|
|
RtlEnterCriticalSection( &BaseSrvDDDBSMCritSec );
|
|
}
|
|
|
|
BaseSrvpBSMThreadCount--;
|
|
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
|
|
CheckForGlobalDriveLetter (
|
|
DWORD iDrive
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Checks if the user sees a drive letter symbolic link that exists in the
|
|
global DosDevices
|
|
|
|
Arguments:
|
|
|
|
iDrive - contains the index of the drive letter relative to 'A'
|
|
|
|
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;
|
|
|
|
//
|
|
// workaround warning tool's unawareness of previous verification of the
|
|
// function parameter, which was always a string of two chars "X:". It was
|
|
// always two chars because this function was only called when (bsmForLuid
|
|
// == TRUE). bsmForLuid is only set to TRUE when a->DeviceName was 2 chars
|
|
// long. So now we use an index, iDrive, which is set when bsmForLuid ==
|
|
// TRUE.
|
|
//
|
|
wcsncpy( DeviceName, L"\\??\\X:", NT_DRIVE_LETTER_PATH_LENGTH - 1 );
|
|
DeviceName[ NT_DRIVE_LETTER_PATH_LENGTH - 1 ] = UNICODE_NULL;
|
|
DeviceName[4] = (WCHAR)(L'A' + iDrive);
|
|
|
|
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();
|
|
}
|
|
|
|
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;
|
|
UNICODE_STRING DllName_U;
|
|
STRING bsmName;
|
|
HANDLE hWinStaDllModule;
|
|
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(SERVERNAME_CURRENT,
|
|
TRUE,
|
|
0,
|
|
DEFAULT_BROADCAST_TIME_OUT,
|
|
dwFlags,
|
|
lpdwRecipients,
|
|
uiMessage,
|
|
wParam,
|
|
lParam,
|
|
&result);
|
|
}
|
|
|
|
LdrUnloadDll(hWinStaDllModule);
|
|
|
|
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;
|
|
}
|
|
|
|
ULONG BaseSrvApphelpDelayLoadComplete = FALSE;
|
|
PFNCheckRunApp pfnCheckRunApp = NULL;
|
|
|
|
|
|
NTSTATUS
|
|
BaseSrvDelayLoadApphelp(
|
|
VOID
|
|
)
|
|
{
|
|
static const UNICODE_STRING ApphelpModuleName = RTL_CONSTANT_STRING(L"\\Apphelp.dll");
|
|
static const STRING CheckRunAppProcedureName = RTL_CONSTANT_STRING("ApphelpCheckRunApp");
|
|
UNICODE_STRING ApphelpFullPath = { 0 };
|
|
NTSTATUS Status;
|
|
HANDLE ModuleHandle = NULL;
|
|
|
|
if (BaseSrvApphelpDelayLoadComplete) {
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
ApphelpFullPath.MaximumLength = ApphelpModuleName.Length +
|
|
BaseSrvWindowsSystemDirectory.Length +
|
|
sizeof(UNICODE_NULL);
|
|
|
|
ApphelpFullPath.Buffer = RtlAllocateHeap(RtlProcessHeap(),
|
|
MAKE_TAG(TMP_TAG),
|
|
ApphelpFullPath.MaximumLength);
|
|
if (ApphelpFullPath.Buffer == NULL) {
|
|
return STATUS_NO_MEMORY;
|
|
}
|
|
|
|
ApphelpFullPath.Length = 0;
|
|
|
|
Status = RtlAppendUnicodeStringToString(&ApphelpFullPath, &BaseSrvWindowsSystemDirectory);
|
|
if (!NT_SUCCESS(Status)) {
|
|
goto cleanup;
|
|
}
|
|
|
|
Status = RtlAppendUnicodeStringToString(&ApphelpFullPath, &ApphelpModuleName);
|
|
if (!NT_SUCCESS(Status)) {
|
|
goto cleanup;
|
|
}
|
|
|
|
//
|
|
// load apphelp
|
|
//
|
|
|
|
Status = LdrLoadDll(NULL,
|
|
NULL,
|
|
&ApphelpFullPath,
|
|
&ModuleHandle);
|
|
|
|
if (NT_SUCCESS(Status)) {
|
|
Status = LdrGetProcedureAddress(ModuleHandle,
|
|
&CheckRunAppProcedureName,
|
|
0,
|
|
(PVOID*)&pfnCheckRunApp);
|
|
}
|
|
|
|
cleanup:
|
|
|
|
|
|
if (ApphelpFullPath.Buffer) {
|
|
RtlFreeHeap(RtlProcessHeap(), 0, ApphelpFullPath.Buffer);
|
|
}
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
|
|
if (ModuleHandle) {
|
|
LdrUnloadDll(ModuleHandle);
|
|
}
|
|
|
|
pfnCheckRunApp = NULL;
|
|
}
|
|
|
|
BaseSrvApphelpDelayLoadComplete = NT_SUCCESS(Status);
|
|
|
|
return Status;
|
|
|
|
}
|
|
|