windows-nt/Source/XPSP1/NT/enduser/windows.com/wuau/wuaueng/updates.h
2020-09-26 16:20:57 +08:00

467 lines
12 KiB
C++

//+-------------------------------------------------------------------------
//
// Microsoft Windows
//
// Copyright (C) Microsoft Corporation, 2000
//
// File: updates.h
// Definition of the Updates class
//
//--------------------------------------------------------------------------
#pragma once
#include "wuauengi.h"
#include "wuaulib.h"
#include <Wtsapi32.h>
#include <wuaustate.h>
#include <wuaueng.h>
#include <accctrl.h>
#include <aclapi.h>
#include "pch.h"
// functions IUpdates uses
void HrUninit(void);
HRESULT StartDownload(void);
HRESULT PauseDownload(BOOL bPause);
HRESULT GetUpdatesList(VARIANT *vList);
HRESULT GetInstallXML(BSTR *pbstrCatalogXML, BSTR *pbstrDownloadXML);
void saveSelection(VARIANT *selection);
HRESULT GetDownloadStatus(UINT *pPercentage, DWORD *pdwnldStatus, BOOL fCareAboutConnection = TRUE);
HRESULT GetInstallStatus(UINT *pNumFinished, DWORD *pStatus);
HRESULT GetEvtHandles(AUEVTHANDLES *pAuEvtHandles);
DWORD AvailableSessions(void);
BOOL IsSessionAUEnabledAdmin(DWORD dwSessionId);
/////////////////////////////////////////////////////////////////////////////
// Updates
class Updates :
public IUpdates
{
public:
HANDLE m_hEngineMutex;
Updates();
~Updates();
BOOL m_fInitializeSecurity(void);
HRESULT m_AccessCheckClient(void);
HRESULT GetServiceHandles();
DWORD ProcessState();
BOOL CheckConnection();
// IUnknown
STDMETHOD(QueryInterface)(REFIID riid, void **ppvObject);
STDMETHOD_(ULONG, AddRef)(void);
STDMETHOD_(ULONG, Release)(void);
// IClassFactory
STDMETHOD(CreateInstance)(IUnknown*,REFIID,void**);
STDMETHOD(LockServer)(BOOL);
// IUpdates
STDMETHOD(get_State)(/*[out, retval]*/ AUSTATE *pAuState);
STDMETHOD(get_Option)(/*[out, retval]*/ AUOPTION * pAuOpt);
STDMETHOD(put_Option)(/*[in]*/ AUOPTION auopt);
STDMETHOD(GetUpdatesList)(/*[out]*/ VARIANT *pUpdates);
STDMETHOD(SaveSelections)(/*[in]*/ VARIANT vUpdates);
STDMETHOD(StartDownload)(void);
STDMETHOD(GetDownloadStatus)(/*[out]*/ UINT *, /*[out]*/ DWORD *);
STDMETHOD(SetDownloadPaused)(/*[in]*/ BOOL bPaused);
STDMETHOD(ConfigureAU)();
STDMETHOD(AvailableSessions(/*[out]*/ UINT *pcSess));
STDMETHOD(get_EvtHandles(/*[in]*/DWORD dwCltProcId, /*[out]*/ AUEVTHANDLES *pauevtHandles));
STDMETHOD(ClientMessage(/*[in]*/ UINT msg));
//STDMETHOD(PingStatus(/*[in]*/ StatusEntry se));
STDMETHOD(GetNotifyData(/*[out]*/ CLIENT_NOTIFY_DATA *pNotifyData));
STDMETHOD(GetInstallXML(/*[out]*/ BSTR *pbstrCatalogXML, /*[out]*/ BSTR *pbstrDownloadXML));
STDMETHOD(LogEvent(/*[in]*/ WORD wType, /*[in]*/ WORD wCategory, /*[in]*/ DWORD dwEventID, /*[in]*/ VARIANT vItems));
private:
SECURITY_DESCRIPTOR m_AdminSecurityDesc;
static GENERIC_MAPPING m_AdminGenericMapping;
PSID m_pAdminSid;
PACL m_pAdminAcl;
long m_refs;
};
class CLIENT_HANDLES
{
public:
CLIENT_HANDLES(void){
InitHandle();
}
~CLIENT_HANDLES(void){
Reset(TRUE);
}
BOOL fRebootWarningMode()
{
return m_fRebootWarningMode;
}
void StopClients(BOOL fRelaunch) {
if (m_fRebootWarningMode)
{
DEBUGMSG("WUAUENG told %d CLIENT(S) to exit", m_dwRebootWarningClientNum);
if (NULL != m_hClientExitEvt)
{
SetEvent(m_hClientExitEvt);
}
}
else
{
if (fClient())
{
if (fRelaunch)
{
DEBUGMSG("WUAUENG told WUAUCLT to relaunch");
NotifyClient(NOTIFY_RELAUNCH_CLIENT);
}
else
{
DEBUGMSG("WUAUENG told WUAUCLT to exit");
NotifyClient(NOTIFY_STOP_CLIENT);
}
}
else
{
DEBUGMSG("WARNING: StopClients() : no existing client");
}
}
}
void ClientAddTrayIcon(void) {
if (m_fRebootWarningMode || m_fAsLocalSystem)
{
DEBUGMSG("WARNING: ClientAddTrayIcon() called in wrong mode");
return;
}
if (fClient())
{
NotifyClient(NOTIFY_ADD_TRAYICON);
}
}
void ClientRemoveTrayIcon(void) {
if (m_fRebootWarningMode || m_fAsLocalSystem)
{
DEBUGMSG("WARNING: ClientRemoveTrayIcon() called in wrong mode");
return;
}
if (fClient())
{
NotifyClient(NOTIFY_REMOVE_TRAYICON);
}
}
void ClientStateChange(void) {
if (m_fRebootWarningMode || m_fAsLocalSystem)
{
DEBUGMSG("WARNING: ClientStateChange() called in wrong mode");
return;
}
NotifyClient(NOTIFY_STATE_CHANGE);
}
void ClientShowInstallWarning(void){
if (m_fRebootWarningMode || m_fAsLocalSystem)
{
DEBUGMSG("WARNING: ClientShowInstallWarning() called in wrong mode");
return;
}
if (fClient())
{
NotifyClient(NOTIFY_SHOW_INSTALLWARNING);
}
}
void ResetClient(void) {
if (m_fRebootWarningMode || m_fAsLocalSystem)
{
DEBUGMSG("WARNING: ResetClient() called in wrong mode");
return;
}
if (fClient())
{
NotifyClient(NOTIFY_RESET);
}
}
//checking existence of client(s)
BOOL fClient(void) {
if (m_fRebootWarningMode)
{
return m_dwRebootWarningClientNum > 0;
}
else
{
return (-1 != m_dwProcId) && (NULL != m_hClientProcess);
}
}
void SetHandle(PROCESS_INFORMATION & ProcessInfo, BOOL fAsLocalSystem)
{
m_fRebootWarningMode = FALSE;
m_fAsLocalSystem = fAsLocalSystem;
m_dwProcId = ProcessInfo.dwProcessId;
m_hClientProcess = ProcessInfo.hProcess;
SafeCloseHandle(ProcessInfo.hThread);
}
BOOL AddHandle(PROCESS_INFORMATION & ProcessInfo)
{
HANDLE *pTmp;
m_fRebootWarningMode = TRUE;
SafeCloseHandle(ProcessInfo.hThread);
pTmp = (HANDLE*) realloc(m_phRebootWarningClients, (m_dwRebootWarningClientNum+1)*sizeof(HANDLE));
if (NULL == pTmp)
{
return FALSE;
}
m_phRebootWarningClients = pTmp;
m_phRebootWarningClients[m_dwRebootWarningClientNum] = ProcessInfo.hProcess;
m_dwRebootWarningClientNum ++;
return TRUE;
}
void RemoveHandle(HANDLE hProcess)
{
if (m_fRebootWarningMode)
{
for (DWORD i = 0; i < m_dwRebootWarningClientNum; i++)
{
if (hProcess == m_phRebootWarningClients[i])
{
CloseHandle(hProcess);
m_phRebootWarningClients[i] = m_phRebootWarningClients[m_dwRebootWarningClientNum -1];
m_dwRebootWarningClientNum --;
DEBUGMSG("RemoveHandle in Reboot warning mode");
}
}
if (0 == m_dwRebootWarningClientNum)
{//all clients are gone
Reset();
}
}
else
{
DEBUGMSG("RemoveHandle in regular mode");
if (hProcess == m_hClientProcess)
{ //all clients are gone
Reset();
}
}
}
void InitHandle(void)
{
DEBUGMSG("WUAUENG client handles initialized");
m_hClientProcess = NULL;
m_dwProcId = -1;
m_dwRebootWarningClientNum = 0;
m_phRebootWarningClients = NULL;
m_fRebootWarningMode = FALSE;
m_fAsLocalSystem = FALSE;
m_hClientExitEvt = NULL;
}
DWORD GetProcId(void)
{
if (m_fRebootWarningMode)
{
DEBUGMSG("WARNING: GetProcId() called in wrong mode");
return -1;
}
return m_dwProcId;
}
CONST HANDLE hClientProcess(void)
{
if (m_fRebootWarningMode)
{
DEBUGMSG("WARNING: hClientProcess() called in wrong mode");
return NULL;
}
return m_hClientProcess;
}
void WaitForClientExits()
{
HANDLE *pTmp;
if (!m_fRebootWarningMode)
{
if (NULL != m_hClientProcess)
{
WaitForSingleObject(m_hClientProcess, INFINITE);
}
}
else
{
if (m_dwRebootWarningClientNum > 0)
{
WaitForMultipleObjects(m_dwRebootWarningClientNum, m_phRebootWarningClients, TRUE, INFINITE);
}
}
Reset();
return;
}
//////////////////////////////////////////////////////////////////
// szName should have size of at least MAX_PATH characters
//////////////////////////////////////////////////////////////////
BOOL CreateClientExitEvt(LPTSTR OUT szName, DWORD dwCchSize)
{
const TCHAR szClientName[] = _T("Global\\Microsoft.WindowsUpdate.AU.ClientExitEvt.");
TCHAR szBuf[50];
GUID guid;
HRESULT hr;
AUASSERT(NULL == m_hClientExitEvt);
if (FAILED(hr = CoCreateGuid(&guid)))
{
DEBUGMSG("Fail to Create guid with error %#lx", hr);
return FALSE;
}
StringFromGUID2(guid, szBuf, ARRAYSIZE(szBuf)); // szBuf should be big enough, function always succeed
if (FAILED(hr = StringCchCopyEx(szName, dwCchSize, szClientName, NULL, NULL, MISTSAFE_STRING_FLAGS)) ||
FAILED(hr = StringCchCatEx(szName, dwCchSize, szBuf, NULL, NULL, MISTSAFE_STRING_FLAGS))) //szName is now 86 characters long
{
DEBUGMSG("Fail to construct client exit event name with error %#lx", hr);
return FALSE;
}
if (NULL == (m_hClientExitEvt = CreateEvent(NULL, TRUE, FALSE, szName)))
{
DEBUGMSG("Fail to create client exit event with error %d", GetLastError());
return FALSE;
}
if (!AllowEveryOne(m_hClientExitEvt))
{
DEBUGMSG("Fail to grant access on client exit event to everyone");
SafeCloseHandleNULL(m_hClientExitEvt);
return FALSE;
}
DEBUGMSG("access granted to everyone on client exit event");
return TRUE;
}
private:
void NotifyClient(CLIENT_NOTIFY_CODE notClientCode)
{
//notify client even before or after it is created
#ifdef DBG
LPCSTR aClientCodeMsg[] = {"stop client", "add trayicon", "remove trayicon", "state change", "show install warning", "reset client", "relaunch client"};
DEBUGMSG("Notify Client for %s", aClientCodeMsg[notClientCode-1]);
#endif
gClientNotifyData.actionCode = notClientCode;
SetEvent(ghNotifyClient);
return;
}
////////////////////////////////////////////////////////////////////
// grant SYNCHRONIZE access on hObject to everyone
////////////////////////////////////////////////////////////////////
BOOL AllowEveryOne (HANDLE hObject) // handle to the event
{
LPTSTR pszTrustee; // trustee for new ACE
TRUSTEE_FORM TrusteeForm; // format of trustee structure
DWORD dwRes;
PACL pOldDACL = NULL, pNewDACL = NULL;
PSECURITY_DESCRIPTOR pSD = NULL;
EXPLICIT_ACCESS ea;
PSID pWorldSid = NULL;
BOOL fRet;
// World SID
SID_IDENTIFIER_AUTHORITY WorldAuth = SECURITY_WORLD_SID_AUTHORITY;
if (! (fRet =AllocateAndInitializeSid(&WorldAuth,1, SECURITY_WORLD_RID, 0,0,0,0,0,0,0,&pWorldSid)))
{
DEBUGMSG("WUAUENG: AllowEveryOne() failed with error %d", GetLastError());
goto Cleanup;
}
// Get a pointer to the existing DACL.
dwRes = GetSecurityInfo(hObject, SE_KERNEL_OBJECT,
DACL_SECURITY_INFORMATION,
NULL, NULL, &pOldDACL, NULL, &pSD);
if (!(fRet = (ERROR_SUCCESS == dwRes))) {
DEBUGMSG( "GetSecurityInfo Error %u", dwRes );
goto Cleanup;
}
// Initialize an EXPLICIT_ACCESS structure for the new ACE.
ZeroMemory(&ea, sizeof(EXPLICIT_ACCESS));
ea.grfAccessPermissions = SYNCHRONIZE;
ea.grfAccessMode = SET_ACCESS;
ea.grfInheritance= NO_INHERITANCE;
ea.Trustee.TrusteeForm = TRUSTEE_IS_SID;
ea.Trustee.TrusteeType = TRUSTEE_IS_GROUP;
ea.Trustee.ptstrName = (LPTSTR)pWorldSid;
// Create a new ACL that merges the new ACE
// into the existing DACL.
dwRes = SetEntriesInAcl(1, &ea, pOldDACL, &pNewDACL);
if (!(fRet = (ERROR_SUCCESS == dwRes))) {
DEBUGMSG( "SetEntriesInAcl Error %u", dwRes );
goto Cleanup;
}
// Attach the new ACL as the object's DACL.
dwRes = SetSecurityInfo(hObject, SE_KERNEL_OBJECT,
DACL_SECURITY_INFORMATION,
NULL, NULL, pNewDACL, NULL);
if (!(fRet = (ERROR_SUCCESS == dwRes))) {
DEBUGMSG( "SetSecurityInfo Error %u", dwRes );
goto Cleanup;
}
Cleanup:
if(pSD != NULL)
LocalFree((HLOCAL) pSD);
if(pNewDACL != NULL)
LocalFree((HLOCAL) pNewDACL);
if (NULL != pWorldSid)
{
FreeSid(pWorldSid);
}
return fRet;
}
void Reset( BOOL fDestructor = FALSE)
{
SafeCloseHandleNULL(m_hClientProcess);
SafeCloseHandleNULL(m_hClientExitEvt);
m_dwProcId = -1;
if (!fDestructor)
{
ResetEvent(ghNotifyClient);
}
if (m_dwRebootWarningClientNum > 0)
{
DEBUGMSG("WUAUENG CLIENT_HANDLES::Reset() close %d handles", m_dwRebootWarningClientNum);
for (DWORD i = 0; i < m_dwRebootWarningClientNum; i++)
{
CloseHandle(m_phRebootWarningClients[i]);
}
}
SafeFreeNULL(m_phRebootWarningClients); //still need to free even m_dwRebootWarningClientNum is 0
m_dwRebootWarningClientNum = 0;
m_phRebootWarningClients = NULL;
m_fRebootWarningMode = FALSE;
m_fAsLocalSystem = FALSE;
}
private:
HANDLE m_hClientProcess; //Handle to the client process
DWORD m_dwProcId;
HANDLE *m_phRebootWarningClients;
DWORD m_dwRebootWarningClientNum; //number of valid handles in m_phRebootWarningClients
BOOL m_fRebootWarningMode;
BOOL m_fAsLocalSystem;
HANDLE m_hClientExitEvt;
};
extern CLIENT_HANDLES ghClientHandles;