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

1451 lines
42 KiB

/**************************************************************************\
* Module Name: server.c
*
* Server support routines for the CSR stuff.
*
* Copyright (c) 1985 - 1999, Microsoft Corporation
*
* Created: 10-Dec-90
*
* History:
* 10-Dec-90 created by sMeans
*
\**************************************************************************/
#include "precomp.h"
#pragma hdrstop
#include <dbt.h>
#include <ntdddisk.h>
#include "ntuser.h"
#include <regstr.h>
HANDLE hKeyPriority;
UNICODE_STRING PriorityValueName;
IO_STATUS_BLOCK IoStatusRegChange;
ULONG RegChangeBuffer;
HANDLE ghNlsEvent;
BOOL gfLogon;
HANDLE ghPowerRequestEvent;
HANDLE ghMediaRequestEvent;
#define ID_NLS 0
#define ID_POWER 1
#define ID_MEDIACHANGE 2
#define ID_NETDEVCHANGE 3
#define ID_NUM_EVENTS 4
//
// Name of event to pulse to request a device-arrival broadcast,
//
#define SC_BSM_EVENT_NAME L"ScNetDrvMsg"
//
// What the net drive bitmask was when we last broadcast (initially 0)
//
DWORD LastNetDrives;
HANDLE CsrApiPort;
HANDLE CsrQueryApiPort(VOID);
ULONG
SrvExitWindowsEx(
IN OUT PCSR_API_MSG m,
IN OUT PCSR_REPLY_STATUS ReplyStatus);
ULONG
SrvEndTask(
IN OUT PCSR_API_MSG m,
IN OUT PCSR_REPLY_STATUS ReplyStatus);
ULONG
SrvLogon(
IN OUT PCSR_API_MSG m,
IN OUT PCSR_REPLY_STATUS ReplyStatus);
ULONG
SrvRegisterServicesProcess(
IN OUT PCSR_API_MSG m,
IN OUT PCSR_REPLY_STATUS ReplyStatus);
ULONG
SrvActivateDebugger(
IN OUT PCSR_API_MSG m,
IN OUT PCSR_REPLY_STATUS ReplyStatus);
ULONG
SrvGetThreadConsoleDesktop(
IN OUT PCSR_API_MSG m,
IN OUT PCSR_REPLY_STATUS ReplyStatus);
ULONG
SrvDeviceEvent(
IN OUT PCSR_API_MSG m,
IN OUT PCSR_REPLY_STATUS ReplyStatus);
ULONG
SrvRegisterLogonProcess(
IN OUT PCSR_API_MSG m,
IN OUT PCSR_REPLY_STATUS ReplyStatus);
#if DBG
ULONG
SrvWin32HeapFail(
IN OUT PCSR_API_MSG m,
IN OUT PCSR_REPLY_STATUS ReplyStatus);
ULONG
SrvWin32HeapStat(
IN OUT PCSR_API_MSG m,
IN OUT PCSR_REPLY_STATUS ReplyStatus);
#endif
ULONG SrvCreateSystemThreads(
IN OUT PCSR_API_MSG m,
IN OUT PCSR_REPLY_STATUS ReplyStatus);
ULONG SrvRecordShutdownReason(
IN OUT PCSR_API_MSG m,
IN OUT PCSR_REPLY_STATUS ReplyStatus);
CONST PCSR_API_ROUTINE UserServerApiDispatchTable[] = {
SrvExitWindowsEx,
SrvEndTask,
SrvLogon,
SrvRegisterServicesProcess,
SrvActivateDebugger,
SrvGetThreadConsoleDesktop,
SrvDeviceEvent,
SrvRegisterLogonProcess,
SrvCreateSystemThreads,
SrvRecordShutdownReason,
#if DBG
SrvWin32HeapFail,
SrvWin32HeapStat,
#endif
};
BOOLEAN UserServerApiServerValidTable[] = {
FALSE, // ExitWindowsEx
FALSE, // EndTask
FALSE, // Logon
FALSE, // RegisterServicesProcess
FALSE, // ActivateDebugger
TRUE, // GetThreadConsoleDesktop
FALSE, // DeviceEvent
FALSE, // RegisterLogonProcess
FALSE, // CreateSystemThreads
TRUE, // RecordShutdownReason
#if DBG
FALSE, // Win32HeapFail
FALSE, // Win32HeapStat
#endif
};
#if DBG
CONST PSZ UserServerApiNameTable[] = {
"SrvExitWindowsEx",
"SrvEndTask",
"SrvLogon",
"SrvRegisterServicesProcess",
"SrvActivateDebugger",
"SrvGetThreadConsoleDesktop",
"SrvDeviceEvent",
"SrvRegisterLogonProcess",
"SrvCreateSystemThreads",
"SrvRecordShutdownReason"
"SrvWin32HeapFail",
"SrvWin32HeapStat",
};
#endif // DBG
NTSTATUS UserServerDllInitialization(
PCSR_SERVER_DLL psrvdll);
NTSTATUS UserClientConnect(
PCSR_PROCESS Process,
PVOID ConnectionInformation,
PULONG pulConnectionLen);
VOID UserHardError(
PCSR_THREAD pcsrt,
PHARDERROR_MSG pmsg);
NTSTATUS UserClientShutdown(
PCSR_PROCESS Process,
ULONG dwFlags,
BOOLEAN fFirstPass);
VOID StartRegReadRead(
VOID);
VOID RegReadApcProcedure(
PVOID RegReadApcContext,
PIO_STATUS_BLOCK IoStatus);
NTSTATUS NotificationThread(
PVOID);
VOID InitializeConsoleAttributes(
VOID);
NTSTATUS GetThreadConsoleDesktop(
DWORD dwThreadId,
HDESK *phdesk);
NTSTATUS MyRegOpenKey(IN HANDLE hKey, IN LPWSTR lpSubKey, OUT PHANDLE phResult);
typedef BOOL (*PFNPROCESSCREATE)(DWORD, DWORD, ULONG_PTR, DWORD);
BOOL BaseSetProcessCreateNotify(
PFNPROCESSCREATE pfn);
VOID BaseSrvNlsUpdateRegistryCache(
PVOID ApcContext,
PIO_STATUS_BLOCK pIoStatusBlock);
NTSTATUS BaseSrvNlsLogon(
BOOL fLogon);
NTSTATUS WinStationAPIInit(
VOID);
/***************************************************************************\
* UserServerDllInitialization
*
* Called by the CSR stuff to allow a server DLL to initialize itself and
* provide information about the APIs it provides.
*
* Several operations are performed during this initialization:
*
* - The shared heap (client read-only) handle is initialized.
* - The Raw Input Thread (RIT) is launched.
* - GDI is initialized.
*
* History:
* 10-19-92 DarrinM Integrated xxxUserServerDllInitialize into this rtn.
* 11-08-91 patrickh move GDI init here from DLL init routine.
* 12-10-90 sMeans Created.
\***************************************************************************/
NTSTATUS UserServerDllInitialization(
PCSR_SERVER_DLL psrvdll)
{
CLIENT_ID ClientId;
BOOL bAllocated;
NTSTATUS Status;
HANDLE hThreadNotification;
if (RtlGetNtGlobalFlags() & FLG_SHOW_LDR_SNAPS) {
RIPMSG0(RIP_WARNING,
"UserServerDllInitialization: entered");
}
/*
* Initialize a critical section structure that will be used to protect
* all of the User Server's critical sections.
*/
Status = RtlInitializeCriticalSection(&gcsUserSrv);
if (!NT_SUCCESS(Status)) {
RIPMSGF1(RIP_WARNING,
"InitializeCriticalSection failed with Status 0x%x",
Status);
return Status;
}
EnterCrit();
/*
* Remember WINSRV.DLL's hmodule so we can grab resources from it later.
*/
ghModuleWin = psrvdll->ModuleHandle;
psrvdll->ApiNumberBase = USERSRV_FIRST_API_NUMBER;
psrvdll->MaxApiNumber = UserpMaxApiNumber;
psrvdll->ApiDispatchTable = UserServerApiDispatchTable;
if (ISTS()) {
UserServerApiServerValidTable[0] = TRUE; // for ExitWindowsEx
}
psrvdll->ApiServerValidTable = UserServerApiServerValidTable;
#if DBG
psrvdll->ApiNameTable = UserServerApiNameTable;
#endif
psrvdll->ConnectRoutine = UserClientConnect;
psrvdll->HardErrorRoutine = UserHardError;
psrvdll->ShutdownProcessRoutine = UserClientShutdown;
/*
* Create the events used by shutdown.
*/
Status = NtCreateEvent(&gheventCancel, EVENT_ALL_ACCESS, NULL,
NotificationEvent, FALSE);
if (!NT_SUCCESS(Status)) {
RIPMSG1(RIP_WARNING,
"UserServerDllInitialization: NtCreateEvent failed with Status 0x%x",
Status);
goto ExitUserInit;
}
Status = NtCreateEvent(&gheventCancelled, EVENT_ALL_ACCESS, NULL,
NotificationEvent, FALSE);
if (!NT_SUCCESS(Status)) {
RIPMSG1(RIP_WARNING,
"UserServerDllInitialization: NtCreateEvent failed with Status 0x%x",
Status);
goto ExitUserInit;
}
/*
* Create the event used by the power request code.
*/
Status = NtCreateEvent(&ghPowerRequestEvent, EVENT_ALL_ACCESS, NULL,
SynchronizationEvent, FALSE);
if (!NT_SUCCESS(Status)) {
RIPMSG1(RIP_WARNING,
"UserServerDllInitialization: NtCreateEvent failed with Status 0x%x",
Status);
goto ExitUserInit;
}
/*
* Create the event used by the media change code.
*/
Status = NtCreateEvent(&ghMediaRequestEvent, EVENT_ALL_ACCESS, NULL,
SynchronizationEvent, FALSE);
if (!NT_SUCCESS(Status)) {
RIPMSG1(RIP_WARNING,
"UserServerDllInitialization: NtCreateEvent failed with Status 0x%x",
Status);
goto ExitUserInit;
}
/*
* Create the event used by the nls code.
*/
Status = NtCreateEvent(&ghNlsEvent,
EVENT_ALL_ACCESS,
NULL,
SynchronizationEvent,
FALSE);
if (!NT_SUCCESS(Status)) {
RIPMSG1(RIP_WARNING,
"UserServerDllInitialization: NtCreateEvent failed with Status 0x%x",
Status);
goto ExitUserInit;
}
/*
* Tell the base what user address to call when it is creating a process
* (but before the process starts running).
*/
BaseSetProcessCreateNotify(NtUserNotifyProcessCreate);
/*
* Load some strings.
*/
gpwszaSUCCESS = (PWSTR)RtlLoadStringOrError(ghModuleWin,
STR_SUCCESS, NULL, &bAllocated, FALSE);
gpwszaSYSTEM_INFORMATION = (PWSTR)RtlLoadStringOrError(ghModuleWin,
STR_SYSTEM_INFORMATION, NULL, &bAllocated, FALSE);
gpwszaSYSTEM_WARNING = (PWSTR)RtlLoadStringOrError(ghModuleWin,
STR_SYSTEM_WARNING, NULL, &bAllocated, FALSE);
gpwszaSYSTEM_ERROR = (PWSTR)RtlLoadStringOrError(ghModuleWin,
STR_SYSTEM_ERROR, NULL, &bAllocated, FALSE);
/*
* Initialize USER
*/
Status = NtUserInitialize(USERCURRENTVERSION, ghPowerRequestEvent, ghMediaRequestEvent);
if (!NT_SUCCESS(Status)) {
RIPMSG1(RIP_WARNING,
"NtUserInitialize failed with Status 0x%x",
Status);
goto ExitUserInit;
}
if (ISTS()) {
Status = WinStationAPIInit();
if (!NT_SUCCESS(Status)) {
RIPMSG1(RIP_WARNING,
"UserServerDllInitialization: WinStationAPIInit failed with Status 0x%x",
Status);
goto ExitUserInit;
}
}
/*
* Start registry notification thread
*/
Status = RtlCreateUserThread(NtCurrentProcess(), NULL, FALSE, 0, 0, 0,
NotificationThread, NULL, &hThreadNotification,
&ClientId);
if (NT_SUCCESS(Status)) {
UserVerify(CsrAddStaticServerThread(hThreadNotification, &ClientId, 0));
} else {
RIPMSG1(RIP_WARNING,
"UserServerDllInitialization: RtlCreateUserThread failed with Status 0x%x",
Status);
}
ExitUserInit:
LeaveCrit();
return Status;
}
/**************************************************************************\
* UserClientConnect
*
* This function is called once for each client process that connects to the
* User server. When the client dynlinks to USER.DLL, USER.DLL's init code
* is executed and calls CsrClientConnectToServer to establish the connection.
* The server portion of ConnectToServer calls out this entrypoint.
*
* UserClientConnect first verifies version numbers to make sure the client
* is compatible with this server and then completes all process-specific
* initialization.
*
* History:
* 02-??-91 SMeans Created.
* 04-02-91 DarrinM Added User intialization code.
\**************************************************************************/
extern WORD gDispatchTableValues;
NTSTATUS UserClientConnect(
PCSR_PROCESS Process,
PVOID ConnectionInformation,
PULONG pulConnectionLen)
{
NTSTATUS Status;
/*
* Pass the api port to the kernel. Do this early so the kernel
* can send a datagram to CSR to activate a debugger.
*/
if (CsrApiPort == NULL) {
CsrApiPort = CsrQueryApiPort();
UserAssert(CsrApiPort != NULL);
Status = NtUserSetInformationThread(
NtCurrentThread(),
UserThreadCsrApiPort,
&CsrApiPort,
sizeof(HANDLE));
if (!NT_SUCCESS(Status)) {
return Status;
}
}
UserAssert(*pulConnectionLen == sizeof(USERCONNECT));
if (*pulConnectionLen != sizeof(USERCONNECT)) {
return STATUS_INVALID_PARAMETER;
}
((PUSERCONNECT)ConnectionInformation)->dwDispatchCount = gDispatchTableValues;
return NtUserProcessConnect(Process->ProcessHandle,
(PUSERCONNECT)ConnectionInformation, *pulConnectionLen);
}
VOID RegReadApcProcedure(
PVOID RegReadApcContext,
PIO_STATUS_BLOCK IoStatus)
{
UNICODE_STRING ValueString;
LONG Status;
BYTE Buf[sizeof(KEY_VALUE_PARTIAL_INFORMATION) + sizeof(DWORD)];
DWORD cbSize;
ULONG l;
UNREFERENCED_PARAMETER(RegReadApcContext);
UNREFERENCED_PARAMETER(IoStatus);
RtlInitUnicodeString(&ValueString, L"Win32PrioritySeparation");
Status = NtQueryValueKey(hKeyPriority,
&ValueString,
KeyValuePartialInformation,
(PKEY_VALUE_PARTIAL_INFORMATION)Buf,
sizeof(Buf),
&cbSize);
if (NT_SUCCESS(Status)) {
l = *((PDWORD)((PKEY_VALUE_PARTIAL_INFORMATION)Buf)->Data);
} else {
l = PROCESS_PRIORITY_SEPARATION_MAX; // last resort default
}
NtSetSystemInformation(SystemPrioritySeperation,&l,sizeof(ULONG));
NtNotifyChangeKey(hKeyPriority,
NULL,
(PIO_APC_ROUTINE)RegReadApcProcedure,
NULL,
&IoStatusRegChange,
REG_NOTIFY_CHANGE_LAST_SET,
FALSE,
&RegChangeBuffer,
sizeof(RegChangeBuffer),
TRUE);
}
VOID StartRegReadRead(
VOID)
{
UNICODE_STRING UnicodeString;
OBJECT_ATTRIBUTES OA;
RtlInitUnicodeString(&UnicodeString,
L"\\Registry\\Machine\\System\\CurrentControlSet\\Control\\PriorityControl");
InitializeObjectAttributes(&OA, &UnicodeString, OBJ_CASE_INSENSITIVE, NULL, NULL);
UserVerify(NT_SUCCESS(NtOpenKey(&hKeyPriority, KEY_READ | KEY_NOTIFY, &OA)));
RegReadApcProcedure(NULL, NULL);
}
/***************************************************************************\
* HandlePowerCallout
*
* This handles properly attaching to a desktop and calling into the kernel
* to handle a power-related event.
*
* History:
* 11/20/2001 JasonSch Created.
\***************************************************************************/
VOID HandlePowerCallout(
VOID)
{
NTSTATUS Status;
USERTHREAD_USEDESKTOPINFO utudi;
/*
* Attach to the desktop before calling into the kernel. This is
* necessary because (at least) if a window gets destroyed when we
* callback in xxxxSendBSMtoDesktop, we will call xxxRedrawWindow,
* which requires the calling thread to be on a desktop if the PWND
* passed in is NULL.
*/
utudi.hThread = NULL;
utudi.drdRestore.pdeskRestore = NULL;
Status = NtUserSetInformationThread(NtCurrentThread(),
UserThreadUseActiveDesktop,
&utudi, sizeof(utudi));
if (NT_SUCCESS(Status)) {
NtUserCallNoParam(SFI_XXXUSERPOWERCALLOUTWORKER);
/*
* Now unattach from the desktop.
*/
Status = NtUserSetInformationThread(NtCurrentThread(),
UserThreadUseDesktop,
&utudi,
sizeof(utudi));
UserAssert(NT_SUCCESS(Status));
}
}
/***************************************************************************\
* HandleMediaChangeEvent
*
* This routine is responsible for broadcasting the WM_DEVICECHANGE message
* when media arrives or is removed from a CD-ROM device.
*
* History:
* 23-Feb-96 BradG Modified to handle event per CD-ROM device
* 23-April-96 Salimc Some CD-ROM drives notify us that media has
* arrived before the drive has recognized that it had
* new media. The call to DeviceIoctl() will fail in
* this case. To fix this we made the following changes
*
* aDriveState is an array of tri-state global variable
* for each drive.Each variable starts off in an UNKNOWN
* state and on the first event with any drive we do the
* full MAX_TRIES or less CHECK_VERIFY's which then gets
* us into either a INSERTED or EJECTED state. From then
* on we know that each new event is going to be the
* opposite of what we currently have.
*
* UNKNOWN => do upto MAX_TRIES CHECK_VERIFY's with
* delay to get into EJECTED or INSERTED state.
*
* INSERTED => do 1 CHECK_VERIFY to get into
* EJECTED state
*
* EJECTED => do upto MAX_TRIES CHECK_VERIFY's with
* delay to get into INSERTED state
*
\***************************************************************************/
VOID HandleMediaChangeEvent(
VOID)
{
/*
* Local variables
*/
DWORD dwRecipients;
BOOL bResult;
NTSTATUS Status;
DEV_BROADCAST_VOLUME dbcvInfo;
USERTHREAD_USEDESKTOPINFO utudi;
ULONG cDrive;
while (cDrive = (ULONG)NtUserCallNoParam(SFI_XXXGETDEVICECHANGEINFO)) {
/*
* Determine if it's an arrival or removal
*/
bResult = (cDrive & HMCE_ARRIVAL);
cDrive &= ~HMCE_ARRIVAL;
/*
* Initialize the structures used for BroadcastSystemMessage
*/
dbcvInfo.dbcv_size = sizeof(dbcvInfo);
dbcvInfo.dbcv_devicetype = DBT_DEVTYP_VOLUME;
dbcvInfo.dbcv_reserved = 0;
dbcvInfo.dbcv_flags = DBTF_MEDIA;
dbcvInfo.dbcv_unitmask = cDrive;
dwRecipients = BSM_ALLCOMPONENTS | BSM_ALLDESKTOPS;
/*
* Temporarily we must assign this thread to a desktop so we can
* call USER's BroascastSystemMessage() routine. We call the
* private SetThreadDesktopToDefault() to assign ourselves to the
* desktop that is currently receiving input.
*/
utudi.hThread = NULL;
utudi.drdRestore.pdeskRestore = NULL;
Status = NtUserSetInformationThread(NtCurrentThread(),
UserThreadUseActiveDesktop,
&utudi, sizeof(utudi));
if (NT_SUCCESS(Status)) {
/*
* Broadcast the message
*/
BroadcastSystemMessage(BSF_FORCEIFHUNG | ((bResult) ? BSF_ALLOWSFW : 0),
&dwRecipients,
WM_DEVICECHANGE,
// HACK: need to or 0x8000 in wParam
// because this is a flag to let
// BSM know that lParam is a pointer
// to a data structure.
0x8000 | ((bResult) ? DBT_DEVICEARRIVAL : DBT_DEVICEREMOVECOMPLETE),
(LPARAM)&dbcvInfo);
/*
* Set our thread's desktop back to NULL. This will decrement
* the desktop's reference count.
*/
NtUserSetInformationThread(NtCurrentThread(),
UserThreadUseDesktop,
&utudi,
sizeof(utudi));
}
}
}
DWORD
GetNetworkDrives(
)
/*++
Routine Description:
Returns a drive bitmask similar to GetLogicalDrives, but including
only the network drives.
Arguments:
Return Value:
--*/
{
DWORD Mask = 0;
DWORD DriveNumber;
PROCESS_DEVICEMAP_INFORMATION ProcessDeviceMapInfo;
if (NT_SUCCESS(NtQueryInformationProcess( NtCurrentProcess(),
ProcessDeviceMap,
&ProcessDeviceMapInfo.Query,
sizeof( ProcessDeviceMapInfo.Query ),
NULL
))) {
// For all the drives from C to Z
for (DriveNumber = 2; DriveNumber < 26; DriveNumber++)
{
if (ProcessDeviceMapInfo.Query.DriveType[DriveNumber] == DOSDEVICE_DRIVE_REMOTE)
{
Mask |= (1 << DriveNumber);
}
}
}
return Mask;
}
VOID
HandleRemoteNetDeviceChangeEvent(
)
/*++
Routine Description:
Arguments:
Return Value:
--*/
{
DWORD NetDrives;
DEV_BROADCAST_VOLUME dbv;
LONG status;
USERTHREAD_USEDESKTOPINFO utudi;
/*
* Temporarily we must assign this thread to a desktop so we can
* call USER's BroascastSystemMessage() routine. We call the
* private SetThreadDesktopToDefault() to assign ourselves to the
* desktop that is currently receiving input.
*/
utudi.hThread = NULL;
utudi.drdRestore.pdeskRestore = NULL;
status = NtUserSetInformationThread(NtCurrentThread(),
UserThreadUseActiveDesktop,
&utudi, sizeof(utudi));
if (!NT_SUCCESS(status)) {
return;
}
//
// Keep broadcasting until the set of net drives stops changing
//
for (;;)
{
//
// Get the current net drive bitmask and compare against the net
// drive bitmask when we last broadcast
//
NetDrives = GetNetworkDrives();
if (NetDrives == LastNetDrives)
{
break;
}
//
// Broadcast about deleted volumes
//
dbv.dbcv_size = sizeof(dbv);
dbv.dbcv_devicetype = DBT_DEVTYP_VOLUME;
dbv.dbcv_reserved = 0;
dbv.dbcv_unitmask = LastNetDrives & ~NetDrives;
dbv.dbcv_flags = DBTF_NET;
if (dbv.dbcv_unitmask != 0)
{
DWORD dwRec = BSM_APPLICATIONS | BSM_ALLDESKTOPS;
status = BroadcastSystemMessage(
BSF_FORCEIFHUNG | BSF_NOHANG | BSF_NOTIMEOUTIFNOTHUNG,
&dwRec,
WM_DEVICECHANGE,
(WPARAM) DBT_DEVICEREMOVECOMPLETE,
(LPARAM)(DEV_BROADCAST_HDR*)(&dbv)
);
}
//
// Broadcast about added volumes
//
dbv.dbcv_unitmask = NetDrives & ~LastNetDrives;
if (dbv.dbcv_unitmask != 0)
{
DWORD dwRec = BSM_APPLICATIONS | BSM_ALLDESKTOPS;
status = BroadcastSystemMessage(
BSF_FORCEIFHUNG | BSF_NOHANG | BSF_NOTIMEOUTIFNOTHUNG,
&dwRec,
WM_DEVICECHANGE,
(WPARAM) DBT_DEVICEARRIVAL,
(LPARAM)(DEV_BROADCAST_HDR*)(&dbv)
);
}
//
// Remember the drive set that we last broadcast about
//
LastNetDrives = NetDrives;
//
// Go around the loop again to detect changes that may have occurred
// while we were broadcasting
//
}
/*
* Set our thread's desktop back to NULL. This will decrement
* the desktop's reference count.
*/
NtUserSetInformationThread(NtCurrentThread(),
UserThreadUseDesktop,
&utudi,
sizeof(utudi));
return;
}
BOOL
CreateBSMEventSD(
PSECURITY_DESCRIPTOR * SecurityDescriptor
)
/*++
Routine Description:
This function creates a security descriptor for the BSM request event.
It grants EVENT_ALL_ACCESS to local system and EVENT_MODIFY_STATE access
to the rest of the world. This prevents principals other than local
system from waiting for the event.
Arguments:
SecurityDescriptor - Receives a pointer to the new security descriptor.
Should be freed with LocalFree.
Return Value:
TRUE - success
FALSE - failure, use GetLastError
--*/
{
NTSTATUS Status;
ULONG AclLength;
PACL EventDacl;
PSID WorldSid = NULL;
PSID SystemSid = NULL;
SID_IDENTIFIER_AUTHORITY NtSidAuthority = SECURITY_NT_AUTHORITY;
SID_IDENTIFIER_AUTHORITY WorldSidAuthority = SECURITY_WORLD_SID_AUTHORITY;
BOOL retval = TRUE;
*SecurityDescriptor = NULL;
Status = RtlAllocateAndInitializeSid( &NtSidAuthority,
1,
SECURITY_LOCAL_SYSTEM_RID,
0, 0, 0, 0, 0, 0, 0,
&SystemSid );
if (!NT_SUCCESS(Status)) {
retval = FALSE;
goto Cleanup;
}
Status = RtlAllocateAndInitializeSid( &WorldSidAuthority,
1,
SECURITY_WORLD_RID,
0, 0, 0, 0, 0, 0, 0,
&WorldSid );
if (!NT_SUCCESS(Status)) {
retval = FALSE;
goto Cleanup;
}
//
// Allocate a buffer to contain the SD followed by the DACL
// Note, the well-known SIDs are expected to have been created
// by this time
//
AclLength = (ULONG)sizeof(ACL) +
(2*((ULONG)sizeof(ACCESS_ALLOWED_ACE))) +
RtlLengthSid( SystemSid ) +
RtlLengthSid( WorldSid ) +
8; // 8 is for good measure
*SecurityDescriptor = (PSECURITY_DESCRIPTOR)
LocalAlloc( 0, SECURITY_DESCRIPTOR_MIN_LENGTH + AclLength );
if (*SecurityDescriptor == NULL) {
retval = FALSE;
goto Cleanup;
}
EventDacl = (PACL) ((BYTE*)(*SecurityDescriptor) + SECURITY_DESCRIPTOR_MIN_LENGTH);
//
// Set up a default ACL
//
// Public: WORLD:EVENT_MODIFY_STATE, SYSTEM:all
Status = RtlCreateAcl( EventDacl, AclLength, ACL_REVISION2);
if (!NT_SUCCESS(Status)) {
retval = FALSE;
goto Cleanup;
}
//
// WORLD access
//
Status = RtlAddAccessAllowedAce (
EventDacl,
ACL_REVISION2,
EVENT_MODIFY_STATE,
WorldSid
);
if (!NT_SUCCESS(Status)) {
retval = FALSE;
goto Cleanup;
}
//
// SYSTEM access
//
Status = RtlAddAccessAllowedAce (
EventDacl,
ACL_REVISION2,
EVENT_ALL_ACCESS,
SystemSid
);
if (!NT_SUCCESS(Status)) {
retval = FALSE;
goto Cleanup;
}
//
// Now initialize security descriptors
// that export this protection
//
Status = RtlCreateSecurityDescriptor(
*SecurityDescriptor,
SECURITY_DESCRIPTOR_REVISION1
);
if (!NT_SUCCESS(Status)) {
retval = FALSE;
goto Cleanup;
}
Status = RtlSetDaclSecurityDescriptor(
*SecurityDescriptor,
TRUE, // DaclPresent
EventDacl,
FALSE // DaclDefaulted
);
if (!NT_SUCCESS(Status)) {
retval = FALSE;
goto Cleanup;
}
Cleanup:
if (WorldSid) {
RtlFreeSid(WorldSid);
}
if (SystemSid) {
RtlFreeSid(SystemSid);
}
if ((retval == FALSE) && (*SecurityDescriptor != NULL)) {
LocalFree(*SecurityDescriptor);
*SecurityDescriptor = NULL;
}
return retval;
}
NTSTATUS NotificationThread(
PVOID ThreadParameter)
{
KPRIORITY Priority;
NTSTATUS Status;
HANDLE hEvent[ID_NUM_EVENTS];
WCHAR szObjectStr[MAX_SESSION_PATH];
OBJECT_ATTRIBUTES Attributes;
UNICODE_STRING UnicodeString;
PSECURITY_DESCRIPTOR pSD = NULL;
ULONG NumEvents = ID_NUM_EVENTS;
UNREFERENCED_PARAMETER(ThreadParameter);
Priority = LOW_PRIORITY + 3;
Status = NtSetInformationThread(GetCurrentThread(),
ThreadPriority,
&Priority,
sizeof(KPRIORITY));
UserAssert(NT_SUCCESS(Status));
UserAssert(ghNlsEvent && ghPowerRequestEvent && ghMediaRequestEvent);
/*
* Setup the NLS event.
*/
hEvent[ID_NLS] = ghNlsEvent;
/*
* Setup the power request event.
*/
hEvent[ID_POWER] = ghPowerRequestEvent;
/*
* Setup the MediaChangeEvent.
*/
hEvent[ID_MEDIACHANGE] = ghMediaRequestEvent;
/*
* Setup the NetDeviceChange Event (only on remote sessions).
*/
if (gSessionId != 0) {
swprintf(szObjectStr,
L"%ws\\%ld\\BaseNamedObjects\\%ws",
SESSION_ROOT,
gSessionId,
SC_BSM_EVENT_NAME);
RtlInitUnicodeString(&UnicodeString, szObjectStr);
if (CreateBSMEventSD(&pSD)) {
InitializeObjectAttributes(&Attributes,
&UnicodeString,
OBJ_CASE_INSENSITIVE | OBJ_OPENIF,
NULL,
pSD);
if (!NT_SUCCESS(NtCreateEvent(&hEvent[ID_NETDEVCHANGE], EVENT_ALL_ACCESS, &Attributes, SynchronizationEvent, FALSE))) {
NumEvents--;
}
LocalFree(pSD);
} else {
NumEvents--;
}
} else {
NumEvents--;
}
StartRegReadRead();
/*
* Sit and wait forever.
*/
while (TRUE) {
Status = NtWaitForMultipleObjects(NumEvents,
hEvent,
WaitAny,
TRUE,
NULL);
if (Status == ID_NLS + WAIT_OBJECT_0) {
/*
* Handle the NLS event.
*/
if (gfLogon) {
gfLogon = FALSE;
BaseSrvNlsUpdateRegistryCache(NULL, NULL);
}
} else if (Status == ID_POWER + WAIT_OBJECT_0) {
/*
* Handle the power request event.
*/
HandlePowerCallout();
} else if (Status == ID_MEDIACHANGE + WAIT_OBJECT_0) {
/*
* Handle the media change event.
*/
HandleMediaChangeEvent();
NtResetEvent(hEvent[ID_MEDIACHANGE], NULL);
} else if (Status == ID_NETDEVCHANGE + WAIT_OBJECT_0) {
/*
* Handle the NetDevice change event for remote sessions.
*/
HandleRemoteNetDeviceChangeEvent();
}
}
UserExitWorkerThread(STATUS_SUCCESS);
return STATUS_SUCCESS;
}
UINT GetRegIntFromID(
HKEY hKey,
int KeyID,
UINT nDefault)
{
LPWSTR lpszValue;
BOOL fAllocated;
UNICODE_STRING Value;
DWORD cbSize;
BYTE Buf[sizeof(KEY_VALUE_PARTIAL_INFORMATION) + 20 * sizeof(WCHAR)];
NTSTATUS Status;
UINT ReturnValue;
lpszValue = RtlLoadStringOrError(ghModuleWin,
KeyID,
NULL,
&fAllocated,
FALSE);
RtlInitUnicodeString(&Value, lpszValue);
Status = NtQueryValueKey(hKey,
&Value,
KeyValuePartialInformation,
(PKEY_VALUE_PARTIAL_INFORMATION)Buf,
sizeof(Buf),
&cbSize);
if (NT_SUCCESS(Status)) {
/*
* Convert string to int.
*/
RtlInitUnicodeString(&Value, (LPWSTR)((PKEY_VALUE_PARTIAL_INFORMATION)Buf)->Data);
RtlUnicodeStringToInteger(&Value, 10, &ReturnValue);
} else {
ReturnValue = nDefault;
}
LocalFree(lpszValue);
return ReturnValue;
}
VOID GetTimeouts(
VOID)
{
HANDLE hCurrentUserKey;
HANDLE hKey;
NTSTATUS Status;
Status = RtlOpenCurrentUser(MAXIMUM_ALLOWED, &hCurrentUserKey);
if (NT_SUCCESS(Status)) {
Status = MyRegOpenKey(hCurrentUserKey,
L"Control Panel\\Desktop",
&hKey);
if (NT_SUCCESS(Status)) {
gCmsHungAppTimeout = GetRegIntFromID(
hKey,
STR_CMSHUNGAPPTIMEOUT,
CMSHUNGAPPTIMEOUT);
gCmsWaitToKillTimeout = GetRegIntFromID(
hKey,
STR_CMSWAITTOKILLTIMEOUT,
CMSWAITTOKILLTIMEOUT);
/*
* Need to protect ourselves from users mucking about the registry.
*/
if (gCmsHungAppTimeout == 0) {
gCmsHungAppTimeout = CMSHUNGAPPTIMEOUT;
}
gdwHungToKillCount = gCmsWaitToKillTimeout / gCmsHungAppTimeout;
gfAutoEndTask = GetRegIntFromID(
hKey,
STR_AUTOENDTASK,
gfAutoEndTask);
NtClose(hKey);
}
NtClose(hCurrentUserKey);
}
Status = MyRegOpenKey(NULL,
L"\\Registry\\Machine\\System\\CurrentControlSet\\Control",
&hKey);
if (NT_SUCCESS(Status)) {
gdwServicesWaitToKillTimeout = GetRegIntFromID(
hKey,
STR_WAITTOKILLSERVICETIMEOUT,
gCmsWaitToKillTimeout);
gdwProcessTerminateTimeout = GetRegIntFromID(
hKey,
STR_PROCESSTERMINATETIMEOUT,
PROCESSTERMINATETIMEOUT);
if (gdwProcessTerminateTimeout < CMSHUNGAPPTIMEOUT) {
gdwProcessTerminateTimeout = CMSHUNGAPPTIMEOUT;
}
NtClose(hKey);
}
}
ULONG
SrvLogon(
IN OUT PCSR_API_MSG m,
IN OUT PCSR_REPLY_STATUS ReplyStatus)
{
PLOGONMSG a = (PLOGONMSG)&m->u.ApiMessageData;
NTSTATUS Status;
UNREFERENCED_PARAMETER(ReplyStatus);
if (!CsrImpersonateClient(NULL)) {
return STATUS_UNSUCCESSFUL;
}
if (a->fLogon) {
/*
* Flush the MultiLingual UI (MUI) alternate resource modules from
* within NTDLL, so that the new user logging-on gets his chance to
* load his own.
*/
LdrFlushAlternateResourceModules();
/*
* Take care of NLS cache for LogON.
*/
BaseSrvNlsLogon(TRUE);
/*
* Set the cleanup event so that the RIT can handle the NLS
* registry notification.
*/
gfLogon = TRUE;
Status = NtSetEvent(ghNlsEvent, NULL);
ASSERT(NT_SUCCESS(Status));
} else {
/*
* Take care of NLS cache for LogOFF.
*/
BaseSrvNlsLogon(FALSE);
}
/*
* Get timeout values from registry.
*/
GetTimeouts();
CsrRevertToSelf();
/*
* Initialize console attributes.
*/
InitializeConsoleAttributes();
return STATUS_SUCCESS;
}
ULONG
SrvRegisterLogonProcess(
IN OUT PCSR_API_MSG m,
IN OUT PCSR_REPLY_STATUS ReplyStatus)
{
NTSTATUS Status;
UNREFERENCED_PARAMETER(ReplyStatus);
/*
* Fail if this is not the first call.
*/
EnterCrit();
if (gIdLogon == 0) {
gIdLogon = *(DWORD*)m->u.ApiMessageData;
Status = STATUS_SUCCESS;
} else {
Status = STATUS_UNSUCCESSFUL;
}
LeaveCrit();
return Status;
}
#if DBG
ULONG
SrvWin32HeapFail(
IN OUT PCSR_API_MSG m,
IN OUT PCSR_REPLY_STATUS ReplyStatus)
{
PWIN32HEAPFAILMSG a = (PWIN32HEAPFAILMSG)&m->u.ApiMessageData;
UNREFERENCED_PARAMETER(ReplyStatus);
Win32HeapFailAllocations(a->bFail);
return STATUS_SUCCESS;
}
ULONG
SrvWin32HeapStat(
IN OUT PCSR_API_MSG m,
IN OUT PCSR_REPLY_STATUS ReplyStatus)
{
extern DWORD Win32HeapStat(PDBGHEAPSTAT phs, DWORD dwLen, BOOL bNeedTagShift);
PWIN32HEAPSTATMSG a = (PWIN32HEAPSTATMSG)&m->u.ApiMessageData;
UNREFERENCED_PARAMETER(ReplyStatus);
if (!CsrValidateMessageBuffer(m, &a->phs, a->dwLen, sizeof(BYTE))) {
return STATUS_INVALID_PARAMETER;
}
a->dwMaxTag = Win32HeapStat(a->phs, a->dwLen, TRUE);
return STATUS_SUCCESS;
}
#endif
ULONG
SrvGetThreadConsoleDesktop(
IN OUT PCSR_API_MSG m,
IN OUT PCSR_REPLY_STATUS ReplyStatus)
{
PGETTHREADCONSOLEDESKTOPMSG a = (PGETTHREADCONSOLEDESKTOPMSG)&m->u.ApiMessageData;
UNREFERENCED_PARAMETER(ReplyStatus);
return GetThreadConsoleDesktop(a->dwThreadId, &a->hdeskConsole);
}
/***************************************************************************\
* FindWindowFromThread
*
* This is a callback function passed to EnumThreadWindows by to find a top
* level window owned by a given thread. Ideally, we want to find a top level
* owner.
*
* 07/18/96 GerardoB Created
\***************************************************************************/
BOOL CALLBACK FindWindowFromThread(
HWND hwnd,
LPARAM lParam)
{
BOOL fTopLevelOwner;
#ifdef FE_IME
if (IsImeWindow(hwnd)) {
return TRUE;
}
#endif
fTopLevelOwner = (GetWindow(hwnd, GW_OWNER) == NULL);
if (*((HWND *)lParam) == NULL || fTopLevelOwner) {
*((HWND *)lParam) = hwnd;
}
return !fTopLevelOwner;
}
#if DBG
DWORD GetRipComponent(
VOID)
{
return RIP_USERSRV;
}
#endif
/***************************************************************************\
* StartCreateSystemThreads
*
* Simply calls xxxCreateSystemThreads which will calls to the right
* thread routine (depending on uThreadID).
*
* History:
* 15-Mar-00 MHamid Created.
\***************************************************************************/
VOID StartCreateSystemThreads(
PVOID pUnused)
{
PCSR_THREAD pt = CsrConnectToUser();
UNREFERENCED_PARAMETER(pUnused);
NtUserCallOneParam(FALSE, SFI_XXXCREATESYSTEMTHREADS);
if (pt) {
CsrDereferenceThread(pt);
}
UserExitWorkerThread(STATUS_SUCCESS);
}
/***************************************************************************\
* SrvCreateSystemThreads
*
* Just creates a thread (at StartCreateSystemThreads) and return.
*
* History:
* 15-Mar-00 MHamid Created.
\***************************************************************************/
ULONG SrvCreateSystemThreads(
IN OUT PCSR_API_MSG m,
IN OUT PCSR_REPLY_STATUS ReplyStatus)
{
NTSTATUS Status = STATUS_UNSUCCESSFUL;
HANDLE UniqueProcessId;
HANDLE hProcess;
BOOL bRemoteThread;
CLIENT_ID ClientId;
PCREATESYSTEMTHREADSMSG pCreateSystemThreads = (PCREATESYSTEMTHREADSMSG)&m->u.ApiMessageData;
UNREFERENCED_PARAMETER(ReplyStatus);
if ((bRemoteThread = pCreateSystemThreads->bRemoteThread)) {
LPTHREAD_START_ROUTINE ThreadStart = NULL;
PCSR_PROCESS Process;
HANDLE huser32 = GetModuleHandle(TEXT("user32.dll"));
if (huser32) {
ThreadStart = (LPTHREAD_START_ROUTINE)GetProcAddress(huser32, "CreateSystemThreads");
}
if (ThreadStart) {
UniqueProcessId = (HANDLE)NtUserCallNoParam(SFI_GETREMOTEPROCESSID);
Status = CsrLockProcessByClientId(UniqueProcessId, &Process);
if (!NT_SUCCESS(Status)) {
RIPMSG1(RIP_WARNING,
"SrvCreateSystemThreads: CsrLockProcessByClientId failed for remote thread with Status 0x%x", Status);
NtUserCallOneParam(TRUE, SFI_HANDLESYSTEMTHREADCREATIONFAILURE);
return Status;
}
hProcess = Process->ProcessHandle;
Status = RtlCreateUserThread(hProcess, NULL, FALSE, 0, 0, 0x4000, ThreadStart, NULL, NULL, &ClientId);
CsrUnlockProcess(Process);
}
if (!NT_SUCCESS(Status)) {
RIPMSG1(RIP_WARNING,
"SrvCreateSystemThreads: Failed for remote thread with Status 0x%x", Status);
NtUserCallOneParam(TRUE, SFI_HANDLESYSTEMTHREADCREATIONFAILURE);
}
} else {
Status = CsrExecServerThread((PUSER_THREAD_START_ROUTINE)StartCreateSystemThreads, 0);
if (!NT_SUCCESS(Status)) {
RIPMSG1(RIP_WARNING,
"SrvCreateSystemThreads: RtlCreateUserThread failed with Status 0x%x", Status);
NtUserCallOneParam(FALSE, SFI_HANDLESYSTEMTHREADCREATIONFAILURE);
}
}
return Status;
}