// -------------------------------------------------------------------------- // Module Name: ThemeManagerAPIRequest.cpp // // Copyright (c) 2000, Microsoft Corporation // // This file contains a class that implements the work for the theme server. // // History: 2000-10-10 vtan created // 2000-11-29 vtan moved to separate file // -------------------------------------------------------------------------- #include "StandardHeader.h" #include "ThemeManagerAPIRequest.h" #include "ThemeManagerService.h" #include #include #include #include "RegistryResources.h" #include "SingleThreadedExecution.h" #include "StatusCode.h" #include "TokenInformation.h" #define STRSAFE_LIB #include // -------------------------------------------------------------------------- // Static member variables. // // History: 2000-11-09 vtan created // -------------------------------------------------------------------------- CDynamicCountedObjectArray* CThemeManagerAPIRequest::s_pSessionData = NULL; CCriticalSection* CThemeManagerAPIRequest::s_pLock = NULL; DWORD CThemeManagerAPIRequest::s_dwServerChangeNumber = 0; const TCHAR CThemeManagerAPIRequest::s_szServerChangeNumberValue[] = TEXT("ServerChangeNumber"); #ifdef DEBUG #define PROCESSLOADERWAIT DISPATCHSYNC_TIMEOUT * 10 #else #define PROCESSLOADERWAIT DISPATCHSYNC_TIMEOUT #endif DEBUG // -------------------------------------------------------------------------- // Forward decls // // History: 2002-02-26 scotthan created // -------------------------------------------------------------------------- inline NTSTATUS _CheckTokenPrivilege( HANDLE hToken, DWORD dwPrivilege ) { CTokenInformation tokenInformation(hToken); return tokenInformation.UserHasPrivilege(dwPrivilege) ? STATUS_SUCCESS : STATUS_ACCESS_DENIED; } // -------------------------------------------------------------------------- // CThemeManagerAPIRequest::CThemeManagerAPIRequest // // Arguments: pAPIDispatcher = CAPIDispatcher that calls this object. // pAPIConnection = CAPIConnection for access change. // // Returns: // // Purpose: Constructor for the CThemeManagerAPIRequest class. It just passes the // control to the super class. // // History: 2000-10-10 vtan created // -------------------------------------------------------------------------- CThemeManagerAPIRequest::CThemeManagerAPIRequest (CAPIDispatcher* pAPIDispatcher) : CAPIRequest(pAPIDispatcher), _hToken(NULL), _pSessionData(NULL) { } // -------------------------------------------------------------------------- // CThemeManagerAPIRequest::CThemeManagerAPIRequest // // Arguments: pAPIDispatcher = CAPIDispatcher that calls this object. // pAPIConnection = CAPIConnection for access change. // portMessage = CPortMessage to copy construct. // // Returns: // // Purpose: Constructor for the CThemeManagerAPIRequest class. It just // passes the control to the super class. // // History: 2000-10-10 vtan created // -------------------------------------------------------------------------- CThemeManagerAPIRequest::CThemeManagerAPIRequest ( CAPIDispatcher* pAPIDispatcher, const CPortMessage& portMessage) : CAPIRequest(pAPIDispatcher, portMessage), _hToken(NULL), _pSessionData(NULL) { } // -------------------------------------------------------------------------- // CThemeManagerAPIRequest::~CThemeManagerAPIRequest // // Arguments: // // Returns: // // Purpose: Destructor for the CThemeManagerAPIRequest class. // // History: 2000-10-10 vtan created // -------------------------------------------------------------------------- CThemeManagerAPIRequest::~CThemeManagerAPIRequest (void) { ASSERTMSG(_hToken == NULL, "Impersonation token not released in CThemeManagerAPIRequest::~CThemeManagerAPIRequest"); } // -------------------------------------------------------------------------- // CThemeManagerAPIRequest::Execute // // Arguments: pAPIDispatchSync - allows request execution access to various // service notifications and events // // Returns: NTSTATUS // // Purpose: Execute implementation for theme manager API requests. This // function dispatches requests based on the API request number. // // History: 2000-10-10 vtan created // 2002-03-11 scotthan add API_THEMES_PROCESSLOADTHEME, // API_THEMES_PROCESSASSIGNSECTION handlers // 2002-03-24 scotthan add DispatchSync arg // -------------------------------------------------------------------------- NTSTATUS CThemeManagerAPIRequest::Execute (CAPIDispatchSync* pAPIDispatchSync) { NTSTATUS status; unsigned long ulAPINumber; ulAPINumber = reinterpret_cast(&_data)->apiGeneric.ulAPINumber & API_GENERIC_NUMBER_MASK; // First try and get the client session data. If this fails then // there's no object to execute the request on. Fail it. // Exception to this is API_THEMES_SESSIONCREATE which creates one. // Note: GetClientSessionData will store the session data in the // _pSessionData member variable. While doing so it will increase // reference count on this so that it doesn't get pulled from the // array while the API request is being executed. The reference is // released at the end of this function. status = GetClientSessionData(); if (NT_SUCCESS(status) || (ulAPINumber == API_THEMES_SESSIONCREATE)) { switch (ulAPINumber) { case API_THEMES_THEMEHOOKSON: status = Execute_ThemeHooksOn(); break; case API_THEMES_THEMEHOOKSOFF: status = Execute_ThemeHooksOff(); break; case API_THEMES_GETSTATUSFLAGS: status = Execute_GetStatusFlags(); break; case API_THEMES_GETCURRENTCHANGENUMBER: status = Execute_GetCurrentChangeNumber(); break; case API_THEMES_GETNEWCHANGENUMBER: status = Execute_GetNewChangeNumber(); break; case API_THEMES_SETGLOBALTHEME: status = Execute_SetGlobalTheme(); break; case API_THEMES_MARKSECTION: status = Execute_MarkSection(); break; case API_THEMES_GETGLOBALTHEME: status = Execute_GetGlobalTheme(); break; case API_THEMES_CHECKTHEMESIGNATURE: status = Execute_CheckThemeSignature(); break; case API_THEMES_LOADTHEME: status = Execute_LoadTheme(); break; case API_THEMES_PROCESSLOADTHEME: status = Execute_ProcessLoadTheme(pAPIDispatchSync); break; case API_THEMES_PROCESSASSIGNSECTION: status = Execute_ProcessAssignSection(); break; case API_THEMES_USERLOGON: status = Execute_UserLogon(); break; case API_THEMES_USERLOGOFF: status = Execute_UserLogoff(); break; case API_THEMES_SESSIONCREATE: status = Execute_SessionCreate(); break; case API_THEMES_SESSIONDESTROY: status = Execute_SessionDestroy(); break; case API_THEMES_PING: status = Execute_Ping(); break; default: DISPLAYMSG("Unknown API request in CThemeManagerAPIRequest::Execute"); status = STATUS_NOT_IMPLEMENTED; break; } } // If the execution function needed to impersonate the client then // revert here and release the token used. if (_hToken != NULL) { if (RevertToSelf() == FALSE) { status = CStatusCode::StatusCodeOfLastError(); } ReleaseHandle(_hToken); } // Release the _pSessionData object now. NULL it out to prevent // accidentally using it after being released. if (_pSessionData != NULL) { _pSessionData->Release(); _pSessionData = NULL; } // Return to caller. TSTATUS(status); return(status); } // -------------------------------------------------------------------------- // CThemeManagerAPIRequest::SessionDestroy // // Arguments: dwSessionID = Session ID to destroy. // // Returns: NTSTATUS // // Purpose: External entry point for session client (winlogon) watcher. // When winlogon dies we clean up the session information for // that session and release resources. // // History: 2000-12-09 vtan created // -------------------------------------------------------------------------- NTSTATUS CThemeManagerAPIRequest::SessionDestroy (DWORD dwSessionID) { NTSTATUS status; int iIndex; CSingleThreadedExecution lock(*s_pLock); iIndex = FindIndexSessionData(dwSessionID); if (iIndex >= 0) { status = s_pSessionData->Remove(iIndex); } else { status = STATUS_SUCCESS; } return(status); } // -------------------------------------------------------------------------- // CThemeManagerAPIRequest::InitializeServerChangeNumber // // Arguments: // // Returns: NTSTATUS // // Purpose: Initializes the static server change number. Every time the // service starts up this number is incremented. If the number // isn't present then 0 is used. // // History: 2000-12-09 vtan created // 2000-12-09 vtan split from StaticInitialize // -------------------------------------------------------------------------- NTSTATUS CThemeManagerAPIRequest::InitializeServerChangeNumber (void) { LONG lErrorCodeOpen, lErrorCodeRead; DWORD dwServerChangeNumber; CRegKey regKey; dwServerChangeNumber = s_dwServerChangeNumber; // Initialize the static member variable now in case of failure. // We ignore failures because at GUI setup the key does NOT exist // because the server dll hasn't been regsvr'd yet. After GUI setup // this gets regsvr'd and the key exists and we are happy campers. lErrorCodeOpen = regKey.Open(HKEY_LOCAL_MACHINE, TEXT("SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\ThemeManager"), KEY_QUERY_VALUE | KEY_SET_VALUE); if (ERROR_SUCCESS == lErrorCodeOpen) { lErrorCodeRead = regKey.GetDWORD(s_szServerChangeNumberValue, dwServerChangeNumber); } else { lErrorCodeRead = ERROR_FILE_NOT_FOUND; } dwServerChangeNumber = static_cast(dwServerChangeNumber + 1); if ((ERROR_SUCCESS == lErrorCodeOpen) && (ERROR_SUCCESS == lErrorCodeRead)) { TW32(regKey.SetDWORD(s_szServerChangeNumberValue, dwServerChangeNumber)); } s_dwServerChangeNumber = dwServerChangeNumber; return(STATUS_SUCCESS); } // -------------------------------------------------------------------------- // CThemeManagerAPIRequest::StaticInitialize // // Arguments: // // Returns: NTSTATUS // // Purpose: Static initializer for the class. // // History: 2000-10-10 vtan created // -------------------------------------------------------------------------- NTSTATUS CThemeManagerAPIRequest::StaticInitialize (void) { NTSTATUS status; status = STATUS_SUCCESS; if (s_pLock == NULL) { s_pLock = new CCriticalSection; if (s_pLock != NULL) { status = s_pLock->Status(); if (!NT_SUCCESS(status)) { delete s_pLock; s_pLock = NULL; } } else { status = STATUS_NO_MEMORY; } } return(status); } // -------------------------------------------------------------------------- // CThemeManagerAPIRequest::StaticTerminate // // Arguments: // // Returns: NTSTATUS // // Purpose: Static destructor for the class. // // History: 2000-10-10 vtan created // -------------------------------------------------------------------------- NTSTATUS CThemeManagerAPIRequest::StaticTerminate (void) { if (s_pLock != NULL) { delete s_pLock; s_pLock = NULL; } return(STATUS_SUCCESS); } // -------------------------------------------------------------------------- // CThemeManagerAPIRequest::ArrayInitialize // // Arguments: // // Returns: NTSTATUS // // Purpose: Initializes (allocates) the session array. // // History: 2001-01-05 vtan created // -------------------------------------------------------------------------- NTSTATUS CThemeManagerAPIRequest::ArrayInitialize (void) { NTSTATUS status; CSingleThreadedExecution lock(*s_pLock); status = STATUS_SUCCESS; if (s_pSessionData == NULL) { s_pSessionData = new CDynamicCountedObjectArray; if (s_pSessionData == NULL) { status = STATUS_NO_MEMORY; } } return(status); } // -------------------------------------------------------------------------- // CThemeManagerAPIRequest::ArrayTerminate // // Arguments: // // Returns: NTSTATUS // // Purpose: Releases all objects in the session array (removes the waits) // and releases the session array object. // // History: 2001-01-05 vtan created // -------------------------------------------------------------------------- NTSTATUS CThemeManagerAPIRequest::ArrayTerminate (void) { CSingleThreadedExecution lock(*s_pLock); if (s_pSessionData != NULL) { int i, iLimit; iLimit = s_pSessionData->GetCount(); for (i = iLimit - 1; i >= 0; --i) { TSTATUS(static_cast(s_pSessionData->Get(i))->Cleanup()); TSTATUS(s_pSessionData->Remove(i)); } delete s_pSessionData; s_pSessionData = NULL; } return(STATUS_SUCCESS); } // -------------------------------------------------------------------------- // CThemeManagerAPIRequest::ImpersonateClientIfRequired // // Arguments: // // Returns: NTSTATUS // // Purpose: Impersonates the client if the client is NOT the SYSTEM. // There's usually no point impersonating the system unless the // token is actually a filtered token. // // History: 2000-10-19 vtan created // -------------------------------------------------------------------------- NTSTATUS CThemeManagerAPIRequest::ImpersonateClientIfRequired (void) { NTSTATUS status; status = OpenClientToken(_hToken); if (NT_SUCCESS(status)) { CTokenInformation tokenInformation(_hToken); if (tokenInformation.IsUserTheSystem()) { ReleaseHandle(_hToken); status = STATUS_SUCCESS; } else if (ImpersonateLoggedOnUser(_hToken) != FALSE) { status = STATUS_SUCCESS; } else { status = CStatusCode::StatusCodeOfLastError(); } } return(status); } // -------------------------------------------------------------------------- // CThemeManagerAPIRequest::ClientHasTcbPrivilege // // Arguments: // // Returns: NTSTATUS // // Purpose: Returns whether the client has the SE_TCB_PRIVILEGE as a // status code. // // History: 2000-11-09 vtan created // -------------------------------------------------------------------------- NTSTATUS CThemeManagerAPIRequest::ClientHasTcbPrivilege (void) { NTSTATUS status; HANDLE hTokenClient; if (OpenProcessToken(_pAPIDispatcher->GetClientProcess(), TOKEN_QUERY, &hTokenClient) != FALSE) { status = _CheckTokenPrivilege(hTokenClient, SE_TCB_PRIVILEGE); TBOOL(CloseHandle(hTokenClient)); } else { status = CStatusCode::StatusCodeOfLastError(); } return(status); } // -------------------------------------------------------------------------- // CThemeManagerAPIRequest::FindIndexSessionData // // Arguments: dwSessionID = Session ID to find. // // Returns: int // // Purpose: Iterates the session data array looking for the sessions that // matches the given session. // // History: 2000-11-30 vtan created // -------------------------------------------------------------------------- int CThemeManagerAPIRequest::FindIndexSessionData (DWORD dwSessionID) { int iIndex; iIndex = -1; if ((s_pLock != NULL) && (s_pSessionData != NULL)) { int i, iLimit; ASSERTMSG(s_pLock->IsOwned(), "s_pLock must be acquired in CThemeManagerAPIRequest::FindIndexSessionData"); iLimit = s_pSessionData->GetCount(); for (i = 0; (iIndex < 0) && (i < iLimit); ++i) { CThemeManagerSessionData *pSessionData; pSessionData = static_cast(s_pSessionData->Get(i)); if ((pSessionData != NULL) && (pSessionData->EqualSessionID(dwSessionID))) { iIndex = i; } } } return(iIndex); } // -------------------------------------------------------------------------- // CThemeManagerAPIRequest::GetClientSessionData // // Arguments: // // Returns: NTSTATUS // // Purpose: Retrieves the session data associated with the client's // session ID. This abstracts the information from uxtheme's // loader code and just passes it an object it knows how to deal // with. // // History: 2000-11-09 vtan created // -------------------------------------------------------------------------- NTSTATUS CThemeManagerAPIRequest::GetClientSessionData (void) { NTSTATUS status; int iIndex; CSingleThreadedExecution lock(*s_pLock); status = STATUS_UNSUCCESSFUL; iIndex = FindIndexSessionData(_pAPIDispatcher->GetClientSessionID()); if (iIndex >= 0) { _pSessionData = static_cast(s_pSessionData->Get(iIndex)); if (_pSessionData != NULL) { _pSessionData->AddRef(); status = STATUS_SUCCESS; } } else { _pSessionData = NULL; } return(status); } // -------------------------------------------------------------------------- // CThemeManagerAPIRequest::Execute_ThemeHooksOn // // Arguments: // // Returns: NTSTATUS // // Purpose: Handles API_THEMES_THEMEHOOKSON. // // History: 2000-10-10 vtan created // -------------------------------------------------------------------------- NTSTATUS CThemeManagerAPIRequest::Execute_ThemeHooksOn (void) { NTSTATUS status; status = ImpersonateClientIfRequired(); if (NT_SUCCESS(status)) { API_THEMES_THEMEHOOKSON_OUT *pAPIOut; pAPIOut = &reinterpret_cast(&_data)->apiSpecific.apiThemeHooksOn.out; pAPIOut->hr = ThemeHooksOn(_pSessionData->GetData()); } SetDataLength(sizeof(API_THEMES)); return(status); } // -------------------------------------------------------------------------- // CThemeManagerAPIRequest::Execute_ThemeHooksOff // // Arguments: // // Returns: NTSTATUS // // Purpose: Handles API_THEMES_THEMEHOOKSOFF. // // History: 2000-10-10 vtan created // -------------------------------------------------------------------------- NTSTATUS CThemeManagerAPIRequest::Execute_ThemeHooksOff (void) { NTSTATUS status; status = ImpersonateClientIfRequired(); if (NT_SUCCESS(status)) { API_THEMES_THEMEHOOKSOFF_OUT *pAPIOut; pAPIOut = &reinterpret_cast(&_data)->apiSpecific.apiThemeHooksOff.out; pAPIOut->hr = ThemeHooksOff(_pSessionData->GetData()); } SetDataLength(sizeof(API_THEMES)); return(status); } // -------------------------------------------------------------------------- // CThemeManagerAPIRequest::Execute_GetStatusFlags // // Arguments: // // Returns: NTSTATUS // // Purpose: Handles API_THEMES_GETSTATUSFLAGS. // // History: 2000-10-10 vtan created // -------------------------------------------------------------------------- NTSTATUS CThemeManagerAPIRequest::Execute_GetStatusFlags (void) { NTSTATUS status; status = ImpersonateClientIfRequired(); if (NT_SUCCESS(status)) { DWORD dwFlags; API_THEMES_GETSTATUSFLAGS_OUT *pAPIOut; pAPIOut = &reinterpret_cast(&_data)->apiSpecific.apiGetStatusFlags.out; dwFlags = QTS_AVAILABLE; if (AreThemeHooksActive(_pSessionData->GetData())) { dwFlags |= QTS_RUNNING; } pAPIOut->dwFlags = dwFlags; } SetDataLength(sizeof(API_THEMES)); return(status); } // -------------------------------------------------------------------------- // CThemeManagerAPIRequest::Execute_GetCurrentChangeNumber // // Arguments: // // Returns: NTSTATUS // // Purpose: Handles API_THEMES_GETCURRENTCHANGENUMBER. // // History: 2000-10-10 vtan created // -------------------------------------------------------------------------- NTSTATUS CThemeManagerAPIRequest::Execute_GetCurrentChangeNumber (void) { NTSTATUS status; status = ImpersonateClientIfRequired(); if (NT_SUCCESS(status)) { API_THEMES_GETCURRENTCHANGENUMBER_OUT *pAPIOut; pAPIOut = &reinterpret_cast(&_data)->apiSpecific.apiGetCurrentChangeNumber.out; pAPIOut->iChangeNumber = GetCurrentChangeNumber(_pSessionData->GetData()); } SetDataLength(sizeof(API_THEMES)); return(status); } // -------------------------------------------------------------------------- // CThemeManagerAPIRequest::Execute_GetNewChangeNumber // // Arguments: // // Returns: NTSTATUS // // Purpose: Handles API_THEMES_GETNEWCHANGENUMBER. // // History: 2000-10-10 vtan created // -------------------------------------------------------------------------- NTSTATUS CThemeManagerAPIRequest::Execute_GetNewChangeNumber (void) { NTSTATUS status; status = ImpersonateClientIfRequired(); if (NT_SUCCESS(status)) { API_THEMES_GETNEWCHANGENUMBER_OUT *pAPIOut; pAPIOut = &reinterpret_cast(&_data)->apiSpecific.apiGetNewChangeNumber.out; pAPIOut->iChangeNumber = GetNewChangeNumber(_pSessionData->GetData()); } SetDataLength(sizeof(API_THEMES)); return(status); } // -------------------------------------------------------------------------- // CThemeManagerAPIRequest::Execute_SetGlobalTheme // // Arguments: // // Returns: NTSTATUS // // Purpose: Handles API_THEMES_SETGLOBALTHEME. // // History: 2000-10-10 vtan created // -------------------------------------------------------------------------- NTSTATUS CThemeManagerAPIRequest::Execute_SetGlobalTheme (void) { NTSTATUS status; // Note: we must not impersonate the user here, since we need write access to the section HANDLE hSection; API_THEMES_SETGLOBALTHEME_IN *pAPIIn; API_THEMES_SETGLOBALTHEME_OUT *pAPIOut; pAPIIn = &reinterpret_cast(&_data)->apiSpecific.apiSetGlobalTheme.in; pAPIOut = &reinterpret_cast(&_data)->apiSpecific.apiSetGlobalTheme.out; if (pAPIIn->hSection != NULL) { if (DuplicateHandle(_pAPIDispatcher->GetClientProcess(), pAPIIn->hSection, GetCurrentProcess(), &hSection, FILE_MAP_ALL_ACCESS, FALSE, 0) != FALSE) { status = STATUS_SUCCESS; } else { status = CStatusCode::StatusCodeOfLastError(); } } else { hSection = NULL; status = STATUS_SUCCESS; } if (NT_SUCCESS(status)) { pAPIOut->hr = SetGlobalTheme(_pSessionData->GetData(), hSection); if (hSection != NULL) { TBOOL(CloseHandle(hSection)); } } else { pAPIOut->hr = HRESULT_FROM_NT(status); } SetDataLength(sizeof(API_THEMES)); return(STATUS_SUCCESS); } // -------------------------------------------------------------------------- // CThemeManagerAPIRequest::Execute_MarkSection // // Arguments: // // Returns: NTSTATUS // // Purpose: Handles API_THEMES_MARKSECTION. // // History: 2001-05-08 lmouton created // -------------------------------------------------------------------------- NTSTATUS CThemeManagerAPIRequest::Execute_MarkSection (void) { NTSTATUS status; // Note: we must not impersonate the user here, since we need write access to the section HANDLE hSection; DWORD dwAdd; DWORD dwRemove; API_THEMES_MARKSECTION_IN *pAPIIn; API_THEMES_MARKSECTION_OUT *pAPIOut; pAPIIn = &reinterpret_cast(&_data)->apiSpecific.apiMarkSection.in; pAPIOut = &reinterpret_cast(&_data)->apiSpecific.apiMarkSection.out; dwAdd = pAPIIn->dwAdd; dwRemove = pAPIIn->dwRemove; if (pAPIIn->hSection != NULL) { if (DuplicateHandle(_pAPIDispatcher->GetClientProcess(), pAPIIn->hSection, GetCurrentProcess(), &hSection, FILE_MAP_ALL_ACCESS, FALSE, 0) != FALSE) { status = STATUS_SUCCESS; } else { status = CStatusCode::StatusCodeOfLastError(); DISPLAYMSG("Execute_MarkSection: Can't get a write handle"); } } else { hSection = NULL; status = STATUS_SUCCESS; } if (NT_SUCCESS(status)) { if (hSection != NULL) { MarkSection(hSection, dwAdd, dwRemove); TBOOL(CloseHandle(hSection)); } } SetDataLength(sizeof(API_THEMES)); return(STATUS_SUCCESS); } // -------------------------------------------------------------------------- // CThemeManagerAPIRequest::Execute_GetGlobalTheme // // Arguments: // // Returns: NTSTATUS // // Purpose: Handles API_THEMES_GETGLOBALTHEME. // // History: 2000-10-10 vtan created // -------------------------------------------------------------------------- NTSTATUS CThemeManagerAPIRequest::Execute_GetGlobalTheme (void) { NTSTATUS status; status = ImpersonateClientIfRequired(); if (NT_SUCCESS(status)) { HRESULT hr; HANDLE hSection; API_THEMES_GETGLOBALTHEME_OUT *pAPIOut; pAPIOut = &reinterpret_cast(&_data)->apiSpecific.apiGetGlobalTheme.out; hr = GetGlobalTheme(_pSessionData->GetData(), &hSection); if (SUCCEEDED(hr) && (hSection != NULL)) { if (DuplicateHandle(GetCurrentProcess(), hSection, _pAPIDispatcher->GetClientProcess(), &pAPIOut->hSection, FILE_MAP_READ, FALSE, 0) != FALSE) { hr = S_OK; } else { DWORD dwErrorCode; dwErrorCode = GetLastError(); hr = HRESULT_FROM_WIN32(dwErrorCode); } TBOOL(CloseHandle(hSection)); } pAPIOut->hr = hr; } SetDataLength(sizeof(API_THEMES)); return(status); } // -------------------------------------------------------------------------- // LOADTHEME_STRINGS + supporting functions // // Purpose: Manages and validates LoadTheme string parameters // // History: 2002-02-26 scotthan created // -------------------------------------------------------------------------- typedef struct { LPWSTR pszFilename; LPWSTR pszColor; LPWSTR pszSize; } LOADTHEME_STRINGS; #define MAX_THEME_STRING MAX_PATH // -------------------------------------------------------------------------- void _FreeThemeStrings( IN LOADTHEME_STRINGS* plts ) { if( plts ) { _FreeMappedClientString(plts->pszFilename); _FreeMappedClientString(plts->pszColor); _FreeMappedClientString(plts->pszSize); delete plts; } } // -------------------------------------------------------------------------- NTSTATUS _AllocAndMapThemeStrings( IN HANDLE hProcessClient, IN LPCWSTR pszFilenameIn, IN UINT cchFilenameIn, IN LPCWSTR pszColorIn, IN UINT cchColorIn, IN LPCWSTR pszSizeIn, IN UINT cchSizeIn, OUT LOADTHEME_STRINGS** pplts ) { NTSTATUS status; ASSERTMSG(pplts != NULL, "_AllocAndMapThemeStrings: NULL outbound parameter, LOADTHEME_STRINGS**."); ASSERTMSG(hProcessClient != NULL, "_AllocAndMapThemeStrings: NULL process handle."); // note: cchFileNameIn, cchColorIn, cchSizeIn are char counts that include the NULL terminus. if( pszFilenameIn && pszColorIn && pszSizeIn && cchFilenameIn > 0 && cchColorIn > 0 && cchSizeIn > 0 && cchFilenameIn <= MAX_THEME_STRING && cchColorIn <= MAX_THEME_STRING && cchSizeIn <= MAX_THEME_STRING ) { *pplts = NULL; LOADTHEME_STRINGS *plts = new LOADTHEME_STRINGS; if( plts != NULL ) { ZeroMemory(plts, sizeof(*plts)); status = _AllocAndMapClientString(hProcessClient, pszFilenameIn, cchFilenameIn, MAX_THEME_STRING, &plts->pszFilename); if( NT_SUCCESS(status) ) { status = _AllocAndMapClientString(hProcessClient, pszColorIn, cchColorIn, MAX_THEME_STRING, &plts->pszColor); if( NT_SUCCESS(status) ) { status = _AllocAndMapClientString(hProcessClient, pszSizeIn, cchSizeIn, MAX_THEME_STRING, &plts->pszSize); if( NT_SUCCESS(status) ) { *pplts = plts; } } } if( !NT_SUCCESS(status) ) { _FreeThemeStrings(plts); } } else { status = STATUS_NO_MEMORY; } } else { status = STATUS_INVALID_PARAMETER; } return status; } // -------------------------------------------------------------------------- // CThemeManagerAPIRequest::Execute_CheckThemeSignature // // Arguments: // // Returns: NTSTATUS // // Purpose: Handles API_THEMES_CHECKTHEMESIGNATURE. // // History: 2000-10-10 vtan created // -------------------------------------------------------------------------- NTSTATUS CThemeManagerAPIRequest::Execute_CheckThemeSignature (void) { NTSTATUS status; status = ImpersonateClientIfRequired(); if (NT_SUCCESS(status)) { API_THEMES_CHECKTHEMESIGNATURE_IN *pAPIIn; API_THEMES_CHECKTHEMESIGNATURE_OUT *pAPIOut; LPWSTR pszThemeFileName; pAPIIn = &reinterpret_cast(&_data)->apiSpecific.apiCheckThemeSignature.in; pAPIOut = &reinterpret_cast(&_data)->apiSpecific.apiCheckThemeSignature.out; status = _AllocAndMapClientString(_pAPIDispatcher->GetClientProcess(), pAPIIn->pszName, pAPIIn->cchName, MAX_PATH, &pszThemeFileName); if( NT_SUCCESS(status) ) { pAPIOut->hr = CheckThemeSignature(pszThemeFileName); status = STATUS_SUCCESS; _FreeMappedClientString(pszThemeFileName); } } SetDataLength(sizeof(API_THEMES)); return(status); } // -------------------------------------------------------------------------- // CThemeManagerAPIRequest::Execute_LoadTheme // // Arguments: // // Returns: NTSTATUS // // Purpose: Handles API_THEMES_LOADTHEME. // // History: 2000-10-10 vtan created // -------------------------------------------------------------------------- NTSTATUS CThemeManagerAPIRequest::Execute_LoadTheme (void) { NTSTATUS status; BOOL fTcb = NT_SUCCESS(ClientHasTcbPrivilege()); status = ImpersonateClientIfRequired(); if (NT_SUCCESS(status)) { HANDLE hProcessClient; API_THEMES_LOADTHEME_IN *pAPIIn; API_THEMES_LOADTHEME_OUT *pAPIOut; LOADTHEME_STRINGS* plts; hProcessClient = _pAPIDispatcher->GetClientProcess(); pAPIIn = &reinterpret_cast(&_data)->apiSpecific.apiLoadTheme.in; pAPIOut = &reinterpret_cast(&_data)->apiSpecific.apiLoadTheme.out; status = _AllocAndMapThemeStrings( hProcessClient, pAPIIn->pszName, pAPIIn->cchName, pAPIIn->pszColor, pAPIIn->cchColor, pAPIIn->pszSize, pAPIIn->cchSize, &plts ); if( NT_SUCCESS(status) ) { HANDLE hSectionIn, hSectionOut; if (DuplicateHandle(hProcessClient, pAPIIn->hSection, GetCurrentProcess(), &hSectionIn, FILE_MAP_ALL_ACCESS, FALSE, 0) != FALSE) { status = STATUS_SUCCESS; // Only clients with TCB privilege can load a global theme section. // We don't want stock object ownership here; let the client clean them up on failure. DWORD dwLoadFlags = fTcb ? LTF_GLOBALPRIVILEGEDCLIENT : 0; // Warning: this function will revert to self in order to create the section in system context. // Impersonate the user again after it if needed pAPIOut->hr = LoadTheme(_pSessionData->GetData(), hSectionIn, &hSectionOut, plts->pszFilename, plts->pszColor, plts->pszSize, dwLoadFlags); if (SUCCEEDED(pAPIOut->hr)) { // Still running in the system context here if (DuplicateHandle(GetCurrentProcess(), hSectionOut, hProcessClient, &pAPIOut->hSection, FILE_MAP_READ, FALSE, 0) == FALSE) { status = CStatusCode::StatusCodeOfLastError(); } TBOOL(CloseHandle(hSectionOut)); } TBOOL(CloseHandle(hSectionIn)); } else { status = CStatusCode::StatusCodeOfLastError(); } _FreeThemeStrings(plts); } } SetDataLength(sizeof(API_THEMES)); return(status); } // -------------------------------------------------------------------------- // CThemeManagerAPIRequest::Execute_ProcessLoadTheme // // Arguments: // // Returns: NTSTATUS // // Purpose: Handles API_THEMES_PROCESSLOADTHEME. // // History: 2002-02-26 scotthan created // -------------------------------------------------------------------------- NTSTATUS CThemeManagerAPIRequest::Execute_ProcessLoadTheme( CAPIDispatchSync* pAPIDispatchSync) { NTSTATUS status; CLoaderProcess *pLoader = NULL; API_THEMES_PROCESSLOADTHEME_IN *pAPIIn; API_THEMES_PROCESSLOADTHEME_OUT *pAPIOut; HANDLE hProcessClient; HANDLE hLoaderProcess = NULL; s_pLock->Acquire(); pAPIIn = &reinterpret_cast(&_data)->apiSpecific.apiProcessLoadTheme.in; pAPIOut = &reinterpret_cast(&_data)->apiSpecific.apiProcessLoadTheme.out; hProcessClient = _pAPIDispatcher->GetClientProcess(); if( !CAPIDispatchSync::IsServiceStopping(pAPIDispatchSync) ) { status = _pSessionData->GetLoaderProcess(&pLoader); if( NT_SUCCESS(status) ) { status = // do we already have a loader? status = pLoader->IsAlive() ? STATUS_ACCESS_DENIED : STATUS_SUCCESS; if( NT_SUCCESS(status) ) { status = ImpersonateClientIfRequired(); if (NT_SUCCESS(status)) { LOADTHEME_STRINGS* plts; if( NT_SUCCESS(status) ) { status = _AllocAndMapThemeStrings( hProcessClient, pAPIIn->pszName, pAPIIn->cchName, pAPIIn->pszColor, pAPIIn->cchColor, pAPIIn->pszSize, pAPIIn->cchSize, &plts ); if( NT_SUCCESS(status) ) { HANDLE hTokenClient; if( OpenProcessToken(hProcessClient, TOKEN_ASSIGN_PRIMARY| TOKEN_DUPLICATE | TOKEN_QUERY, &hTokenClient) ) { // SECURITY: Launch process with client credentials to load the theme. status = pLoader->Create(_pSessionData->GetData(), hTokenClient, NULL, plts->pszFilename, plts->pszColor, plts->pszSize, &hLoaderProcess); CloseHandle(hTokenClient); } else { status = CStatusCode::StatusCodeOfLastError(); } _FreeThemeStrings(plts); } } } } } } else // !CThemeManagerService::'() { status = STATUS_PORT_DISCONNECTED; } s_pLock->Release(); pAPIOut->hSection = NULL; pAPIOut->hr = E_FAIL; // If we launched a loader process, block until its finished if( NT_SUCCESS(status) ) { ASSERTMSG(hLoaderProcess != NULL, "CThemeManagerAPIRequest::Execute_ProcessLoadTheme - NULL loader process."); HANDLE hStopEvent = CAPIDispatchSync::GetServiceStoppingEvent(pAPIDispatchSync); ASSERTMSG(hStopEvent != NULL, "CThemeManagerAPIRequest::Execute_ProcessLoadTheme - NULL Stop event"); HANDLE rgHandles[2]; rgHandles[0] = hLoaderProcess; rgHandles[1] = hStopEvent; // paranoia: assign default status in case we fall out status = STATUS_REQUEST_ABORTED; DWORD dwWait = WaitForMultipleObjects(ARRAYSIZE(rgHandles), rgHandles, FALSE, PROCESSLOADERWAIT); switch(dwWait) { case WAIT_OBJECT_0: // hLoaderProcess status = STATUS_SUCCESS; break; case WAIT_OBJECT_0+1: // hStopEvent status = STATUS_PORT_DISCONNECTED; break; case WAIT_TIMEOUT: status = STATUS_TIMEOUT; DISPLAYMSG("Execute_ProcessLoadTheme - Timed out waiting for loader process."); break; } // default the LPC return code to current status code pAPIOut->hr = HRESULT_FROM_NT(status); // By the time the process is finished, we'll have a theme memory section // stored in the loader process object, transacted via API_THEMES_PROCESSASSIGNSECTION. // Let's fetch it and hand it back to our caller. s_pLock->Acquire(); NTSTATUS statusLoader = _pSessionData->GetLoaderProcess(&pLoader); if( NT_SUCCESS(statusLoader) ) { HANDLE hSectionOut = pLoader->GetSectionHandle(TRUE); // did we unblock from the loader? if( NT_SUCCESS(status) ) { pAPIOut->hr = pLoader->GetHResult(); #ifdef DEBUG if( SUCCEEDED(pAPIOut->hr) ) { ASSERTMSG(hSectionOut != NULL, "CThemeManagerAPIRequest::Execute_ProcessLoadTheme - Success means valid section handle!"); } #endif DEBUG if( hSectionOut ) { BOOL fDuped = DuplicateHandle(GetCurrentProcess(), hSectionOut, hProcessClient, &pAPIOut->hSection, FILE_MAP_READ, FALSE, 0); // Still running in the system context here if( !fDuped ) { // couldn't duplicate handle. This means we'll never clean stock objects. ASSERTMSG(fDuped, "Failed to duplicate theme handle; leaking visual style stock objects"); status = CStatusCode::StatusCodeOfLastError(); pAPIOut->hr = HRESULT_FROM_NT(status); } } } else { ASSERTMSG(pAPIOut->hr == HRESULT_FROM_NT(status), "CThemeManagerAPIRequest::Execute_ProcessLoadTheme - failing to preserve proper status errror code."); if( hSectionOut ) { THR(ServiceClearStockObjects(_pSessionData->GetData(), hSectionOut)); pAPIOut->hSection = NULL; } } if( hSectionOut ) { CloseHandle(hSectionOut); } // prepare the loader object for the next request. pLoader->Clear(_pSessionData->GetData(), TRUE); } else // NT_SUCCESS(_pSessionData->GetLoaderProcess). { status = statusLoader; pAPIOut->hr = HRESULT_FROM_NT(status); } s_pLock->Release(); } else { pAPIOut->hr = HRESULT_FROM_NT(status); } SetDataLength(sizeof(API_THEMES)); return(status); } // -------------------------------------------------------------------------- // CThemeManagerAPIRequest::Execute_ProcessAssignSection // // Arguments: // // Returns: NTSTATUS // // Purpose: Handles API_THEMES_PROCESSASSIGNSECTION. // // History: 2002-02-26 scotthan created // -------------------------------------------------------------------------- NTSTATUS CThemeManagerAPIRequest::Execute_ProcessAssignSection (void) { // Note: The following must be true for this part of the // Load/ApplySecureTheme sequence to be truly secure: // // 1. There is a theme service worker thread handling API_THEMES_PROCESSLOADTHEME // now. That handler launched a secure session loader process, // 2. The API_THEMES_PROCESSLOADTHEME handler's thread is waiting for // secure session loader process to terminate. // 3. The only process that should be sending the API_THEMES_PROCESSASSIGNSECTION request // is the same secure session loader process. // _data is a hippo-union; store off in params to init out params API_THEMES_PROCESSASSIGNSECTION_IN *pAPIIn; pAPIIn = &reinterpret_cast(&_data)->apiSpecific.apiProcessAssignSection.in; HRESULT hrClient = pAPIIn->hrLoad; HANDLE hClientSection = pAPIIn->hSection; // init out params API_THEMES_PROCESSASSIGNSECTION_OUT *pAPIOut; pAPIOut = &reinterpret_cast(&_data)->apiSpecific.apiProcessAssignSection.out; pAPIOut->hr = E_FAIL; // all of this takes place under the session data lock. s_pLock->Acquire(); CLoaderProcess* pLoader = NULL; NTSTATUS status = _pSessionData->GetLoaderProcess(&pLoader); if( NT_SUCCESS(status) ) { HANDLE hProcessClient = _pAPIDispatcher->GetClientProcess(); if( pLoader->IsProcessLoader(hProcessClient) ) { // manage errors as nt status codes until the very end. status = hrClient & ~FACILITY_NT_BIT; // any work to do? if( NT_SUCCESS(status) ) { status = ImpersonateClientIfRequired(); if( NT_SUCCESS(status) ) { HANDLE hSectionReadWrite = NULL; // Map the incoming read-write section handle to // theme service's address space if (DuplicateHandle(hProcessClient, hClientSection, GetCurrentProcess(), &hSectionReadWrite, FILE_MAP_ALL_ACCESS, FALSE, 0) != FALSE) { // Copy the incoming read-write section to a read-only section, update the theme change count // Warning: this function will revert to self in order to create the // section in the system context. Impersonate the user again after it if needed HANDLE hSectionReadOnly = NULL; status = pLoader->ValidateAndCopySection(_pSessionData->GetData(), hSectionReadWrite, &hSectionReadOnly); // no longer don't need our dupe of the incoming, read-write section. CloseHandle(hSectionReadWrite); } else // DuplicateHandle { status = CStatusCode::StatusCodeOfLastError(); pLoader->SetHResult(HRESULT_FROM_NT(status)); } } else // NT_SUCCESS(ImpersonateClientIfRequired) { status = CStatusCode::StatusCodeOfLastError(); pLoader->SetHResult(HRESULT_FROM_NT(status)); } } else // SUCCEEDED(hrClient) { pLoader->SetHResult(HRESULT_FROM_NT(status)); DISPLAYMSG("CThemeManagerAPIRequest::Execute_ProcessAssignSection: client failed section creation"); } } else // CLoaderProcess::IsProcessLoader { status = E_ACCESSDENIED; DISPLAYMSG("CThemeManagerAPIRequest::Execute_ProcessAssignSection::IsProcessLoader failed"); } } pAPIOut->hr = HRESULT_FROM_NT(status); s_pLock->Release(); SetDataLength(sizeof(API_THEMES)); return(status); } // -------------------------------------------------------------------------- // CThemeManagerAPIRequest::Execute_UserLogon // // Arguments: // // Returns: NTSTATUS // // Purpose: Handles API_THEMES_USERLOGON. To call this API you must have // the SE_TCB_PRIVILEGE in your token. // // History: 2000-10-12 vtan created // -------------------------------------------------------------------------- NTSTATUS CThemeManagerAPIRequest::Execute_UserLogon (void) { NTSTATUS status; status = ClientHasTcbPrivilege(); if (NT_SUCCESS(status)) { HANDLE hToken; API_THEMES_USERLOGON_IN *pAPIIn; pAPIIn = &reinterpret_cast(&_data)->apiSpecific.apiUserLogon.in; if (DuplicateHandle(_pAPIDispatcher->GetClientProcess(), pAPIIn->hToken, GetCurrentProcess(), &hToken, 0, FALSE, DUPLICATE_SAME_ACCESS) != FALSE) { status = _pSessionData->UserLogon(hToken); TBOOL(CloseHandle(hToken)); } else { status = CStatusCode::StatusCodeOfLastError(); } } SetDataLength(sizeof(API_THEMES)); return(status); } // -------------------------------------------------------------------------- // CThemeManagerAPIRequest::Execute_UserLogoff // // Arguments: // // Returns: NTSTATUS // // Purpose: Handles API_THEMES_USERLOGOFF. To call this API you must have // the SE_TCB_PRIVILEGE in your token. // // History: 2000-10-12 vtan created // -------------------------------------------------------------------------- NTSTATUS CThemeManagerAPIRequest::Execute_UserLogoff (void) { NTSTATUS status; status = ClientHasTcbPrivilege(); if (NT_SUCCESS(status)) { status = _pSessionData->UserLogoff(); } SetDataLength(sizeof(API_THEMES)); return(status); } // -------------------------------------------------------------------------- // CThemeManagerAPIRequest::Execute_SessionCreate // // Arguments: // // Returns: NTSTATUS // // Purpose: Handles API_THEMES_SESSIONCREATE. To call this API you must // have the SE_TCB_PRIVILEGE in your token. // // History: 2000-11-09 vtan created // -------------------------------------------------------------------------- NTSTATUS CThemeManagerAPIRequest::Execute_SessionCreate (void) { NTSTATUS status; status = ClientHasTcbPrivilege(); if (NT_SUCCESS(status)) { HANDLE hProcessClient; CThemeManagerSessionData *pSessionData; ASSERTMSG(_pSessionData == NULL, "Session data already exists in CThemeManagerAPIRequest::Execute_SessionCreate"); if (DuplicateHandle(GetCurrentProcess(), _pAPIDispatcher->GetClientProcess(), GetCurrentProcess(), &hProcessClient, PROCESS_CREATE_THREAD | PROCESS_VM_OPERATION | PROCESS_VM_READ | PROCESS_VM_WRITE | PROCESS_QUERY_INFORMATION | PROCESS_DUP_HANDLE, FALSE, 0) != FALSE) { DWORD dwSessionID; dwSessionID = _pAPIDispatcher->GetClientSessionID(); pSessionData = new CThemeManagerSessionData(dwSessionID); if (pSessionData != NULL) { API_THEMES_SESSIONCREATE_IN *pAPIIn; pAPIIn = &reinterpret_cast(&_data)->apiSpecific.apiSessionCreate.in; status = pSessionData->Allocate(hProcessClient, s_dwServerChangeNumber, pAPIIn->pfnRegister, pAPIIn->pfnUnregister, pAPIIn->pfnClearStockObjects, pAPIIn->dwStackSizeReserve, pAPIIn->dwStackSizeCommit); if (NT_SUCCESS(status)) { int iIndex; CSingleThreadedExecution lock(*s_pLock); // Find the session data in the static array. If found // then remove the entry (don't allow duplicates). iIndex = FindIndexSessionData(dwSessionID); if (iIndex >= 0) { status = s_pSessionData->Remove(iIndex); } // If the static array has been destroyed (the service has been // stopped) then don't do anything - this is not an error. if (NT_SUCCESS(status) && (s_pSessionData != NULL)) { status = s_pSessionData->Add(pSessionData); } } pSessionData->Release(); } else { status = STATUS_NO_MEMORY; } TBOOL(CloseHandle(hProcessClient)); } else { status = CStatusCode::StatusCodeOfLastError(); } } SetDataLength(sizeof(API_THEMES)); return(status); } // -------------------------------------------------------------------------- // CThemeManagerAPIRequest::Execute_SessionDestroy // // Arguments: // // Returns: NTSTATUS // // Purpose: Handles API_THEMES_SESSIONDESTROY. To call this API you must // have the SE_TCB_PRIVILEGE in your token. // // History: 2000-11-09 vtan created // -------------------------------------------------------------------------- NTSTATUS CThemeManagerAPIRequest::Execute_SessionDestroy (void) { NTSTATUS status; status = ClientHasTcbPrivilege(); if (NT_SUCCESS(status)) { int iIndex; CSingleThreadedExecution lock(*s_pLock); iIndex = FindIndexSessionData(_pAPIDispatcher->GetClientSessionID()); if (iIndex >= 0) { status = s_pSessionData->Remove(iIndex); } else { status = STATUS_SUCCESS; } } SetDataLength(sizeof(API_THEMES)); return(status); } // -------------------------------------------------------------------------- // CThemeManagerAPIRequest::Execute_Ping // // Arguments: // // Returns: NTSTATUS // // Purpose: Handles API_THEMES_PING. Tell the client we're alive. // // History: 2000-11-30 vtan created // -------------------------------------------------------------------------- NTSTATUS CThemeManagerAPIRequest::Execute_Ping (void) { SetDataLength(sizeof(API_THEMES)); return(STATUS_SUCCESS); }