mirror of https://github.com/tongzx/nt5src
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.
1070 lines
24 KiB
1070 lines
24 KiB
/************************************************************************
|
|
|
|
Copyright (c) 2000 - 2000 Microsoft Corporation
|
|
|
|
Module Name :
|
|
|
|
logontable.cpp
|
|
|
|
Abstract :
|
|
|
|
Source file for the logon table.
|
|
|
|
Author :
|
|
|
|
Revision History :
|
|
|
|
***********************************************************************/
|
|
|
|
|
|
#include "stdafx.h"
|
|
|
|
#include <winsta.h>
|
|
#include <wtsapi32.h>
|
|
#include <userenv.h>
|
|
|
|
#if !defined(BITS_V12_ON_NT4)
|
|
#include "logontable.tmh"
|
|
#endif
|
|
|
|
HRESULT DetectTerminalServer( bool * pfTS );
|
|
|
|
HRESULT
|
|
GetUserToken(
|
|
ULONG LogonId,
|
|
PHANDLE pUserToken
|
|
);
|
|
|
|
HRESULT
|
|
WaitForUserToken(
|
|
DWORD session,
|
|
HANDLE * pToken
|
|
)
|
|
{
|
|
const MaxWait = 30 * 1000;
|
|
const WaitInterval = 500;
|
|
|
|
long StartTime = GetTickCount();
|
|
|
|
HRESULT Hr = E_FAIL;
|
|
|
|
do
|
|
{
|
|
|
|
Hr = GetUserToken( session, pToken );
|
|
|
|
if ( SUCCEEDED( Hr ) )
|
|
return Hr;
|
|
|
|
LogError("logon : unable to get token : %!winerr!", Hr );
|
|
|
|
Sleep( WaitInterval );
|
|
}
|
|
while ( GetTickCount() - StartTime < MaxWait );
|
|
|
|
return Hr;
|
|
}
|
|
|
|
CLoggedOnUsers::CLoggedOnUsers(
|
|
TaskScheduler & sched
|
|
) : m_TaskScheduler( sched ),
|
|
m_SensNotifier( NULL )
|
|
{
|
|
FILETIME time;
|
|
GetSystemTimeAsFileTime( &time );
|
|
|
|
m_CurrentCookie = time.dwLowDateTime;
|
|
|
|
if ( WINDOWS2000_PLATFORM == g_PlatformVersion )
|
|
{
|
|
try
|
|
{
|
|
bool fTS;
|
|
|
|
THROW_HRESULT( DetectTerminalServer( &fTS ));
|
|
|
|
if (fTS)
|
|
{
|
|
m_SensNotifier = new CTerminalServerLogonNotification;
|
|
LogInfo( "TS-enabled SENS notification activated" );
|
|
}
|
|
else
|
|
{
|
|
m_SensNotifier = new CLogonNotification;
|
|
LogInfo( "regular SENS notification activated" );
|
|
}
|
|
}
|
|
catch( ComError Error )
|
|
{
|
|
if ( Error.Error() == TYPE_E_CANTLOADLIBRARY ||
|
|
Error.Error() == TYPE_E_LIBNOTREGISTERED )
|
|
{
|
|
LogInfo( "SENS doesn't exist on this platform, skipping" );
|
|
return;
|
|
}
|
|
else
|
|
{
|
|
LogInfo("SENS object failed with %x", Error.Error() );
|
|
throw;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
CLoggedOnUsers::~CLoggedOnUsers()
|
|
{
|
|
if (m_SensNotifier)
|
|
{
|
|
m_SensNotifier->DeRegisterNotification();
|
|
m_SensNotifier->Release();
|
|
}
|
|
}
|
|
|
|
|
|
HRESULT
|
|
CLoggedOnUsers::LogonSession(
|
|
DWORD session
|
|
)
|
|
{
|
|
CUser * user = NULL;
|
|
|
|
try
|
|
{
|
|
HANDLE Token = NULL;
|
|
auto_HANDLE<NULL> AutoToken;
|
|
|
|
//
|
|
// Get the user's token and SID, then create a user object.
|
|
//
|
|
THROW_HRESULT( WaitForUserToken( session, &Token ));
|
|
|
|
ASSERT( Token ); // Token can't be NULL
|
|
|
|
AutoToken = Token;
|
|
|
|
user = new CUser( Token );
|
|
|
|
//
|
|
// Add the user to our by-session and by-SID indexes.
|
|
//
|
|
HoldWriterLock lock ( m_TaskScheduler );
|
|
|
|
try
|
|
{
|
|
// Just in case...delete any previously recorded user.
|
|
//
|
|
LogoffSession( session );
|
|
|
|
//
|
|
// Subtlety: if the node for m_ActiveSessions[ session ] doesn't exist,
|
|
// then the first reference to it will cause a node to be allocated. This may
|
|
// throw E_OUTOFMEMORY.
|
|
//
|
|
m_ActiveSessions[ session ] = user;
|
|
|
|
m_ActiveUsers.insert( make_pair( user->QuerySid(), user ) );
|
|
}
|
|
catch( ComError Error )
|
|
{
|
|
m_ActiveSessions.erase( session );
|
|
throw;
|
|
}
|
|
|
|
Dump();
|
|
|
|
g_Manager->UserLoggedOn( user->QuerySid() );
|
|
|
|
return S_OK;
|
|
}
|
|
catch( ComError err )
|
|
{
|
|
delete user;
|
|
|
|
LogError("logon : returning error 0x%x", err.Error() );
|
|
Dump();
|
|
return err.Error();
|
|
}
|
|
}
|
|
|
|
HRESULT
|
|
CLoggedOnUsers::LogoffSession(
|
|
DWORD session
|
|
)
|
|
{
|
|
try
|
|
{
|
|
HoldWriterLock lock ( m_TaskScheduler );
|
|
|
|
CUser * user = m_ActiveSessions[ session ];
|
|
|
|
if (!user)
|
|
return S_OK;
|
|
|
|
bool b = m_ActiveUsers.RemovePair( user->QuerySid(), user );
|
|
|
|
ASSERT( b );
|
|
|
|
m_ActiveSessions.erase( session );
|
|
|
|
Dump();
|
|
|
|
if (false == g_Manager->IsUserLoggedOn( user->QuerySid() ))
|
|
{
|
|
g_Manager->UserLoggedOff( user->QuerySid() );
|
|
}
|
|
|
|
user->DecrementRefCount();
|
|
|
|
return S_OK;
|
|
}
|
|
catch( ComError err )
|
|
{
|
|
LogWarning("logoff : exception 0x%x thrown", err.Error());
|
|
Dump();
|
|
return err.Error();
|
|
}
|
|
}
|
|
|
|
CUser *
|
|
CLoggedOnUsers::CUserList::FindSid(
|
|
SidHandle sid
|
|
)
|
|
{
|
|
iterator iter = find( sid );
|
|
|
|
if (iter == end())
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
return iter->second;
|
|
}
|
|
|
|
|
|
bool
|
|
CLoggedOnUsers::CUserList::RemovePair(
|
|
SidHandle sid,
|
|
CUser * user
|
|
)
|
|
{
|
|
//
|
|
// Find the user in the user list and delete it.
|
|
//
|
|
pair<iterator, iterator> b = equal_range( sid );
|
|
|
|
for (iterator i = b.first; i != b.second; ++i)
|
|
{
|
|
if (i->second == user)
|
|
{
|
|
erase( i );
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
CUser *
|
|
CLoggedOnUsers::CUserList::RemoveByCookie(
|
|
SidHandle sid,
|
|
DWORD cookie
|
|
)
|
|
{
|
|
//
|
|
// Find the user in the user list and delete it.
|
|
//
|
|
pair<iterator, iterator> b = equal_range( sid );
|
|
|
|
for (iterator i = b.first; i != b.second; ++i)
|
|
{
|
|
CUser * user = i->second;
|
|
|
|
if (user->GetCookie() == cookie)
|
|
{
|
|
erase( i );
|
|
return user;
|
|
}
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
HRESULT
|
|
CLoggedOnUsers::LogonService(
|
|
HANDLE Token,
|
|
DWORD * pCookie
|
|
)
|
|
{
|
|
CUser * user = NULL;
|
|
|
|
try
|
|
{
|
|
user = new CUser( Token );
|
|
|
|
*pCookie = InterlockedIncrement( &m_CurrentCookie );
|
|
|
|
user->SetCookie( *pCookie );
|
|
|
|
HoldWriterLock lock ( m_TaskScheduler );
|
|
|
|
m_ActiveServiceAccounts.insert( make_pair( user->QuerySid(), user ));
|
|
|
|
return S_OK;
|
|
}
|
|
catch( ComError err )
|
|
{
|
|
delete user;
|
|
|
|
LogError("logon service : returning error 0x%x", err.Error() );
|
|
return err.Error();
|
|
}
|
|
}
|
|
|
|
|
|
HRESULT
|
|
CLoggedOnUsers::LogoffService(
|
|
SidHandle Sid,
|
|
DWORD Cookie
|
|
)
|
|
{
|
|
try
|
|
{
|
|
HoldWriterLock lock ( m_TaskScheduler );
|
|
|
|
CUser * user = m_ActiveServiceAccounts.RemoveByCookie( Sid, Cookie );
|
|
|
|
if (!user)
|
|
{
|
|
LogWarning("logoff : invalid cookie %d", Cookie);
|
|
return E_INVALIDARG;
|
|
}
|
|
|
|
user->DecrementRefCount();
|
|
|
|
return S_OK;
|
|
}
|
|
catch( ComError err)
|
|
{
|
|
LogWarning("logoff : exception 0x%x thrown", err.Error());
|
|
return err.Error();
|
|
}
|
|
}
|
|
|
|
HRESULT
|
|
CLoggedOnUsers::AddServiceAccounts()
|
|
{
|
|
HRESULT hr = S_OK;
|
|
DWORD ignore;
|
|
|
|
HoldWriterLock lock ( m_TaskScheduler );
|
|
|
|
//
|
|
// Add the LOCAL_SYSTEM account.
|
|
//
|
|
HANDLE Token;
|
|
if (OpenProcessToken( GetCurrentProcess(), TOKEN_QUERY | TOKEN_DUPLICATE, &Token ))
|
|
{
|
|
hr = LogonService( Token, &ignore );
|
|
CloseHandle( Token );
|
|
}
|
|
else
|
|
{
|
|
hr = HRESULT_FROM_WIN32( GetLastError() );
|
|
}
|
|
|
|
if (FAILED(hr))
|
|
{
|
|
LogWarning( "failed to register LocalSystem : %!winerr!", hr );
|
|
return hr;
|
|
}
|
|
|
|
if (g_PlatformVersion >= WINDOWSXP_PLATFORM)
|
|
{
|
|
//
|
|
// Add the LocalService account.
|
|
//
|
|
if (LogonUser( L"LocalService",
|
|
L"NT AUTHORITY",
|
|
L"",
|
|
LOGON32_LOGON_SERVICE,
|
|
LOGON32_PROVIDER_DEFAULT,
|
|
&Token))
|
|
{
|
|
hr = LogonService( Token, &ignore );
|
|
CloseHandle( Token );
|
|
}
|
|
else
|
|
{
|
|
hr = HRESULT_FROM_WIN32( GetLastError() );
|
|
}
|
|
|
|
if (FAILED(hr))
|
|
{
|
|
LogWarning( "failed to register LocalService : %!winerr!", hr );
|
|
if ( HRESULT_FROM_WIN32( ERROR_LOGON_FAILURE ) == hr )
|
|
LogWarning( "LocalService doesn't exist, skip it.\n");
|
|
else
|
|
return hr;
|
|
}
|
|
|
|
//
|
|
// Add the NetworkService account.
|
|
//
|
|
if (LogonUser( L"NetworkService",
|
|
L"NT AUTHORITY",
|
|
L"",
|
|
LOGON32_LOGON_SERVICE,
|
|
LOGON32_PROVIDER_DEFAULT,
|
|
&Token))
|
|
{
|
|
hr = LogonService( Token, &ignore );
|
|
CloseHandle( Token );
|
|
}
|
|
else
|
|
{
|
|
hr = HRESULT_FROM_WIN32( GetLastError() );
|
|
}
|
|
|
|
if (FAILED(hr))
|
|
{
|
|
LogWarning( "failed to register NetworkService : %!winerr!", hr );
|
|
if ( HRESULT_FROM_WIN32( ERROR_LOGON_FAILURE ) == hr )
|
|
LogWarning( "NetworkService doesn't exist, skip it.\n");
|
|
else
|
|
return hr;
|
|
}
|
|
}
|
|
|
|
//
|
|
// done
|
|
//
|
|
return S_OK;
|
|
}
|
|
|
|
HRESULT
|
|
CLoggedOnUsers::AddActiveUsers()
|
|
{
|
|
HRESULT hr = S_OK;
|
|
WTS_SESSION_INFO * SessionInfo = 0;
|
|
|
|
HANDLE Token;
|
|
|
|
HoldWriterLock lock ( m_TaskScheduler );
|
|
|
|
//
|
|
// Get the console token, if any, without using Terminal Services.
|
|
//
|
|
if ( SUCCEEDED( GetUserToken( 0, &Token) ) )
|
|
{
|
|
CloseHandle( Token );
|
|
|
|
hr = LogonSession( 0 );
|
|
if (FAILED(hr))
|
|
{
|
|
// ignore it and try to carry on...
|
|
LogWarning( "service : unable to logon session zero : %!winerr!", hr );
|
|
}
|
|
}
|
|
|
|
#if !defined( BITS_V12_ON_NT4 )
|
|
|
|
//
|
|
// The call may return FALSE, because Terminal Services is not always loaded.
|
|
//
|
|
DWORD SessionCount = 0;
|
|
|
|
BOOL b = WTSEnumerateSessions( WTS_CURRENT_SERVER_HANDLE,
|
|
0, // reserved
|
|
1, // version 1 is the only supported v.
|
|
&SessionInfo,
|
|
&SessionCount
|
|
);
|
|
|
|
if (b)
|
|
{
|
|
int i;
|
|
for (i=0; i < SessionCount; ++i)
|
|
{
|
|
if (SessionInfo[i].SessionId == 0)
|
|
{
|
|
// console was handled by GetCurrentUserToken.
|
|
continue;
|
|
}
|
|
|
|
if (SessionInfo[i].State == WTSActive ||
|
|
SessionInfo[i].State == WTSDisconnected)
|
|
{
|
|
LogInfo("service : logon session %d, state %d",
|
|
SessionInfo[i].SessionId,
|
|
SessionInfo[i].State );
|
|
|
|
hr = LogonSession( SessionInfo[i].SessionId );
|
|
if (FAILED(hr))
|
|
{
|
|
// ignore it and try to carry on...
|
|
LogWarning( "service : unable to logon session %d : %!winerr!",
|
|
SessionInfo[i].SessionId,
|
|
hr );
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (SessionInfo)
|
|
{
|
|
WTSFreeMemory( SessionInfo );
|
|
}
|
|
|
|
//
|
|
// Now that the current population is recorded, keep abreast of changes.
|
|
//
|
|
if (m_SensNotifier)
|
|
{
|
|
m_SensNotifier->SetEnableState( true );
|
|
}
|
|
|
|
#endif
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
CUser *
|
|
CLoggedOnUsers::FindUser(
|
|
SidHandle sid,
|
|
DWORD session
|
|
)
|
|
{
|
|
HoldReaderLock lock ( m_TaskScheduler );
|
|
|
|
CUser * user = 0;
|
|
|
|
//
|
|
// Look for a session with the right user.
|
|
//
|
|
if (session == ANY_SESSION)
|
|
{
|
|
user = m_ActiveUsers.FindSid( sid );
|
|
}
|
|
else
|
|
{
|
|
try
|
|
{
|
|
user = m_ActiveSessions[ session ];
|
|
|
|
if (user && user->QuerySid() != sid)
|
|
{
|
|
user = 0;
|
|
}
|
|
}
|
|
catch( ComError Error )
|
|
{
|
|
user = 0;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Look in the service account list, if the session is compatible.
|
|
//
|
|
if (!user && (session == 0 || session == ANY_SESSION))
|
|
{
|
|
user = m_ActiveServiceAccounts.FindSid( sid );
|
|
}
|
|
|
|
if (user)
|
|
{
|
|
user->IncrementRefCount();
|
|
}
|
|
|
|
return user;
|
|
|
|
}
|
|
|
|
void CLoggedOnUsers::Dump()
|
|
{
|
|
HoldReaderLock lock ( m_TaskScheduler );
|
|
|
|
LogInfo("sessions:");
|
|
|
|
m_ActiveSessions.Dump();
|
|
|
|
LogInfo("users:");
|
|
|
|
m_ActiveUsers.Dump();
|
|
|
|
LogInfo("service accounts:");
|
|
|
|
m_ActiveServiceAccounts.Dump();
|
|
}
|
|
|
|
void CLoggedOnUsers::CSessionList::Dump()
|
|
{
|
|
for (iterator iter = begin(); iter != end(); ++iter)
|
|
{
|
|
LogInfo(" session %d user %p", iter->first, iter->second);
|
|
}
|
|
}
|
|
|
|
void CLoggedOnUsers::CUserList::Dump()
|
|
{
|
|
for (iterator iter = begin(); iter != end(); ++iter)
|
|
{
|
|
if (iter->second)
|
|
{
|
|
(iter->second)->Dump();
|
|
}
|
|
}
|
|
}
|
|
|
|
CLoggedOnUsers::CUserList::~CUserList()
|
|
{
|
|
iterator iter;
|
|
|
|
while ((iter=begin()) != end())
|
|
{
|
|
delete iter->second;
|
|
|
|
erase( iter );
|
|
}
|
|
}
|
|
|
|
|
|
void CUser::Dump()
|
|
{
|
|
LogInfo( " user at %p:", this);
|
|
|
|
LogInfo( " %d refs, sid %!sid!", _ReferenceCount, _Sid.get());
|
|
}
|
|
|
|
long CUser::IncrementRefCount()
|
|
{
|
|
long count = InterlockedIncrement( & _ReferenceCount );
|
|
|
|
LogRef("refs %d", count);
|
|
|
|
return count;
|
|
}
|
|
|
|
long CUser::DecrementRefCount()
|
|
{
|
|
long count = InterlockedDecrement( & _ReferenceCount );
|
|
|
|
LogRef("refs %d", count);
|
|
|
|
if (0 == count)
|
|
{
|
|
delete this;
|
|
}
|
|
|
|
return count;
|
|
}
|
|
|
|
CUser::CUser(
|
|
HANDLE Token
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Initializes a new CUser.
|
|
|
|
At entry:
|
|
|
|
<Sid> points to the user's SID.
|
|
<Token> points to the user's token. It can be an impersonation or primary token.
|
|
<phr> points to an error-return variable.
|
|
|
|
At exit:
|
|
|
|
The CUser is set up. The caller can delete <Sid> and <Token> if it wants to.
|
|
|
|
if an error occurs, it is mapped to an HRESULT and written to <phr>.
|
|
otherwise <*phr> is untouched and the CUser is ready for use.
|
|
|
|
--*/
|
|
{
|
|
_ReferenceCount = 1;
|
|
|
|
_Sid = CopyTokenSid( Token );
|
|
|
|
//
|
|
// Copy the token. Whether the source is primary or impersonation,
|
|
// the result will be a primary token.
|
|
//
|
|
if (!DuplicateHandle(
|
|
GetCurrentProcess(),
|
|
Token,
|
|
GetCurrentProcess(),
|
|
&_Token,
|
|
TOKEN_ALL_ACCESS,
|
|
FALSE, // not inheritable
|
|
0 // no funny options
|
|
))
|
|
{
|
|
|
|
HRESULT HrError = HRESULT_FROM_WIN32( GetLastError() );
|
|
|
|
LogError( "CUser: can't duplicate token %!winerr!", HrError );
|
|
|
|
throw ComError( HrError );
|
|
}
|
|
}
|
|
|
|
CUser::~CUser()
|
|
{
|
|
CloseHandle( _Token );
|
|
}
|
|
|
|
HRESULT
|
|
CUser::LaunchProcess(
|
|
StringHandle CmdLine
|
|
)
|
|
{
|
|
DWORD s;
|
|
|
|
PVOID EnvironmentBlock = 0;
|
|
|
|
PROCESS_INFORMATION ProcessInformation;
|
|
memset( &ProcessInformation, 0, sizeof( PROCESS_INFORMATION ));
|
|
|
|
try
|
|
{
|
|
STARTUPINFO si;
|
|
memset( &si, 0, sizeof( STARTUPINFO ));
|
|
si.cb = sizeof(STARTUPINFO);
|
|
|
|
CAutoString WritableCmdLine ( CopyString( CmdLine ));
|
|
|
|
LogInfo( "creating process: %S", WritableCmdLine.get() );
|
|
|
|
if (!CreateEnvironmentBlock( &EnvironmentBlock,
|
|
_Token,
|
|
FALSE
|
|
))
|
|
{
|
|
ThrowLastError();
|
|
}
|
|
|
|
if (!CreateProcessAsUser( _Token,
|
|
NULL, // CmdLine contains the .exe name as well as the args
|
|
WritableCmdLine.get(),
|
|
0, // no special security attributes
|
|
0, // no special thread attributes
|
|
false, // don't inherit my handles
|
|
NORMAL_PRIORITY_CLASS | CREATE_UNICODE_ENVIRONMENT,
|
|
EnvironmentBlock,
|
|
NULL, // default current directory
|
|
&si,
|
|
&ProcessInformation
|
|
))
|
|
{
|
|
ThrowLastError();
|
|
}
|
|
|
|
DestroyEnvironmentBlock( EnvironmentBlock );
|
|
EnvironmentBlock = 0;
|
|
|
|
CloseHandle( ProcessInformation.hThread );
|
|
ProcessInformation.hThread = 0;
|
|
|
|
CloseHandle( ProcessInformation.hProcess );
|
|
ProcessInformation.hProcess = 0;
|
|
|
|
LogInfo("success, pid is 0x%x", ProcessInformation.dwProcessId);
|
|
|
|
//
|
|
// We succeeded.
|
|
//
|
|
return S_OK;
|
|
}
|
|
catch ( ComError err )
|
|
{
|
|
LogError("unable to create process, %x", err.Error() );
|
|
|
|
if (EnvironmentBlock)
|
|
{
|
|
DestroyEnvironmentBlock( EnvironmentBlock );
|
|
}
|
|
|
|
return err.Error();
|
|
}
|
|
}
|
|
|
|
#if ENABLE_STL_LOCK_OVERRIDE
|
|
|
|
/*
|
|
This file implements the STL lockit class to avoid linking to msvcprt.dll.
|
|
*/
|
|
CCritSec CrtLock;
|
|
|
|
#pragma warning(push)
|
|
#pragma warning(disable:4273) // __declspec(dllimport) attribute overridden
|
|
|
|
std::_Lockit::_Lockit()
|
|
{
|
|
CrtLock.WriteLock();
|
|
}
|
|
|
|
std::_Lockit::~_Lockit()
|
|
{
|
|
CrtLock.WriteUnlock();
|
|
}
|
|
|
|
#pragma warning(pop)
|
|
|
|
#endif
|
|
|
|
extern "C"
|
|
{
|
|
HANDLE
|
|
GetCurrentUserTokenW(
|
|
WCHAR Winsta[],
|
|
DWORD DesiredAccess
|
|
);
|
|
|
|
void * __RPC_USER MIDL_user_allocate(size_t size)
|
|
{
|
|
try
|
|
{
|
|
return new char[size];
|
|
}
|
|
catch( ComError Error )
|
|
{
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
void __RPC_USER MIDL_user_free( void * block)
|
|
{
|
|
delete block;
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
HRESULT
|
|
GetUserToken(
|
|
ULONG LogonId,
|
|
PHANDLE pUserToken
|
|
)
|
|
{
|
|
//
|
|
// This gets the token of the user logged onto the WinStation
|
|
// if we are an admin caller.
|
|
//
|
|
if (LogonId == 0)
|
|
{
|
|
// don't need the TS API.
|
|
|
|
*pUserToken = GetCurrentUserTokenW( L"WinSta0", TOKEN_ALL_ACCESS );
|
|
if (*pUserToken != NULL)
|
|
{
|
|
return S_OK;
|
|
}
|
|
|
|
// if not, try the TS API.
|
|
}
|
|
|
|
//
|
|
// Use Terminal Services for non-console Logon IDs.
|
|
//
|
|
BOOL Result;
|
|
ULONG ReturnLength;
|
|
NTSTATUS Status;
|
|
OBJECT_ATTRIBUTES ObjA;
|
|
HANDLE ImpersonationToken;
|
|
WINSTATIONUSERTOKEN Info;
|
|
SECURITY_QUALITY_OF_SERVICE SecurityQualityOfService;
|
|
|
|
static PWINSTATIONQUERYINFORMATIONW pWinstationQueryInformation = 0;
|
|
|
|
//
|
|
// See if the entry point is loaded yet.
|
|
//
|
|
if (!pWinstationQueryInformation)
|
|
{
|
|
HMODULE module = LoadLibrary(_T("winsta.dll"));
|
|
if (module == NULL)
|
|
{
|
|
HRESULT HrError = HRESULT_FROM_WIN32( GetLastError() );
|
|
ASSERT( S_OK != HrError );
|
|
LogInfo( "Load library of winsta failed, error %!winerr!", HrError );
|
|
return HrError;
|
|
}
|
|
|
|
pWinstationQueryInformation = (PWINSTATIONQUERYINFORMATIONW) GetProcAddress( module, "WinStationQueryInformationW" );
|
|
if (!pWinstationQueryInformation)
|
|
{
|
|
HRESULT HrError = HRESULT_FROM_WIN32( GetLastError() );
|
|
ASSERT( S_OK != HrError );
|
|
LogInfo( "GetProcAddress of WinStationQueryInformationW, error %!winerr!", HrError );
|
|
FreeLibrary(module);
|
|
return HrError;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Ask for the token.
|
|
//
|
|
Info.ProcessId = UlongToHandle(GetCurrentProcessId());
|
|
Info.ThreadId = UlongToHandle(GetCurrentThreadId());
|
|
|
|
Result = (*pWinstationQueryInformation)(
|
|
SERVERNAME_CURRENT,
|
|
LogonId,
|
|
WinStationUserToken,
|
|
&Info,
|
|
sizeof(Info),
|
|
&ReturnLength
|
|
);
|
|
|
|
if( !Result )
|
|
{
|
|
HRESULT HrError = HRESULT_FROM_WIN32( GetLastError() );
|
|
ASSERT( S_OK != HrError );
|
|
LogError("token : WinstationQueryInfo failed with %!winerr!%", HrError );
|
|
return HrError;
|
|
}
|
|
|
|
//
|
|
// The token returned is a duplicate of a primary token.
|
|
//
|
|
*pUserToken = Info.UserToken;
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
BOOL
|
|
SidToString(
|
|
PSID sid,
|
|
wchar_t buffer[],
|
|
USHORT bytes
|
|
)
|
|
{
|
|
UNICODE_STRING UnicodeString;
|
|
|
|
UnicodeString.Buffer = buffer;
|
|
UnicodeString.Length = 0;
|
|
UnicodeString.MaximumLength = bytes;
|
|
|
|
NTSTATUS NtStatus;
|
|
NtStatus = RtlConvertSidToUnicodeString( &UnicodeString,
|
|
sid,
|
|
FALSE
|
|
);
|
|
if (!NT_SUCCESS(NtStatus))
|
|
{
|
|
LogWarning( "RtlConvertSid failed %x", NtStatus);
|
|
StringCbCopy( buffer, bytes, L"(conversion failed)" );
|
|
return FALSE;
|
|
}
|
|
|
|
buffer[ UnicodeString.Length ] = 0;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
HRESULT
|
|
SetStaticCloaking(
|
|
IUnknown *pUnk
|
|
)
|
|
{
|
|
// Sets static cloaking on the current object so that we
|
|
// should always impersonate the current context.
|
|
// Also sets the impersonation level to identify.
|
|
|
|
HRESULT Hr = S_OK;
|
|
|
|
IClientSecurity *pSecurity = NULL;
|
|
OLECHAR *ServerPrincName = NULL;
|
|
|
|
try
|
|
{
|
|
Hr = pUnk->QueryInterface( __uuidof( IClientSecurity ),
|
|
(void**)&pSecurity );
|
|
if (Hr == E_NOINTERFACE)
|
|
{
|
|
//
|
|
// This is not a proxy; the client is in the same apartment as we are.
|
|
// Identity issn't an issue, because the client already has access to system
|
|
// credentials.
|
|
//
|
|
return S_OK;
|
|
}
|
|
|
|
DWORD AuthnSvc, AuthzSvc;
|
|
DWORD AuthnLevel, ImpLevel, Capabilites;
|
|
|
|
THROW_HRESULT(
|
|
pSecurity->QueryBlanket(
|
|
pUnk,
|
|
&AuthnSvc,
|
|
&AuthzSvc,
|
|
&ServerPrincName,
|
|
&AuthnLevel,
|
|
NULL, // Don't need impersonation handle since were setting that
|
|
NULL, // don't need indenty handle since were setting that
|
|
&Capabilites ) );
|
|
|
|
THROW_HRESULT(
|
|
pSecurity->SetBlanket(
|
|
pUnk,
|
|
AuthnSvc,
|
|
AuthzSvc,
|
|
ServerPrincName,
|
|
AuthnLevel,
|
|
RPC_C_IMP_LEVEL_IDENTIFY,
|
|
NULL, // COM use indentity from token
|
|
#if !defined( BITS_V12_ON_NT4 )
|
|
EOAC_STATIC_CLOAKING // The point of the exercise
|
|
#else
|
|
0
|
|
#endif
|
|
) );
|
|
|
|
}
|
|
catch( ComError Error )
|
|
{
|
|
Hr = Error.Error();
|
|
}
|
|
|
|
CoTaskMemFree( ServerPrincName );
|
|
SafeRelease( pSecurity );
|
|
|
|
return Hr;
|
|
}
|
|
|
|
HRESULT DetectTerminalServer( bool * pfTS )
|
|
{
|
|
|
|
//
|
|
// Test for Terminal Services.
|
|
//
|
|
INT * pConnectState;
|
|
DWORD size;
|
|
if (WTSQuerySessionInformation( WTS_CURRENT_SERVER,
|
|
0,
|
|
WTSConnectState,
|
|
reinterpret_cast<LPTSTR *>(&pConnectState),
|
|
&size))
|
|
{
|
|
LogInfo("TS test: TS is installed");
|
|
*pfTS = true;
|
|
|
|
WTSFreeMemory( pConnectState );
|
|
return S_OK;
|
|
}
|
|
else if (GetLastError() == ERROR_APP_WRONG_OS)
|
|
{
|
|
LogInfo("TS test: no TS");
|
|
|
|
*pfTS = false;
|
|
return S_OK;
|
|
}
|
|
else
|
|
{
|
|
DWORD s = GetLastError();
|
|
LogError("TS test returned %d", s);
|
|
return HRESULT_FROM_WIN32( s );
|
|
}
|
|
}
|