You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
617 lines
16 KiB
617 lines
16 KiB
/************************************************************************
|
|
|
|
Copyright (c) 2001 - Microsoft Corporation
|
|
|
|
Module Name :
|
|
|
|
csens.cpp
|
|
|
|
Abstract :
|
|
|
|
Code for recieving logon notifications from SENS.
|
|
|
|
Author :
|
|
|
|
Revision History :
|
|
|
|
***********************************************************************/
|
|
|
|
#include "stdafx.h"
|
|
#include <wtsapi32.h>
|
|
|
|
#if !defined(BITS_V12_ON_NT4)
|
|
#include "csens.tmh"
|
|
#endif
|
|
|
|
HRESULT GetConsoleUserPresent( bool * pfPresent );
|
|
|
|
HRESULT GetConsoleUsername( LPWSTR * User );
|
|
|
|
//------------------------------------------------------------------------
|
|
|
|
CLogonNotification::CLogonNotification() :
|
|
m_EventSystem( NULL ),
|
|
m_TypeLib( NULL ),
|
|
m_TypeInfo( NULL )
|
|
{
|
|
try
|
|
{
|
|
m_EventSubscriptions[0] = NULL;
|
|
m_EventSubscriptions[1] = NULL;
|
|
|
|
#if defined( BITS_V12_ON_NT4 )
|
|
|
|
{
|
|
|
|
// try to load the SENS typelibrary
|
|
// {D597DEED-5B9F-11D1-8DD2-00AA004ABD5E}
|
|
|
|
HRESULT Hr;
|
|
static const GUID SensTypeLibGUID =
|
|
{ 0xD597DEED, 0x5B9F, 0x11D1, { 0x8D, 0xD2, 0x00, 0xAA, 0x00, 0x4A, 0xBD, 0x5E } };
|
|
|
|
|
|
Hr = LoadRegTypeLib( SensTypeLibGUID, 2, 0, GetSystemDefaultLCID(), &m_TypeLib);
|
|
|
|
if ( TYPE_E_CANTLOADLIBRARY == Hr || TYPE_E_LIBNOTREGISTERED == Hr )
|
|
{
|
|
|
|
Hr = LoadRegTypeLib( SensTypeLibGUID, 1, 0, GetSystemDefaultLCID(), &m_TypeLib );
|
|
|
|
if ( TYPE_E_CANTLOADLIBRARY == Hr || TYPE_E_LIBNOTREGISTERED == Hr )
|
|
Hr = LoadTypeLibEx( L"SENS.DLL", REGKIND_NONE, &m_TypeLib );
|
|
|
|
}
|
|
|
|
THROW_HRESULT( Hr );
|
|
|
|
}
|
|
|
|
#else
|
|
|
|
THROW_HRESULT( LoadTypeLibEx( L"SENS.DLL", REGKIND_NONE, &m_TypeLib ) );
|
|
|
|
#endif
|
|
|
|
THROW_HRESULT( m_TypeLib->GetTypeInfoOfGuid( __uuidof( ISensLogon ), &m_TypeInfo ) );
|
|
|
|
THROW_HRESULT( CoCreateInstance( CLSID_CEventSystem,
|
|
NULL,
|
|
CLSCTX_SERVER,
|
|
IID_IEventSystem,
|
|
(void**)&m_EventSystem
|
|
) );
|
|
|
|
|
|
// Register for the individual methods
|
|
const WCHAR *MethodNames[] =
|
|
{
|
|
L"Logon",
|
|
L"Logoff"
|
|
};
|
|
|
|
const WCHAR *UniqueIdentifies[] =
|
|
{
|
|
L"{c69c8f03-b25c-45d1-96fa-6dfb1f292b26}",
|
|
L"{5f4f5e8d-4599-4ba0-b53d-1de5440b8770}"
|
|
};
|
|
|
|
for( SIZE_T i = 0; i < ( sizeof(MethodNames) / sizeof(*MethodNames) ); i++ )
|
|
{
|
|
|
|
WCHAR EventGuidString[ 50 ];
|
|
|
|
THROW_HRESULT( CoCreateInstance( CLSID_CEventSubscription,
|
|
NULL,
|
|
CLSCTX_SERVER,
|
|
IID_IEventSubscription,
|
|
(LPVOID *) &m_EventSubscriptions[i]
|
|
) );
|
|
|
|
|
|
StringFromGUID2( SENSGUID_EVENTCLASS_LOGON, EventGuidString, 50 );
|
|
|
|
THROW_HRESULT( m_EventSubscriptions[i]->put_EventClassID( EventGuidString ) );
|
|
THROW_HRESULT( m_EventSubscriptions[i]->put_SubscriberInterface( this ) );
|
|
THROW_HRESULT( m_EventSubscriptions[i]->put_SubscriptionName( (BSTR) L"Microsoft-BITS" ) );
|
|
THROW_HRESULT( m_EventSubscriptions[i]->put_Description( (BSTR) L"BITS Notification" ) );
|
|
THROW_HRESULT( m_EventSubscriptions[i]->put_Enabled( FALSE ) );
|
|
|
|
THROW_HRESULT( m_EventSubscriptions[i]->put_MethodName( (BSTR)MethodNames[i] ) );
|
|
THROW_HRESULT( m_EventSubscriptions[i]->put_SubscriptionID( (BSTR)UniqueIdentifies[i] ) );
|
|
|
|
THROW_HRESULT( m_EventSystem->Store(PROGID_EventSubscription, m_EventSubscriptions[i] ) );
|
|
}
|
|
}
|
|
catch( ComError Error )
|
|
{
|
|
Cleanup();
|
|
|
|
throw;
|
|
}
|
|
}
|
|
|
|
void
|
|
CLogonNotification::DeRegisterNotification()
|
|
{
|
|
SafeRelease( m_EventSubscriptions[0] );
|
|
SafeRelease( m_EventSubscriptions[1] );
|
|
|
|
if ( m_EventSystem )
|
|
{
|
|
int ErrorIndex;
|
|
|
|
m_EventSystem->Remove( PROGID_EventSubscription,
|
|
L"EventClassID == {D5978630-5B9F-11D1-8DD2-00AA004ABD5E} AND SubscriptionName == Microsoft-BITS",
|
|
&ErrorIndex );
|
|
|
|
SafeRelease( m_EventSystem );
|
|
}
|
|
}
|
|
|
|
void
|
|
CLogonNotification::Cleanup()
|
|
{
|
|
DeRegisterNotification();
|
|
|
|
SafeRelease( m_TypeInfo );
|
|
SafeRelease( m_TypeLib );
|
|
|
|
LogInfo("cleanup complete");
|
|
}
|
|
|
|
HRESULT CLogonNotification::SetEnableState( bool fEnable )
|
|
{
|
|
try
|
|
{
|
|
for (int i=0; i <= 1; ++i)
|
|
{
|
|
THROW_HRESULT( m_EventSubscriptions[i]->put_Enabled( fEnable ) );
|
|
}
|
|
|
|
for (int i=0; i <= 1; ++i)
|
|
{
|
|
THROW_HRESULT( m_EventSystem->Store(PROGID_EventSubscription, m_EventSubscriptions[i] ) );
|
|
}
|
|
|
|
LogInfo("SENS enable state is %d", fEnable);
|
|
return S_OK;
|
|
}
|
|
catch ( ComError err )
|
|
{
|
|
LogInfo("SENS set enable state (%d) returned %x", fEnable, err.Error());
|
|
return err.Error();
|
|
}
|
|
}
|
|
|
|
STDMETHODIMP
|
|
CLogonNotification::GetIDsOfNames(
|
|
REFIID,
|
|
OLECHAR FAR* FAR* rgszNames,
|
|
unsigned int cNames,
|
|
LCID,
|
|
DISPID FAR* rgDispId )
|
|
{
|
|
|
|
return m_TypeInfo->GetIDsOfNames(
|
|
rgszNames,
|
|
cNames,
|
|
rgDispId );
|
|
|
|
}
|
|
|
|
|
|
STDMETHODIMP
|
|
CLogonNotification::GetTypeInfo(
|
|
unsigned int iTInfo,
|
|
LCID,
|
|
ITypeInfo FAR* FAR* ppTInfo )
|
|
{
|
|
|
|
if ( iTInfo != 0 )
|
|
return DISP_E_BADINDEX;
|
|
|
|
*ppTInfo = m_TypeInfo;
|
|
m_TypeInfo->AddRef();
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
STDMETHODIMP
|
|
CLogonNotification::GetTypeInfoCount(
|
|
unsigned int FAR* pctinfo )
|
|
{
|
|
*pctinfo = 1;
|
|
return S_OK;
|
|
|
|
}
|
|
|
|
STDMETHODIMP
|
|
CLogonNotification::Invoke(
|
|
DISPID dispID,
|
|
REFIID riid,
|
|
LCID,
|
|
WORD wFlags,
|
|
DISPPARAMS FAR* pDispParams,
|
|
VARIANT FAR* pvarResult,
|
|
EXCEPINFO FAR* pExcepInfo,
|
|
unsigned int FAR* 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::Logon(
|
|
BSTR UserName )
|
|
{
|
|
LogInfo( "SENS logon notification for %S", (WCHAR*)UserName );
|
|
|
|
HRESULT Hr = SessionLogonCallback( 0 );
|
|
|
|
LogInfo( "SENS logon notification for %S processed, %!winerr!", (WCHAR*)UserName, Hr );
|
|
|
|
return Hr;
|
|
}
|
|
|
|
STDMETHODIMP
|
|
CLogonNotification::Logoff(
|
|
BSTR UserName )
|
|
{
|
|
LogInfo( "SENS logoff notification for %S", (WCHAR*)UserName );
|
|
|
|
HRESULT Hr = SessionLogoffCallback( 0 );
|
|
|
|
LogInfo( "SENS logoff notification for %S processed, %!winerr!", (WCHAR*)UserName, Hr );
|
|
|
|
return Hr;
|
|
}
|
|
|
|
STDMETHODIMP
|
|
CLogonNotification::StartShell(
|
|
BSTR UserName )
|
|
{
|
|
return S_OK;
|
|
}
|
|
|
|
//------------------------------------------------------------------------
|
|
|
|
CTerminalServerLogonNotification::CTerminalServerLogonNotification()
|
|
: m_PendingUserChecks( 0 ),
|
|
m_fConsoleUser( false )
|
|
{
|
|
}
|
|
|
|
CTerminalServerLogonNotification::~CTerminalServerLogonNotification()
|
|
{
|
|
while (m_PendingUserChecks)
|
|
{
|
|
LogInfo("m_PendingUserChecks is %d", m_PendingUserChecks);
|
|
Sleep(50);
|
|
}
|
|
}
|
|
|
|
STDMETHODIMP
|
|
CTerminalServerLogonNotification::Logon(
|
|
BSTR UserName )
|
|
{
|
|
HRESULT Hr = S_OK;
|
|
|
|
LogInfo( "TS SENS logon notification for %S", (WCHAR*)UserName );
|
|
|
|
if (!m_fConsoleUser)
|
|
{
|
|
// Wait a few seconds in case TS hasn't seen the notification yet, then
|
|
// check whetherthe notification was for the console.
|
|
// if it fails, not much recourse.
|
|
//
|
|
Hr = QueueConsoleUserCheck();
|
|
}
|
|
|
|
LogInfo( "hr = %!winerr!", Hr );
|
|
|
|
return Hr;
|
|
}
|
|
|
|
STDMETHODIMP
|
|
CTerminalServerLogonNotification::Logoff(
|
|
BSTR UserName )
|
|
{
|
|
HRESULT Hr = S_OK;
|
|
|
|
LogInfo( "TS SENS logoff notification for %S", (WCHAR*)UserName );
|
|
|
|
if (m_fConsoleUser)
|
|
{
|
|
bool fSame;
|
|
LPWSTR ConsoleUserName = NULL;
|
|
|
|
Hr = GetConsoleUsername( &ConsoleUserName );
|
|
|
|
if (FAILED( Hr ))
|
|
{
|
|
//
|
|
// unable to check. Security dictates that we be conservative and remove the user.
|
|
//
|
|
LogError("unable to fetch console username %x, thus logoff callback", Hr);
|
|
|
|
Hr = SessionLogoffCallback( 0 );
|
|
m_fConsoleUser = false;
|
|
}
|
|
else if (ConsoleUserName == NULL)
|
|
{
|
|
//
|
|
// no user logged in at the console
|
|
//
|
|
LogInfo("no one logged in at the console, thus logoff callback");
|
|
|
|
Hr = SessionLogoffCallback( 0 );
|
|
m_fConsoleUser = false;
|
|
}
|
|
else if (0 != _wcsicmp( UserName, ConsoleUserName))
|
|
{
|
|
LogInfo("console user is %S; doesn't match", ConsoleUserName);
|
|
|
|
delete [] ConsoleUserName;
|
|
Hr = S_OK;
|
|
}
|
|
else
|
|
{
|
|
// correct user, but (s)he might have logged off from a TS session.
|
|
// We should wait a few seconds before checking the console state because the
|
|
// TS code may not have seen the logoff notification yet. Because Logoff is a synchronous
|
|
// notification, we cannot just Sleep before checking..
|
|
//
|
|
delete [] ConsoleUserName;
|
|
|
|
if (FAILED(QueueConsoleUserCheck()))
|
|
{
|
|
//
|
|
// unable to check. Security dictates that we be conservative and remove the user.
|
|
//
|
|
LogError("unable to queue check, thus logoff callback");
|
|
Hr = SessionLogoffCallback( 0 );
|
|
m_fConsoleUser = false;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
LogInfo("ignoring, no console user");
|
|
}
|
|
|
|
LogInfo( "hr = %!winerr!", Hr );
|
|
|
|
return Hr;
|
|
}
|
|
|
|
HRESULT
|
|
CTerminalServerLogonNotification::QueueConsoleUserCheck()
|
|
{
|
|
if (QueueUserWorkItem( UserCheckThreadProc, this, WT_EXECUTELONGFUNCTION ))
|
|
{
|
|
InterlockedIncrement( &m_PendingUserChecks );
|
|
LogInfo("queued user check: about %d pending", m_PendingUserChecks );
|
|
return S_OK;
|
|
}
|
|
else
|
|
{
|
|
DWORD s = GetLastError();
|
|
LogError("unable to queue user check %!winerr!", s);
|
|
return HRESULT_FROM_WIN32( s );
|
|
}
|
|
}
|
|
|
|
DWORD WINAPI
|
|
CTerminalServerLogonNotification::UserCheckThreadProc(
|
|
LPVOID arg
|
|
)
|
|
{
|
|
CTerminalServerLogonNotification * _this = reinterpret_cast<CTerminalServerLogonNotification *>(arg);
|
|
|
|
LogInfo("sleeping before user check");
|
|
Sleep( 5 * 1000 );
|
|
|
|
_this->ConsoleUserCheck();
|
|
|
|
return 0;
|
|
}
|
|
|
|
void CTerminalServerLogonNotification::ConsoleUserCheck()
|
|
{
|
|
HRESULT Hr;
|
|
|
|
LogInfo("SENS console user check");
|
|
|
|
if (IsServiceShuttingDown())
|
|
{
|
|
LogWarning("service is shutting down.");
|
|
InterlockedDecrement( &m_PendingUserChecks );
|
|
return;
|
|
}
|
|
|
|
bool bConsoleUser;
|
|
|
|
Hr = GetConsoleUserPresent( &bConsoleUser );
|
|
|
|
//
|
|
// Security requires us to be conservative: if we can't tell whether the user
|
|
// is logged in, we must release his token.
|
|
//
|
|
if (FAILED(Hr))
|
|
{
|
|
LogError("GetConsoleUserPresent returned %x", Hr );
|
|
}
|
|
|
|
if (FAILED(Hr) || !bConsoleUser)
|
|
{
|
|
LogInfo("logoff callback");
|
|
if (FAILED(SessionLogoffCallback( 0 )))
|
|
{
|
|
// unusual: the only obvious generator is
|
|
// - no known user at console
|
|
// - TS logon or failing console logon
|
|
// - memory allocation failure referring to m_ActiveSessions[ session ]
|
|
// either way, we don't think a user is at the console, so m_fConsoleUser should be false.
|
|
}
|
|
m_fConsoleUser = false;
|
|
}
|
|
else
|
|
{
|
|
LogInfo("logon callback");
|
|
m_fConsoleUser = true;
|
|
if (FAILED(SessionLogonCallback( 0 )))
|
|
{
|
|
// no user token available, but we still know that there is a console user.
|
|
}
|
|
}
|
|
|
|
InterlockedDecrement( &m_PendingUserChecks );
|
|
}
|
|
|
|
HRESULT
|
|
GetConsoleUserPresent( bool * pfPresent )
|
|
{
|
|
/*
|
|
If logon fails, we still know that there is a user at the console.
|
|
Setting the flag will prevent queued checks for further logons, and
|
|
logoff handles the no-user case.
|
|
|
|
For Logoff, regardless of exit path there is no user recorded for that session.
|
|
Setting the flag prevents queued checks for future logoffs.
|
|
*/
|
|
|
|
INT * pConnectState = 0;
|
|
DWORD size;
|
|
if (WTSQuerySessionInformation( WTS_CURRENT_SERVER,
|
|
0,
|
|
WTSConnectState,
|
|
reinterpret_cast<LPTSTR *>(&pConnectState),
|
|
&size))
|
|
{
|
|
LogInfo("console session state is %d", *pConnectState);
|
|
if (*pConnectState == WTSActive ||
|
|
*pConnectState == WTSDisconnected)
|
|
{
|
|
LogInfo("console user present");
|
|
*pfPresent = true;
|
|
}
|
|
else
|
|
{
|
|
LogInfo("no console user");
|
|
*pfPresent = false;
|
|
}
|
|
|
|
WTSFreeMemory( pConnectState );
|
|
return S_OK;
|
|
}
|
|
else
|
|
{
|
|
DWORD s = GetLastError();
|
|
LogInfo("WTSQuerySessionInformation returned %!winerr!", s);
|
|
return HRESULT_FROM_WIN32( s );
|
|
}
|
|
}
|
|
|
|
HRESULT GetConsoleUsername( LPWSTR * pFinalName )
|
|
{
|
|
HRESULT hr;
|
|
|
|
LPWSTR UserName = NULL;
|
|
LPWSTR DomainName = NULL;
|
|
|
|
*pFinalName = NULL;
|
|
|
|
try
|
|
{
|
|
DWORD UserSize;
|
|
DWORD DomainSize;
|
|
|
|
if (!WTSQuerySessionInformationW( WTS_CURRENT_SERVER,
|
|
0,
|
|
WTSUserName,
|
|
&UserName,
|
|
&UserSize))
|
|
{
|
|
ThrowLastError();
|
|
}
|
|
|
|
if (!WTSQuerySessionInformationW( WTS_CURRENT_SERVER,
|
|
0,
|
|
WTSDomainName,
|
|
&DomainName,
|
|
&DomainSize))
|
|
{
|
|
ThrowLastError();
|
|
}
|
|
|
|
*pFinalName = new WCHAR[ DomainSize + 1 + UserSize + 1 ];
|
|
|
|
hr = StringCchPrintf( *pFinalName,
|
|
UserSize + 1 + DomainSize + 1,
|
|
L"%s\\%s", DomainName, UserName
|
|
);
|
|
}
|
|
catch ( ComError err )
|
|
{
|
|
delete [] *pFinalName;
|
|
*pFinalName = NULL;
|
|
|
|
hr = err.Error();
|
|
}
|
|
|
|
if (DomainName)
|
|
{
|
|
WTSFreeMemory( DomainName );
|
|
}
|
|
|
|
if (UserName)
|
|
{
|
|
WTSFreeMemory( UserName );
|
|
}
|
|
|
|
return hr;
|
|
}
|