//+------------------------------------------------------------------------- // // Microsoft Windows // // Copyright (C) Microsoft Corporation, 2000 // // File: updates.cpp // //-------------------------------------------------------------------------- #include "pch.h" #pragma hdrstop ///////////////////////////////////////////////////////////////////////////// // GENERIC_MAPPING Updates::m_AdminGenericMapping = { STANDARD_RIGHTS_READ, // Generic read STANDARD_RIGHTS_WRITE, // Generic write STANDARD_RIGHTS_EXECUTE, // Generic execute STANDARD_RIGHTS_READ | // Generic all STANDARD_RIGHTS_WRITE | STANDARD_RIGHTS_EXECUTE }; Updates::Updates() : m_pAdminSid(NULL), m_pAdminAcl(NULL), m_refs(0) { m_hEngineMutex = CreateMutex(NULL, FALSE, NULL); } Updates::~Updates() { CloseHandle(m_hEngineMutex); if ( NULL != m_pAdminAcl ) { delete m_pAdminAcl; } if ( NULL != m_pAdminSid ) { FreeSid(m_pAdminSid); } DEBUGMSG("Updates: CoDisconnectObject"); if ( FAILED(CoDisconnectObject((IUnknown *)this, 0)) ) { DEBUGMSG("CoDisconnectObject() failed"); } } BOOL Updates::m_fInitializeSecurity(void) { BOOL fStatus; ULONG cbAcl; SID_IDENTIFIER_AUTHORITY ntAuthority = SECURITY_NT_AUTHORITY; fStatus = AllocateAndInitializeSid( &ntAuthority, 2, SECURITY_BUILTIN_DOMAIN_RID, DOMAIN_ALIAS_RID_ADMINS, 0,0,0,0,0,0, &m_pAdminSid); if ( !fStatus ) { DEBUGMSG("Fail to initialize SID with error %d", GetLastError()); return FALSE; } cbAcl = sizeof(ACL) + sizeof(ACCESS_ALLOWED_ACE) - sizeof(DWORD) //sizeof(ACCESS_ALLOWED_ACE.SidStart) + GetLengthSid(m_pAdminSid); m_pAdminAcl = (PACL) new BYTE[ cbAcl ]; if ( (NULL == m_pAdminAcl) || !InitializeAcl(m_pAdminAcl, cbAcl, ACL_REVISION) || !AddAccessAllowedAce(m_pAdminAcl, ACL_REVISION, STANDARD_RIGHTS_WRITE, m_pAdminSid) || !InitializeSecurityDescriptor(&m_AdminSecurityDesc, SECURITY_DESCRIPTOR_REVISION) || !SetSecurityDescriptorOwner(&m_AdminSecurityDesc, m_pAdminSid, FALSE) || !SetSecurityDescriptorGroup(&m_AdminSecurityDesc, m_pAdminSid, FALSE) || !SetSecurityDescriptorDacl(&m_AdminSecurityDesc, TRUE, m_pAdminAcl, FALSE) ) { if ( NULL != m_pAdminAcl ) { delete m_pAdminAcl; m_pAdminAcl = NULL; FreeSid(m_pAdminSid); m_pAdminSid = NULL; } return FALSE; } return TRUE; } HRESULT Updates::m_AccessCheckClient(void) { BOOL accessGranted = FALSE; DWORD grantedAccess; HANDLE clientToken = NULL; BYTE privilegeSet[500]; // Large buffer DWORD privilegeSetSize = sizeof(privilegeSet); static BOOL fInitSecurity = FALSE; if ( !fInitSecurity ) { if ( !(fInitSecurity = m_fInitializeSecurity()) ) { DEBUGMSG("Fail to initialized SID"); return E_ACCESSDENIED; } } if (FAILED(CoImpersonateClient())) { return E_ACCESSDENIED; } if ( OpenThreadToken(GetCurrentThread(), TOKEN_QUERY, TRUE, &clientToken) ) { if (FALSE == AccessCheck( &m_AdminSecurityDesc, clientToken, STANDARD_RIGHTS_WRITE, &m_AdminGenericMapping, (PPRIVILEGE_SET) privilegeSet, &privilegeSetSize, &grantedAccess, &accessGranted)) { DEBUGMSG("Fail to call AccessCheck() with error %d", GetLastError()); } } if ( clientToken != NULL ) { CloseHandle( clientToken ); } if (FAILED(CoRevertToSelf())) { return E_ACCESSDENIED;; } return (accessGranted )? S_OK : E_ACCESSDENIED; } STDMETHODIMP Updates::QueryInterface(REFIID riid, void **ppvObject) { if (NULL == ppvObject) { return E_INVALIDARG; } if(riid == IID_IUnknown || riid == IID_IClassFactory || riid == IID_IUpdates) { *ppvObject = this; AddRef(); } else { *ppvObject = NULL; return E_NOINTERFACE; } return S_OK; } ULONG __stdcall Updates::AddRef() { long cRef = InterlockedIncrement(&m_refs); DEBUGMSG("Updates AddRef = %d", cRef); return cRef; } ULONG __stdcall Updates::Release() { long cRef = InterlockedDecrement(&m_refs); DEBUGMSG("Updates Release = %d", cRef); return cRef; } STDMETHODIMP Updates::CreateInstance(IUnknown *pUnkOuter, REFIID riid, void **ppvObject) { HRESULT hr = m_AccessCheckClient(); if ( FAILED(hr) ) { return hr; } if(pUnkOuter != NULL) { return CLASS_E_NOAGGREGATION; } if (NULL == ppvObject) { return E_INVALIDARG; } if(riid == IID_IUnknown || riid == IID_IClassFactory || riid == IID_IUpdates) { *ppvObject = this; AddRef(); } else { *ppvObject = NULL; return E_NOINTERFACE; } return S_OK; } STDMETHODIMP Updates::LockServer(BOOL /*bFlag*/) { HRESULT hr = m_AccessCheckClient(); if ( FAILED(hr) ) { return hr; } return E_FAIL; } STDMETHODIMP Updates::AvailableSessions(UINT *pcSess) { HRESULT hr = m_AccessCheckClient(); if ( FAILED(hr)) { return hr; } if (NULL == pcSess) { return E_INVALIDARG; } *pcSess = ::AvailableSessions(); //DEBUGMSG("WUAUENG Updates::AvailableSessions was called and return value is %d", *pcSess); return S_OK; } STDMETHODIMP Updates::get_State(/*[out, retval]*/ AUSTATE *pAuState) { HRESULT hr = m_AccessCheckClient(); if ( FAILED(hr) ) { return hr; } if (NULL == pAuState) { return E_INVALIDARG; } WaitForSingleObject(m_hEngineMutex, INFINITE); pAuState->dwState = gpState->GetState(); pAuState->fDisconnected = gpState->fDisconnected(); pAuState->dwCltAction = gpState->GetCltAction(); gpState->SetCltAction(AUCLT_ACTION_NONE); //once client read it, reset ReleaseMutex(m_hEngineMutex); return S_OK; } STDMETHODIMP Updates::GetUpdatesList(/*[out]*/ VARIANT *pUpdates) { HRESULT hr = m_AccessCheckClient(); if ( FAILED(hr) ) { return hr; } DEBUGMSG("WUAUENG Getting updates list"); if ( NULL == pUpdates ) { return E_INVALIDARG; } WaitForSingleObject(m_hEngineMutex, INFINITE); hr = ::GetUpdatesList(pUpdates); ReleaseMutex(m_hEngineMutex); return hr; } STDMETHODIMP Updates::GetNotifyData(/*[out]*/ CLIENT_NOTIFY_DATA *pNotifyData) { HRESULT hr = m_AccessCheckClient(); if ( FAILED(hr) ) { return hr; } DEBUGMSG("WUAUENG Getting client notify data"); if ( NULL == pNotifyData ) { return E_INVALIDARG; } WaitForSingleObject(m_hEngineMutex, INFINITE); *pNotifyData = gClientNotifyData; ReleaseMutex(m_hEngineMutex); return hr; } /* fixcode, only used by client, could get rid of and add VARIANT to ClientMessage()*/ STDMETHODIMP Updates::SaveSelections(/*[in]*/ VARIANT vUpdates) { HRESULT hr = m_AccessCheckClient(); DEBUGMSG("Updates::SaveSelections start"); if ( FAILED(hr) ) { goto done; } DEBUGMSG("WUAUENG Saving selections, state is %d", gpState->GetState()); if (vUpdates.vt != (VT_ARRAY | VT_VARIANT)) { DEBUGMSG("WUAUENG invalid variant list"); return E_INVALIDARG; } WaitForSingleObject(m_hEngineMutex, INFINITE); ::saveSelection(&vUpdates); hr = S_OK; ReleaseMutex(m_hEngineMutex); done: DEBUGMSG("Updates::SaveSelections end"); return hr; } STDMETHODIMP Updates::StartDownload(void) { DEBUGMSG("WUAUENG updates->StartDownload called"); HRESULT hr = m_AccessCheckClient(); if ( FAILED(hr) ) { return hr; } WaitForSingleObject(m_hEngineMutex, INFINITE); hr = ::StartDownload(); ReleaseMutex(m_hEngineMutex); return hr; } STDMETHODIMP Updates::GetDownloadStatus(UINT *pPercentage, DWORD *pStatus) { HRESULT hr = m_AccessCheckClient(); if ( FAILED(hr) ) { return hr; } if ((NULL == pPercentage) || (NULL == pStatus)) { return E_INVALIDARG; } WaitForSingleObject(m_hEngineMutex, INFINITE); hr = ::GetDownloadStatus(pPercentage, pStatus); ReleaseMutex(m_hEngineMutex); return hr; } STDMETHODIMP Updates::SetDownloadPaused(/*[in]*/ BOOL bPaused) { HRESULT hr = m_AccessCheckClient(); if ( FAILED(hr) ) { return hr; } return PauseDownload(bPaused); } STDMETHODIMP Updates::ClientMessage(/*[in]*/ UINT msg) { HRESULT hr = m_AccessCheckClient(); if ( FAILED(hr) ) { return hr; } // fixcode is this whole interface just one big security whole? what about other // routines. can't anyone call them? switch (msg) { case AUMSG_PRE_INSTALL: DEBUGMSG("WUAUENG ClientMessage(AUMSG_PRE_INSTALL)"); DEBUGMSG("WUAUENG Msg:Install, State->Install Pending"); gpState->SetState(AUSTATE_INSTALL_PENDING); // is there any benefit to doing this? break; default: DEBUGMSG("WUAUENG ClientMessage(!!unknown!!)"); break; } return S_OK; } STDMETHODIMP Updates::get_Option(AUOPTION * pVal) { HRESULT hr = m_AccessCheckClient(); if ( FAILED(hr) ) { return hr; } if (NULL == pVal) { return E_INVALIDARG; } if (NULL == gpState) { return E_FAIL; } *pVal = gpState->GetOption(); return S_OK; } STDMETHODIMP Updates::put_Option(AUOPTION val) { HRESULT hr = m_AccessCheckClient(); if ( FAILED(hr) ) { return hr; } AUOPTION CurrOption = gpState->GetOption(); if ( FAILED(hr = gpState->SetOption(val)) ) { return hr; } // if asking to disable, post msg if ( (AUOPTION_AUTOUPDATE_DISABLE == val.dwOption) && (CurrOption.dwOption != val.dwOption) ) { DEBUGMSG("AU service disabled"); DisableAU(); } // else if asking to enable, post msg else if ((AUOPTION_AUTOUPDATE_DISABLE == CurrOption.dwOption) && (val.dwOption != CurrOption.dwOption) || gpState->GetState() < AUSTATE_DETECT_PENDING) { ResetEngine(); } if (CurrOption.dwOption != val.dwOption || (AUOPTION_SCHEDULED == val.dwOption && (CurrOption.dwSchedInstallDay != val.dwSchedInstallDay || CurrOption.dwSchedInstallTime != val.dwSchedInstallTime))) { SetEvent(ghSettingsChanged); } return S_OK; } STDMETHODIMP Updates::ConfigureAU() { HRESULT hr = m_AccessCheckClient(); if ( FAILED(hr) ) { return hr; } WaitForSingleObject(m_hEngineMutex, 10000); if ( AUSTATE_NOT_CONFIGURED == gpState->GetState() ) { PostThreadMessage(gdwWorkerThreadId, AUMSG_EULA_ACCEPTED, 0, 0); } ReleaseMutex(m_hEngineMutex); return S_OK; } STDMETHODIMP Updates::get_EvtHandles(DWORD dwCltProcId, AUEVTHANDLES *pauevtHandles) { HRESULT hr = m_AccessCheckClient(); if ( FAILED(hr) ) { return hr; } WaitForSingleObject(ghMutex, INFINITE); // make sure proc id has been populated DWORD dwProcId = ghClientHandles.GetProcId(); ReleaseMutex(ghMutex); if (dwProcId != dwCltProcId) { DEBUGMSG("WUAUENG Unauthorized client %d trying to get event handles for real client %d", dwCltProcId, dwProcId); return E_ACCESSDENIED; } if (NULL == pauevtHandles) { DEBUGMSG("WUAUENG GetEvtHandles invalid argument"); return E_INVALIDARG; } WaitForSingleObject(m_hEngineMutex, INFINITE); hr = ::GetEvtHandles(pauevtHandles); ReleaseMutex(m_hEngineMutex); return hr; } STDMETHODIMP Updates::GetInstallXML(/*[out]*/ BSTR *pbstrCatalogXML, /*[out]*/ BSTR *pbstrDownloadXML) { DEBUGMSG("Updates::GetInstallXML"); HRESULT hr = m_AccessCheckClient(); if ( FAILED(hr) ) { goto done; } if (NULL == pbstrCatalogXML || NULL == pbstrDownloadXML) { return E_INVALIDARG; } WaitForSingleObject(m_hEngineMutex, INFINITE); hr = ::GetInstallXML(pbstrCatalogXML, pbstrDownloadXML); ReleaseMutex(m_hEngineMutex); done: return hr; } STDMETHODIMP Updates::LogEvent(/*[in]*/ WORD wType, /*[in]*/ WORD wCategory, /*[in]*/ DWORD dwEventID, /*[in]*/ VARIANT vItems) { DEBUGMSG("Updates::LogEvent"); HRESULT hr = m_AccessCheckClient(); if ( FAILED(hr) ) { return hr; } if ((VT_ARRAY | VT_BSTR) != vItems.vt || NULL == vItems.parray) { DEBUGMSG("WUAUENG invalid variant list"); return E_INVALIDARG; } WaitForSingleObject(m_hEngineMutex, INFINITE); CAUEventLog aueventlog(g_hInstance); hr = aueventlog.LogEvent( wType, wCategory, dwEventID, vItems.parray) ? S_OK : E_FAIL; ReleaseMutex(m_hEngineMutex); return hr; }