|
|
/**************************************************************************\
* 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; }
|