Windows2003-3790/termsrv/winsta/server/winget.c
2020-09-30 16:53:55 +02:00

1336 lines
51 KiB
C

/****************************************************************************/
// winget.c
//
// TermSrv RPC query handler.
//
// Copyright (C) 1997-2000 Microsoft Corporation
/****************************************************************************/
#include "precomp.h"
#pragma hdrstop
#include "rpcwire.h"
#include "conntfy.h" // for GetLockedState
#include <winsock2.h>
#include <ws2tcpip.h>
#define MODULE_SIZE 1024
extern WCHAR g_DigProductId[CLIENT_PRODUCT_ID_LENGTH];
// Extern function
extern NTSTATUS _CheckCallerLocalAndSystem(VOID);
/*=============================================================================
== Private functions
=============================================================================*/
NTSTATUS xxxGetUserToken(PWINSTATION, WINSTATIONUSERTOKEN UNALIGNED *, ULONG);
/*=============================================================================
== Functions Used
=============================================================================*/
NTSTATUS xxxWinStationQueryInformation(ULONG, WINSTATIONINFOCLASS,
PVOID, ULONG, PULONG);
NTSTATUS
RpcCheckClientAccess(
PWINSTATION pWinStation,
ACCESS_MASK DesiredAccess,
BOOLEAN AlreadyImpersonating
);
NTSTATUS
RpcCheckSystemClientEx(
PWINSTATION pWinStation
);
NTSTATUS
RpcCheckSystemClientNoLogonId(
PWINSTATION pWinStation
);
BOOLEAN
ValidWireBuffer(WINSTATIONINFOCLASS InfoClass,
PVOID WireBuf,
ULONG WireBufLen);
BOOLEAN
IsCallerAllowedPasswordAccess(VOID);
//
// Query client's IP Address.
//
extern NTSTATUS
xxxQueryRemoteAddress(
PWINSTATION pWinStation,
PWINSTATIONREMOTEADDRESS pRemoteAddress
)
{
struct sockaddr_in6 addr6;
ULONG AddrBytesReturned;
NTSTATUS Status;
if( pWinStation->State != State_Active && pWinStation->State != State_Connected )
{
Status = STATUS_CTX_WINSTATION_NOT_FOUND;
}
else
{
Status = IcaStackIoControl( pWinStation->hStack,
IOCTL_TS_STACK_QUERY_REMOTEADDRESS,
pWinStation->pEndpoint,
pWinStation->EndpointLength,
&addr6,
sizeof( addr6 ),
&AddrBytesReturned
);
if( NT_SUCCESS(Status) )
{
pRemoteAddress->sin_family = addr6.sin6_family;
if( AF_INET == addr6.sin6_family )
{
struct sockaddr_in* pAddr = (struct sockaddr_in *)&addr6;
pRemoteAddress->ipv4.sin_port = pAddr->sin_port;
pRemoteAddress->ipv4.in_addr = pAddr->sin_addr.s_addr;
}
else
{
// Support of IPV6 is for next release.
Status = STATUS_NOT_SUPPORTED;
}
}
}
return Status;
}
ULONG GetLoadMetrics(PWINSTATIONLOADINDICATORDATA pLIData)
{
SYSTEM_PERFORMANCE_INFORMATION SysPerfInfo;
SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION ProcessorInfo[MAX_PROCESSORS];
SYSTEM_BASIC_INFORMATION BasicInfo;
LARGE_INTEGER TotalCPU = {0, 0};
LARGE_INTEGER IdleCPU = {0, 0};
LARGE_INTEGER TotalCPUDelta = {0, 0};
LARGE_INTEGER IdleCPUDelta = {0, 0};
ULONG AvgIdleCPU, AvgBusyCPU, CPUConstrainedSessions;
ULONG RemainingSessions = 0;
LOADFACTORTYPE LoadFactor = ErrorConstraint;
ULONG MinSessions;
ULONG NumWinStations;
NTSTATUS StatusPerf, StatusProc, StatusBasic;
ULONG i;
// Initialize additional data area
memset(pLIData->reserved, 0, sizeof(pLIData->reserved));
// Determine the number of active winstations in the system. If there
// aren't any, just assume 1 so we don't have to special case the logic
// too much. Note that this code counts the console.
if (WinStationTotalCount > IdleWinStationPoolCount)
NumWinStations = WinStationTotalCount - IdleWinStationPoolCount;
else
NumWinStations = 1;
TRACE((hTrace, TC_LOAD, TT_API1,
"Session Statistics: Total [%ld], Idle [%ld], Disc [%ld]\n",
WinStationTotalCount, IdleWinStationPoolCount, WinStationDiscCount));
//
// Get basic info like total memory, etc.
//
StatusBasic = NtQuerySystemInformation(SystemBasicInformation,
&BasicInfo, sizeof(BasicInfo),
NULL);
//
// Get resource (memory) utilization metrics
//
StatusPerf = NtQuerySystemInformation(SystemPerformanceInformation,
&SysPerfInfo, sizeof(SysPerfInfo),
NULL);
//
// Get CPU utilization metrics
//
StatusProc = NtQuerySystemInformation(SystemProcessorPerformanceInformation,
ProcessorInfo,
sizeof(ProcessorInfo),
NULL);
if (gLB.fInitialized &&
NT_SUCCESS(StatusPerf) &&
NT_SUCCESS(StatusProc) &&
NT_SUCCESS(StatusBasic)) {
ULONG DefaultPagedPool, DefaultPtes, DefaultCommit;
ULONG CommitAvailable;
//
// Determine resource usage for all sessions, subtracting out the
// resources required by the base system. Readjust the base
// calculations if they become nonsensical.
//
// total committment and average consumption
CommitAvailable = (ULONG)(SysPerfInfo.CommitLimit - SysPerfInfo.CommittedPages);
if (gLB.BaselineCommit < SysPerfInfo.CommittedPages) {
gLB.CommitUsed = (ULONG)(SysPerfInfo.CommittedPages - gLB.BaselineCommit);
gLB.AvgCommitPerUser = max(gLB.CommitUsed / NumWinStations,
gLB.MinCommitPerUser);
DefaultCommit = FALSE;
}
else {
gLB.CommitUsed = 0;
gLB.AvgCommitPerUser = gLB.MinCommitPerUser;
gLB.BaselineCommit = (ULONG)(SysPerfInfo.CommittedPages);
DefaultCommit = TRUE;
}
TRACE((hTrace, TC_LOAD, TT_API1,
" Commit: Base [%6ld], Used [%6ld], Avail: [%6ld], AvgPerUser: [%6ld]%s\n",
gLB.BaselineCommit,
gLB.CommitUsed,
CommitAvailable,
gLB.AvgCommitPerUser,
DefaultCommit ? "*" : ""));
// total system PTEs used and average consumption
if (gLB.BaselineFreePtes > SysPerfInfo.FreeSystemPtes) {
gLB.PtesUsed = gLB.BaselineFreePtes - SysPerfInfo.FreeSystemPtes;
gLB.AvgPtesPerUser = max(gLB.PtesUsed / NumWinStations,
gLB.MinPtesPerUser);
DefaultPtes = FALSE;
}
else {
gLB.PtesUsed = 0;
gLB.AvgPtesPerUser = gLB.MinPtesPerUser;
gLB.BaselineFreePtes = SysPerfInfo.FreeSystemPtes;
DefaultPtes = TRUE;
}
TRACE((hTrace, TC_LOAD, TT_API1,
" Ptes: Base [%6ld], Used [%6ld], Avail: [%6ld], AvgPerUser: [%6ld]%s\n",
gLB.BaselineFreePtes,
gLB.PtesUsed,
SysPerfInfo.FreeSystemPtes,
gLB.AvgPtesPerUser,
DefaultPtes ? "*" : ""));
// paged pool used and average consumption
if (gLB.BaselinePagedPool < SysPerfInfo.PagedPoolPages) {
gLB.PagedPoolUsed = SysPerfInfo.PagedPoolPages - gLB.BaselinePagedPool;
gLB.AvgPagedPoolPerUser = max(gLB.PagedPoolUsed / NumWinStations,
gLB.MinPagedPoolPerUser);
DefaultPagedPool = FALSE;
}
else {
gLB.PagedPoolUsed = 0;
gLB.AvgPagedPoolPerUser = gLB.MinPagedPoolPerUser;
gLB.BaselinePagedPool = SysPerfInfo.PagedPoolPages;
DefaultPagedPool = TRUE;
}
TRACE((hTrace, TC_LOAD, TT_API1,
" PagedPool: Base [%6ld], Used [%6ld], Avail: [%6ld], AvgPerUser: [%6ld]%s\n",
gLB.BaselinePagedPool,
gLB.PagedPoolUsed,
SysPerfInfo.AvailablePagedPoolPages,
gLB.AvgPagedPoolPerUser,
DefaultPagedPool ? "*" : ""));
TRACE((hTrace, TC_LOAD, TT_API1,
" Session Raw: Commit [%4ld], Pte [%4ld], Paged [%4ld]\n",
CommitAvailable / gLB.AvgCommitPerUser,
SysPerfInfo.FreeSystemPtes / gLB.AvgPtesPerUser,
SysPerfInfo.AvailablePagedPoolPages / gLB.AvgPagedPoolPerUser));
// Sum up individual CPU usage
for (i = 0; i < gLB.NumProcessors; i++) {
IdleCPU.QuadPart += ProcessorInfo[i].IdleTime.QuadPart;
TotalCPU.QuadPart += ProcessorInfo[i].KernelTime.QuadPart +
ProcessorInfo[i].UserTime.QuadPart;
}
// Determine CPU deltas for this period
IdleCPUDelta.QuadPart = IdleCPU.QuadPart - gLB.IdleCPU.QuadPart;
TotalCPUDelta.QuadPart = TotalCPU.QuadPart - gLB.TotalCPU.QuadPart;
gLB.IdleCPU.QuadPart = IdleCPU.QuadPart;
gLB.TotalCPU.QuadPart = TotalCPU.QuadPart;
// Determine what portion of 255 units we are idle
AvgIdleCPU = (ULONG) (TotalCPUDelta.QuadPart ?
((IdleCPUDelta.QuadPart << 8) / TotalCPUDelta.QuadPart)
: 0);
//
// Exponential smoothing:
// gLB.AvgIdleCPU = (ULONG) (alpha * gLB.AvgIdleCPU + (1 - alpha) * AvgIdleCPU)
//
// When Alpha = 0.75, the equation simplifies to the following:
//
gLB.AvgIdleCPU = (3 * gLB.AvgIdleCPU + AvgIdleCPU) >> 2 ;
// Based on current smoothed CPU usage, calculate how much a session uses
// on average and extrapolate to max CPU constrained sessions.
AvgBusyCPU = 255 - gLB.AvgIdleCPU;
if ((AvgBusyCPU > 0) && (AvgBusyCPU <= 255))
CPUConstrainedSessions = (NumWinStations << 8) / AvgBusyCPU;
else
CPUConstrainedSessions = 0xFFFFFFFF;
// Now flip it to remaining CPU constrained sessions. We never let this
// number hit zero since it doesn't mean session creation will fail.
if (CPUConstrainedSessions > NumWinStations)
CPUConstrainedSessions -= NumWinStations;
else
CPUConstrainedSessions = 1;
// Bias the averages a bit to account for growth in the existing sessions
gLB.AvgCommitPerUser += (ULONG) (gLB.AvgCommitPerUser >> SimGrowthBias);
gLB.AvgPtesPerUser += (ULONG) (gLB.AvgPtesPerUser >> SimGrowthBias);
gLB.AvgPagedPoolPerUser += (ULONG) (gLB.AvgPagedPoolPerUser >> SimGrowthBias);
TRACE((hTrace, TC_LOAD, TT_API1,
" Session Avg: Commit [%4ld], Pte [%4ld], Paged [%4ld]\n",
CommitAvailable / gLB.AvgCommitPerUser,
SysPerfInfo.FreeSystemPtes / gLB.AvgPtesPerUser,
SysPerfInfo.AvailablePagedPoolPages / gLB.AvgPagedPoolPerUser));
TRACE((hTrace, TC_LOAD, TT_API1,
" CPU Idle: Current [%4ld], Avg [%4ld], Est [%4ld]\n",
(AvgIdleCPU * 100) / 255,
(gLB.AvgIdleCPU * 100) / 255,
CPUConstrainedSessions));
//
// Find the most constrained resource! Failure on any one of these
// items means we will not be likely to start a session.
//
// Commit Constraint (TODO: needs refinement, doesn't consider paging
RemainingSessions = CommitAvailable / gLB.AvgCommitPerUser ;
LoadFactor = AvailablePagesConstraint;
pLIData->reserved[AvailablePagesConstraint] = RemainingSessions;
// Free System PTEs Constraint
MinSessions = SysPerfInfo.FreeSystemPtes / gLB.AvgPtesPerUser;
if (MinSessions < RemainingSessions) {
RemainingSessions = MinSessions;
LoadFactor = SystemPtesConstraint;
}
pLIData->reserved[SystemPtesConstraint] = MinSessions;
// Paged Pool Constraint
MinSessions = SysPerfInfo.AvailablePagedPoolPages / gLB.AvgPagedPoolPerUser;
if (MinSessions < RemainingSessions) {
RemainingSessions = MinSessions;
LoadFactor = PagedPoolConstraint;
}
pLIData->reserved[PagedPoolConstraint] = MinSessions;
gLB.RemainingSessions = RemainingSessions;
//
// Add in constraints that are good indicators of application performance.
// We will likely create a session if these resources are low, but the
// user experience will suffer.
// CPU Contraint
if (CPUConstrainedSessions < RemainingSessions) {
LoadFactor = CPUConstraint;
RemainingSessions = CPUConstrainedSessions;
}
pLIData->reserved[CPUConstraint] = MinSessions;
gLB.EstimatedSessions = RemainingSessions;
TRACE((hTrace, TC_LOAD, TT_API1,
"Remaining Sessions: Raw: [%4ld], Est: [%4ld], Factor = %s, Commit = %ld\n\n",
gLB.RemainingSessions, gLB.EstimatedSessions,
LoadFactor == AvailablePagesConstraint ? "Available Memory" :
(LoadFactor == SystemPtesConstraint ? "SystemPtes" :
(LoadFactor == PagedPoolConstraint ? "PagedPool" :
(LoadFactor == CPUConstraint ? "CPU" :
"Unknown!"))), SysPerfInfo.CommittedPages
));
//
// Return data to caller
//
pLIData->RemainingSessionCapacity = gLB.EstimatedSessions;
pLIData->RawSessionCapacity = gLB.RemainingSessions;
pLIData->LoadFactor = LoadFactor;
pLIData->TotalSessions = NumWinStations;
pLIData->DisconnectedSessions = WinStationDiscCount;
// Had to split this up for WIN64 alignment issues
pLIData->IdleCPU.HighPart = IdleCPUDelta.HighPart;
pLIData->IdleCPU.LowPart = IdleCPUDelta.LowPart;
pLIData->TotalCPU.HighPart = TotalCPUDelta.HighPart;
pLIData->TotalCPU.LowPart = TotalCPUDelta.LowPart;
}
// The load metrics failed to intialize! Set the capacity sky high to still
// allow access to the server.
else {
RemainingSessions = 0xFFFFFFFF;
pLIData->RemainingSessionCapacity = RemainingSessions;
pLIData->RawSessionCapacity = RemainingSessions;
pLIData->LoadFactor = ErrorConstraint;
pLIData->TotalSessions = NumWinStations;
pLIData->DisconnectedSessions = WinStationDiscCount;
// Had to split this up for WIN64 alignment issues
pLIData->IdleCPU.HighPart = 0;
pLIData->IdleCPU.LowPart = 99;
pLIData->TotalCPU.HighPart = 0;
pLIData->TotalCPU.LowPart = 100;
TRACE((hTrace, TC_LOAD, TT_ERROR,
"GetLoadMetrics failed: init [%ld], Proc [%lx], Perf [%lx], Basic [%lx]!\n",
gLB.fInitialized, StatusProc, StatusPerf, StatusBasic));
}
return RemainingSessions;
}
/*******************************************************************************
* xxxWinStationQueryInformation
*
* Query window station information (worker routine)
*
* ENTRY:
* LogonId (input)
* Session ID corresponding to the session.
* WinStationInformationClass (input)
* Specifies the type of information to get from the specified window
* station object.
* pWinStationInformation (output)
* A pointer to a buffer that contains information to get for the
* specified window station. The format and contents of the buffer
* depend on the specified information class being set.
* WinStationInformationLength (input)
* Specifies the length in bytes of the window station information
* buffer.
* pReturnLength (output)
* Specifies the amount returned in the buffer
******************************************************************************/
NTSTATUS xxxWinStationQueryInformation(
ULONG LogonId,
WINSTATIONINFOCLASS WinStationInformationClass,
PVOID pWinStationInformation,
ULONG WinStationInformationLength,
PULONG pReturnLength)
{
NTSTATUS Status = STATUS_SUCCESS;
HINSTANCE hInstance;
PWINSTATION pWinStation = NULL;
ULONG cbReturned;
ICA_STACK_LAST_INPUT_TIME Ica_Stack_Last_Input_Time;
WINSTATION_APIMSG WMsg;
PWINSTATIONVIDEODATA pVideoData;
HANDLE hVirtual;
ULONG i;
*pReturnLength = 0;
TRACE((hTrace,TC_ICASRV,TT_API2,"TERMSRV: WinStationQueryInformation LogonId=%d, Class=%d\n",
LogonId, (ULONG)WinStationInformationClass));
/*
* Find the WinStation
* Return error if not found or currently terminating.
*/
pWinStation = FindWinStationById( LogonId, FALSE );
if (pWinStation == NULL)
return STATUS_CTX_WINSTATION_NOT_FOUND;
if (pWinStation->Terminating) {
ReleaseWinStation(pWinStation);
return STATUS_CTX_CLOSE_PENDING;
}
/*
* Verify that client has QUERY access
*/
Status = RpcCheckClientAccess(pWinStation, WINSTATION_QUERY, FALSE);
if (!NT_SUCCESS(Status)) {
ReleaseWinStation(pWinStation);
return Status;
}
switch ( WinStationInformationClass ) {
case WinStationLoadIndicator:
{
PWINSTATIONLOADINDICATORDATA pLIData =
(PWINSTATIONLOADINDICATORDATA) pWinStationInformation;
if (WinStationInformationLength >= sizeof(WINSTATIONLOADINDICATORDATA)) {
GetLoadMetrics(pLIData);
*pReturnLength = sizeof(WINSTATIONLOADINDICATORDATA);
}
else {
Status = STATUS_BUFFER_TOO_SMALL;
}
}
break;
case WinStationInformation:
{
if (!ValidWireBuffer(WinStationInformationClass,
pWinStationInformation,
WinStationInformationLength))
{
Status = STATUS_INVALID_USER_BUFFER;
}
else
{
WINSTATIONINFORMATION *pInfo;
PROTOCOLSTATUS *pIca_Stack_Query_Status;
pInfo = MemAlloc( sizeof( WINSTATIONINFORMATION ) ) ;
if ( pInfo )
{
pIca_Stack_Query_Status = MemAlloc( sizeof( PROTOCOLSTATUS ) );
if ( pIca_Stack_Query_Status )
{
TCHAR *szUserName = NULL, *szDomainName = NULL;
DWORD dwUserSize = MAX_PATH, dwDomainSize = MAX_PATH;
SID_NAME_USE TypeOfAccount;
BOOL LookupResult;
memset( pInfo, 0, sizeof( PWINSTATIONINFORMATION ) );
wcscpy( pInfo->WinStationName, pWinStation->WinStationName );
memcpy( pInfo->Domain, pWinStation->Domain, sizeof( pInfo->Domain ) );
memcpy( pInfo->UserName, pWinStation->UserName, sizeof( pInfo->UserName ) );
// Since the Username stored maybe stale, query the Username again
// Intentionally we do not fail if we are not able to allocate szUserName and szDomainName
// This is because we can send the cached credentials in that case
szUserName = MemAlloc(MAX_PATH);
if ( szUserName ) {
szDomainName = MemAlloc(MAX_PATH);
if ( szDomainName ) {
LookupResult = LookupAccountSid(NULL,
pWinStation->pUserSid,
szUserName,
&dwUserSize,
szDomainName,
&dwDomainSize,
&TypeOfAccount);
if (LookupResult) {
// Re-copy and update WINSTATION struct if the Username or Domain has changed
if ( (szUserName) && (lstrcmpi(pWinStation->UserName, szUserName)) ) {
memcpy( pInfo->UserName, szUserName, sizeof(pInfo->UserName) );
memcpy( pWinStation->UserName, szUserName, sizeof(pWinStation->UserName) );
}
if ( (szDomainName) && (lstrcmpi(pWinStation->Domain, szDomainName)) ) {
memcpy( pInfo->Domain, szDomainName, sizeof(pInfo->Domain) );
memcpy( pWinStation->Domain, szDomainName, sizeof(pWinStation->Domain) );
}
}
}
}
if (szUserName != NULL) {
MemFree(szUserName);
}
if (szDomainName != NULL) {
MemFree(szDomainName);
}
pInfo->ConnectState = pWinStation->State;
pInfo->LogonId = pWinStation->LogonId;
pInfo->ConnectTime = pWinStation->ConnectTime;
pInfo->DisconnectTime = pWinStation->DisconnectTime;
pInfo->LogonTime = pWinStation->LogonTime;
if ( pWinStation->hStack && !pWinStation->fOwnsConsoleTerminal ) {
// Check for availability
if ( pWinStation->pWsx &&
pWinStation->pWsx->pWsxIcaStackIoControl ) {
Status = pWinStation->pWsx->pWsxIcaStackIoControl(
pWinStation->pWsxContext,
pWinStation->hIca,
pWinStation->hStack,
IOCTL_ICA_STACK_QUERY_LAST_INPUT_TIME,
NULL,
0,
&Ica_Stack_Last_Input_Time,
sizeof( Ica_Stack_Last_Input_Time ),
&cbReturned );
if ( !NT_SUCCESS( Status ) )
{
MemFree( pInfo );
MemFree( pIca_Stack_Query_Status );
break;
}
pInfo->LastInputTime = Ica_Stack_Last_Input_Time.LastInputTime;
}
// Check for availability
if ( pWinStation->pWsx &&
pWinStation->pWsx->pWsxIcaStackIoControl ) {
Status = pWinStation->pWsx->pWsxIcaStackIoControl(
pWinStation->pWsxContext,
pWinStation->hIca,
pWinStation->hStack,
IOCTL_ICA_STACK_QUERY_STATUS,
NULL,
0,
pIca_Stack_Query_Status,
sizeof( PROTOCOLSTATUS ),
&cbReturned );
if ( !NT_SUCCESS( Status ) )
{
MemFree( pInfo );
MemFree( pIca_Stack_Query_Status );
break;
}
pInfo->Status = *pIca_Stack_Query_Status;
}
/*
* The thinwire cache data is down in WIN32
*/
if ( pWinStation->pWin32Context ) {
WMsg.ApiNumber = SMWinStationThinwireStats;
Status = SendWinStationCommand( pWinStation, &WMsg, gbServer?5:1 );
if ( Status == STATUS_SUCCESS ) {
pInfo->Status.Cache = WMsg.u.ThinwireStats.Stats;
pWinStation->Cache = WMsg.u.ThinwireStats.Stats;
} else {
pInfo->Status.Cache = pWinStation->Cache;
}
Status = STATUS_SUCCESS; // ignore errors getting TW stats
}
} else {
/*
* This makes winadmin Idle time happy.
*/
(VOID) NtQuerySystemTime( &(pInfo->LastInputTime) );
}
(VOID) NtQuerySystemTime( &pInfo->CurrentTime );
CopyInWireBuf(WinStationInformationClass,
(PVOID)pInfo,
pWinStationInformation);
*pReturnLength = WinStationInformationLength;
MemFree( pIca_Stack_Query_Status );
}
else
{
Status = STATUS_NO_MEMORY;
}
MemFree(pInfo);
}
else
{
Status = STATUS_NO_MEMORY;
}
}
}
break;
case WinStationConfiguration:
if (!ValidWireBuffer(WinStationInformationClass,
pWinStationInformation,
WinStationInformationLength)) {
Status = STATUS_INVALID_USER_BUFFER;
break;
}
CopyInWireBuf(WinStationInformationClass,
(PVOID)&pWinStation->Config.Config,
pWinStationInformation);
if (RpcCheckSystemClientEx( pWinStation ) != STATUS_SUCCESS) {
PWINSTACONFIGWIREW p = pWinStationInformation;
PUSERCONFIGW u = (PUSERCONFIGW)((PCHAR)p + p->UserConfig.Offset);
RtlSecureZeroMemory( &u->Password, sizeof(u->Password) );
}
*pReturnLength = WinStationInformationLength;
break;
case WinStationWd:
if (!ValidWireBuffer(WinStationInformationClass,
pWinStationInformation,
WinStationInformationLength)){
Status = STATUS_INVALID_USER_BUFFER;
break;
}
CopyInWireBuf(WinStationInformationClass,
(PVOID)&pWinStation->Config.Wd,
pWinStationInformation);
*pReturnLength = WinStationInformationLength;
break;
case WinStationPd:
if (!ValidWireBuffer(WinStationInformationClass,
pWinStationInformation,
WinStationInformationLength)){
Status = STATUS_INVALID_USER_BUFFER;
break;
}
CopyInWireBuf(WinStationInformationClass,
(PVOID)&pWinStation->Config.Pd[0],
pWinStationInformation);
*pReturnLength = WinStationInformationLength;
break;
case WinStationCd:
if ( WinStationInformationLength > sizeof(CDCONFIG) )
WinStationInformationLength = sizeof(CDCONFIG);
memcpy( pWinStationInformation,
&pWinStation->Config.Cd,
WinStationInformationLength );
*pReturnLength = WinStationInformationLength;
break;
case WinStationPdParams:
{
if (!ValidWireBuffer(WinStationInformationClass,
pWinStationInformation,
WinStationInformationLength)){
Status = STATUS_INVALID_USER_BUFFER;
break;
}
else
{
PDPARAMS *pPdParams;
pPdParams = MemAlloc( sizeof( PDPARAMS ) );
if (pPdParams)
{
CopyOutWireBuf(WinStationInformationClass,
(PVOID) pPdParams,
pWinStationInformation);
/*
* Based on PDClass, this can query any PD
*/
if ( pWinStation->hStack &&
pWinStation->pWsx &&
pWinStation->pWsx->pWsxIcaStackIoControl ) {
Status = pWinStation->pWsx->pWsxIcaStackIoControl(
pWinStation->pWsxContext,
pWinStation->hIca,
pWinStation->hStack,
IOCTL_ICA_STACK_QUERY_PARAMS,
pPdParams,
sizeof(PDPARAMS ),
pPdParams,
sizeof( PDPARAMS ),
pReturnLength );
/*
* If we get an error in the idle/disconnected state,
* or if this is a session on the local console.
* then just clear the return buffer and return success.
*/
if ( !NT_SUCCESS( Status ) ) {
if ((pWinStation->fOwnsConsoleTerminal) ||
(pWinStation->State != State_Active &&
pWinStation->State != State_Connected )) {
memset(pPdParams, 0, sizeof(PDPARAMS));
*pReturnLength = WinStationInformationLength;
Status = STATUS_SUCCESS;
}
}
} else {
memset( (PVOID)pPdParams, 0, sizeof(PDPARAMS) );
*pReturnLength = WinStationInformationLength;
Status = STATUS_SUCCESS;
}
if (NT_SUCCESS(Status)) {
CopyInWireBuf(WinStationInformationClass,
(PVOID)pPdParams,
pWinStationInformation);
}
*pReturnLength = WinStationInformationLength;
MemFree( pPdParams );
}
else
{
Status = STATUS_NO_MEMORY;
}
}
}
break;
case WinStationClient:
if (!ValidWireBuffer(WinStationInformationClass,
pWinStationInformation,
WinStationInformationLength)){
Status = STATUS_INVALID_USER_BUFFER;
break;
}
CopyInWireBuf(WinStationInformationClass,
(PVOID)&pWinStation->Client,
pWinStationInformation);
// if caller is not allow to see it, then scrub the password
if ( !IsCallerAllowedPasswordAccess() ) {
PWINSTATIONCLIENT pWSClient = (PWINSTATIONCLIENT)pWinStationInformation;
PBYTE pStart;
PBYTE pEnd;
ULONG ulMaxToScrub;
pEnd = (PBYTE) ( pWinStationInformation ) + WinStationInformationLength;
if ((ULONG_PTR) pEnd > (ULONG_PTR)pWSClient->Password) {
ulMaxToScrub = (ULONG)((ULONG_PTR) pEnd - (ULONG_PTR)pWSClient->Password);
if (ulMaxToScrub > sizeof(pWSClient->Password))
ulMaxToScrub = sizeof(pWSClient->Password);
memset(pWSClient->Password, 0,ulMaxToScrub);
}
}
*pReturnLength = WinStationInformationLength;
break;
case WinStationModules:
// Check for availability
if (pWinStation->hStack &&
pWinStation->pWsx &&
pWinStation->pWsx->pWsxIcaStackIoControl) {
ULONG b = (ULONG) IsCallerAllowedPasswordAccess();
Status = pWinStation->pWsx->pWsxIcaStackIoControl(
pWinStation->pWsxContext,
pWinStation->hIca,
pWinStation->hStack,
IOCTL_ICA_STACK_QUERY_MODULE_DATA,
(PVOID) &b,
sizeof(b),
pWinStationInformation,
WinStationInformationLength,
pReturnLength );
} else {
memset( pWinStationInformation, 0, WinStationInformationLength );
Status = STATUS_SUCCESS;
}
break;
case WinStationCreateData:
if ( WinStationInformationLength > sizeof(WINSTATIONCREATE) )
WinStationInformationLength = sizeof(WINSTATIONCREATE);
memcpy( pWinStationInformation,
&pWinStation->Config.Create,
WinStationInformationLength );
*pReturnLength = WinStationInformationLength;
break;
case WinStationPrinter:
Status = STATUS_INVALID_DEVICE_REQUEST;
break;
case WinStationUserToken:
if ( WinStationInformationLength < sizeof(WINSTATIONUSERTOKEN) ) {
Status = STATUS_BUFFER_TOO_SMALL;
break;
}
/*
* Check it for WINSTATION_ALL_ACCESS. This will generate an
* access audit if on.
*/
Status = RpcCheckClientAccess( pWinStation, WINSTATION_ALL_ACCESS, FALSE );
if ( !NT_SUCCESS( Status ) ) {
break;
}
//
// Make sure only system mode callers can get this token.
//
// A Token is a very dangerous thing to allow someone to
// get a hold of, since they can create processes that
// have the tokens subject context.
//
Status = RpcCheckSystemClientNoLogonId( pWinStation );
if (!NT_SUCCESS(Status)) {
break;
}
Status = xxxGetUserToken(
pWinStation,
(WINSTATIONUSERTOKEN UNALIGNED *)pWinStationInformation,
WinStationInformationLength
);
*pReturnLength = sizeof(WINSTATIONUSERTOKEN);
break;
case WinStationVideoData:
if ( !pWinStation->LogonId || !pWinStation->hStack ) {
Status = STATUS_PROCEDURE_NOT_FOUND;
break;
}
if ( WinStationInformationLength < sizeof(WINSTATIONVIDEODATA) ) {
Status = STATUS_BUFFER_TOO_SMALL;
break;
}
pVideoData = (PWINSTATIONVIDEODATA) pWinStationInformation;
pVideoData->HResolution = pWinStation->Client.HRes;
pVideoData->VResolution = pWinStation->Client.VRes;
pVideoData->fColorDepth = pWinStation->Client.ColorDepth;
*pReturnLength = sizeof(WINSTATIONVIDEODATA);
break;
case WinStationVirtualData:
if ( !pWinStation->hStack ) {
Status = STATUS_INVALID_DEVICE_REQUEST;
break;
}
if ( WinStationInformationLength < sizeof(VIRTUALCHANNELNAME) ) {
Status = STATUS_BUFFER_TOO_SMALL;
break;
}
/*
* Open virtual channel handle
*/
Status = IcaChannelOpen( pWinStation->hIca,
Channel_Virtual,
pWinStationInformation,
&hVirtual );
if ( !NT_SUCCESS( Status ) )
break;
/*
* Query client virtual channel data
*/
Status = IcaChannelIoControl( hVirtual,
IOCTL_ICA_VIRTUAL_QUERY_MODULE_DATA,
NULL,
0,
pWinStationInformation,
WinStationInformationLength,
pReturnLength );
/*
* Close virtual channel
*/
IcaChannelClose(hVirtual);
break;
case WinStationLoadBalanceSessionTarget:
// This query requests the target session ID for a
// client redirected from another server in a load balancing
// cluster. Returns -1 for no redirection. This call is
// normally made only by WinLogon.
if ( WinStationInformationLength < sizeof(ULONG) ) {
Status = STATUS_BUFFER_TOO_SMALL;
break;
}
if (WinStationInformationLength > sizeof(ULONG))
WinStationInformationLength = sizeof(ULONG);
if (!pWinStation->bRequestedSessionIDFieldValid)
*((ULONG *)pWinStationInformation) = (ULONG)-1;
else
*((ULONG *)pWinStationInformation) =
pWinStation->RequestedSessionID;
*pReturnLength = WinStationInformationLength;
break;
case WinStationShadowInfo:
{
PWINSTATIONSHADOW pWinstationShadow;
if (WinStationInformationLength >= sizeof(WINSTATIONSHADOW)) {
pWinstationShadow = (PWINSTATIONSHADOW) pWinStationInformation;
if ( pWinStation->State == State_Shadow ) {
// The current state is Shadow so it's a viewer
pWinstationShadow->ShadowState = State_Shadowing;
} else if ( pWinStation->State == State_Active &&
!IsListEmpty(&pWinStation->ShadowHead) ) {
// Active and being shadowed
pWinstationShadow->ShadowState = State_Shadowed;
} else {
pWinstationShadow->ShadowState = State_NoShadow;
}
pWinstationShadow->ShadowClass = pWinStation->Config.Config.User.Shadow;
pWinstationShadow->SessionId = LogonId;
pWinstationShadow->ProtocolType = pWinStation->Client.ProtocolType;
*pReturnLength = sizeof(WINSTATIONSHADOW);
}
else {
Status = STATUS_BUFFER_TOO_SMALL;
}
}
break;
case WinStationDigProductId:
{
PWINSTATIONPRODID pWinStationProdId;
if ( WinStationInformationLength >= sizeof(WINSTATIONPRODID) )
{
pWinStationProdId = (PWINSTATIONPRODID)pWinStationInformation;
memcpy( pWinStationProdId->DigProductId, g_DigProductId, sizeof( g_DigProductId ));
memcpy( pWinStationProdId->ClientDigProductId, pWinStation->Client.clientDigProductId, sizeof( pWinStation->Client.clientDigProductId ));
pWinStationProdId->curentSessionId = pWinStation->LogonId;
pWinStationProdId->ClientSessionId = pWinStation->Client.ClientSessionId;
*pReturnLength = WinStationInformationLength;
}
else
{
Status = STATUS_BUFFER_TOO_SMALL;
}
break;
}
case WinStationLockedState:
{
BOOL bLockedState;
if ( pWinStationInformation && (WinStationInformationLength >= sizeof(bLockedState)))
{
Status = GetLockedState(pWinStation, &bLockedState);
*(LPBOOL)pWinStationInformation = bLockedState;
*pReturnLength = sizeof(bLockedState);
}
else
{
Status = STATUS_BUFFER_TOO_SMALL;
}
break;
}
case WinStationRemoteAddress:
{
PWINSTATIONREMOTEADDRESS pRemoteAddress = (PWINSTATIONREMOTEADDRESS) pWinStationInformation;
if( WinStationInformationLength >= sizeof(WINSTATIONREMOTEADDRESS) )
{
Status = xxxQueryRemoteAddress( pWinStation, pRemoteAddress );
}
else
{
*pReturnLength = sizeof(WINSTATIONREMOTEADDRESS);
Status = STATUS_BUFFER_TOO_SMALL;
}
break;
}
case WinStationIdleTime:
{ // Return the idle time for the winstation.
LASTINPUTINFO LastInputInfo;
ULONG Now;
// Check on validity of the parameters.
if ( (pWinStationInformation) && (WinStationInformationLength >= sizeof(ULONG)) ) {
// Get last input info on this winstation.
LastInputInfo.cbSize = sizeof(LASTINPUTINFO);
if (!GetLastInputInfo(&LastInputInfo)) {
// Failed. Set the output to 0.
*((ULONG *)pWinStationInformation) = 0;
}
else {
// Find out how much time has passed since the system was booted.
Now = GetTickCount();
// The current time may be less than the last input time due to 49.71 days wrap-around.
if (Now < LastInputInfo.dwTime) {
// If this is the case, we really don't know whether session was idle for more than
// 49 days or the last input time for the session is close to MAX_LONG and the wrap-
// around occurred. Better to report lesser time here.
*((ULONG *)pWinStationInformation) = MAXULONG - LastInputInfo.dwTime + Now;
}
else {
// If current time is greater, then the idle time just the difference between the
// current time and the last input time.
*((ULONG *)pWinStationInformation) = Now - LastInputInfo.dwTime;
}
}
*pReturnLength = sizeof(ULONG);
}
else
{
Status = STATUS_BUFFER_TOO_SMALL;
}
break;
}
case WinStationLastReconnectType:
{
if ( pWinStationInformation && (WinStationInformationLength >= sizeof(ULONG)))
{
*((ULONG *)pWinStationInformation) = pWinStation->LastReconnectType;
*pReturnLength = sizeof(ULONG);
}
else
{
Status = STATUS_BUFFER_TOO_SMALL;
}
break;
}
case WinStationMprNotifyInfo:
{
pExtendedClientCredentials pMprNotifyInfo;
// Only System can query this information
Status = _CheckCallerLocalAndSystem();
if (Status != STATUS_SUCCESS) {
break;
}
if (WinStationInformationLength >= sizeof(ExtendedClientCredentials)) {
pMprNotifyInfo = (pExtendedClientCredentials) pWinStationInformation;
*pMprNotifyInfo = g_MprNotifyInfo;
*pReturnLength = sizeof(ExtendedClientCredentials);
// Erase the sensitive information now since its no longer needed in TermSrv
RtlSecureZeroMemory( g_MprNotifyInfo.Domain, wcslen(g_MprNotifyInfo.Domain) * sizeof(WCHAR) );
RtlSecureZeroMemory( g_MprNotifyInfo.UserName, wcslen(g_MprNotifyInfo.UserName) * sizeof(WCHAR) );
RtlSecureZeroMemory( g_MprNotifyInfo.Password, wcslen(g_MprNotifyInfo.Password) * sizeof(WCHAR) );
} else {
Status = STATUS_BUFFER_TOO_SMALL;
}
}
break;
case WinStationExecSrvSystemPipe:
{
if ( pWinStationInformation && (WinStationInformationLength >= EXECSRVPIPENAMELEN*sizeof(WCHAR) ) )
{
memcpy( pWinStationInformation, &pWinStation->ExecSrvSystemPipe, WinStationInformationLength );
}
else
{
Status = STATUS_BUFFER_TOO_SMALL;
}
break;
}
case WinStationSDRedirectedSmartCardLogon:
{
// Only System can query this information
Status = _CheckCallerLocalAndSystem();
if (Status != STATUS_SUCCESS) {
break;
}
if ( pWinStationInformation && (WinStationInformationLength >= sizeof(BOOLEAN)))
{
*((ULONG *)pWinStationInformation) = pWinStation->fSDRedirectedSmartCardLogon;
*pReturnLength = sizeof(BOOLEAN);
// Reset the flag here
pWinStation->fSDRedirectedSmartCardLogon = FALSE;
}
else
{
Status = STATUS_BUFFER_TOO_SMALL;
}
break;
}
case WinStationIsAdminLoggedOn:
{
// Only System can query this information
Status = _CheckCallerLocalAndSystem();
if (Status != STATUS_SUCCESS) {
break;
}
if ( pWinStationInformation && (WinStationInformationLength >= sizeof(BOOLEAN)))
{
*((ULONG *)pWinStationInformation) = pWinStation->fUserIsAdmin;
*pReturnLength = sizeof(BOOLEAN);
}
else
{
Status = STATUS_BUFFER_TOO_SMALL;
}
break;
}
default:
/*
* Fail the call
*/
Status = STATUS_INVALID_INFO_CLASS;
break;
}
ReleaseWinStation(pWinStation);
TRACE((hTrace,TC_ICASRV,TT_API2,"TERMSRV: WinStationQueryInformation "
"LogonId=%d, Class=%d, Status=0x%x\n",
LogonId, (ULONG)WinStationInformationClass, Status));
return Status;
}
/*****************************************************************************
* xxxGetUserToken
*
* Duplicate the users token into the process space of the caller
* if they are an admin.
*
* ENTRY:
* p (input/output)
* Argument buffer
*
* Length (input)
* Size of argument buffer
****************************************************************************/
NTSTATUS xxxGetUserToken(
PWINSTATION pWinStation,
WINSTATIONUSERTOKEN UNALIGNED *p,
ULONG Size)
{
NTSTATUS Status;
HANDLE RemoteToken;
HANDLE RemoteProcess = NULL;
CLIENT_ID ClientId;
OBJECT_ATTRIBUTES ObjA;
// Determine if the caller is an admin
//
// If the token is not NULL, duplicate it into the callers
// process space.
//
if (pWinStation->UserToken == NULL) {
return STATUS_NO_TOKEN;
}
InitializeObjectAttributes(&ObjA, NULL, 0, NULL, NULL);
ClientId.UniqueProcess = p->ProcessId;
ClientId.UniqueThread = p->ThreadId;
Status = NtOpenProcess(
&RemoteProcess,
PROCESS_ALL_ACCESS,
&ObjA,
&ClientId);
if (!NT_SUCCESS(Status)) {
TRACE((hTrace,TC_ICASRV,TT_ERROR,"TermSrv GETTOKEN: Error 0x%x "
"opening remote process %d\n", Status,p->ProcessId));
return Status;
}
Status = NtDuplicateObject(
NtCurrentProcess(),
pWinStation->UserToken,
RemoteProcess,
&RemoteToken,
0,
0,
DUPLICATE_SAME_ACCESS);
if (!NT_SUCCESS(Status)) {
TRACE((hTrace,TC_ICASRV,TT_ERROR, "TermSrv GETTOKEN: Error 0x%x "
"duplicating UserToken\n", Status));
NtClose( RemoteProcess );
return Status;
}
p->UserToken = RemoteToken;
NtClose(RemoteProcess);
return STATUS_SUCCESS;
}