|
|
/*************************************************************************
* * winsta.c * * System Library WinStation utilities * * This file contains common routines needed in many places in the * system. An example is that (3) separate DLL's in the spooler need * functions to deal with the current user of a WinStation. IE: Get * name, find which LogonId, get name by logonid, etc. * * This common library at least keeps the source management in one * place. This is likely to become another Hydra DLL's in the future * to reduce memory. * * * * *************************************************************************/
#include <nt.h>
#include <ntrtl.h>
#include <nturtl.h>
#include <windows.h>
#include <stdio.h>
#include "winsta.h"
#include "syslib.h"
#pragma warning (error:4312)
#if DBG
#define DBGPRINT(x) DbgPrint x
#if DBGTRACE
#define TRACE0(x) DbgPrint x
#define TRACE1(x) DbgPrint x
#else
#define TRACE0(x)
#define TRACE1(x)
#endif
#else
#define DBGPRINT(x)
#define TRACE0(x)
#define TRACE1(x)
#endif
//
// Structure for FindUserOnWinStation
//
typedef struct _FINDUSERDATA { LPWSTR pName; ULONG ResultLogonId; } FINDUSERDATA, *PFINDUSERDATA;
/*****************************************************************************
* * WinStationGetUserName * * Return the user name for the WinStation * * ENTRY: * Param1 (input/output) * Comments * * EXIT: * STATUS_SUCCESS - no error * ****************************************************************************/
BOOL WinStationGetUserName( ULONG LogonId, PWCHAR pBuf, ULONG BufSize ) { BOOL Result; ULONG ReturnLength; WINSTATIONINFORMATION WSInfo;
memset( &WSInfo, 0, sizeof(WSInfo) );
// Query it
Result = WinStationQueryInformation( SERVERNAME_CURRENT, LogonId, WinStationInformation, &WSInfo, sizeof(WSInfo), &ReturnLength );
if( !Result ) { DBGPRINT(("GetWinStationInfo: Error %d getting info on WinStation %d\n",GetLastError(),LogonId)); return( FALSE ); }
// Scale BufSize to UNICODE characters
if( BufSize >= sizeof(WCHAR) ) { BufSize /= sizeof(WCHAR); } else { BufSize = 0; }
if( (BufSize > 1) && WSInfo.UserName[0] ) { wcsncpy( pBuf, WSInfo.UserName, BufSize ); pBuf[BufSize-1] = (WCHAR)NULL; } else { pBuf[0] = (WCHAR)NULL; }
return( TRUE ); }
/*****************************************************************************
* * SearchUserCallback * * Callback for search function * * ENTRY: * Param1 (input/output) * Comments * * EXIT: * STATUS_SUCCESS - no error * ****************************************************************************/
BOOLEAN SearchUserCallback( ULONG CurrentIndex, PLOGONIDW pInfo, ULONG_PTR lParam ) { BOOL Result; PFINDUSERDATA p; WCHAR UserName[USERNAME_LENGTH+1];
// Only active WinStations are valid
if( pInfo->State != State_Active ) { // continue the search
return( TRUE ); }
// Check the user on the WinStation
Result = WinStationGetUserName( pInfo->LogonId, UserName, sizeof(UserName) ); if( !Result ) { DBGPRINT(("SearchUserCallback: Error getting WinStation User Name LogonId %d\n",pInfo->LogonId,GetLastError())); // continue the search
return( TRUE ); }
p = (PFINDUSERDATA)lParam;
if( _wcsicmp(p->pName, UserName) == 0 ) { TRACE0(("SearchUserCallback: Found username %ws on WinStation LogonId %d\n",UserName,pInfo->LogonId)); // Found it, return the LogonId
p->ResultLogonId = pInfo->LogonId; // Stop the search
return( FALSE ); }
// continue the search
return( TRUE ); }
/*****************************************************************************
* * FindUsersWinStation * * Find the given users WinStation. * * ENTRY: * Param1 (input/output) * Comments * * EXIT: * STATUS_SUCCESS - no error * ****************************************************************************/
BOOL FindUsersWinStation( PWCHAR pName, PULONG pLogonId ) { BOOL Result; FINDUSERDATA Data;
ASSERT( pLogonId != NULL );
// If a NULL name, we are not going to find it.
if( (pName == NULL) || (pName[0] == (WCHAR)NULL) ) { TRACE0(("FindUsersWinStation: NULL user name\n")); return( FALSE ); }
Data.ResultLogonId = (ULONG)(-1); Data.pName = pName;
//
// Use the WinStation Enumerator to check all the WinStations
//
Result = WinStationEnumeratorW( 0, // StartIndex
SearchUserCallback, // enumerator callback function
(ULONG_PTR)&Data // lParam is our structure
);
if( !Result ) { // Problem with enumerator
DBGPRINT(("FindUsersWinStation: Problem with enumerator\n")); return(FALSE); }
//
// If ResultLogonId != (-1), a WinStation was found for the user
//
if( Data.ResultLogonId != (ULONG)(-1) ) { TRACE0(("FindUsersWinStation: Found LogonId %d\n",Data.ResultLogonId)); *pLogonId = Data.ResultLogonId; return(TRUE); }
TRACE0(("FindUsersWinStation: Could not find user %ws\n",pName)); return(FALSE); }
/*****************************************************************************
* * WinStationGetIcaNameA * * ANSI version * * Get the ICA name from the supplied WinStations Logonid * * Returns it in newly allocated memory that must be freed with * RtlFreeHeap(). * * ENTRY: * Param1 (input/output) * Comments * * EXIT: * STATUS_SUCCESS - no error * ****************************************************************************/
PCHAR WinStationGetICANameA( ULONG LogonId ) { BOOL Result; ULONG ReturnLength; PCHAR pName = NULL; WINSTATIONCLIENTA ClientInfo; WINSTATIONINFORMATIONA WSInfo; CHAR NameBuf[MAX_PATH+1];
memset( &WSInfo, 0, sizeof(WSInfo) );
Result = WinStationQueryInformationA( SERVERNAME_CURRENT, LogonId, WinStationInformation, &WSInfo, sizeof(WSInfo), &ReturnLength );
if( !Result ) { DBGPRINT(("GetWinStationICANameA: Error %d getting info on WinStation\n",GetLastError())); return( NULL ); }
memset( &ClientInfo, 0, sizeof(ClientInfo) );
// Query its Info
Result = WinStationQueryInformationA( SERVERNAME_CURRENT, LogonId, WinStationClient, &ClientInfo, sizeof(ClientInfo), &ReturnLength );
if( !Result ) { DBGPRINT(("GetWinStationICANameA: Error %d getting client info\n",GetLastError())); return( NULL ); }
//
// If the ClientName is NULL, then we use the user
// as the ICA name.
//
if( ClientInfo.ClientName[0] == (CHAR)NULL ) { #ifdef notdef // spec change...
if( ClientInfo.SerialNumber ) wsprintf( NameBuf, L"%ws-%d", WSInfo.UserName, ClientInfo.SerialNumber); else #endif
sprintf( NameBuf, "%s", WSInfo.UserName);
} else { // copy out the Client name
strcpy( NameBuf, ClientInfo.ClientName ); }
ReturnLength = strlen( NameBuf ) + 1;
pName = RtlAllocateHeap( RtlProcessHeap(), 0, ReturnLength ); if( pName == NULL ) { return( NULL ); }
strcpy( pName, NameBuf );
return( pName ); }
/*****************************************************************************
* * WinStationGetIcaNameW * * UNICODE Version * * Get the ICA name from the supplied WinStations Logonid * * Returns it in newly allocated memory that must be freed with * RtlFreeHeap(). * * ENTRY: * Param1 (input/output) * Comments * * EXIT: * STATUS_SUCCESS - no error * ****************************************************************************/
PWCHAR WinStationGetICANameW( ULONG LogonId ) { BOOL Result; ULONG ReturnLength; PWCHAR pName = NULL; WINSTATIONCLIENT ClientInfo; WINSTATIONINFORMATION WSInfo; WCHAR NameBuf[MAX_PATH+1];
memset( &WSInfo, 0, sizeof(WSInfo) );
Result = WinStationQueryInformationW( SERVERNAME_CURRENT, LogonId, WinStationInformation, &WSInfo, sizeof(WSInfo), &ReturnLength );
if( !Result ) { DBGPRINT(("GetWinStationICANameW: Error %d getting info on WinStation\n",GetLastError())); return( NULL ); }
memset( &ClientInfo, 0, sizeof(ClientInfo) );
// Query its Info
Result = WinStationQueryInformationW( SERVERNAME_CURRENT, LogonId, WinStationClient, &ClientInfo, sizeof(ClientInfo), &ReturnLength );
if( !Result ) { DBGPRINT(("GetWinStationICANameW: Error %d getting client info\n",GetLastError())); return( NULL ); }
//
// If the ClientName is NULL, then we use the user
// as the ICA name.
//
if( ClientInfo.ClientName[0] == (WCHAR)NULL ) { #ifdef notdef // spec change...
if( ClientInfo.SerialNumber ) wsprintf( NameBuf, L"%ws-%d", WSInfo.UserName, ClientInfo.SerialNumber); else #endif
wsprintf( NameBuf, L"%ws", WSInfo.UserName);
} else { // copy out the Client name
wcscpy( NameBuf, ClientInfo.ClientName ); }
ReturnLength = wcslen( NameBuf ) + 1; ReturnLength *= sizeof(WCHAR);
pName = RtlAllocateHeap( RtlProcessHeap(), 0, ReturnLength ); if( pName == NULL ) { return( NULL ); }
wcscpy( pName, NameBuf );
return( pName ); }
/*****************************************************************************
* * WinStationIsHardWire * * Returns whether the WinStation is hardwired. IE: No modem * or network. Like a dumb terminal. * * ENTRY: * Param1 (input/output) * Comments * * EXIT: * STATUS_SUCCESS - no error * ****************************************************************************/
BOOLEAN WinStationIsHardWire( ULONG LogonId ) { return( FALSE ); }
/*****************************************************************************
* * GetWinStationUserToken * * Return the token for the user currently logged onto the WinStation * * ENTRY: * LogonId (input) * LogonId of WinStation * * pUserToken (output) * Variable to place the returned token handle if successfull. * * EXIT: * STATUS_SUCCESS - no error * ****************************************************************************/
BOOL GetWinStationUserToken( ULONG LogonId, PHANDLE pUserToken ) { BOOL Result; ULONG ReturnLength; NTSTATUS Status; OBJECT_ATTRIBUTES ObjA; HANDLE ImpersonationToken; WINSTATIONUSERTOKEN Info; SECURITY_QUALITY_OF_SERVICE SecurityQualityOfService;
//
// This gets the token of the user logged onto the WinStation
// if we are an admin caller.
//
// This is so that CSRSS can dup the handle to our process
Info.ProcessId = LongToHandle(GetCurrentProcessId()); Info.ThreadId = LongToHandle(GetCurrentThreadId());
Result = WinStationQueryInformation( SERVERNAME_CURRENT, LogonId, WinStationUserToken, &Info, sizeof(Info), &ReturnLength );
if( !Result ) { DBGPRINT(("GetWinStationUserToken: Error %d getting UserToken LogonId %d\n",GetLastError(),LogonId)); return( FALSE ); }
//
// The token returned is a duplicate of a primary token.
//
// We must make it into an IMPERSONATION TOKEN or the
// AccessCheck() routine will fail since it only operates
// against impersonation tokens.
//
InitializeObjectAttributes( &ObjA, NULL, 0L, NULL, NULL );
SecurityQualityOfService.Length = sizeof(SECURITY_QUALITY_OF_SERVICE); SecurityQualityOfService.ImpersonationLevel = SecurityImpersonation; SecurityQualityOfService.ContextTrackingMode = SECURITY_DYNAMIC_TRACKING; SecurityQualityOfService.EffectiveOnly = FALSE;
ObjA.SecurityQualityOfService = &SecurityQualityOfService;
Status = NtDuplicateToken( Info.UserToken, 0, // inherit granted accesses TOKEN_IMPERSONATE
&ObjA, FALSE, TokenImpersonation, &ImpersonationToken );
if ( !NT_SUCCESS( Status ) ) { DBGPRINT(("GetWinStationUserToken: Error %d duping UserToken to impersonation LogonId %d\n",GetLastError(),LogonId)); NtClose( Info.UserToken ); return( FALSE ); }
// return the impersonation token
*pUserToken = ImpersonationToken;
NtClose( Info.UserToken );
return( TRUE ); }
//
// This is not in winnt.h, but in ntseapi.h which we can not
// include readily since we are a WIN32 program as we 'hide' this
// new information type from WIN32 programs.
//
/*****************************************************************************
* * GetClientLogonId * * Get the logonid from the client who we should be impersonating. If any * errors, we return 0 to mean the Console Logon Id since this may be a * remote network call. * * ENTRY: * * EXIT: * STATUS_SUCCESS - no error * ****************************************************************************/
ULONG GetClientLogonId() { BOOL Result; HANDLE TokenHandle; ULONG LogonId, ReturnLength;
//
// We should be impersonating the client, so we will get the
// LogonId from out token.
//
// We may not have a valid one if this is a remote network
// connection.
Result = OpenThreadToken( GetCurrentThread(), TOKEN_QUERY, FALSE, // Use impersonation
&TokenHandle );
if( Result ) {
// This identifies which WinStation is making this request.
//
Result = GetTokenInformation( TokenHandle, TokenSessionId, &LogonId, sizeof(LogonId), &ReturnLength );
if( Result ) { #if DBG
if( ReturnLength != sizeof(LogonId) ) { DbgPrint("LOCALSPOOL: CompleteRead: ReturnLength %d != sizeof(LogonId)\n", ReturnLength ); } #endif
} else { DBGPRINT(("SYSLIB: Error getting token information %d\n", GetLastError())); LogonId = 0; // Default to console
} CloseHandle( TokenHandle ); } else { TRACE0(("SYSLIB: Error opening token %d\n", GetLastError())); LogonId = 0; }
return( LogonId ); }
/*****************************************************************************
* * WinStationEnumeratorW * * Enumerator for WinStations * * ENTRY: * StartIndex (input) * WinStation Index to start Enumeration at * * pProc (input) * Pointer to function that gets called for each WinStation * entry. * * EXAMPLE: * * BOOLEAN * EnumCallBack( * ULONG CurrentIndex, // Current Index of this entry
* PLOGONIDW pInfo, // WinStation Entry
* ULONG_PTR lParam // Passed through from caller of WinStationEnumeratorW
* ); * * If the EnumCallback function returns TRUE, the WinStationEnumeratorW() * continues the search. If it returns FALSE, the search is stopped. * * lParam (input) * Caller supplied argument passed through to caller supplied function * * EXIT: * TRUE - no error * FALSE - Error * ****************************************************************************/
BOOLEAN WinStationEnumeratorW( ULONG StartIndex, WINSTATIONENUMPROC pProc, ULONG_PTR lParam ) { BOOLEAN Result; ULONG Entries, i; ULONG ByteCount, ReqByteCount, Index; ULONG Error, CurrentIndex; PLOGONIDW ptr; ULONG QuerySize = 32; PLOGONIDW SmNameCache = NULL; // WinStation namelist
Index = StartIndex; CurrentIndex = StartIndex;
Entries = QuerySize; ByteCount = Entries * sizeof( LOGONIDW ); SmNameCache = (PLOGONIDW)RtlAllocateHeap( RtlProcessHeap(), 0, ByteCount ); if ( SmNameCache == NULL ) return(FALSE);
while( 1 ) {
ReqByteCount = ByteCount; ptr = SmNameCache; Result = WinStationEnumerate_IndexedW( SERVERNAME_CURRENT, &Entries, ptr, &ByteCount, &Index );
if( !Result ) { Error = GetLastError(); if( Error == ERROR_NO_MORE_ITEMS ) { // Done
RtlFreeHeap( RtlProcessHeap(), 0, SmNameCache ); return(TRUE); } else if( Error == ERROR_ALLOTTED_SPACE_EXCEEDED ) { // Entries contains the maximum query size
if( QuerySize <= Entries ) { DBGPRINT(("CPMMON: SM Query Size < RetCapable. ?View Memory Leak? Query %d, Capable %d\n", QuerySize, Entries )); QuerySize--; // See when it recovers. On retail, it will still work.
} else { // We asked for more than it can handle
QuerySize = Entries; }
Entries = QuerySize; ByteCount = Entries * sizeof( LOGONIDW ); SmNameCache = (PLOGONIDW)RtlReAllocateHeap( RtlProcessHeap(), 0, SmNameCache, ByteCount ); if ( SmNameCache == NULL ) return(FALSE);
continue; } else { // Other error
DBGPRINT(("CPMMON: Error emumerating WinStations %d\n",Error)); RtlFreeHeap( RtlProcessHeap(), 0, SmNameCache ); return(FALSE); } }
ASSERT( ByteCount <= ReqByteCount );
// We got some entries, now call the enumerator function
for( i=0; i < Entries; i++ ) { Result = pProc( CurrentIndex, &SmNameCache[i], lParam ); CurrentIndex++; if( !Result ) { // The Enumerator proc wants us to stop the search
RtlFreeHeap( RtlProcessHeap(), 0, SmNameCache ); return(TRUE); } } } // Outer while
return(FALSE); }
|