Windows2003-3790/termsrv/admtools/utilsub/wstutil.c
2020-09-30 16:53:55 +02:00

557 lines
15 KiB
C

// Copyright (c) 1998-1999 Microsoft Corporation
/******************************************************************************
*
* WSTUTIL.C
*
* Various useful utilities for dealing with multi-user WinStations and User
* accounts that are useful across a range of utilities and apps.
*
*
*
*******************************************************************************/
#include <nt.h>
#include <ntrtl.h>
#include <nturtl.h>
#include <windows.h>
#include <ntddkbd.h>
#include <ntddmou.h>
#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <process.h>
#include <string.h>
#include <malloc.h>
#include <winstaw.h>
#include <utilsub.h>
/*
* RefreshAllCaches
*
* Invalidate any caches maintained by the UTILSUB.DLL
*
* This does not need to be called for utilities that exit when done, but
* are for server, or monitoring type programs that need to periodicly
* see the latest system information.
* IE: A new user could have logged onto a given winstation since the last
* call.
*
*
* Exit
*
* Any caches in the UTILSUB.DLL have been invalidated insuring fresh
* system information on future calls.
*
*/
VOID WINAPI
RefreshAllCaches()
{
RefreshWinStationCaches();
RefreshProcessObjectCaches();
}
/*
* RefreshWinStationCaches
*
* Invalidate any caches maintained by the WinStation helper utilities.
*
* This does not need to be called for utilities that exit when done, but
* are for server, or monitoring type programs that need to periodicly
* see the latest system information.
* IE: A new user could have logged onto a given winstation since the last
* call.
*
*
* Exit
*
* Makes sure that any WinStation helper utility calls will return the
* system information at least up to date as the time that this call
* was made.
*
*/
VOID WINAPI
RefreshWinStationCaches()
{
RefreshWinStationObjectCache();
RefreshWinStationNameCache();
}
/*
* GetCurrentLogonId
*
* Gets the WinStation ID for the current processes WinStation
*
* Exit
*
* ID of the current processes WinStation
*
*/
ULONG WINAPI
GetCurrentLogonId()
{
return( NtCurrentPeb()->SessionId );
}
/*
* GetCurrentWinStationName
*
* Get the current UNICODE name for the WinStation for this process
*
* Input:
*
* pName - Pointer to wide character buffer for name
*
* MaxSize - Maximum number of characters in buffer (including terminator).
*
* pName - Pointer to wide character buffer for name
*
* Output:
*
*/
VOID WINAPI
GetCurrentWinStationName( PWCHAR pName, int MaxSize )
{
GetWinStationNameFromId( NtCurrentPeb()->SessionId, pName, MaxSize );
}
/*
* This is the cache maintained by the GetWinStationNameFromId function
*
* It is thread safe through the use of WLock.
*/
typedef struct TAGWINSTATIONLIST {
struct TAGWINSTATIONLIST *Next;
LOGONID LogonId;
} WINSTATIONLIST, *PWINSTATIONLIST;
static PWINSTATIONLIST pWList = NULL;
static RTL_CRITICAL_SECTION WLock;
static BOOLEAN WLockInited = FALSE;
/***************************************************************************
*
* InitWLock
*
* Since we do not require the user to call an initialize function,
* we must initialize our critical section in a thread safe manner.
*
* The problem is, a critical section is needed to guard against multiple
* threads trying to init the critical section at the same time.
*
* The solution that Nt uses, in which RtlInitializeCriticalSection itself
* uses, is to wait on a kernel supported process wide Mutant before proceding.
* This Mutant almost works by itself, but RtlInitializeCriticalSection does
* not wait on it until after trashing the semaphore count. So we wait on
* it ourselves, since it can be acquired recursively.
*
***************************************************************************/
NTSTATUS InitWLock()
{
NTSTATUS status = STATUS_SUCCESS;
RtlEnterCriticalSection((PRTL_CRITICAL_SECTION)NtCurrentPeb()->LoaderLock);
/*
* Make sure another thread did not beat us here
*/
if( WLockInited == FALSE ){
status = RtlInitializeCriticalSection( &WLock );
if (status == STATUS_SUCCESS) {
WLockInited = TRUE;
}
}
RtlLeaveCriticalSection((PRTL_CRITICAL_SECTION)NtCurrentPeb()->LoaderLock);
return status;
}
/***************************************************************************
*
* RefreshWinStationNameCache
*
* Invalidate the WinStation Name cache so that the newest information
* will be fetched from the system.
*
***************************************************************************/
VOID WINAPI
RefreshWinStationNameCache( )
{
NTSTATUS status = STATUS_SUCCESS;
PWINSTATIONLIST pEntry, pNext;
if( pWList == NULL ) return;
/*
* Make sure critical section has been inited
*/
if( !WLockInited ) {
status = InitWLock();
}
if (status == STATUS_SUCCESS) {
RtlEnterCriticalSection( &WLock );
pEntry = pWList;
while( pEntry ) {
pNext = pEntry->Next;
free( pEntry );
pEntry = pNext;
}
pWList = NULL;
RtlLeaveCriticalSection( &WLock );
}
}
/*
* GetWinStationNameFromId
*
* Attempt to retrieve the WinStationName for the given LogonId.
*
* Parameters:
*
* LogonId (input)
* Unique LogonId
*
* pName (output)
* Pointer to buffer for name
*
* MaxSize (input)
* Maximum number of characters in buffer (including terminator).
*
* Returns
* TRUE if name was retreived, FALSE otherwise.
*
*/
BOOLEAN WINAPI
GetWinStationNameFromId( ULONG LogonId, PWCHAR pName, int MaxSize )
{
return xxxGetWinStationNameFromId( SERVERNAME_CURRENT , LogonId , pName , MaxSize );
}
/*----------------------------------------------------------------------------------------
/*
* xxxGetWinStationNameFromId
*
* Attempt to retrieve the WinStationName for the given LogonId.
*
* Parameters:
*
* hServer ( input )
* rpc handle to termsrv
*
* LogonId (input)
* Unique LogonId
*
* pName (output)
* Pointer to buffer for name
*
* MaxSize (input)
* Maximum number of characters in buffer (including terminator).
*
* Returns
* TRUE if name was retreived, FALSE otherwise.
*
*/
BOOLEAN WINAPI
xxxGetWinStationNameFromId( HANDLE hServer , ULONG LogonId, PWCHAR pName, int MaxSize )
{
NTSTATUS status = STATUS_SUCCESS;
PLOGONID pIdBase, pId;
int rc;
ULONG Count;
PWINSTATIONLIST pEntryBase, pEntry;
// Since We do not have a WinStationNamefromId Sm Api like we do for
// LogonIdfromName, we will perform a WinStationEnumerate function across
// all WinStations known by the Session Manager, and store them in a locally
// maintained list. We do this so we that this search against the session
// manager is not done every time we're called.
//
// Another alternative that was tested is to open the WinStation itself
// and then do a WinStationQueryInformation against it in order to
// retrieve its name from itself. This is much slower because we must
// set up and tear down an LPC connection to each WinStation, as opposed
// to the one connection we get to the session manager.
/*
* Make sure critical section has been inited
*/
if( !WLockInited ) {
status = InitWLock();
}
if (status == STATUS_SUCCESS) {
RtlEnterCriticalSection( &WLock );
// Initialize the list the first time
if( pWList == NULL ) {
rc = WinStationEnumerate( hServer, &pIdBase, &Count );
if( rc ) {
/*
* Allocate an Entry for each enumerated winstation.
*/
pEntryBase = (PWINSTATIONLIST)malloc( Count * sizeof(WINSTATIONLIST) );
if( pEntryBase == NULL ) {
pWList = NULL; // We are having severe problems
SetLastError( ERROR_NOT_ENOUGH_MEMORY );
WinStationFreeMemory(pIdBase);
RtlLeaveCriticalSection( &WLock );
return( FALSE );
}
/*
* Load up Entries.
*/
for ( pEntry = pEntryBase, pId = pIdBase;
Count ;
Count--, pEntry++, pId++ ) {
pEntry->LogonId = *pId;
pEntry->Next = pWList;
pWList = pEntry;
}
/*
* Free enumerate buffer.
*/
WinStationFreeMemory(pIdBase);
}
} // End if pWList == NULL
pEntry = pWList;
while ( pEntry ) {
if( pEntry->LogonId.LogonId == LogonId ) {
wcsncpy( pName, pEntry->LogonId.WinStationName, MaxSize-1 );
pName[MaxSize-1] = 0;
RtlLeaveCriticalSection( &WLock );
return( TRUE );
}
pEntry = pEntry->Next;
}
RtlLeaveCriticalSection( &WLock );
}
// If we can not find its name, print its ID #
wsprintf( pName, L"ID %d", LogonId );
return( TRUE );
}
/*----------------------------------------------------------------------------------------
/*
* GetCurrentUserName
*
* Get the current UNICODE name for the logon USER for this process
*
* Input:
*
* pName - Pointer to wide character buffer for name
*
* MaxSize - Maximum number of characters in buffer (including terminator)
*
*
* Output:
*
*/
VOID WINAPI
GetCurrentUserName( PWCHAR pName, int MaxSize )
{
/*
* The quickest way appears to open the current processes WinStation and
* get the name from it. The other way would be to open the process, then
* its token, extract the SID, then lookup the SID in the SAM database.
* We have conviently stored the user name in the WinStation at Logon
* time, so we'll use that.
*/
GetWinStationUserName( SERVERNAME_CURRENT , LOGONID_CURRENT, pName, MaxSize );
return;
}
/*
* GetWinStationUserName
*
* Get the UNICODE name for the USER for the winstation
*
* Input:
*
* hServer - handle to termsrv
*
* LogonId - integer identifier for WinStation
*
* pName - Pointer to wide character buffer for name
*
* MaxSize - Maximum number of characters in buffer (including terminator)
*
*
* Output:
*
*/
BOOLEAN WINAPI
GetWinStationUserName( HANDLE hServer , ULONG LogonId, PWCHAR pName, int MaxSize )
{
BOOLEAN rc;
ULONG ReturnLength;
WINSTATIONINFORMATION Info;
if( MaxSize == 0) return( FALSE );
memset( &Info, 0, sizeof(WINSTATIONINFORMATION) );
rc = WinStationQueryInformation( hServer,
LogonId,
WinStationInformation,
(PVOID)&Info,
sizeof(WINSTATIONINFORMATION),
&ReturnLength);
if(!rc){
pName[0] = 0;
return( FALSE );
}
if(ReturnLength != sizeof(WINSTATIONINFORMATION)) {
pName[0] = 0; // Version mismatch
return( FALSE );
}
/*
* Now copy the name out
*/
if( MaxSize > USERNAME_LENGTH ) {
MaxSize = USERNAME_LENGTH;
}
wcsncpy( pName, Info.UserName, MaxSize-1 );
pName[MaxSize-1] = 0; // insure null termination if string is truncated
return( TRUE );
}
/*
* These variables maintain a one entry cache so that we
* do not have to keep querying the winstation (causes an LPC)
* each time called.
*/
static ULONG CachedId = (ULONG)(-1);
static WCHAR CachedUserName[USERNAME_LENGTH];
/**************************************************************************
*
* RefreshWinStationObjectCache
*
* Flush the cache for the WinStationObject name comparision function.
*
**************************************************************************/
VOID WINAPI
RefreshWinStationObjectCache()
{
CachedId = (ULONG)(-1);
CachedUserName[0] = 0;
}
/*
* WinStationObjectMatch
*
* General Name match function against a WinStation.
*
* The admin utilities can take a user name, winstation name, or
* a winstation id as an argument to a command that targets a winstation
* for some action (send a message, query status, reset, etc.)
*
* This function does general compares of the supplied name to see if it
* applies to the given winstation because the name represents the logged
* on user of the winstation, the winstations system name when attached, or
* the winstations unique id.
*
*
* NOTE: The caching for this function assumes typical use of comparing this
* winstation against a list of names across multiple calls.
* It does not optimize for comparing one name at a time across all
* winstation(s) in succession.
*
* Parameters:
*
* hServer ( input ) remote termsrv
*
* Id (input) WinStation Id for do the match against
*
* pName (input) UNICODE name for match testing
*/
BOOLEAN WINAPI
WinStationObjectMatch( HANDLE hServer , PLOGONID Id, PWCHAR pName )
{
ULONG tmp;
/*
* Handle the wild card case
*/
if( pName[0] == L'*' ) {
return( TRUE );
}
/*
* See if the supplied name is the name assigned to the WinStation
*/
if( !_wcsnicmp( pName, Id->WinStationName, WINSTATIONNAME_LENGTH ) ) {
return( TRUE );
}
/*
* See if it represents the numerical id for the winstation
*/
if( iswdigit( pName[0] ) ) {
tmp = (ULONG)wcstol( pName, NULL, 10 );
if( tmp == Id->LogonId ) {
return( TRUE );
}
}
/*
* Else extract the logged on user name from the winstation itself
* and compare this.
*/
if( CachedId == Id->LogonId ) {
if( !_wcsnicmp( CachedUserName, pName, USERNAME_LENGTH ) ) {
return( TRUE );
}
}
if ( Id->State == State_Down )
return( FALSE );
if( GetWinStationUserName( hServer , Id->LogonId, CachedUserName, USERNAME_LENGTH ) ) {
CachedId = Id->LogonId;
}
else {
CachedId = (ULONG)(-1); // In case name was trashed
return( FALSE );
}
if( !_wcsnicmp( CachedUserName, pName, USERNAME_LENGTH ) ) {
return( TRUE );
}
return( FALSE );
}