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