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

663 lines
16 KiB
C++

//+---------------------------------------------------------------------------
//
// Microsoft Windows
// Copyright (C) Microsoft Corporation, 2001.
//
// sens.cpp
//
// Modele that implements a COM+ subscriber for use with SENS notifications.
//
// 10/9/2001 annah Created
// Ported code from BITS sources. Removed SEH code,
// changed methods that threw exceptions to return
// error codes.
//
//----------------------------------------------------------------------------
#include "pch.h"
#include "ausens.h"
#include "tscompat.h"
#include "service.h"
static CLogonNotification *g_SensLogonNotification = NULL;
HRESULT ActivateSensLogonNotification()
{
HRESULT hr = S_OK;
// only activate once
if ( g_SensLogonNotification )
{
DEBUGMSG("AUSENS Logon object was already created; reusing object.");
return S_OK;
}
g_SensLogonNotification = new CLogonNotification();
if (!g_SensLogonNotification)
{
DEBUGMSG("AUSENS Failed to alocate memory for CLogonNotification object.");
return E_OUTOFMEMORY;
}
hr = g_SensLogonNotification->Initialize();
if (FAILED(hr))
{
DEBUGMSG( "AUSENS notification activation failed." );
}
else
{
DEBUGMSG( "AUSENS notification activated" );
}
return hr;
}
HRESULT DeactivateSensLogonNotification()
{
if (!g_SensLogonNotification)
{
DEBUGMSG("AUSENS Logon object is not activated; ignoring call.");
return S_OK;
}
delete g_SensLogonNotification;
g_SensLogonNotification = NULL;
DEBUGMSG( "AUSENS notification deactivated" );
return S_OK;
}
//----------------------------------------------------------------------------
// BSTR manipulation
//----------------------------------------------------------------------------
HRESULT AppendBSTR(BSTR *bstrDest, BSTR bstrAppend)
{
HRESULT hr = S_OK;
BSTR bstrNew = NULL;
if (bstrDest == NULL || *bstrDest == NULL)
return E_INVALIDARG;
if (bstrAppend == NULL)
return S_OK;
hr = VarBstrCat(*bstrDest, bstrAppend, &bstrNew);
if (SUCCEEDED(hr))
{
SysFreeString(*bstrDest);
*bstrDest = bstrNew;
}
return hr;
}
// Caller is responsible for freeing bstrOut
HRESULT BSTRFromIID(IN REFIID riid, OUT BSTR *bstrOut)
{
HRESULT hr = S_OK;
LPOLESTR lpszGUID = NULL;
if (bstrOut == NULL)
{
hr = E_INVALIDARG;
goto done;
}
hr = StringFromIID(riid, &lpszGUID);
if (FAILED(hr))
{
DEBUGMSG("AUSENS Failed to extract GUID from string");
goto done;
}
*bstrOut = SysAllocString(lpszGUID);
if (*bstrOut == NULL)
{
hr = E_OUTOFMEMORY;
goto done;
}
done:
if (lpszGUID)
{
CoTaskMemFree(lpszGUID);
}
return hr;
}
HRESULT CBstrTable::Initialize()
{
HRESULT hr = S_OK;
hr = BSTRFromIID(g_oLogonSubscription.MethodGuid, &m_bstrLogonMethodGuid);
if (FAILED(hr))
{
goto done;
}
m_bstrLogonMethodName = SysAllocString(g_oLogonSubscription.pszMethodName);
if (m_bstrLogonMethodName == NULL)
{
hr = E_OUTOFMEMORY;
goto done;
}
m_bstrSubscriptionName = SysAllocString(SUBSCRIPTION_NAME_TEXT);
if (m_bstrSubscriptionName == NULL)
{
hr = E_OUTOFMEMORY;
goto done;
}
m_bstrSubscriptionDescription = SysAllocString(SUBSCRIPTION_DESCRIPTION_TEXT);
if (m_bstrSubscriptionDescription == NULL)
{
hr = E_OUTOFMEMORY;
goto done;
}
hr = BSTRFromIID(_uuidof(ISensLogon), &m_bstrSensLogonGuid);
if (FAILED(hr))
{
goto done;
}
hr = BSTRFromIID(SENSGUID_EVENTCLASS_LOGON, &m_bstrSensEventClassGuid);
if (FAILED(hr))
{
goto done;
}
done:
return hr;
}
//----------------------------------------------------------------------------
// Implementation for CLogonNotification methods
//----------------------------------------------------------------------------
HRESULT CLogonNotification::Initialize()
{
HRESULT hr = S_OK;
SIZE_T cSubscriptions = 0;
//
// Create auxiliary object with BSTR names used for several actions
//
m_oBstrTable = new CBstrTable();
if (!m_oBstrTable)
{
return E_OUTOFMEMORY;
}
hr = m_oBstrTable->Initialize();
if (FAILED(hr))
{
DEBUGMSG("AUSENS Could not create auxiliary structure BstrTable");
return hr;
}
//
// Load the type library from SENS
//
hr = LoadTypeLibEx(L"SENS.DLL", REGKIND_NONE, &m_TypeLib);
if (FAILED(hr))
{
DEBUGMSG("AUSENS Could not load type library from SENS DLL");
goto done;
}
//
// Get TypeInfo for ISensLogon from SENS typelib -- this will
// simplify thing for us when implementing IDispatch methods
//
hr = m_TypeLib->GetTypeInfoOfGuid(__uuidof( ISensLogon ), &m_TypeInfo);
if (FAILED(hr))
{
DEBUGMSG("AUSENS Could not get type info for ISensLogon.");
goto done;
}
//
// Grab an interface pointer of the EventSystem object
//
hr = CoCreateInstance(CLSID_CEventSystem, NULL, CLSCTX_INPROC_SERVER | CLSCTX_LOCAL_SERVER, IID_IEventSystem, (void**)&m_EventSystem );
if (FAILED(hr))
{
DEBUGMSG("AUSENS Failed to create EventSytem instance for Sens subscription. Error is %x.", hr);
goto done;
}
//
// Subscribe for the Logon notifications
//
DEBUGMSG("AUSENS Subscribing method '%S' with SENS (%S)", m_oBstrTable->m_bstrLogonMethodName, m_oBstrTable->m_bstrLogonMethodGuid);
hr = SubscribeMethod(m_oBstrTable->m_bstrLogonMethodName, m_oBstrTable->m_bstrLogonMethodGuid);
if (FAILED(hr))
{
DEBUGMSG("AUSENS Subscription for method failed.");
goto done;
}
m_fSubscribed = TRUE;
done:
return hr;
}
HRESULT CLogonNotification::UnsubscribeAllMethods()
{
HRESULT hr = S_OK;
BSTR bstrQuery = NULL;
BSTR bstrAux = NULL;
int ErrorIndex;
DEBUGMSG("AUSENS Unsubscribing all methods");
//
// The query should be a string in the following format:
// EventClassID == {D5978630-5B9F-11D1-8DD2-00AA004ABD5E} and SubscriptioniD == {XXXXXXX-5B9F-11D1-8DD2-00AA004ABD5E}
//
bstrQuery = SysAllocString(L"EventClassID == ");
if (bstrQuery == NULL)
{
hr = E_OUTOFMEMORY;
goto done;
}
bstrAux = SysAllocString(L" and SubscriptionID == ");
if (bstrAux == NULL)
{
hr = E_OUTOFMEMORY;
goto done;
}
hr = AppendBSTR(&bstrQuery, m_oBstrTable->m_bstrSensLogonGuid);
if (FAILED(hr))
{
DEBUGMSG("AUSENS Failed to append BSTR string");
goto done;
}
hr = AppendBSTR(&bstrQuery, bstrAux);
if (FAILED(hr))
{
DEBUGMSG("AUSENS Failed to append BSTR string");
goto done;
}
hr = AppendBSTR(&bstrQuery, m_oBstrTable->m_bstrLogonMethodGuid);
if (FAILED(hr))
{
DEBUGMSG("AUSENS Failed to append BSTR string");
goto done;
}
if (bstrQuery != NULL)
{
// Remove subscription for all ISensLogon subscription that were added for this WU component
DEBUGMSG("AUSENS remove subscription query: %S", bstrQuery);
hr = m_EventSystem->Remove( PROGID_EventSubscription, bstrQuery, &ErrorIndex );
if (FAILED(hr))
{
DEBUGMSG("AUSENS Failed to remove AU Subscription from COM Event System");
goto done;
}
m_fSubscribed = FALSE;
}
else
{
hr = E_OUTOFMEMORY;
goto done;
}
done:
SafeFreeBSTR(bstrQuery);
SafeFreeBSTR(bstrAux);
return hr;
}
HRESULT CLogonNotification::SubscribeMethod(const BSTR bstrMethodName, const BSTR bstrMethodGuid)
{
HRESULT hr = S_OK;
IEventSubscription *pEventSubscription = NULL;
//
// Create an instance of EventSubscription
//
hr = CoCreateInstance(CLSID_CEventSubscription, NULL, CLSCTX_INPROC_SERVER | CLSCTX_LOCAL_SERVER, IID_IEventSubscription, (void**)&pEventSubscription);
if (FAILED(hr))
{
DEBUGMSG("AUSENS Failed to instanciate EventSubscription object");
goto done;
}
//
// Subscribe the method
//
hr = pEventSubscription->put_EventClassID(m_oBstrTable->m_bstrSensEventClassGuid);
if (FAILED(hr))
{
DEBUGMSG("AUSENS Failed to set EventClassID during method subscription");
goto done;
}
hr = pEventSubscription->put_SubscriberInterface(this);
if (FAILED(hr))
{
DEBUGMSG("AUSENS Failed to set EventClassID during method subscription");
goto done;
}
hr = pEventSubscription->put_SubscriptionName(m_oBstrTable->m_bstrSubscriptionName);
if (FAILED(hr))
{
DEBUGMSG("AUSENS Failed to set EventClassID during method subscription");
goto done;
}
hr = pEventSubscription->put_Description(m_oBstrTable->m_bstrSubscriptionDescription);
if (FAILED(hr))
{
DEBUGMSG("AUSENS Failed to set subscription method during method subscription");
goto done;
}
hr = pEventSubscription->put_Enabled(TRUE);
if (FAILED(hr))
{
DEBUGMSG("AUSENS Failed to set enabled flag during method subscription");
goto done;
}
hr = pEventSubscription->put_MethodName(bstrMethodName);
if (FAILED(hr))
{
DEBUGMSG("AUSENS Failed to set MethodName during method subscription");
goto done;
}
hr = pEventSubscription->put_SubscriptionID(bstrMethodGuid);
if (FAILED(hr))
{
DEBUGMSG("AUSENS Failed to set SubscriptionID during method subscription");
goto done;
}
hr = m_EventSystem->Store(PROGID_EventSubscription, pEventSubscription);
if (FAILED(hr))
{
DEBUGMSG("AUSENS Failed to store Event Subscription in the Event System");
goto done;
}
done:
SafeRelease(pEventSubscription);
return hr;
}
void CLogonNotification::Cleanup()
{
__try
{
if (m_EventSystem)
{
if (m_fSubscribed)
{
UnsubscribeAllMethods();
}
SafeRelease(m_EventSystem);
}
SafeRelease(m_TypeInfo);
SafeRelease(m_TypeLib);
}
__except(EXCEPTION_EXECUTE_HANDLER)
{
DEBUGMSG("AUSENS Cleanup raised and execution exception that was trapped.");
}
if (m_oBstrTable)
{
delete m_oBstrTable;
m_oBstrTable = NULL;
}
}
HRESULT CLogonNotification::CheckLocalSystem()
{
HRESULT hr = E_FAIL;
PSID pLocalSid = NULL;
HANDLE hToken = NULL;
SID_IDENTIFIER_AUTHORITY IDAuthorityNT = SECURITY_NT_AUTHORITY;
BOOL fRet = FALSE;
BOOL fIsLocalSystem = FALSE;
fRet = AllocateAndInitializeSid(
&IDAuthorityNT,
1,
SECURITY_LOCAL_SYSTEM_RID,
0,0,0,0,0,0,0,
&pLocalSid );
if (fRet == FALSE)
{
hr = HRESULT_FROM_WIN32(GetLastError());
DEBUGMSG("AUSENS AllocateAndInitializeSid failed with error %x", hr);
goto done;
}
if (FAILED(hr = CoImpersonateClient()))
{
DEBUGMSG("AUSENS Failed to impersonate client", hr);
hr = E_ACCESSDENIED;
goto done;
}
fRet = OpenThreadToken(GetCurrentThread(), TOKEN_QUERY, TRUE, &hToken);
if (fRet == FALSE)
{
hr = HRESULT_FROM_WIN32(GetLastError());
DEBUGMSG("AUSENS Failed to OpenProcessToken");
goto done;
}
if (FAILED(CoRevertToSelf()))
{
AUASSERT(FALSE); //should never be there
hr = E_ACCESSDENIED;
goto done;
}
fRet = CheckTokenMembership(hToken, pLocalSid, &fIsLocalSystem);
if (fRet == FALSE)
{
hr = HRESULT_FROM_WIN32(GetLastError());
DEBUGMSG("AUSENS fail to Check token membership with error %x", hr);
goto done;
}
if (fIsLocalSystem)
{
hr = S_OK;
}
else
{
hr = E_ACCESSDENIED;
DEBUGMSG("AUSENS SECURITY CHECK Current thread is not running as LocalSystem!!");
}
done:
if (hToken != NULL)
CloseHandle(hToken);
if (pLocalSid != NULL)
FreeSid(pLocalSid);
return hr;
}
STDMETHODIMP CLogonNotification::Logon( BSTR UserName )
{
DEBUGMSG("AUSENS logon notification for %S", (WCHAR*)UserName );
DEBUGMSG("AUSENS Forcing the rebuilt of the cachesessions array");
//
// fix for security bug 563054 -- annah
//
// The Logon() method is called by the COM+ Event System to notify us of a logon event
// We expose the ISensLogon interface but don't want anybody calling us
// that is not the COM+ Event System.
//
// The COM+ Event System service runs as Local System in the netsvcs svchost group.
// We will check if the caller is really the Event System by checking for
// the Local System account.
//
HRESULT hr = CheckLocalSystem();
if (FAILED(hr))
{
DEBUGMSG("AUSENS CheckLocalSystem failed with error %x. Will not trigger logon notification", hr);
goto done;
}
//
// One big problem of the code below is that although we're validating that
// there are admins on the machine who are valid users for AU, it could be the
// same that was already there, because we don't have a reliable way of receiving
// logoff notifications. So we will raise the NEW_ADMIN event here, and
// we will block the cretiion of a new client if we detect that there's a
// a client still running.
//
gAdminSessions.RebuildSessionCache();
if (gAdminSessions.CSessions() > 0)
{
DEBUGMSG("AU SENS Logon: There are admins in the admin cache, raising NEW_ADMIN event (it could be a false alarm)");
SetActiveAdminSessionEvent();
}
#if DBG
gAdminSessions.m_DumpSessions();
#endif
done:
return S_OK;
}
STDMETHODIMP CLogonNotification::Logoff( BSTR UserName )
{
DEBUGMSG( "AUSENS logoff notification for %S", (WCHAR*)UserName );
// do nothing
#if DBG
gAdminSessions.m_DumpSessions();
#endif
return S_OK;
}
STDMETHODIMP CLogonNotification::QueryInterface(REFIID iid, void** ppvObject)
{
HRESULT hr = S_OK;
*ppvObject = NULL;
if ((iid == IID_IUnknown) || (iid == _uuidof(IDispatch)) || (iid == _uuidof(ISensLogon)))
{
*ppvObject = static_cast<ISensLogon *> (this);
(static_cast<IUnknown *>(*ppvObject))->AddRef();
}
else
{
hr = E_NOINTERFACE;
}
return hr;
}
STDMETHODIMP CLogonNotification::GetIDsOfNames(REFIID, OLECHAR ** rgszNames, unsigned int cNames, LCID, DISPID *rgDispId )
{
return m_TypeInfo->GetIDsOfNames(rgszNames, cNames, rgDispId);
}
STDMETHODIMP CLogonNotification::GetTypeInfo(unsigned int iTInfo, LCID, ITypeInfo **ppTInfo )
{
if ( iTInfo != 0 )
return DISP_E_BADINDEX;
*ppTInfo = m_TypeInfo;
m_TypeInfo->AddRef();
return S_OK;
}
STDMETHODIMP CLogonNotification::GetTypeInfoCount(unsigned int *pctinfo)
{
*pctinfo = 1;
return S_OK;
}
STDMETHODIMP CLogonNotification::Invoke(
DISPID dispID,
REFIID riid,
LCID,
WORD wFlags,
DISPPARAMS *pDispParams,
VARIANT *pvarResult,
EXCEPINFO *pExcepInfo,
unsigned int *puArgErr )
{
if (riid != IID_NULL)
{
return DISP_E_UNKNOWNINTERFACE;
}
return m_TypeInfo->Invoke(
(IDispatch*) this,
dispID,
wFlags,
pDispParams,
pvarResult,
pExcepInfo,
puArgErr
);
}
STDMETHODIMP CLogonNotification::DisplayLock( BSTR UserName )
{
return S_OK;
}
STDMETHODIMP CLogonNotification::DisplayUnlock( BSTR UserName )
{
return S_OK;
}
STDMETHODIMP CLogonNotification::StartScreenSaver( BSTR UserName )
{
return S_OK;
}
STDMETHODIMP CLogonNotification::StopScreenSaver( BSTR UserName )
{
return S_OK;
}
STDMETHODIMP CLogonNotification::StartShell( BSTR UserName )
{
return S_OK;
}