|
|
// --------------------------------------------------------------------------
// Module Name: ThemeServerClient.cpp
//
// Copyright (c) 2000, Microsoft Corporation
//
// This file contains a class that implements the theme server functions that
// are executed in a client context (winlogon context).
//
// History: 2000-11-29 vtan created
// --------------------------------------------------------------------------
#include "StandardHeader.h"
#define STRSAFE_LIB
#include <strsafe.h>
#include "ThemeServerClient.h"
#include <lpcthemes.h>
#include <uxthemep.h>
#include <UxThemeServer.h>
#include "SingleThreadedExecution.h"
#include "StatusCode.h"
#include "ThemeManagerService.h"
#include <Impersonation.h>
// --------------------------------------------------------------------------
// CThemeManagerAPI::s_pThemeManagerAPIServer
// CThemeManagerAPI::s_hPort
// CThemeManagerAPI::s_hToken
// CThemeManagerAPI::s_hEvent
// CThemeManagerAPI::s_hWaitObject
// CThemeManagerAPI::s_pLock
//
// Purpose: Static member variables.
//
// History: 2000-11-29 vtan created
// --------------------------------------------------------------------------
CThemeManagerAPIServer* CThemeServerClient::s_pThemeManagerAPIServer = NULL; HANDLE CThemeServerClient::s_hPort = NULL; HANDLE CThemeServerClient::s_hToken = NULL; HANDLE CThemeServerClient::s_hEvent = NULL; HANDLE CThemeServerClient::s_hWaitObject = NULL; HMODULE CThemeServerClient::s_hModuleUxTheme = NULL; CCriticalSection* CThemeServerClient::s_pLock = NULL;
// --------------------------------------------------------------------------
// CThemeServerClient::WaitForServiceReady
//
// Arguments: dwTimeout = Number of ticks to wait.
//
// Returns: DWORD
//
// Purpose: Check if the service is autostart. If so then wait the
// designated amount of time for the service. If the service
// is then running or was running but isn't autostart then
// re-establish the connection to the server.
//
// History: 2000-10-10 vtan created
// 2000-11-29 vtan converted to a Win32 service
// --------------------------------------------------------------------------
DWORD CThemeServerClient::WaitForServiceReady (DWORD dwTimeout)
{ DWORD dwWaitResult; NTSTATUS status;
dwWaitResult = WAIT_TIMEOUT; if (s_pThemeManagerAPIServer->IsAutoStart()) { status = s_pThemeManagerAPIServer->Wait(dwTimeout); #ifdef DBG
if (STATUS_TIMEOUT == status) { INFORMATIONMSG("Wait on auto start theme service timed out."); } #endif /* DBG */
} else { status = STATUS_SUCCESS; } if (NT_SUCCESS(status) && s_pThemeManagerAPIServer->IsRunning()) { status = ReestablishConnection(); if (NT_SUCCESS(status)) { THR(InitUserRegistry()); THR(InitUserTheme(FALSE)); dwWaitResult = WAIT_OBJECT_0; } } return(dwWaitResult); }
// --------------------------------------------------------------------------
// CThemeServerClient::WatchForStart
//
// Arguments: <none>
//
// Returns: NTSTATUS
//
// Purpose: Opens or creates the theme server announce event. This is a
// manual reset event which the theme server pulses when it
// starts up. This allows winlogon to initiate new connections
// to the theme server without having to wait for logon or
// logoff events to happen.
//
// This event is intentionally leaked and cleaned up when the
// winlogon process for the session goes away.
//
// History: 2000-11-29 vtan created
// --------------------------------------------------------------------------
NTSTATUS CThemeServerClient::WatchForStart (void)
{ NTSTATUS status;
s_hEvent = CThemeManagerService::OpenStartEvent(NtCurrentPeb()->SessionId, SYNCHRONIZE); if (s_hEvent != NULL) { if (RegisterWaitForSingleObject(&s_hWaitObject, s_hEvent, CB_ServiceStart, NULL, INFINITE, WT_EXECUTEDEFAULT | WT_EXECUTEONLYONCE) != FALSE) { status = STATUS_SUCCESS; } else { status = CStatusCode::StatusCodeOfLastError(); } } else { status = CStatusCode::StatusCodeOfLastError(); } return(status); }
// --------------------------------------------------------------------------
// CThemeServerClient::UserLogon
//
// Arguments: hToken = Token of user logging on.
//
// Returns: NTSTATUS
//
// Purpose: Signals the server that a user is logging on and gives the
// server the handle to the token. The server will grant access
// to the port based on the user's logon SID. Then perform work
// to initialize the environment for the user logging on.
//
// History: 2000-10-10 vtan created
// 2000-11-29 vtan converted to a Win32 service
// --------------------------------------------------------------------------
NTSTATUS CThemeServerClient::UserLogon (HANDLE hToken)
{ NTSTATUS status;
status = NotifyUserLogon(hToken); if (STATUS_PORT_DISCONNECTED == status) { status = ReestablishConnection(); if (NT_SUCCESS(status)) { status = NotifyUserLogon(hToken); } } return(status); }
// --------------------------------------------------------------------------
// CThemeServerClient::UserLogoff
//
// Arguments: <none>
//
// Returns: NTSTATUS
//
// Purpose: Signals the server that the current user for this session is
// logging off. The server will remove the access that was
// granted at logon and reinitialize the theme settings to the
// ".Default" settings.
//
// History: 2000-10-10 vtan created
// 2000-11-29 vtan converted to a Win32 service
// --------------------------------------------------------------------------
NTSTATUS CThemeServerClient::UserLogoff (void)
{ NTSTATUS status;
status = NotifyUserLogoff(); if (STATUS_PORT_DISCONNECTED == status) { status = ReestablishConnection(); if (NT_SUCCESS(status)) { status = NotifyUserLogoff(); } } return(status); }
// --------------------------------------------------------------------------
// CThemeServerClient::UserInitTheme
//
// Arguments: BOOL
//
// Returns: NTSTATUS
//
// Purpose: Called at logon, or when Terminal Server connects a user to a
// remote session or reconnects to a local session. Needs to
// evaluate the environment and decide if themes need to be loaded
// or unloaded.
//
// History: 2000-01-18 rfernand created
// --------------------------------------------------------------------------
NTSTATUS CThemeServerClient::UserInitTheme (BOOL fPolicyCheckOnly)
{ bool fSuccessfulImpersonation;
// If there's a token impersonate the user. Otherwise use the system context.
if (s_hToken != NULL) { fSuccessfulImpersonation = NT_SUCCESS(CImpersonation::ImpersonateUser(GetCurrentThread(), s_hToken)); } else { fSuccessfulImpersonation = true; } if (fSuccessfulImpersonation) { (HRESULT)InitUserTheme(fPolicyCheckOnly); } if (fSuccessfulImpersonation && (s_hToken != NULL)) { if (RevertToSelf() == FALSE) { return CStatusCode::StatusCodeOfLastError(); } } return STATUS_SUCCESS; }
// --------------------------------------------------------------------------
// CThemeServerClient::StaticInitialize
//
// Arguments: <none>
//
// Returns: NTSTATUS
//
// Purpose: Initializes static member variables. Allocate a
// CThemeManagerAPIServer and a lock for this object.
//
// History: 2000-11-29 vtan created
// --------------------------------------------------------------------------
NTSTATUS CThemeServerClient::StaticInitialize (void)
{ NTSTATUS status;
if (s_pThemeManagerAPIServer == NULL) { status = STATUS_NO_MEMORY; s_pThemeManagerAPIServer = new CThemeManagerAPIServer; if (s_pThemeManagerAPIServer != NULL) { s_pLock = new CCriticalSection; if (s_pLock != NULL) { status = STATUS_SUCCESS; } } } else { status = STATUS_SUCCESS; } return(status); }
// --------------------------------------------------------------------------
// CThemeServerClient::StaticTerminate
//
// Arguments: <none>
//
// Returns: NTSTATUS
//
// Purpose: Release static member variables initialized.
//
// History: 2000-11-29 vtan created
// --------------------------------------------------------------------------
NTSTATUS CThemeServerClient::StaticTerminate (void)
{ if (s_pLock != NULL) { delete s_pLock; s_pLock = NULL; } if (s_pThemeManagerAPIServer != NULL) { s_pThemeManagerAPIServer->Release(); s_pThemeManagerAPIServer = NULL; } return(STATUS_SUCCESS); }
// --------------------------------------------------------------------------
// CThemeServerClient::NotifyUserLogon
//
// Arguments: <none>
//
// Returns: NTSTATUS
//
// Purpose: Execute the send message to the server and tell it that the
// given user is now logged on. This will instruct the server
// to grant access to the ThemeApiPort.
//
// History: 2000-11-29 vtan created
// --------------------------------------------------------------------------
NTSTATUS CThemeServerClient::NotifyUserLogon (HANDLE hToken)
{ NTSTATUS status = STATUS_NO_MEMORY;
if (s_pLock != NULL) { CSingleThreadedExecution lock(*s_pLock);
if (s_hPort != NULL) { status = InformServerUserLogon(hToken); } else { status = STATUS_PORT_DISCONNECTED; }
// Keep a copy of the token as well in case of demand start of
// the theme server so we can impersonate the user when we load
// their theme using InitUserTheme. Don't copy it if it already
// exists.
if (s_hToken == NULL) { TBOOL(DuplicateHandle(GetCurrentProcess(), hToken, GetCurrentProcess(), &s_hToken, 0, FALSE, DUPLICATE_SAME_ACCESS)); } } return(status); }
// --------------------------------------------------------------------------
// CThemeServerClient::NotifyUserLogoff
//
// Arguments: <none>
//
// Returns: NTSTATUS
//
// Purpose: Tell the server that the logged on user is logged off. This
// will remove access to ThemeApiPort.
//
// History: 2000-11-29 vtan created
// --------------------------------------------------------------------------
NTSTATUS CThemeServerClient::NotifyUserLogoff (void)
{ NTSTATUS status = STATUS_NO_MEMORY;
if (s_pLock != NULL) { CSingleThreadedExecution lock(*s_pLock);
if (s_hToken != NULL) { ReleaseHandle(s_hToken); if (s_hPort != NULL) { status = InformServerUserLogoff(); } else { status = STATUS_PORT_DISCONNECTED; } } else { status = STATUS_SUCCESS; } } return(status); }
// --------------------------------------------------------------------------
// CThemeServerClient::InformServerUserLogon
//
// Arguments: <none>
//
// Returns: NTSTATUS
//
// Purpose: Tell the server that the logged on user is logged off. This
// will remove access to ThemeApiPort.
//
// History: 2000-12-05 vtan created
// --------------------------------------------------------------------------
NTSTATUS CThemeServerClient::InformServerUserLogon (HANDLE hToken)
{ NTSTATUS status; THEMESAPI_PORT_MESSAGE portMessageIn, portMessageOut;
ZeroMemory(&portMessageIn, sizeof(portMessageIn)); ZeroMemory(&portMessageOut, sizeof(portMessageOut)); portMessageIn.apiThemes.apiGeneric.ulAPINumber = API_THEMES_USERLOGON; portMessageIn.apiThemes.apiSpecific.apiUserLogon.in.hToken = hToken; portMessageIn.portMessage.u1.s1.DataLength = sizeof(API_THEMES); portMessageIn.portMessage.u1.s1.TotalLength = static_cast<CSHORT>(sizeof(THEMESAPI_PORT_MESSAGE)); status = NtRequestWaitReplyPort(s_hPort, &portMessageIn.portMessage, &portMessageOut.portMessage); if (NT_SUCCESS(status)) { status = portMessageOut.apiThemes.apiGeneric.status; if (NT_SUCCESS(status)) { THR(InitUserTheme(FALSE)); } } return(status); }
// --------------------------------------------------------------------------
// CThemeServerClient::InformServerUserLogoff
//
// Arguments: <none>
//
// Returns: NTSTATUS
//
// Purpose: Tell the server that the logged on user is logged off. This
// will remove access to ThemeApiPort.
//
// History: 2000-12-05 vtan created
// --------------------------------------------------------------------------
NTSTATUS CThemeServerClient::InformServerUserLogoff (void)
{ NTSTATUS status; THEMESAPI_PORT_MESSAGE portMessageIn, portMessageOut;
ZeroMemory(&portMessageIn, sizeof(portMessageIn)); ZeroMemory(&portMessageOut, sizeof(portMessageOut)); portMessageIn.apiThemes.apiGeneric.ulAPINumber = API_THEMES_USERLOGOFF; portMessageIn.portMessage.u1.s1.DataLength = sizeof(API_THEMES); portMessageIn.portMessage.u1.s1.TotalLength = static_cast<CSHORT>(sizeof(THEMESAPI_PORT_MESSAGE)); status = NtRequestWaitReplyPort(s_hPort, &portMessageIn.portMessage, &portMessageOut.portMessage); if (NT_SUCCESS(status)) { status = portMessageOut.apiThemes.apiGeneric.status; if (NT_SUCCESS(status)) { THR(InitUserRegistry()); THR(InitUserTheme(FALSE)); } } return(status); }
// --------------------------------------------------------------------------
// CThemeServerClient::SessionCreate
//
// Arguments: <none>
//
// Returns: NTSTATUS
//
// Purpose: Signal the server that a new session is being created. This
// allows the server to allocate a data blob for this session.
//
// History: 2000-11-11 vtan created
// --------------------------------------------------------------------------
NTSTATUS CThemeServerClient::SessionCreate (void)
{ NTSTATUS status = STATUS_NO_MEMORY;
if (s_pLock != NULL) { CSingleThreadedExecution lock(*s_pLock);
if (s_hModuleUxTheme == NULL) { s_hModuleUxTheme = LoadLibrary(TEXT("uxtheme.dll")); } if (s_hModuleUxTheme != NULL) { void *pfnRegister, *pfnUnregister, *pfnClearStockObjects;
// Get the uxtheme function addresses in this process address space.
// 34 = ThemeHooksInstall
// 35 = ThemeHooksRemove
// 62 = ServerClearStockObjects
pfnRegister = GetProcAddress(s_hModuleUxTheme, MAKEINTRESOURCEA(34)); pfnUnregister = GetProcAddress(s_hModuleUxTheme, MAKEINTRESOURCEA(35)); pfnClearStockObjects = GetProcAddress(s_hModuleUxTheme, MAKEINTRESOURCEA(62));
if ((pfnRegister != NULL) && (pfnUnregister != NULL) && (pfnClearStockObjects != NULL)) { DWORD dwStackSizeReserve, dwStackSizeCommit; ULONG ulReturnLength; IMAGE_NT_HEADERS *pNTHeaders; SYSTEM_BASIC_INFORMATION systemBasicInformation; THEMESAPI_PORT_MESSAGE portMessageIn, portMessageOut;
// Get system basic information for stack size defaults.
status = NtQuerySystemInformation(SystemBasicInformation, &systemBasicInformation, sizeof(systemBasicInformation), &ulReturnLength); if (NT_SUCCESS(status)) { dwStackSizeReserve = systemBasicInformation.AllocationGranularity; dwStackSizeCommit = systemBasicInformation.PageSize; } else { dwStackSizeReserve = dwStackSizeCommit = 0; }
// Go to the image header for this process and get the stack size
// defaults if they are specified. Otherwise use system defaults (above).
pNTHeaders = RtlImageNtHeader(NtCurrentPeb()->ImageBaseAddress); if (pNTHeaders != NULL) { dwStackSizeReserve = static_cast<DWORD>(pNTHeaders->OptionalHeader.SizeOfStackReserve); dwStackSizeCommit = static_cast<DWORD>(pNTHeaders->OptionalHeader.SizeOfStackCommit); }
// Make the call.
ZeroMemory(&portMessageIn, sizeof(portMessageIn)); ZeroMemory(&portMessageOut, sizeof(portMessageOut)); portMessageIn.apiThemes.apiGeneric.ulAPINumber = API_THEMES_SESSIONCREATE; portMessageIn.apiThemes.apiSpecific.apiSessionCreate.in.pfnRegister = pfnRegister; portMessageIn.apiThemes.apiSpecific.apiSessionCreate.in.pfnUnregister = pfnUnregister; portMessageIn.apiThemes.apiSpecific.apiSessionCreate.in.pfnClearStockObjects = pfnClearStockObjects; portMessageIn.apiThemes.apiSpecific.apiSessionCreate.in.dwStackSizeReserve = dwStackSizeReserve; portMessageIn.apiThemes.apiSpecific.apiSessionCreate.in.dwStackSizeCommit = dwStackSizeCommit; portMessageIn.portMessage.u1.s1.DataLength = sizeof(API_THEMES); portMessageIn.portMessage.u1.s1.TotalLength = static_cast<CSHORT>(sizeof(THEMESAPI_PORT_MESSAGE)); status = NtRequestWaitReplyPort(s_hPort, &portMessageIn.portMessage, &portMessageOut.portMessage); if (NT_SUCCESS(status)) { status = portMessageOut.apiThemes.apiGeneric.status; } } else { status = CStatusCode::StatusCodeOfLastError(); } } else { status = CStatusCode::StatusCodeOfLastError(); } } return(status); }
// --------------------------------------------------------------------------
// CThemeServerClient::SessionDestroy
//
// Arguments: <none>
//
// Returns: NTSTATUS
//
// Purpose: Signal the server that the current session is about to be
// destroyed. This allows the server to release the data blob
// allocated.
//
// History: 2000-11-11 vtan created
// --------------------------------------------------------------------------
NTSTATUS CThemeServerClient::SessionDestroy (void)
{ NTSTATUS status = STATUS_NO_MEMORY;
if (s_pLock != NULL) { THEMESAPI_PORT_MESSAGE portMessageIn, portMessageOut; CSingleThreadedExecution lock(*s_pLock);
ZeroMemory(&portMessageIn, sizeof(portMessageIn)); ZeroMemory(&portMessageOut, sizeof(portMessageOut)); portMessageIn.apiThemes.apiGeneric.ulAPINumber = API_THEMES_SESSIONDESTROY; portMessageIn.portMessage.u1.s1.DataLength = sizeof(API_THEMES); portMessageIn.portMessage.u1.s1.TotalLength = static_cast<CSHORT>(sizeof(THEMESAPI_PORT_MESSAGE)); status = NtRequestWaitReplyPort(s_hPort, &portMessageIn.portMessage, &portMessageOut.portMessage); if (NT_SUCCESS(status)) { status = portMessageOut.apiThemes.apiGeneric.status; } if (s_hModuleUxTheme != NULL) { TBOOL(FreeLibrary(s_hModuleUxTheme)); s_hModuleUxTheme = NULL; } } return(status); }
// --------------------------------------------------------------------------
// CThemeServerClient::ReestablishConnection
//
// Arguments: <none>
//
// Returns: NTSTATUS
//
// Purpose: Reconnects to theme server. If the reconnection is established
// the re-create the session data. This will not correct any
// disconnected ports that some clients may have but because this
// is called in winlogon it re-establish this correctly for
// session 0 in all cases.
//
// UnregisterUserApiHook must be called to clear any left over
// registrations from a server that died. Then go ahead and
// re-initialize the environment anyway.
//
// History: 2000-11-17 vtan created
// --------------------------------------------------------------------------
NTSTATUS CThemeServerClient::ReestablishConnection (void)
{ NTSTATUS status;
ReleaseHandle(s_hPort); status = s_pThemeManagerAPIServer->ConnectToServer(&s_hPort); if (NT_SUCCESS(status)) { status = SessionCreate(); if (NT_SUCCESS(status)) { (BOOL)UnregisterUserApiHook(); THR(ReestablishServerConnection()); } } return(status); }
// --------------------------------------------------------------------------
// CThemeServerClient::CB_ServiceStart
//
// Arguments: pParameter = User parameter.
// TimerOrWaitFired = Timer or wait fired.
//
// Returns: <none>
//
// Purpose: Callback called when the theme server ready event is signaled.
// This indicates that the service was demand started or
// restarted in the event of failure.
//
// History: 2000-11-29 vtan created
// --------------------------------------------------------------------------
void CALLBACK CThemeServerClient::CB_ServiceStart (void *pParameter, BOOLEAN TimerOrWaitFired)
{ UNREFERENCED_PARAMETER(pParameter); UNREFERENCED_PARAMETER(TimerOrWaitFired);
NTSTATUS status = STATUS_NO_MEMORY;
if (s_pLock != NULL) { CSingleThreadedExecution lock(*s_pLock);
// If there is a connection ping it.
if (s_hPort != NULL) { THEMESAPI_PORT_MESSAGE portMessageIn, portMessageOut;
ZeroMemory(&portMessageIn, sizeof(portMessageIn)); ZeroMemory(&portMessageOut, sizeof(portMessageOut)); portMessageIn.apiThemes.apiGeneric.ulAPINumber = API_THEMES_PING; portMessageIn.portMessage.u1.s1.DataLength = sizeof(API_THEMES); portMessageIn.portMessage.u1.s1.TotalLength = static_cast<CSHORT>(sizeof(THEMESAPI_PORT_MESSAGE)); status = NtRequestWaitReplyPort(s_hPort, &portMessageIn.portMessage, &portMessageOut.portMessage); if (NT_SUCCESS(status)) { status = portMessageOut.apiThemes.apiGeneric.status; } } else { status = STATUS_PORT_DISCONNECTED; } if (STATUS_PORT_DISCONNECTED == status) { HDESK hDeskCurrent, hDeskInput;
// Set this thread's desktop to the input desktop so
// that the theme change can be broadcast to the input
// desktop. This is Default in most cases where a logged
// on user is active but in the non-logged on user case
// this will be Winlogon. Restore the thread's desktop
// when done.
TSTATUS(ReestablishConnection()); hDeskCurrent = hDeskInput = NULL; if (s_hToken != NULL) { hDeskCurrent = GetThreadDesktop(GetCurrentThreadId()); hDeskInput = OpenInputDesktop(0, FALSE, MAXIMUM_ALLOWED); if ((hDeskCurrent != NULL) && (hDeskInput != NULL)) { TBOOL(SetThreadDesktop(hDeskInput)); } if (NT_SUCCESS(CImpersonation::ImpersonateUser(GetCurrentThread(), s_hToken))) { TSTATUS(InformServerUserLogon(s_hToken)); } if ((hDeskCurrent != NULL) && (hDeskInput != NULL)) { SetThreadDesktop(hDeskCurrent); (BOOL)CloseDesktop(hDeskInput); } TBOOL(RevertToSelf()); // Not much we can do if RevertToSelf() fails. We probably failed the ImpersonateUser() call above,
// meaning we didn't do actual work.
} else { THR(InitUserRegistry()); THR(InitUserTheme(FALSE)); } }
// Reset the event here and now.
TBOOL(ResetEvent(s_hEvent));
// Unregister the original wait (it only executes once anyway). This
// call will return a failure code with the callback in progress.
// Ignore this error. The thread pool will clean up the wait.
(BOOL)UnregisterWait(s_hWaitObject);
// Reregister the wait as execute once only again waiting for
// the next time the event is signaled.
TBOOL(RegisterWaitForSingleObject(&s_hWaitObject, s_hEvent, CB_ServiceStart, NULL, INFINITE, WT_EXECUTEDEFAULT | WT_EXECUTEONLYONCE)); } }
|