|
|
/************************************************************************
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; }
|