mirror of https://github.com/tongzx/nt5src
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
11203 lines
332 KiB
11203 lines
332 KiB
/****************************************************************************/
|
|
// wstrpc.c
|
|
//
|
|
// TermSrv API RPC server code.
|
|
//
|
|
// Copyright (C) 1997-2000 Microsoft Corporation
|
|
/****************************************************************************/
|
|
|
|
#include "precomp.h"
|
|
#pragma hdrstop
|
|
|
|
#include <winuserp.h>
|
|
|
|
#define SECURITY_WIN32
|
|
|
|
#include <stdlib.h>
|
|
#include <time.h>
|
|
#include <tchar.h>
|
|
#include <lmaccess.h>
|
|
#include <lmerr.h>
|
|
#include <allproc.h>
|
|
#include <winsta.h>
|
|
#include <rpc.h>
|
|
#include <vdmdbg.h>
|
|
#include <dsrole.h>
|
|
#include <security.h>
|
|
#include <ntsecapi.h>
|
|
#include <lmapibuf.h>
|
|
#include "..\rpcwire.h"
|
|
#define INITGUID
|
|
#include "objbase.h"
|
|
#include "initguid.h"
|
|
#include <netcfgx.h>
|
|
#include "devguid.h"
|
|
|
|
#include <malloc.h>
|
|
#include <dsgetdc.h>
|
|
#include <winsock2.h>
|
|
#include <tdi.h>
|
|
#include "tsremdsk.h"
|
|
|
|
/*
|
|
* Include the RPC generated common header
|
|
*/
|
|
#include "tsrpc.h"
|
|
|
|
#include "icaevent.h"
|
|
#include "sessdir.h"
|
|
|
|
#include "conntfy.h"
|
|
|
|
#define MIN_GAP_DATABASE_SIZE 100
|
|
|
|
#define MAX_IDS_LEN 256 // maximum length that the input parm can be
|
|
|
|
#define MAX_BUF 256
|
|
#define MAX_STRING_BYTES 512
|
|
|
|
//
|
|
// Winlogon defines
|
|
//
|
|
#define APPLICATION_NAME TEXT("Winlogon")
|
|
#define WINSTATIONS_DISABLED TEXT("WinStationsDisabled")
|
|
|
|
|
|
#ifdef NTSDDEBUG
|
|
#define NTSDDBGPRINT(x) DbgPrint x
|
|
#else
|
|
#define NTSDDBGPRINT(x)
|
|
#endif
|
|
|
|
//
|
|
// Value passed to RPC runtime libraries for the maximum number of
|
|
// cached threads it will keep around. We can actually service more RPC
|
|
// calls than this, but it will delete threads when they are done.
|
|
//
|
|
#define MAX_WINSTATION_RPC_THREADS 1000
|
|
|
|
#ifdef notdef
|
|
// This validates the user pointer to be within the View memory region
|
|
#define ISPOINTERVALID_SERVER(pContext, p, length) \
|
|
(((ULONG)(p) >= (ULONG)(pContext)->ViewBase) && \
|
|
((char *)(p) + (length)) < (char *)((ULONG)(pContext)->ViewBase+pContext->ViewSize))
|
|
#endif
|
|
|
|
|
|
//
|
|
// Extern funcs, these three are from acl.c
|
|
extern VOID CleanUpSD(PSECURITY_DESCRIPTOR);
|
|
extern BOOL IsCallerSystem( VOID );
|
|
extern BOOL IsCallerAdmin( VOID );
|
|
extern BOOLEAN WinStationCheckConsoleSession(VOID);
|
|
extern NTSTATUS CheckIdleWinstation(VOID);
|
|
|
|
extern NTSTATUS WaitForConsoleConnectWorker( PWINSTATION pWinStation );
|
|
|
|
extern BOOL IsClientOnSameMachine(PWINSTATION pWinStation);
|
|
|
|
extern WCHAR gpszServiceName[];
|
|
|
|
extern BOOL g_fAppCompat;
|
|
|
|
extern ULONG MaxOutStandingConnect;
|
|
extern ULONG NumOutStandingConnect;
|
|
extern HANDLE hConnectEvent;
|
|
|
|
|
|
// TermSrv counter values
|
|
extern DWORD g_TermSrvTotalSessions;
|
|
extern DWORD g_TermSrvReconSessions;
|
|
extern DWORD g_TermSrvDiscSessions;
|
|
extern PSID gSystemSid;
|
|
extern PSID gAdminSid;
|
|
|
|
extern HANDLE WinStationIdleControlEvent;
|
|
|
|
extern HANDLE ConsoleLogoffEvent;
|
|
extern RTL_CRITICAL_SECTION ConsoleLock;
|
|
extern ULONG gConsoleCreationDisable;
|
|
|
|
extern NTSTATUS
|
|
WinStationEnableSessionIo(
|
|
PWINSTATION pWinStation,
|
|
BOOL bEnable
|
|
);
|
|
|
|
extern NTSTATUS
|
|
_CheckShadowLoop(
|
|
IN ULONG ClientLogonId,
|
|
IN PWSTR pTargetServerName,
|
|
IN ULONG TargetLogonId
|
|
);
|
|
|
|
extern BOOL
|
|
Filter_RemoveOutstandingConnection(
|
|
IN PBYTE pin_addr,
|
|
IN UINT uAddrSize
|
|
);
|
|
|
|
|
|
typedef struct _SID_CACHE_LIST_ENTRY {
|
|
LIST_ENTRY ListEntry;
|
|
HANDLE ProcId;
|
|
LARGE_INTEGER CreateTime;
|
|
PSID pSid;
|
|
} SID_CACHE_LIST_ENTRY, *PSID_CACHE_LIST_ENTRY;
|
|
|
|
#define MAX_SID_CACHE_ENTRIES 4000
|
|
ULONG gMaxSidCacheEntries = 0;
|
|
|
|
#define REG_GUID_TABLE REG_CONTROL_TSERVER L"\\lanatable\\"
|
|
#define LANA_ID L"LanaId"
|
|
|
|
|
|
/*=============================================================================
|
|
== Functions Defined
|
|
=============================================================================*/
|
|
VOID NotifySystemEvent( ULONG );
|
|
VOID CheckSidCacheSize();
|
|
BOOL IsValidLoopBack(PWINSTATION, ULONG, ULONG);
|
|
|
|
|
|
/*=============================================================================
|
|
== External Functions used
|
|
=============================================================================*/
|
|
|
|
NTSTATUS WinStationEnumerateWorker( PULONG, PLOGONID, PULONG, PULONG );
|
|
NTSTATUS WinStationRenameWorker( PWINSTATIONNAME, ULONG, PWINSTATIONNAME, ULONG );
|
|
NTSTATUS xxxWinStationQueryInformation( ULONG, ULONG, PVOID, ULONG, PULONG );
|
|
NTSTATUS xxxWinStationSetInformation( ULONG, WINSTATIONINFOCLASS, PVOID, ULONG );
|
|
NTSTATUS LogonIdFromWinStationNameWorker( PWINSTATIONNAME, ULONG, PULONG );
|
|
NTSTATUS IcaWinStationNameFromLogonId( ULONG, PWINSTATIONNAME );
|
|
NTSTATUS WaitForConnectWorker( PWINSTATION pWinStation, HANDLE ClientProcessId );
|
|
DWORD xxxWinStationGenerateLicense( PWCHAR, ULONG, PCHAR, ULONG );
|
|
DWORD xxxWinStationInstallLicense( PCHAR, ULONG );
|
|
DWORD xxxWinStationEnumerateLicenses( PULONG, PULONG, PCHAR, PULONG );
|
|
DWORD xxxWinStationActivateLicense( PCHAR, ULONG, PWCHAR, ULONG );
|
|
DWORD xxxWinStationRemoveLicense( PCHAR, ULONG );
|
|
DWORD xxxWinStationSetPoolCount( PCHAR, ULONG );
|
|
DWORD xxxWinStationQueryUpdateRequired( PULONG );
|
|
NTSTATUS WinStationShadowWorker( ULONG, PWSTR, ULONG, BYTE, USHORT );
|
|
NTSTATUS WinStationShadowTargetSetupWorker( ULONG );
|
|
NTSTATUS WinStationShadowTargetWorker( BOOLEAN, BOOL, ULONG, PWINSTATIONCONFIG2, PICA_STACK_ADDRESS,
|
|
PVOID, ULONG, PVOID, ULONG, PVOID );
|
|
NTSTATUS WinStationStopAllShadows( PWINSTATION );
|
|
|
|
VOID WinStationTerminate( PWINSTATION );
|
|
VOID WinStationDeleteWorker( PWINSTATION );
|
|
NTSTATUS WinStationDoDisconnect( PWINSTATION, PRECONNECT_INFO, BOOLEAN );
|
|
NTSTATUS WinStationDoReconnect( PWINSTATION, PRECONNECT_INFO );
|
|
VOID CleanupReconnect( PRECONNECT_INFO );
|
|
NTSTATUS ShutdownLogoff( ULONG, ULONG );
|
|
|
|
NTSTATUS SelfRelativeToAbsoluteSD( PSECURITY_DESCRIPTOR,
|
|
PSECURITY_DESCRIPTOR *, PULONG );
|
|
NTSTATUS QueueWinStationCreate( PWINSTATIONNAME );
|
|
ULONG WinStationShutdownReset( PVOID );
|
|
NTSTATUS DoForWinStationGroup( PULONG, ULONG, LPTHREAD_START_ROUTINE );
|
|
NTSTATUS InitializeGAPPointersDatabase();
|
|
NTSTATUS IncreaseGAPPointersDatabaseSize();
|
|
NTSTATUS InsertPointerInGAPDatabase(PVOID Pointer);
|
|
VOID ReleaseGAPPointersDatabase();
|
|
BOOLEAN PointerIsInGAPDatabase(PVOID Pointer);
|
|
VOID ValidateGAPPointersDatabase(ULONG n);
|
|
VOID ResetAutoReconnectInfo( PWINSTATION );
|
|
|
|
NTSTATUS
|
|
GetSidFromProcessId(
|
|
HANDLE UniqueProcessId,
|
|
LARGE_INTEGER CreateTime,
|
|
PSID *ppProcessSid
|
|
);
|
|
|
|
PSECURITY_DESCRIPTOR
|
|
WinStationGetSecurityDescriptor(
|
|
PWINSTATION pWinStation
|
|
);
|
|
|
|
NTSTATUS
|
|
WinStationConnectWorker(
|
|
ULONG ClientLogonId,
|
|
ULONG ConnectLogonId,
|
|
ULONG TargetLogonId,
|
|
PWCHAR pPassword,
|
|
DWORD PasswordSize,
|
|
BOOLEAN bWait,
|
|
BOOLEAN bAutoReconnecting
|
|
);
|
|
|
|
NTSTATUS
|
|
WinStationDisconnectWorker(
|
|
ULONG LogonId,
|
|
BOOLEAN bWait,
|
|
BOOLEAN CallerIsRpc
|
|
);
|
|
|
|
NTSTATUS
|
|
WinStationWaitSystemEventWorker(
|
|
HANDLE hServer,
|
|
ULONG EventMask,
|
|
PULONG pEventFlags
|
|
);
|
|
|
|
NTSTATUS
|
|
WinStationCallbackWorker(
|
|
ULONG LogonId,
|
|
PWCHAR pPhoneNumber
|
|
);
|
|
|
|
NTSTATUS
|
|
WinStationBreakPointWorker(
|
|
ULONG LogonId,
|
|
BOOLEAN KernelFlag
|
|
);
|
|
|
|
NTSTATUS
|
|
WinStationReadRegistryWorker(
|
|
VOID
|
|
);
|
|
|
|
NTSTATUS
|
|
ReInitializeSecurityWorker(
|
|
VOID
|
|
);
|
|
|
|
NTSTATUS
|
|
WinStationNotifyLogonWorker(
|
|
DWORD ClientLogonId,
|
|
DWORD ClientProcessId,
|
|
BOOLEAN fUserIsAdmin,
|
|
DWORD UserToken,
|
|
PWCHAR pDomain,
|
|
DWORD DomainSize,
|
|
PWCHAR pUserName,
|
|
DWORD UserNameSize,
|
|
PWCHAR pPassword,
|
|
DWORD PasswordSize,
|
|
UCHAR Seed,
|
|
PCHAR pUserConfig,
|
|
DWORD ConfigSize
|
|
);
|
|
|
|
NTSTATUS
|
|
WinStationNotifyLogoffWorker(
|
|
DWORD ClientLogonId,
|
|
DWORD ClientProcessId
|
|
);
|
|
|
|
NTSTATUS
|
|
WinStationNotifyNewSession(
|
|
DWORD ClientLogonId
|
|
);
|
|
|
|
NTSTATUS
|
|
WinStationShutdownSystemWorker(
|
|
ULONG ClientLogonId,
|
|
ULONG ShutdownFlags
|
|
);
|
|
|
|
NTSTATUS
|
|
WinStationTerminateProcessWorker(
|
|
ULONG ProcessId,
|
|
ULONG ExitCode
|
|
);
|
|
|
|
PSECURITY_DESCRIPTOR
|
|
BuildEveryOneAllowSD();
|
|
|
|
NTSTATUS AddUserAce( PWINSTATION );
|
|
NTSTATUS RemoveUserAce( PWINSTATION );
|
|
NTSTATUS ApplyWinStaMapping( PWINSTATION pWinStation );
|
|
|
|
NTSTATUS
|
|
RpcCheckClientAccess(
|
|
PWINSTATION pWinStation,
|
|
ACCESS_MASK DesiredAccess,
|
|
BOOLEAN AlreadyImpersonating
|
|
);
|
|
|
|
NTSTATUS
|
|
RpcCheckClientAccessLocal(
|
|
PWINSTATION pWinStation,
|
|
ACCESS_MASK DesiredAccess,
|
|
BOOLEAN AlreadyImpersonating
|
|
);
|
|
|
|
_CheckConnectAccess(
|
|
PWINSTATION pSourceWinStation,
|
|
PSID pClientSid,
|
|
ULONG ClientLogonId,
|
|
PWCHAR pPassword,
|
|
DWORD PasswordSize
|
|
);
|
|
|
|
NTSTATUS
|
|
RpcCheckSystemClient(
|
|
ULONG LogonId
|
|
);
|
|
|
|
NTSTATUS
|
|
RpcCheckSystemClientNoLogonId(
|
|
PWINSTATION pWinStation
|
|
);
|
|
|
|
NTSTATUS
|
|
RpcGetClientLogonId(
|
|
PULONG pLogonId
|
|
);
|
|
|
|
BOOL
|
|
ConfigurePerSessionSecurity(
|
|
PWINSTATION pWinStation
|
|
);
|
|
|
|
BOOL
|
|
IsCallerAdmin( VOID );
|
|
|
|
BOOL
|
|
IsCallerSystem( VOID );
|
|
|
|
BOOLEAN
|
|
ValidWireBuffer(WINSTATIONINFOCLASS InfoClass,
|
|
PVOID WireBuf,
|
|
ULONG WireBufLen);
|
|
|
|
NTSTATUS
|
|
IsZeroterminateStringA(
|
|
PBYTE pString,
|
|
DWORD dwLength
|
|
);
|
|
|
|
NTSTATUS
|
|
IsZeroterminateStringW(
|
|
PWCHAR pwString,
|
|
DWORD dwLength
|
|
) ;
|
|
|
|
NTSTATUS
|
|
WinStationUpdateClientCachedCredentialsWorker(
|
|
DWORD ClientLogonId,
|
|
ULONG_PTR ClientProcessId,
|
|
PWCHAR pDomain,
|
|
DWORD DomainSize,
|
|
PWCHAR pUserName,
|
|
DWORD UserNameSize
|
|
);
|
|
|
|
NTSTATUS
|
|
WinStationFUSCanRemoteUserDisconnectWorker(
|
|
DWORD TargetLogonId,
|
|
DWORD ClientLogonId,
|
|
ULONG_PTR ClientProcessId,
|
|
PWCHAR pDomain,
|
|
DWORD DomainSize,
|
|
PWCHAR pUserName,
|
|
DWORD UserNameSize
|
|
);
|
|
|
|
NTSTATUS
|
|
WinStationCheckLoopBackWorker(
|
|
DWORD TargetLogonId,
|
|
DWORD ClientLogonId,
|
|
PWCHAR pTargetServerName,
|
|
DWORD NameSize
|
|
);
|
|
|
|
NTSTATUS WinStationNotifyDisconnectPipeWorker(
|
|
DWORD ClientLogonId,
|
|
ULONG_PTR ClientProcessId
|
|
);
|
|
|
|
|
|
|
|
/*=============================================================================
|
|
== Internal Functions used
|
|
=============================================================================*/
|
|
VOID AuditShutdownEvent(VOID);
|
|
BOOL AuditingEnabled(VOID);
|
|
|
|
NTSTATUS LogoffWinStation( PWINSTATION, ULONG );
|
|
|
|
//
|
|
//Used by IsGinaVersionCurrent()
|
|
//
|
|
#define WLX_NEGOTIATE_NAME "WlxNegotiate"
|
|
typedef BOOL (WINAPI * PWLX_NEGOTIATE)(DWORD, DWORD *);
|
|
BOOL IsGinaVersionCurrent();
|
|
|
|
|
|
#if DBG
|
|
void DumpOutLastErrorString()
|
|
{
|
|
LPVOID lpMsgBuf;
|
|
DWORD error = GetLastError();
|
|
|
|
DBGPRINT(("GetLastError() = 0x%lx \n", error ));
|
|
|
|
FormatMessage(
|
|
FORMAT_MESSAGE_ALLOCATE_BUFFER |
|
|
FORMAT_MESSAGE_FROM_SYSTEM |
|
|
FORMAT_MESSAGE_IGNORE_INSERTS,
|
|
NULL,
|
|
error,
|
|
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Default language
|
|
(LPTSTR) &lpMsgBuf,
|
|
0,
|
|
NULL
|
|
);
|
|
|
|
//
|
|
// Process any inserts in lpMsgBuf.
|
|
// ...
|
|
// Display the string.
|
|
//
|
|
DBGPRINT(("%s\n", (LPCTSTR)lpMsgBuf ));
|
|
|
|
//
|
|
// Free the buffer.
|
|
//
|
|
LocalFree( lpMsgBuf );
|
|
}
|
|
#endif
|
|
|
|
#if DBG
|
|
#define DumpOutLastError DumpOutLastErrorString()
|
|
#else
|
|
#define DumpOutLastError
|
|
#endif
|
|
|
|
|
|
/*=============================================================================
|
|
== Data
|
|
=============================================================================*/
|
|
BOOLEAN gbPointersDatabaseIsValid = FALSE;
|
|
ULONG gPointersDatabaseSize;
|
|
ULONG gNbProcesses;
|
|
PVOID *gPointersDatabase = NULL;
|
|
|
|
RTL_CRITICAL_SECTION gRpcGetAllProcessesLock;
|
|
RTL_CRITICAL_SECTION gRpcPointersDatabaseLock;
|
|
RTL_CRITICAL_SECTION gRpcSidCacheLock;
|
|
BOOLEAN gbRpcGetAllProcessesOK;
|
|
BOOLEAN gbRpcSidCacheOK;
|
|
|
|
extern RTL_CRITICAL_SECTION WsxListLock;
|
|
extern LIST_ENTRY WsxListHead;
|
|
extern LIST_ENTRY WinStationListHead; // protected by WinStationListLock
|
|
|
|
LIST_ENTRY gSidCacheHead;
|
|
|
|
BOOLEAN bConsoleConnected=FALSE;
|
|
|
|
extern POLICY_TS_MACHINE g_MachinePolicy; // declared in winsta.c
|
|
|
|
|
|
/*****************************************************************************
|
|
* WinStationInitRPC
|
|
*
|
|
* Setup the RPC bindings, and listen for incoming requests.
|
|
****************************************************************************/
|
|
RPC_STATUS
|
|
WinStationInitRPC( VOID
|
|
)
|
|
{
|
|
RPC_STATUS Status;
|
|
DWORD Result;
|
|
RPC_BINDING_VECTOR *pBindingVector;
|
|
PSECURITY_DESCRIPTOR pSd;
|
|
|
|
TRACE((hTrace,TC_ICASRV,TT_API2,"RPC WinStationInitRPC\n"));
|
|
|
|
//
|
|
// Initialize the Critical Sections that are
|
|
// necessary for RpcWinStationGetAllProcesses
|
|
//
|
|
gbRpcGetAllProcessesOK =
|
|
((NT_SUCCESS(RtlInitializeCriticalSection(&gRpcGetAllProcessesLock))
|
|
&& (NT_SUCCESS(RtlInitializeCriticalSection(&gRpcPointersDatabaseLock)))
|
|
)? TRUE : FALSE);
|
|
|
|
gbRpcSidCacheOK =
|
|
NT_SUCCESS(RtlInitializeCriticalSection(&gRpcSidCacheLock)) ? TRUE :
|
|
FALSE;
|
|
InitializeListHead(&gSidCacheHead);
|
|
|
|
pSd = BuildEveryOneAllowSD();
|
|
if( pSd == NULL ) {
|
|
TRACE((hTrace,TC_ICASRV,TT_ERROR,"TERMSRV: WinStationInitRPC: Defaulting SD!\n"));
|
|
}
|
|
|
|
// register the LPC (local only) interface
|
|
Status = RpcServerUseProtseqEp(
|
|
L"ncalrpc", // Protocol Sequence (LPC)
|
|
MAX_WINSTATION_RPC_THREADS, // Maximum calls at one time
|
|
L"IcaApi", // Endpoint
|
|
pSd // Security
|
|
);
|
|
|
|
if( Status != RPC_S_OK ) {
|
|
TRACE((hTrace,TC_ICASRV,TT_ERROR,"IcaServ: Error %d RpcUseProtseqEp on ncalrpc\n",Status));
|
|
CleanUpSD(pSd);
|
|
return( Status );
|
|
}
|
|
|
|
//
|
|
// register the Named pipes interface
|
|
// (remote with NT domain authentication)
|
|
//
|
|
Status = RpcServerUseProtseqEp(
|
|
L"ncacn_np", // Protocol Sequence
|
|
MAX_WINSTATION_RPC_THREADS, // Maximum calls at one time
|
|
L"\\pipe\\Ctx_WinStation_API_service", // Endpoint
|
|
NULL // Security
|
|
);
|
|
|
|
if( Status != RPC_S_OK ) {
|
|
TRACE((hTrace,TC_ICASRV,TT_ERROR,"TERMSRV: Error %d RpcUseProtseqEp on ncacn_np\n",Status));
|
|
return( Status );
|
|
}
|
|
|
|
// Register our interface handle
|
|
Status = RpcServerRegisterIf(
|
|
IcaApi_ServerIfHandle, // icaapi.h from MIDL
|
|
NULL, // MgrTypeUUID
|
|
NULL // default EPV
|
|
);
|
|
|
|
if( Status != RPC_S_OK ) {
|
|
TRACE((hTrace,TC_ICASRV,TT_ERROR,"TERMSRV: Error %d RpcServerRegisterIf\n",Status));
|
|
return( Status );
|
|
}
|
|
|
|
// By default, rpc will serialize access to context handles. Since
|
|
// the ICASRV needs to be able to have two threads access a context
|
|
// handle at once, and it knows what it is doing, we will tell rpc
|
|
// not to serialize access to context handles.
|
|
|
|
|
|
// We cannot call this function as its effects are process wide, and since we are
|
|
// part of SvcHost, other services also get affected with this call.
|
|
// instead we will use context_handle_noserialize attribute in our acf files.
|
|
//
|
|
// I_RpcSsDontSerializeContext();
|
|
|
|
// Now do the RPC listen to service calls
|
|
Status = RpcServerListen(
|
|
1, // Min calls
|
|
MAX_WINSTATION_RPC_THREADS,
|
|
TRUE // fDontWait
|
|
);
|
|
|
|
if( Status != RPC_S_OK ) {
|
|
TRACE((hTrace,TC_ICASRV,TT_ERROR,"TERMSRV: Error %d RpcServerListen\n",Status));
|
|
return( Status );
|
|
}
|
|
|
|
return( 0 );
|
|
}
|
|
|
|
|
|
/*****************************************************************************
|
|
* RpcWinStationOpenServer
|
|
*
|
|
* Function to open the server for WinStation API's.
|
|
*
|
|
* The purpose of this function is the allocation of the
|
|
* RPC context handle for server side state information.
|
|
****************************************************************************/
|
|
BOOLEAN
|
|
RpcWinStationOpenServer(
|
|
handle_t hBinding,
|
|
DWORD *pResult,
|
|
HANDLE *phContext
|
|
)
|
|
{
|
|
PRPC_CLIENT_CONTEXT p;
|
|
|
|
//
|
|
// Allocate our open context structure
|
|
//
|
|
p = midl_user_allocate( sizeof(RPC_CLIENT_CONTEXT) );
|
|
|
|
if( p == NULL ) {
|
|
*pResult = (DWORD) STATUS_NO_MEMORY;
|
|
return( FALSE );
|
|
}
|
|
|
|
//
|
|
// zero it out
|
|
//
|
|
memset( p, 0, sizeof(RPC_CLIENT_CONTEXT) );
|
|
|
|
//
|
|
// Initialize it
|
|
//
|
|
p->pWaitEvent = NULL;
|
|
|
|
//
|
|
// Return the RPC context handle
|
|
//
|
|
*phContext = (PRPC_CLIENT_CONTEXT)p;
|
|
|
|
// Return success
|
|
*pResult = STATUS_SUCCESS;
|
|
|
|
return( *pResult == STATUS_SUCCESS ? TRUE : FALSE );
|
|
}
|
|
|
|
|
|
/*****************************************************************************
|
|
* RpcWinStationCloseServer
|
|
*
|
|
* Function to close the server for WinStation API's.
|
|
* This function is obsolete. use RpcWinstationCloseServerEx instead.
|
|
* Its kept here for compatibility with older (w2k) clients.
|
|
****************************************************************************/
|
|
BOOLEAN
|
|
RpcWinStationCloseServer(
|
|
HANDLE hContext,
|
|
DWORD *pResult
|
|
)
|
|
{
|
|
PRPC_CLIENT_CONTEXT pContext = (PRPC_CLIENT_CONTEXT)hContext;
|
|
ULONG EventFlags;
|
|
NTSTATUS Status;
|
|
|
|
// Free the wait event block if one was allocated
|
|
if ( pContext->pWaitEvent ) {
|
|
WinStationWaitSystemEventWorker( hContext, WEVENT_NONE, &EventFlags );
|
|
}
|
|
|
|
*pResult = STATUS_SUCCESS;
|
|
|
|
return( TRUE );
|
|
}
|
|
|
|
/*****************************************************************************
|
|
* RpcWinStationCloseServerEx
|
|
*
|
|
* Function to close the server for WinStation API's.
|
|
* This function supersades the RpcWinStationCloseServer
|
|
****************************************************************************/
|
|
BOOLEAN
|
|
RpcWinStationCloseServerEx(
|
|
HANDLE *phContext,
|
|
DWORD *pResult
|
|
)
|
|
{
|
|
PRPC_CLIENT_CONTEXT pContext = (PRPC_CLIENT_CONTEXT)*phContext;
|
|
|
|
ULONG EventFlags;
|
|
NTSTATUS Status;
|
|
|
|
// Free the wait event block if one was allocated
|
|
if ( pContext->pWaitEvent ) {
|
|
WinStationWaitSystemEventWorker( *phContext, WEVENT_NONE, &EventFlags );
|
|
}
|
|
|
|
Status = RpcSsContextLockExclusive(NULL, pContext);
|
|
if (RPC_S_OK == Status)
|
|
{
|
|
midl_user_free(pContext);
|
|
|
|
// This is required to signal the RPC that we are done with this context handle
|
|
*phContext = NULL;
|
|
|
|
*pResult = STATUS_SUCCESS;
|
|
return( TRUE );
|
|
}
|
|
else
|
|
{
|
|
DbgPrint("-------------RpcWinStationCloseServerEx: failed to lock the Context exclusively, Status = 0x%X\n", Status);
|
|
return (FALSE);
|
|
}
|
|
}
|
|
|
|
|
|
/*****************************************************************************
|
|
* RpcIcaServerPing
|
|
*
|
|
* Called on an external ping to this Terminal Server.
|
|
****************************************************************************/
|
|
BOOLEAN
|
|
RpcIcaServerPing(
|
|
HANDLE hServer,
|
|
DWORD *pResult
|
|
)
|
|
{
|
|
TRACE((hTrace,TC_ICASRV,TT_API1,"PING Received!\n"));
|
|
|
|
*pResult = STATUS_SUCCESS;
|
|
|
|
return( TRUE );
|
|
}
|
|
|
|
|
|
/*****************************************************************************
|
|
* RpcWinStationEnumerate
|
|
*
|
|
* WinStationEnumerate API
|
|
****************************************************************************/
|
|
BOOLEAN
|
|
RpcWinStationEnumerate(
|
|
HANDLE hServer,
|
|
DWORD *pResult,
|
|
PULONG pEntries,
|
|
PCHAR pLogonId,
|
|
PULONG pByteCount,
|
|
PULONG pIndex
|
|
)
|
|
{
|
|
|
|
if (!pEntries || !pLogonId || !pByteCount || !pIndex) {
|
|
*pResult = STATUS_INVALID_USER_BUFFER;
|
|
return FALSE;
|
|
}
|
|
|
|
*pResult = WinStationEnumerateWorker(
|
|
pEntries,
|
|
(PLOGONID)pLogonId,
|
|
pByteCount,
|
|
pIndex
|
|
);
|
|
|
|
return( *pResult == STATUS_SUCCESS ? TRUE : FALSE );
|
|
}
|
|
|
|
|
|
/*****************************************************************************
|
|
* RpcWinStationEnumerateProcesses
|
|
*
|
|
* WinStationEnumerateProcesses API
|
|
****************************************************************************/
|
|
BOOLEAN
|
|
RpcWinStationEnumerateProcesses(
|
|
HANDLE hServer,
|
|
DWORD *pResult,
|
|
PBYTE pProcessBuffer,
|
|
DWORD ByteCount
|
|
)
|
|
{
|
|
PBYTE pSrcProcessBuffer = NULL;
|
|
|
|
|
|
#ifdef _X86_
|
|
|
|
RtlEnterCriticalSection(&gRpcGetAllProcessesLock);
|
|
|
|
// Check if SID cache hasn't grown too much
|
|
CheckSidCacheSize();
|
|
|
|
//
|
|
// Allocate a temporary buffer
|
|
//
|
|
pSrcProcessBuffer = MemAlloc (ByteCount);
|
|
if (pSrcProcessBuffer == NULL)
|
|
{
|
|
RtlLeaveCriticalSection(&gRpcGetAllProcessesLock);
|
|
*pResult = STATUS_NO_MEMORY;
|
|
return FALSE;
|
|
}
|
|
|
|
/*
|
|
* Perform process enumeration.
|
|
*/
|
|
*pResult = NtQuerySystemInformation( SystemProcessInformation,
|
|
(PVOID)pSrcProcessBuffer,
|
|
ByteCount,
|
|
NULL);
|
|
if ( *pResult == STATUS_SUCCESS ) {
|
|
PSYSTEM_PROCESS_INFORMATION pSrcProcessInfo;
|
|
PSYSTEM_PROCESS_INFORMATION pDestProcessInfo;
|
|
PCITRIX_PROCESS_INFORMATION pDestCitrixInfo;
|
|
ULONG TotalOffset;
|
|
PSID pSid;
|
|
ULONG SizeOfSid;
|
|
PBYTE pSrc, pDest;
|
|
ULONG i;
|
|
ULONG Size = 0;
|
|
|
|
/*
|
|
* Walk the returned buffer to calculate the required size.
|
|
*/
|
|
pSrcProcessInfo = (PSYSTEM_PROCESS_INFORMATION)pSrcProcessBuffer;
|
|
TotalOffset = 0;
|
|
do
|
|
{
|
|
Size += (SIZEOF_TS4_SYSTEM_PROCESS_INFORMATION
|
|
+ (SIZEOF_TS4_SYSTEM_THREAD_INFORMATION * pSrcProcessInfo->NumberOfThreads)
|
|
+ pSrcProcessInfo->ImageName.Length
|
|
);
|
|
//
|
|
// Get the Sid (will be remembered in the Sid cache)
|
|
// Maybe it would be better to add here a "Sidmaximumlength" ??
|
|
//
|
|
if (NT_SUCCESS(GetSidFromProcessId(
|
|
pSrcProcessInfo->UniqueProcessId,
|
|
pSrcProcessInfo->CreateTime,
|
|
&pSid
|
|
) ) )
|
|
{
|
|
Size += RtlLengthSid(pSid);
|
|
}
|
|
|
|
TotalOffset += pSrcProcessInfo->NextEntryOffset;
|
|
pSrcProcessInfo = (PSYSTEM_PROCESS_INFORMATION)&pSrcProcessBuffer[TotalOffset];
|
|
}
|
|
while (pSrcProcessInfo->NextEntryOffset != 0);
|
|
|
|
if (ByteCount < Size)
|
|
{
|
|
RtlLeaveCriticalSection(&gRpcGetAllProcessesLock);
|
|
MemFree(pSrcProcessBuffer);
|
|
*pResult = STATUS_INFO_LENGTH_MISMATCH;
|
|
return FALSE;
|
|
}
|
|
|
|
/*
|
|
* Walk the returned buffer (it's in new Win2000 SYSTEM_PROCESS_INFORMATION format),
|
|
* copy it to the old TS4 SYSTEM_PROCESS_INFORMATION format, and fixup the addresses
|
|
* (now containing pointers in our address space within pProcessBuffer) to offsets.
|
|
*/
|
|
|
|
// back to the beginning
|
|
pSrcProcessInfo = (PSYSTEM_PROCESS_INFORMATION)pSrcProcessBuffer;
|
|
pDestProcessInfo = (PSYSTEM_PROCESS_INFORMATION)pProcessBuffer;
|
|
|
|
// initialize current pointers
|
|
pSrc = pSrcProcessBuffer;
|
|
pDest = pProcessBuffer;
|
|
TotalOffset = 0;
|
|
for(;;) {
|
|
//
|
|
// Copy process information
|
|
//
|
|
memcpy(pDest,pSrc,SIZEOF_TS4_SYSTEM_PROCESS_INFORMATION);
|
|
pSrc += sizeof(SYSTEM_PROCESS_INFORMATION);
|
|
pDest += SIZEOF_TS4_SYSTEM_PROCESS_INFORMATION;
|
|
|
|
//
|
|
// Copy all the threads info
|
|
//
|
|
for (i=0; i < pSrcProcessInfo->NumberOfThreads ; i++)
|
|
{
|
|
memcpy(pDest,pSrc,SIZEOF_TS4_SYSTEM_THREAD_INFORMATION);
|
|
pSrc += sizeof(SYSTEM_THREAD_INFORMATION);
|
|
pDest += SIZEOF_TS4_SYSTEM_THREAD_INFORMATION;
|
|
}
|
|
|
|
//
|
|
// Set the old TS4 info
|
|
//
|
|
pDestCitrixInfo = (PCITRIX_PROCESS_INFORMATION) pDest;
|
|
pDest += sizeof(CITRIX_PROCESS_INFORMATION);
|
|
|
|
pDestCitrixInfo->MagicNumber = CITRIX_PROCESS_INFO_MAGIC;
|
|
pDestCitrixInfo->LogonId = pSrcProcessInfo->SessionId;
|
|
pDestCitrixInfo->ProcessSid = NULL;
|
|
|
|
//
|
|
// Get the Sid again (from the cache)
|
|
//
|
|
if (NT_SUCCESS(GetSidFromProcessId(
|
|
pDestProcessInfo->UniqueProcessId,
|
|
pDestProcessInfo->CreateTime,
|
|
&pSid
|
|
) ) )
|
|
{
|
|
//
|
|
// Copy the Sid
|
|
//
|
|
SizeOfSid = RtlLengthSid(pSid);
|
|
|
|
pDestCitrixInfo->ProcessSid = NULL;
|
|
if ( NT_SUCCESS(RtlCopySid(SizeOfSid, pDest, pSid) ) )
|
|
{
|
|
pDestCitrixInfo->ProcessSid = (PSID)((ULONG_PTR)pDest - (ULONG_PTR)pProcessBuffer);
|
|
pDest += SizeOfSid;
|
|
}
|
|
}
|
|
|
|
pDestCitrixInfo->Pad = 0;
|
|
|
|
//
|
|
// Copy the image file name
|
|
//
|
|
if ((pSrcProcessInfo->ImageName.Buffer != NULL) && (pSrcProcessInfo->ImageName.Length != 0) )
|
|
{
|
|
memcpy(pDest, pSrcProcessInfo->ImageName.Buffer, pSrcProcessInfo->ImageName.Length);
|
|
pDestProcessInfo->ImageName.Buffer = (PWSTR) ((ULONG_PTR)pDest - (ULONG_PTR)pProcessBuffer);
|
|
pDestProcessInfo->ImageName.Length = pSrcProcessInfo->ImageName.Length;
|
|
pDest += (pSrcProcessInfo->ImageName.Length);
|
|
memcpy(pDest,L"\0",sizeof(WCHAR));
|
|
pDest += sizeof(WCHAR);
|
|
}
|
|
else
|
|
{
|
|
pDestProcessInfo->ImageName.Buffer = NULL;
|
|
pDestProcessInfo->ImageName.Length = 0;
|
|
}
|
|
|
|
//
|
|
// loop...
|
|
//
|
|
if( pSrcProcessInfo->NextEntryOffset == 0 )
|
|
{
|
|
pDestProcessInfo->NextEntryOffset = 0;
|
|
break;
|
|
}
|
|
|
|
pDestProcessInfo->NextEntryOffset =
|
|
(ULONG)((ULONG_PTR)pDest - (ULONG_PTR)pDestProcessInfo);
|
|
|
|
pDestProcessInfo = (PSYSTEM_PROCESS_INFORMATION)pDest;
|
|
|
|
TotalOffset += pSrcProcessInfo->NextEntryOffset;
|
|
pSrcProcessInfo = (PSYSTEM_PROCESS_INFORMATION)&pSrcProcessBuffer[TotalOffset];
|
|
pSrc = (PBYTE)pSrcProcessInfo;
|
|
}
|
|
}
|
|
|
|
RtlLeaveCriticalSection(&gRpcGetAllProcessesLock);
|
|
MemFree(pSrcProcessBuffer);
|
|
return( *pResult == STATUS_SUCCESS ? TRUE : FALSE );
|
|
#else
|
|
|
|
*pResult = STATUS_NOT_SUPPORTED;
|
|
return FALSE;
|
|
#endif
|
|
}
|
|
|
|
|
|
/*******************************************************************************
|
|
* AllocateGAPPointer
|
|
******************************************************************************/
|
|
void __RPC_FAR * __RPC_USER
|
|
AllocateGAPPointer( size_t Size )
|
|
{
|
|
void __RPC_FAR * pMyPointer;
|
|
|
|
pMyPointer = MIDL_user_allocate(Size);
|
|
if (pMyPointer != NULL)
|
|
{
|
|
if (gbRpcGetAllProcessesOK == TRUE)
|
|
{
|
|
//
|
|
// store the pointer in our database
|
|
// so that the RPC server stub do not try to free them.
|
|
//
|
|
if (!NT_SUCCESS(InsertPointerInGAPDatabase(pMyPointer)))
|
|
{
|
|
LocalFree(pMyPointer);
|
|
pMyPointer = NULL;
|
|
}
|
|
}
|
|
else // nothing can be done
|
|
{
|
|
LocalFree(pMyPointer);
|
|
pMyPointer = NULL;
|
|
}
|
|
}
|
|
return pMyPointer;
|
|
}
|
|
|
|
|
|
//********************************************************************************
|
|
//
|
|
// Functions used to handle the memory allocations and de-allocations
|
|
// in RpcWinStationGetAllProcesses (GAP = Get All Processes)
|
|
//
|
|
//********************************************************************************
|
|
NTSTATUS
|
|
InitializeGAPPointersDatabase()
|
|
{
|
|
NTSTATUS Status = STATUS_SUCCESS;
|
|
|
|
RtlEnterCriticalSection(&gRpcPointersDatabaseLock);
|
|
{
|
|
gPointersDatabaseSize = MIN_GAP_DATABASE_SIZE;
|
|
gPointersDatabase = MemAlloc(MIN_GAP_DATABASE_SIZE * sizeof(PVOID));
|
|
|
|
if (gPointersDatabase == NULL)
|
|
{
|
|
Status = STATUS_NO_MEMORY;
|
|
}
|
|
else
|
|
{
|
|
RtlZeroMemory(gPointersDatabase,MIN_GAP_DATABASE_SIZE * sizeof(PVOID));
|
|
}
|
|
|
|
gNbProcesses = 0;
|
|
gbPointersDatabaseIsValid = FALSE;
|
|
}
|
|
RtlLeaveCriticalSection(&gRpcPointersDatabaseLock);
|
|
|
|
return Status;
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
IncreaseGAPPointersDatabaseSize()
|
|
{
|
|
NTSTATUS Status = STATUS_SUCCESS;
|
|
PVOID *NewPointersDatabase;
|
|
|
|
NewPointersDatabase = MemAlloc(gPointersDatabaseSize * 2 * sizeof(PVOID));
|
|
|
|
if (NewPointersDatabase == NULL)
|
|
{
|
|
Status = STATUS_NO_MEMORY;
|
|
}
|
|
else
|
|
{
|
|
RtlCopyMemory(NewPointersDatabase,
|
|
gPointersDatabase,
|
|
gPointersDatabaseSize * sizeof(PVOID));
|
|
RtlZeroMemory(&NewPointersDatabase[gPointersDatabaseSize],
|
|
gPointersDatabaseSize * sizeof(PVOID));
|
|
|
|
MemFree(gPointersDatabase);
|
|
gPointersDatabase = NewPointersDatabase;
|
|
gPointersDatabaseSize = gPointersDatabaseSize * 2;
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
InsertPointerInGAPDatabase(PVOID Pointer)
|
|
{
|
|
ULONG i;
|
|
NTSTATUS Status = STATUS_SUCCESS;
|
|
|
|
// DBGPRINT(("TERMSRV: InsertPointerInGAPDatabase 0x%x\n",Pointer));
|
|
|
|
RtlEnterCriticalSection(&gRpcPointersDatabaseLock);
|
|
{
|
|
for (i=0; i < gPointersDatabaseSize; i++)
|
|
{
|
|
if (gPointersDatabase[i] == NULL)
|
|
{
|
|
gPointersDatabase[i] = Pointer;
|
|
break;
|
|
}
|
|
}
|
|
if (i == gPointersDatabaseSize)
|
|
{
|
|
Status = IncreaseGAPPointersDatabaseSize();
|
|
if (NT_SUCCESS(Status))
|
|
{
|
|
gPointersDatabase[i] = Pointer;
|
|
}
|
|
}
|
|
}
|
|
RtlLeaveCriticalSection(&gRpcPointersDatabaseLock);
|
|
return Status;
|
|
}
|
|
|
|
|
|
VOID
|
|
ReleaseGAPPointersDatabase()
|
|
{
|
|
PTS_ALL_PROCESSES_INFO_NT6 pProcessArray;
|
|
ULONG i;
|
|
|
|
if (gPointersDatabase != NULL)
|
|
{
|
|
RtlEnterCriticalSection(&gRpcPointersDatabaseLock);
|
|
{
|
|
//
|
|
// free all the "autonomic" pointers
|
|
//
|
|
|
|
// the first one is ProcessArray
|
|
if ((gPointersDatabase[0] != NULL) && (gNbProcesses != 0))
|
|
{
|
|
pProcessArray = (PTS_ALL_PROCESSES_INFO_NT6) gPointersDatabase[0];
|
|
|
|
//
|
|
// free the Process Info buffer
|
|
//
|
|
if (pProcessArray[0].pTsProcessInfo != NULL)
|
|
{
|
|
LocalFree(pProcessArray[0].pTsProcessInfo);
|
|
}
|
|
|
|
//
|
|
// free all the SIDs
|
|
//
|
|
for (i=0; i < gNbProcesses ; i++)
|
|
{
|
|
if (pProcessArray[i].pSid != NULL)
|
|
{
|
|
LocalFree(pProcessArray[i].pSid);
|
|
}
|
|
}
|
|
//
|
|
// free the returned array
|
|
//
|
|
LocalFree(pProcessArray);
|
|
}
|
|
|
|
//
|
|
// free the database
|
|
//
|
|
MemFree(gPointersDatabase);
|
|
gPointersDatabase = NULL;
|
|
gNbProcesses = 0;
|
|
//
|
|
// disable the checking
|
|
//
|
|
gbPointersDatabaseIsValid = FALSE;
|
|
}
|
|
RtlLeaveCriticalSection(&gRpcPointersDatabaseLock);
|
|
}
|
|
}
|
|
|
|
|
|
BOOLEAN
|
|
PointerIsInGAPDatabase(PVOID Pointer)
|
|
{
|
|
ULONG i;
|
|
BOOLEAN bRet = FALSE;
|
|
|
|
// spend time only if necessary
|
|
if ((Pointer != NULL)
|
|
&& (gbRpcGetAllProcessesOK == TRUE)
|
|
&& (gbPointersDatabaseIsValid == TRUE)
|
|
)
|
|
{
|
|
RtlEnterCriticalSection(&gRpcPointersDatabaseLock);
|
|
{
|
|
// we need to check because the database may have been released
|
|
// while we were waiting for the lock
|
|
if (gPointersDatabase != NULL)
|
|
{
|
|
for (i=0; i < gPointersDatabaseSize; i++)
|
|
{
|
|
if (gPointersDatabase[i] == Pointer)
|
|
{
|
|
bRet = TRUE;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
RtlLeaveCriticalSection(&gRpcPointersDatabaseLock);
|
|
}
|
|
|
|
return bRet;
|
|
}
|
|
|
|
|
|
VOID
|
|
ValidateGAPPointersDatabase(ULONG n)
|
|
{
|
|
gbPointersDatabaseIsValid = TRUE;
|
|
gNbProcesses = n;
|
|
}
|
|
|
|
|
|
/*******************************************************************************
|
|
* SidCacheAdd
|
|
*
|
|
* NOTE: Do not call with GAP allocated, or otherwise controlled pointers.
|
|
******************************************************************************/
|
|
VOID
|
|
SidCacheAdd(
|
|
HANDLE UniqueProcessId,
|
|
LARGE_INTEGER CreateTime,
|
|
PSID pNewSid
|
|
)
|
|
{
|
|
DWORD SidLength;
|
|
NTSTATUS Status;
|
|
PLIST_ENTRY pNewListEntry;
|
|
PSID_CACHE_LIST_ENTRY pNewCacheRecord;
|
|
PSID pSid;
|
|
|
|
//
|
|
// If the lock didn't initialize, bail.
|
|
//
|
|
if (!gbRpcSidCacheOK) {
|
|
return;
|
|
}
|
|
|
|
//
|
|
// Initialize memory for cache record. Failure is not a problem;
|
|
// the sid just won't be cached.
|
|
//
|
|
pNewCacheRecord = MemAlloc(sizeof(SID_CACHE_LIST_ENTRY));
|
|
if (pNewCacheRecord == NULL) {
|
|
return;
|
|
}
|
|
|
|
pNewCacheRecord->pSid = pNewSid;
|
|
pNewCacheRecord->ProcId = UniqueProcessId;
|
|
pNewCacheRecord->CreateTime = CreateTime;
|
|
pNewListEntry = &pNewCacheRecord->ListEntry;
|
|
|
|
//
|
|
// Lock the Sid Cache and add the new member.
|
|
//
|
|
RtlEnterCriticalSection(&gRpcSidCacheLock);
|
|
InsertTailList(&gSidCacheHead, pNewListEntry);
|
|
|
|
gMaxSidCacheEntries++;
|
|
RtlLeaveCriticalSection(&gRpcSidCacheLock);
|
|
}
|
|
|
|
|
|
/*******************************************************************************
|
|
*
|
|
* SidCacheFind
|
|
*
|
|
* NOTE: Use RtlLengthSid, alloc memory, and RtlCopySid for the return value!
|
|
* Otherwise, you may free memory being used by the Cache!!
|
|
*
|
|
******************************************************************************/
|
|
PSID
|
|
SidCacheFind(
|
|
HANDLE UniqueProcessId,
|
|
LARGE_INTEGER CreateTime
|
|
)
|
|
{
|
|
PLIST_ENTRY pTempEntry;
|
|
PSID_CACHE_LIST_ENTRY pSidCacheEntry;
|
|
PSID pRetSid = NULL;
|
|
|
|
//
|
|
// If the lock didn't initialize, bail.
|
|
//
|
|
if (!gbRpcSidCacheOK) {
|
|
return(NULL);
|
|
}
|
|
|
|
//
|
|
// Lock the Sid Cache.
|
|
//
|
|
RtlEnterCriticalSection(&gRpcSidCacheLock);
|
|
|
|
//
|
|
// The list head is a place holder, start searching from the head's
|
|
// Flink. Stop when we reach the list head again.
|
|
//
|
|
pTempEntry = gSidCacheHead.Flink;
|
|
|
|
while(pTempEntry != &gSidCacheHead) {
|
|
pSidCacheEntry = CONTAINING_RECORD(
|
|
pTempEntry,
|
|
SID_CACHE_LIST_ENTRY,
|
|
ListEntry
|
|
);
|
|
|
|
if (pSidCacheEntry->ProcId != UniqueProcessId) {
|
|
pTempEntry = pTempEntry->Flink;
|
|
} else {
|
|
if (pSidCacheEntry->CreateTime.QuadPart == CreateTime.QuadPart) {
|
|
pRetSid = pSidCacheEntry->pSid;
|
|
} else {
|
|
//
|
|
// If the PID matches, but the create time doesn't, this record is
|
|
// stale. Remove it from the list and free the memory associated with
|
|
// it. There can only be one entry in the cache per PID, therefore,
|
|
// stop searching after freeing.
|
|
//
|
|
RemoveEntryList(pTempEntry);
|
|
if (pSidCacheEntry->pSid != NULL) {
|
|
MemFree(pSidCacheEntry->pSid);
|
|
}
|
|
MemFree(pSidCacheEntry);
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Release the Sid Cache.
|
|
//
|
|
RtlLeaveCriticalSection(&gRpcSidCacheLock);
|
|
return(pRetSid);
|
|
}
|
|
|
|
|
|
/*******************************************************************************
|
|
*
|
|
* SidCacheFree
|
|
*
|
|
*
|
|
******************************************************************************/
|
|
VOID
|
|
SidCacheFree(
|
|
PLIST_ENTRY pListHead
|
|
)
|
|
{
|
|
PLIST_ENTRY pTempEntry = pListHead->Flink;
|
|
|
|
//
|
|
// Lock the Sid Cache.
|
|
//
|
|
|
|
RtlEnterCriticalSection(&gRpcSidCacheLock);
|
|
|
|
|
|
//
|
|
// The list head is a place holder, start freeing from the head's
|
|
// Flink. Stop when we reach the list head again.
|
|
//
|
|
|
|
while (pTempEntry != pListHead) {
|
|
PSID_CACHE_LIST_ENTRY pSidCacheEntry;
|
|
pSidCacheEntry = CONTAINING_RECORD(
|
|
pTempEntry,
|
|
SID_CACHE_LIST_ENTRY,
|
|
ListEntry
|
|
);
|
|
|
|
if (pSidCacheEntry->pSid != NULL) {
|
|
MemFree(pSidCacheEntry->pSid);
|
|
}
|
|
|
|
|
|
RemoveEntryList(pTempEntry);
|
|
pTempEntry = pTempEntry->Flink;
|
|
MemFree(pSidCacheEntry);
|
|
|
|
}
|
|
|
|
//
|
|
// Release the Sid Cache.
|
|
//
|
|
|
|
RtlLeaveCriticalSection(&gRpcSidCacheLock);
|
|
}
|
|
|
|
/*******************************************************************************
|
|
*
|
|
* SidCacheUpdate
|
|
*
|
|
*
|
|
******************************************************************************/
|
|
VOID
|
|
SidCacheUpdate(
|
|
VOID
|
|
)
|
|
{
|
|
|
|
//
|
|
// TODO: Figure out a way to get rid of stale cache entries!
|
|
//
|
|
|
|
}
|
|
|
|
/*******************************************************************************
|
|
*
|
|
* GetSidFromProcessId
|
|
*
|
|
* NOTE: GetSid returns a normal, local memory pointer. The caller should
|
|
* copy this sid based on its needs. For example, RpcWinStationGAP
|
|
* would copy this sid to a GAP pointer. The caller should NOT free
|
|
* the returned pointer, as it is also being cached!
|
|
*
|
|
******************************************************************************/
|
|
NTSTATUS
|
|
GetSidFromProcessId(
|
|
HANDLE UniqueProcessId,
|
|
LARGE_INTEGER CreateTime,
|
|
PSID *ppProcessSid
|
|
)
|
|
{
|
|
BOOLEAN bResult = FALSE;
|
|
DWORD ReturnLength;
|
|
DWORD BufferLength;
|
|
DWORD SidLength;
|
|
PSID pSid;
|
|
NTSTATUS Status;
|
|
PTOKEN_USER pTokenUser = NULL;
|
|
HANDLE hProcess;
|
|
HANDLE hToken;
|
|
OBJECT_ATTRIBUTES ObjectAttributes;
|
|
CLIENT_ID ClientId;
|
|
KERNEL_USER_TIMES TimeInfo;
|
|
|
|
//
|
|
// Look in sid cache first.
|
|
//
|
|
|
|
*ppProcessSid = SidCacheFind(UniqueProcessId, CreateTime);
|
|
if (*ppProcessSid != NULL) {
|
|
return(STATUS_SUCCESS);
|
|
}
|
|
|
|
//
|
|
// Take the long road...
|
|
// Get the Process Handle
|
|
//
|
|
|
|
InitializeObjectAttributes(
|
|
&ObjectAttributes,
|
|
NULL,
|
|
0,
|
|
NULL,
|
|
NULL
|
|
);
|
|
ClientId.UniqueThread = (HANDLE)NULL;
|
|
ClientId.UniqueProcess = (HANDLE)UniqueProcessId;
|
|
|
|
Status = NtOpenProcess(
|
|
&hProcess,
|
|
PROCESS_QUERY_INFORMATION,
|
|
&ObjectAttributes,
|
|
&ClientId
|
|
);
|
|
|
|
if (NT_SUCCESS(Status)) {
|
|
|
|
//
|
|
// Get the Process CreateTime information
|
|
//
|
|
|
|
Status = NtQueryInformationProcess(
|
|
hProcess,
|
|
ProcessTimes,
|
|
(PVOID)&TimeInfo,
|
|
sizeof(TimeInfo),
|
|
NULL
|
|
);
|
|
|
|
//
|
|
// Verify that the passed in PID and CreateTime match
|
|
// the current environment (i.e. don't bother getting
|
|
// information on a stale PID).
|
|
//
|
|
|
|
if (NT_SUCCESS(Status)) {
|
|
if ((TimeInfo.CreateTime.LowPart != CreateTime.LowPart) ||
|
|
(TimeInfo.CreateTime.HighPart != CreateTime.HighPart)) {
|
|
CloseHandle(hProcess);
|
|
return(STATUS_INVALID_HANDLE); // What should we return?
|
|
}
|
|
} else {
|
|
CloseHandle(hProcess);
|
|
return(Status);
|
|
}
|
|
|
|
//
|
|
// Get the Process Token
|
|
//
|
|
|
|
Status = NtOpenProcessToken(
|
|
hProcess,
|
|
TOKEN_QUERY,
|
|
&hToken
|
|
);
|
|
|
|
if (NT_SUCCESS(Status)) {
|
|
|
|
//
|
|
// Query the TokenUser size, then allocate space
|
|
// and re-query.
|
|
//
|
|
|
|
Status = NtQueryInformationToken(
|
|
hToken,
|
|
TokenUser,
|
|
NULL,
|
|
0,
|
|
&ReturnLength
|
|
);
|
|
|
|
if (ReturnLength == 0) {
|
|
CloseHandle(hProcess);
|
|
CloseHandle(hToken);
|
|
return(Status);
|
|
}
|
|
|
|
BufferLength = ReturnLength;
|
|
pTokenUser = MemAlloc(BufferLength);
|
|
if( pTokenUser == NULL ) {
|
|
CloseHandle(hProcess);
|
|
CloseHandle(hToken);
|
|
return(STATUS_NO_MEMORY);
|
|
}
|
|
|
|
Status = NtQueryInformationToken(
|
|
hToken,
|
|
TokenUser,
|
|
pTokenUser,
|
|
BufferLength,
|
|
&ReturnLength
|
|
);
|
|
|
|
CloseHandle(hToken);
|
|
|
|
if (NT_SUCCESS(Status)) {
|
|
|
|
//
|
|
// A valid Sid has been found; copy it and add it to
|
|
// the cache.
|
|
//
|
|
|
|
SidLength = RtlLengthSid(pTokenUser->User.Sid);
|
|
pSid = MemAlloc(SidLength);
|
|
|
|
if (pSid != NULL) {
|
|
Status = RtlCopySid(
|
|
SidLength,
|
|
pSid,
|
|
pTokenUser->User.Sid
|
|
);
|
|
|
|
if (NT_SUCCESS(Status)) {
|
|
*ppProcessSid = pSid;
|
|
bResult = TRUE;
|
|
SidCacheAdd(UniqueProcessId, CreateTime, pSid);
|
|
}
|
|
} else {
|
|
Status = STATUS_NO_MEMORY;
|
|
}
|
|
}
|
|
MemFree(pTokenUser);
|
|
}
|
|
CloseHandle(hProcess);
|
|
}
|
|
return(Status);
|
|
}
|
|
|
|
|
|
VOID
|
|
CheckSidCacheSize()
|
|
|
|
{
|
|
|
|
// if cache has grown over limit, free all the entries
|
|
// and reset the cache entries counter
|
|
|
|
if (gMaxSidCacheEntries >= MAX_SID_CACHE_ENTRIES) {
|
|
SidCacheFree(&gSidCacheHead);
|
|
InitializeListHead(&gSidCacheHead);
|
|
gMaxSidCacheEntries = 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
//
|
|
// This function converts sys buffer to ts buffer. also counts the processes, and returns array procids.
|
|
// CALLER NEED TO LocalFree the *ppProcIds, thats allocaed.
|
|
BOOL ConvertSysBufferToTSBuffer(PBYTE *ppSysProcessBuffer, DWORD ByteCount, ULONG *pProcesses, PHANDLE *ppProcIds)
|
|
{
|
|
PBYTE pTSProcessBuffer = NULL;
|
|
PSYSTEM_PROCESS_INFORMATION pSysProcessInfo = NULL;
|
|
PTS_SYS_PROCESS_INFORMATION_NT6 pTSProcessInfo = NULL;
|
|
ULONG TotalOffset = 0;
|
|
ULONG i;
|
|
|
|
HANDLE *pProcessIds = NULL;
|
|
UINT uiNumProcess = 0;
|
|
|
|
ASSERT(ppSysProcessBuffer);
|
|
ASSERT(*ppSysProcessBuffer);
|
|
ASSERT(ByteCount);
|
|
ASSERT(pProcesses);
|
|
ASSERT(ppProcIds);
|
|
|
|
// allocate target buffer.
|
|
pTSProcessBuffer = MIDL_user_allocate(ByteCount);
|
|
|
|
if (pTSProcessBuffer == NULL)
|
|
{
|
|
*ppProcIds = NULL;
|
|
*pProcesses = 0;
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
uiNumProcess = 0;
|
|
TotalOffset = 0;
|
|
|
|
do
|
|
{
|
|
ASSERT(TotalOffset < ByteCount);
|
|
pSysProcessInfo = (PSYSTEM_PROCESS_INFORMATION) &((*ppSysProcessBuffer)[TotalOffset]);
|
|
pTSProcessInfo = (PTS_SYS_PROCESS_INFORMATION_NT6)&(pTSProcessBuffer[TotalOffset]);
|
|
|
|
// now copy the information over.
|
|
/* ULONG */ pTSProcessInfo->NextEntryOffset = pSysProcessInfo->NextEntryOffset;
|
|
/* ULONG */ pTSProcessInfo->NumberOfThreads = pSysProcessInfo->NumberOfThreads;
|
|
/* LARGE_INTEGER */ pTSProcessInfo->SpareLi1 = pSysProcessInfo->SpareLi1;
|
|
/* LARGE_INTEGER */ pTSProcessInfo->SpareLi2 = pSysProcessInfo->SpareLi2;
|
|
/* LARGE_INTEGER */ pTSProcessInfo->SpareLi3 = pSysProcessInfo->SpareLi3;
|
|
/* LARGE_INTEGER */ pTSProcessInfo->CreateTime = pSysProcessInfo->CreateTime;
|
|
/* LARGE_INTEGER */ pTSProcessInfo->UserTime = pSysProcessInfo->UserTime;
|
|
/* LARGE_INTEGER */ pTSProcessInfo->KernelTime = pSysProcessInfo->KernelTime;
|
|
|
|
if (pSysProcessInfo->ImageName.Buffer != NULL && pSysProcessInfo->ImageName.Length != 0)
|
|
{
|
|
pTSProcessInfo->ImageName.Buffer = (PWSTR)(pTSProcessBuffer + ((PBYTE) pSysProcessInfo->ImageName.Buffer - *ppSysProcessBuffer));
|
|
memcpy(pTSProcessInfo->ImageName.Buffer, pSysProcessInfo->ImageName.Buffer, pSysProcessInfo->ImageName.Length );
|
|
pTSProcessInfo->ImageName.Length = pSysProcessInfo->ImageName.Length;
|
|
pTSProcessInfo->ImageName.MaximumLength = pSysProcessInfo->ImageName.MaximumLength;
|
|
}
|
|
else
|
|
{
|
|
pTSProcessInfo->ImageName.Buffer = NULL;
|
|
pTSProcessInfo->ImageName.Length = 0;
|
|
}
|
|
|
|
/* KPRIORITY */ pTSProcessInfo->BasePriority = pSysProcessInfo->BasePriority;
|
|
/* HANDLE */ pTSProcessInfo->UniqueProcessId = HandleToULong(pSysProcessInfo->UniqueProcessId);
|
|
/* HANDLE */ pTSProcessInfo->InheritedFromUniqueProcessId = HandleToULong(pSysProcessInfo->InheritedFromUniqueProcessId);
|
|
/* ULONG */ pTSProcessInfo->HandleCount = pSysProcessInfo->HandleCount;
|
|
/* ULONG */ pTSProcessInfo->SessionId = pSysProcessInfo->SessionId;
|
|
// /* ULONG_PTR */ pTSProcessInfo->PageDirectoryBase = pSysProcessInfo->PageDirectoryBase;
|
|
/* SIZE_T */ pTSProcessInfo->PeakVirtualSize = pSysProcessInfo->PeakVirtualSize;
|
|
/* SIZE_T */ pTSProcessInfo->VirtualSize = pSysProcessInfo->VirtualSize;
|
|
/* ULONG */ pTSProcessInfo->PageFaultCount = pSysProcessInfo->PageFaultCount;
|
|
/* SIZE_T */ pTSProcessInfo->PeakWorkingSetSize = (ULONG)pSysProcessInfo->PeakWorkingSetSize;
|
|
/* SIZE_T */ pTSProcessInfo->WorkingSetSize = (ULONG)pSysProcessInfo->WorkingSetSize;
|
|
/* SIZE_T */ pTSProcessInfo->QuotaPeakPagedPoolUsage = (ULONG)pSysProcessInfo->QuotaPeakPagedPoolUsage;
|
|
/* SIZE_T */ pTSProcessInfo->QuotaPagedPoolUsage = (ULONG)pSysProcessInfo->QuotaPagedPoolUsage;
|
|
/* SIZE_T */ pTSProcessInfo->QuotaPeakNonPagedPoolUsage = (ULONG)pSysProcessInfo->QuotaPeakNonPagedPoolUsage;
|
|
/* SIZE_T */ pTSProcessInfo->QuotaNonPagedPoolUsage = (ULONG)pSysProcessInfo->QuotaNonPagedPoolUsage;
|
|
/* SIZE_T */ pTSProcessInfo->PagefileUsage = (ULONG)pSysProcessInfo->PagefileUsage;
|
|
/* SIZE_T */ pTSProcessInfo->PeakPagefileUsage = (ULONG)pSysProcessInfo->PeakPagefileUsage;
|
|
/* SIZE_T */ pTSProcessInfo->PrivatePageCount = (ULONG)pSysProcessInfo->PrivatePageCount;
|
|
|
|
|
|
TotalOffset += pSysProcessInfo->NextEntryOffset;
|
|
uiNumProcess++;
|
|
}
|
|
while (pSysProcessInfo->NextEntryOffset != 0);
|
|
|
|
|
|
//
|
|
// now keep the original pid form the original buffer, we have lost some data while converting it to ts format.
|
|
|
|
// allocate memory for process ids.
|
|
pProcessIds = LocalAlloc(LMEM_FIXED, sizeof(HANDLE) * uiNumProcess);
|
|
|
|
if (!pProcessIds)
|
|
{
|
|
MIDL_user_free(pTSProcessBuffer);
|
|
*ppProcIds = NULL;
|
|
*pProcesses = 0;
|
|
return FALSE;
|
|
}
|
|
|
|
TotalOffset = 0;
|
|
for (i = 0; i < uiNumProcess; i++)
|
|
{
|
|
pSysProcessInfo = (PSYSTEM_PROCESS_INFORMATION) &((*ppSysProcessBuffer)[TotalOffset]);
|
|
ASSERT(TotalOffset < ByteCount);
|
|
|
|
pProcessIds[i] = pSysProcessInfo->UniqueProcessId;
|
|
|
|
TotalOffset += pSysProcessInfo->NextEntryOffset;
|
|
}
|
|
|
|
ASSERT(pSysProcessInfo->NextEntryOffset == 0);
|
|
|
|
// now lets get rid of the original buffer, and replace it with our new TS process buffer
|
|
LocalFree(*ppSysProcessBuffer);
|
|
|
|
*ppProcIds = pProcessIds;
|
|
*pProcesses = uiNumProcess;
|
|
*ppSysProcessBuffer = pTSProcessBuffer;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
/***********************************************************************************************************
|
|
* WinStationGetAllProcessesWorker
|
|
*
|
|
* Worker routine for RpcWinStationGetAllProcesses(Win2K) and RpcWinStationGetAllProcesses_NT6(Whistler).
|
|
*
|
|
* EXIT:
|
|
* TRUE -- The query succeeded, and the buffer contains the requested data.
|
|
* FALSE -- The operation failed. Extended error status is returned in pResult.
|
|
***********************************************************************************************************/
|
|
BOOLEAN
|
|
WinStationGetAllProcessesWorker(
|
|
HANDLE hServer,
|
|
DWORD *pResult,
|
|
ULONG Level,
|
|
ULONG *pNumberOfProcesses,
|
|
PBYTE *ppTsAllProcessesInfo
|
|
)
|
|
{
|
|
PTS_SYS_PROCESS_INFORMATION_NT6 pProcessInfo = NULL;
|
|
PTS_ALL_PROCESSES_INFO_NT6 pProcessArray = NULL;
|
|
|
|
ULONG TotalOffset;
|
|
ULONG NumberOfProcesses = 1; // at least 1 process
|
|
ULONG i;
|
|
|
|
if (gbRpcGetAllProcessesOK == FALSE)
|
|
{
|
|
*pResult = STATUS_NO_MEMORY;
|
|
*pNumberOfProcesses = 0;
|
|
*ppTsAllProcessesInfo = NULL;
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// this critical section will be released in
|
|
// RpcWinStationGetAllProcesses_notify_flag
|
|
//
|
|
RtlEnterCriticalSection(&gRpcGetAllProcessesLock);
|
|
|
|
// Check if SID cache hasn't grown too much
|
|
|
|
CheckSidCacheSize();
|
|
|
|
|
|
if (!NT_SUCCESS(InitializeGAPPointersDatabase()))
|
|
{
|
|
*pResult = STATUS_NO_MEMORY;
|
|
*pNumberOfProcesses = 0;
|
|
*ppTsAllProcessesInfo = NULL;
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// make sure requested information level is known
|
|
//
|
|
if (Level != GAP_LEVEL_BASIC) // only info level known on this version
|
|
{
|
|
*pResult = STATUS_NOT_IMPLEMENTED;
|
|
}
|
|
else // OK
|
|
{
|
|
PBYTE pProcessBuffer = NULL;
|
|
PSID pSid;
|
|
DWORD RequiredByteCount = 0;
|
|
DWORD ByteCount = (MAX_PATH*sizeof(WCHAR)) + 1; // Give the minimum length first. We will get the correct on subsequent calls.
|
|
NTSTATUS Status = STATUS_INFO_LENGTH_MISMATCH; // Assume that this length is wrong. In fact it *is* wrong initially.
|
|
HANDLE *pProcessids = NULL;
|
|
|
|
while ( Status == STATUS_INFO_LENGTH_MISMATCH)
|
|
{
|
|
//
|
|
// Allocate a buffer
|
|
//
|
|
pProcessBuffer = MIDL_user_allocate(ByteCount);
|
|
|
|
if (pProcessBuffer == NULL)
|
|
{
|
|
Status = STATUS_NO_MEMORY;
|
|
*pResult = STATUS_NO_MEMORY;
|
|
*pNumberOfProcesses = 0;
|
|
*ppTsAllProcessesInfo = NULL;
|
|
break;
|
|
}
|
|
|
|
//
|
|
// Perform process enumeration.
|
|
// NOTE: We had a mismatch in the structure for the process information in Win2K and whistler from RPC point of view.
|
|
// Win2K RPC interpretes the size of the image name is twice of what whistler does. So, to take care of this problem,
|
|
// where Win2K client may call Whistler server, we will allocate more bytes than actually required. So, we will pass
|
|
// less number of bytes to the system call by the same amount.
|
|
//
|
|
Status = NtQuerySystemInformation( SystemProcessInformation,
|
|
(PVOID)pProcessBuffer,
|
|
ByteCount - (MAX_PATH*sizeof(WCHAR)),
|
|
&RequiredByteCount );
|
|
if (Status == STATUS_INFO_LENGTH_MISMATCH)
|
|
{
|
|
// Allocate little more bytes than required.
|
|
ByteCount = RequiredByteCount + (MAX_PATH*sizeof(WCHAR));
|
|
LocalFree(pProcessBuffer);
|
|
}
|
|
}
|
|
|
|
if ( Status != STATUS_SUCCESS)
|
|
{
|
|
*pResult = STATUS_NO_MEMORY;
|
|
if (pProcessBuffer != NULL)
|
|
{
|
|
LocalFree(pProcessBuffer);
|
|
pProcessBuffer = NULL;
|
|
}
|
|
}
|
|
else if (!ConvertSysBufferToTSBuffer(&pProcessBuffer, ByteCount, &NumberOfProcesses, &pProcessids))
|
|
{
|
|
// failed to convert sysbuffer to ts buffer.
|
|
// lets bail out.
|
|
*pResult = STATUS_NO_MEMORY;
|
|
if (pProcessBuffer != NULL)
|
|
{
|
|
LocalFree(pProcessBuffer);
|
|
pProcessBuffer = NULL;
|
|
}
|
|
}
|
|
else // everything's fine
|
|
{
|
|
ASSERT(pProcessids);
|
|
ASSERT(pProcessBuffer);
|
|
ASSERT(NumberOfProcesses > 0);
|
|
|
|
|
|
pProcessArray = AllocateGAPPointer(NumberOfProcesses * sizeof(TS_ALL_PROCESSES_INFO_NT6));
|
|
|
|
if (pProcessArray == NULL)
|
|
{
|
|
*pResult = STATUS_NO_MEMORY;
|
|
LocalFree(pProcessBuffer);
|
|
pProcessBuffer = NULL;
|
|
}
|
|
else
|
|
{
|
|
|
|
RtlZeroMemory(pProcessArray,
|
|
NumberOfProcesses * sizeof(TS_ALL_PROCESSES_INFO_NT6));
|
|
*pResult = STATUS_SUCCESS;
|
|
pProcessInfo = (PTS_SYS_PROCESS_INFORMATION_NT6)pProcessBuffer;
|
|
TotalOffset = 0;
|
|
|
|
//
|
|
// Walk the returned buffer again to set the correct pointers in pProcessArray
|
|
//
|
|
for (i=0; i < NumberOfProcesses; i++)
|
|
{
|
|
pProcessArray[i].pTsProcessInfo = (PTS_SYS_PROCESS_INFORMATION_NT6)pProcessInfo;
|
|
|
|
//
|
|
// keep some trace of the "internal" pointers
|
|
// so that the RPC server stub do not try to free them.
|
|
//
|
|
|
|
if (!NT_SUCCESS(InsertPointerInGAPDatabase(pProcessArray[i].pTsProcessInfo)))
|
|
{
|
|
*pResult = STATUS_NO_MEMORY;
|
|
break;
|
|
}
|
|
|
|
if ( pProcessInfo->ImageName.Buffer )
|
|
{
|
|
if (!NT_SUCCESS(InsertPointerInGAPDatabase(pProcessInfo->ImageName.Buffer)))
|
|
{
|
|
*pResult = STATUS_NO_MEMORY;
|
|
break;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Get the Sid
|
|
//
|
|
if (NT_SUCCESS(GetSidFromProcessId(
|
|
pProcessids[i],
|
|
pProcessInfo->CreateTime,
|
|
&pSid
|
|
)
|
|
)
|
|
)
|
|
{
|
|
//
|
|
// set the length for the Sid
|
|
//
|
|
pProcessArray[i].SizeOfSid = RtlLengthSid(pSid);
|
|
// GAP allocate a pointer and copy!
|
|
pProcessArray[i].pSid = AllocateGAPPointer(
|
|
pProcessArray[i].SizeOfSid
|
|
);
|
|
if (pProcessArray[i].pSid == NULL) {
|
|
*pResult = STATUS_NO_MEMORY;
|
|
break;
|
|
}
|
|
|
|
*pResult = RtlCopySid(
|
|
pProcessArray[i].SizeOfSid,
|
|
pProcessArray[i].pSid,
|
|
pSid
|
|
);
|
|
if (!(NT_SUCCESS(*pResult))) {
|
|
break;
|
|
}
|
|
|
|
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// set a NULL Sid
|
|
//
|
|
pProcessArray[i].pSid = NULL;
|
|
pProcessArray[i].SizeOfSid = 0;
|
|
}
|
|
//
|
|
// next entry
|
|
//
|
|
|
|
TotalOffset += pProcessInfo->NextEntryOffset;
|
|
|
|
|
|
pProcessInfo = (PTS_SYS_PROCESS_INFORMATION_NT6)&pProcessBuffer[TotalOffset];
|
|
|
|
|
|
}
|
|
|
|
if (*pResult != STATUS_SUCCESS) // we finally failed !
|
|
{
|
|
// DBGPRINT(( "TERMSRV: RpcWinStationGAP: ultimate failure\n"));
|
|
|
|
//
|
|
// free all SIDs
|
|
//
|
|
for (i=0; i < NumberOfProcesses; i++)
|
|
{
|
|
if (pProcessArray[i].pSid != NULL)
|
|
{
|
|
LocalFree(pProcessArray[i].pSid);
|
|
}
|
|
}
|
|
//
|
|
// free the array
|
|
//
|
|
LocalFree(pProcessArray);
|
|
pProcessArray = NULL;
|
|
//
|
|
// free the buffer
|
|
//
|
|
LocalFree(pProcessBuffer);
|
|
pProcessBuffer = NULL;
|
|
}
|
|
}
|
|
|
|
if (pProcessids)
|
|
LocalFree(pProcessids);
|
|
|
|
}
|
|
}
|
|
|
|
if (NT_SUCCESS(*pResult))
|
|
{
|
|
// DBGPRINT(( "TERMSRV: RpcWinStationGAP: Everything went fine\n"));
|
|
|
|
//
|
|
// From that moment, we may receive some MIDL_user_free
|
|
// so enable the database checking
|
|
//
|
|
ValidateGAPPointersDatabase(NumberOfProcesses);
|
|
|
|
*pNumberOfProcesses = NumberOfProcesses;
|
|
*ppTsAllProcessesInfo = (PBYTE) pProcessArray;
|
|
|
|
}
|
|
else // error case
|
|
{
|
|
*pNumberOfProcesses = 0;
|
|
*ppTsAllProcessesInfo = NULL;
|
|
}
|
|
|
|
|
|
return ( (NT_SUCCESS(*pResult))? TRUE : FALSE);
|
|
}
|
|
|
|
|
|
/*******************************************************************************
|
|
* RpcWinStationGetAllProcesses_NT6
|
|
*
|
|
* Replaces RpcWinStationGetAllProcesses for Win2K servers.
|
|
*
|
|
* EXIT:
|
|
* TRUE -- The query succeeded, and the buffer contains the requested data.
|
|
* FALSE -- The operation failed. Extended error status is returned in pResult.
|
|
******************************************************************************/
|
|
BOOLEAN
|
|
RpcWinStationGetAllProcesses_NT6(
|
|
HANDLE hServer,
|
|
DWORD *pResult,
|
|
ULONG Level,
|
|
ULONG *pNumberOfProcesses,
|
|
PTS_ALL_PROCESSES_INFO_NT6 *ppTsAllProcessesInfo
|
|
)
|
|
{
|
|
BOOLEAN Result;
|
|
PTS_ALL_PROCESSES_INFO_NT6 pProcessInfo;
|
|
|
|
Result = WinStationGetAllProcessesWorker(
|
|
hServer,
|
|
pResult,
|
|
Level,
|
|
pNumberOfProcesses,
|
|
(PBYTE *)&pProcessInfo
|
|
);
|
|
|
|
*ppTsAllProcessesInfo = pProcessInfo;
|
|
|
|
return Result;
|
|
}
|
|
|
|
|
|
|
|
/*******************************************************************************
|
|
*
|
|
* RpcWinStationGetAllProcesses
|
|
*
|
|
* Replaces RpcWinStationEnumerateProcesses for NT5.0 servers.
|
|
* (Now used only by winsta client of Win2K)
|
|
*
|
|
* ENTRY:
|
|
*
|
|
* EXIT:
|
|
*
|
|
* TRUE -- The query succeeded, and the buffer contains the requested data.
|
|
*
|
|
* FALSE -- The operation failed. Extended error status is returned in pResult.
|
|
*
|
|
******************************************************************************/
|
|
|
|
BOOLEAN
|
|
RpcWinStationGetAllProcesses(
|
|
HANDLE hServer,
|
|
DWORD *pResult,
|
|
ULONG Level,
|
|
ULONG *pNumberOfProcesses,
|
|
PTS_ALL_PROCESSES_INFO *ppTsAllProcessesInfo
|
|
)
|
|
{
|
|
BOOLEAN Result;
|
|
PTS_ALL_PROCESSES_INFO pProcessInfo;
|
|
|
|
Result = WinStationGetAllProcessesWorker(
|
|
hServer,
|
|
pResult,
|
|
Level,
|
|
pNumberOfProcesses,
|
|
(PBYTE *)&pProcessInfo
|
|
);
|
|
|
|
*ppTsAllProcessesInfo = pProcessInfo;
|
|
|
|
return Result;
|
|
}
|
|
|
|
|
|
/*******************************************************************************
|
|
* RpcWinStationGetLanAdapterName
|
|
*
|
|
* EXIT:
|
|
* TRUE -- The query succeeded, and the buffer contains the requested data.
|
|
* FALSE -- The operation failed. Extended error status is returned in pResult.
|
|
******************************************************************************/
|
|
|
|
#define RELEASEPTR(iPointer) \
|
|
if (iPointer) { \
|
|
iPointer->lpVtbl->Release(iPointer); \
|
|
iPointer = NULL; \
|
|
}
|
|
|
|
BOOLEAN RpcWinStationGetLanAdapterName(
|
|
HANDLE hServer,
|
|
DWORD *pResult,
|
|
DWORD PdNameSize,
|
|
PWCHAR pPdName,
|
|
ULONG LanAdapter,
|
|
ULONG *pLength,
|
|
PWCHAR *ppLanAdapterName)
|
|
{
|
|
HRESULT hResult = S_OK;
|
|
HRESULT hr = S_OK;
|
|
|
|
//Interface pointer declarations
|
|
WCHAR szProtocol[256];
|
|
INetCfg * pnetCfg = NULL;
|
|
INetCfgClass * pNetCfgClass = NULL;
|
|
INetCfgClass * pNetCfgClassAdapter = NULL;
|
|
INetCfgComponent * pNetCfgComponent = NULL;
|
|
INetCfgComponent * pNetCfgComponentprot = NULL;
|
|
IEnumNetCfgComponent * pEnumComponent = NULL;
|
|
INetCfgComponentBindings * pBinding = NULL;
|
|
LPWSTR pDisplayName = NULL;
|
|
DWORD dwCharacteristics;
|
|
ULONG count = 0;
|
|
PWCHAR pLanAdapter = NULL;
|
|
|
|
*ppLanAdapterName = NULL;
|
|
*pLength = 0;
|
|
*pResult = STATUS_SUCCESS;
|
|
|
|
// 0 corresponds to "All network adapters"
|
|
|
|
if (0 == LanAdapter) {
|
|
pLanAdapter = MIDL_user_allocate((DEVICENAME_LENGTH + 1) * sizeof(WCHAR));
|
|
if (pLanAdapter == NULL) {
|
|
*pResult = STATUS_NO_MEMORY;
|
|
goto done;
|
|
}
|
|
else {
|
|
if (!LoadString(hModuleWin, STR_ALL_LAN_ADAPTERS, pLanAdapter,
|
|
DEVICENAME_LENGTH + 1)) {
|
|
*pResult = STATUS_UNSUCCESSFUL;
|
|
MIDL_user_free(pLanAdapter);
|
|
goto done;
|
|
}
|
|
|
|
*ppLanAdapterName = pLanAdapter;
|
|
*pLength = (DEVICENAME_LENGTH + 1);
|
|
goto done;
|
|
}
|
|
}
|
|
|
|
try{
|
|
|
|
NTSTATUS StringStatus ;
|
|
|
|
// Do some buffer validation
|
|
// No of bytes (and not no of WCHARS) is sent from the Client, so send half the length got
|
|
StringStatus = IsZeroterminateStringW(pPdName, PdNameSize / sizeof(WCHAR) );
|
|
if (StringStatus != STATUS_SUCCESS) {
|
|
*pResult = STATUS_INVALID_PARAMETER;
|
|
goto done;
|
|
}
|
|
|
|
if (0 == _wcsnicmp( pPdName , L"tcp", PdNameSize/sizeof(WCHAR))) {
|
|
lstrcpy(szProtocol,NETCFG_TRANS_CID_MS_TCPIP);
|
|
}
|
|
else if( 0 == _wcsnicmp( pPdName , L"netbios", PdNameSize/sizeof(WCHAR)) ) {
|
|
lstrcpy(szProtocol,NETCFG_SERVICE_CID_MS_NETBIOS);
|
|
}
|
|
else if( 0 == _wcsnicmp( pPdName , L"ipx", PdNameSize/sizeof(WCHAR)) ) {
|
|
lstrcpy(szProtocol,NETCFG_TRANS_CID_MS_NWIPX);
|
|
}
|
|
else if( 0 == _wcsnicmp( pPdName , L"spx", PdNameSize/sizeof(WCHAR)) ) {
|
|
lstrcpy(szProtocol,NETCFG_TRANS_CID_MS_NWSPX);
|
|
}
|
|
else {
|
|
*pResult = STATUS_INVALID_PARAMETER;
|
|
goto done;
|
|
}
|
|
} except(EXCEPTION_EXECUTE_HANDLER){
|
|
*pResult = STATUS_INVALID_PARAMETER;
|
|
goto done;
|
|
}
|
|
|
|
|
|
hResult = CoCreateInstance(&CLSID_CNetCfg, NULL, CLSCTX_SERVER,
|
|
&IID_INetCfg, (LPVOID *)&pnetCfg);
|
|
if (FAILED(hResult)) {
|
|
*pResult = STATUS_UNSUCCESSFUL;
|
|
goto done;
|
|
}
|
|
|
|
if (pnetCfg != NULL) {
|
|
hResult = pnetCfg->lpVtbl->Initialize(pnetCfg,NULL );
|
|
if (FAILED(hResult) || pnetCfg == NULL) {
|
|
*pResult = STATUS_UNSUCCESSFUL;
|
|
goto done;
|
|
}
|
|
|
|
if (lstrcmpi(szProtocol, NETCFG_SERVICE_CID_MS_NETBIOS) == 0) {
|
|
hResult = pnetCfg->lpVtbl->QueryNetCfgClass(pnetCfg,
|
|
&GUID_DEVCLASS_NETSERVICE, &IID_INetCfgClass,
|
|
(void **)&pNetCfgClass);
|
|
if (FAILED(hResult) || pNetCfgClass == NULL) {
|
|
*pResult = STATUS_UNSUCCESSFUL;
|
|
goto done;
|
|
}
|
|
}
|
|
else {
|
|
hResult = pnetCfg->lpVtbl->QueryNetCfgClass(pnetCfg,
|
|
&GUID_DEVCLASS_NETTRANS, &IID_INetCfgClass,
|
|
(void **)&pNetCfgClass);
|
|
if (FAILED( hResult ) || pNetCfgClass == NULL) {
|
|
*pResult = STATUS_UNSUCCESSFUL;
|
|
goto done;
|
|
}
|
|
}
|
|
|
|
hResult = pnetCfg->lpVtbl->QueryNetCfgClass(pnetCfg,
|
|
&GUID_DEVCLASS_NET, &IID_INetCfgClass,
|
|
(void **)&pNetCfgClassAdapter);
|
|
if (FAILED( hResult ) || pNetCfgClassAdapter == NULL) {
|
|
*pResult = STATUS_UNSUCCESSFUL;
|
|
goto done;
|
|
}
|
|
|
|
hResult = pNetCfgClass->lpVtbl->FindComponent(pNetCfgClass,
|
|
szProtocol, &pNetCfgComponentprot);
|
|
if (FAILED( hResult ) || pNetCfgComponentprot == NULL) {
|
|
*pResult = STATUS_UNSUCCESSFUL;
|
|
goto done;
|
|
}
|
|
|
|
hResult = pNetCfgComponentprot->lpVtbl->QueryInterface(
|
|
pNetCfgComponentprot, &IID_INetCfgComponentBindings,
|
|
(void **)&pBinding);
|
|
if (FAILED( hResult ) || pBinding == NULL) {
|
|
*pResult = STATUS_UNSUCCESSFUL;
|
|
goto done;
|
|
}
|
|
|
|
hResult = pNetCfgClassAdapter->lpVtbl->EnumComponents(
|
|
pNetCfgClassAdapter, &pEnumComponent);
|
|
|
|
RELEASEPTR(pNetCfgClassAdapter);
|
|
|
|
if (FAILED( hResult ) || pEnumComponent == NULL) {
|
|
*pResult = STATUS_UNSUCCESSFUL;
|
|
goto done;
|
|
}
|
|
|
|
*pResult = STATUS_UNSUCCESSFUL;
|
|
|
|
while(TRUE) {
|
|
hr = pEnumComponent->lpVtbl->Next(pEnumComponent, 1,
|
|
&pNetCfgComponent,&count);
|
|
if (count == 0 || NULL == pNetCfgComponent)
|
|
break;
|
|
|
|
hr = pNetCfgComponent->lpVtbl->GetCharacteristics(
|
|
pNetCfgComponent,&dwCharacteristics);
|
|
if (FAILED(hr)) {
|
|
RELEASEPTR(pNetCfgComponent);
|
|
continue;
|
|
}
|
|
|
|
if (dwCharacteristics & NCF_PHYSICAL) {
|
|
if (S_OK == pBinding->lpVtbl->IsBoundTo(pBinding,
|
|
pNetCfgComponent)) {
|
|
GUID guidNIC;
|
|
/*index++;
|
|
if(index == LanAdapter)
|
|
{
|
|
hResult = pNetCfgComponent->lpVtbl->GetDisplayName(pNetCfgComponent,&pLanAdapter);
|
|
|
|
if( FAILED( hResult ) )
|
|
{
|
|
*pResult = STATUS_UNSUCCESSFUL;
|
|
}
|
|
else
|
|
{
|
|
*ppLanAdapterName = MIDL_user_allocate((lstrlen(pLanAdapter) + 1) * sizeof(WCHAR));
|
|
if (*ppLanAdapterName == NULL)
|
|
{
|
|
*pResult = STATUS_NO_MEMORY;
|
|
}
|
|
else
|
|
{
|
|
lstrcpy(*ppLanAdapterName,pLanAdapter);
|
|
*pLength = (lstrlen(pLanAdapter) + 1);
|
|
*pResult = STATUS_SUCCESS;
|
|
}
|
|
CoTaskMemFree(pLanAdapter);
|
|
break;
|
|
}
|
|
}
|
|
*/
|
|
|
|
hResult = pNetCfgComponent->lpVtbl->GetInstanceGuid(
|
|
pNetCfgComponent , &guidNIC);
|
|
if (SUCCEEDED( hResult )) {
|
|
hResult = pNetCfgComponent->lpVtbl->GetDisplayName(
|
|
pNetCfgComponent, &pLanAdapter);
|
|
}
|
|
if (SUCCEEDED(hResult)) {
|
|
WCHAR wchRegKey[ MAX_PATH ];
|
|
WCHAR wchGUID[ 40 ];
|
|
HKEY hKey;
|
|
|
|
lstrcpy( wchRegKey , REG_GUID_TABLE );
|
|
|
|
// convert the 128bit value into a string
|
|
StringFromGUID2(&guidNIC, wchGUID,
|
|
sizeof( wchGUID ) / sizeof( WCHAR ));
|
|
|
|
// create the full regkey
|
|
lstrcat( wchRegKey , wchGUID );
|
|
|
|
// find guid in guid table
|
|
hResult = (HRESULT)RegOpenKeyEx(HKEY_LOCAL_MACHINE,
|
|
wchRegKey, 0, KEY_READ, &hKey);
|
|
|
|
if (hResult == ERROR_SUCCESS) {
|
|
DWORD dwSize = sizeof( DWORD );
|
|
DWORD dwLana = 0;
|
|
|
|
RegQueryValueEx(hKey, LANA_ID, NULL, NULL,
|
|
(LPBYTE)&dwLana, &dwSize);
|
|
RegCloseKey(hKey);
|
|
|
|
// if we have a match allocate space for the lanadapter name
|
|
// and then lets split
|
|
if (LanAdapter == dwLana) {
|
|
*ppLanAdapterName = MIDL_user_allocate(
|
|
(lstrlen(pLanAdapter) + 1) *
|
|
sizeof(WCHAR));
|
|
|
|
if ( *ppLanAdapterName == NULL ) {
|
|
*pResult = STATUS_NO_MEMORY;
|
|
}
|
|
else {
|
|
lstrcpy( *ppLanAdapterName , pLanAdapter );
|
|
*pLength = ( lstrlen( pLanAdapter ) + 1 );
|
|
*pResult = STATUS_SUCCESS;
|
|
}
|
|
|
|
CoTaskMemFree(pLanAdapter);
|
|
break;
|
|
}
|
|
}
|
|
|
|
CoTaskMemFree(pLanAdapter);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
RELEASEPTR(pNetCfgComponent);
|
|
}
|
|
|
|
|
|
done:
|
|
RELEASEPTR(pBinding);
|
|
RELEASEPTR(pEnumComponent);
|
|
RELEASEPTR(pNetCfgComponentprot);
|
|
RELEASEPTR(pNetCfgComponent);
|
|
RELEASEPTR(pNetCfgClass);
|
|
|
|
if ( pnetCfg != NULL )
|
|
pnetCfg->lpVtbl->Uninitialize(pnetCfg);
|
|
|
|
RELEASEPTR(pnetCfg);
|
|
|
|
CoUninitialize();
|
|
|
|
return *pResult == STATUS_SUCCESS ? TRUE : FALSE;
|
|
}
|
|
|
|
|
|
/*******************************************************************************
|
|
*
|
|
* RpcWinStationGetAllProcesses_NT6_notify_flag
|
|
*
|
|
* This callback function is called by the RPC server stub at the very end
|
|
* (after all the calls to MIDL_user_free).
|
|
* This allows us to free the remaining pointers.
|
|
* We also release the lock so that a new RpcWinStationGetAllProcesses
|
|
* can be processed.
|
|
*
|
|
******************************************************************************/
|
|
void RpcWinStationGetAllProcesses_NT6_notify_flag(boolean fServerCalled)
|
|
{
|
|
// DBGPRINT(( "TERMSRV: Entering RpcWinStationGAP_notify\n"));
|
|
|
|
if (!fServerCalled)
|
|
return;
|
|
|
|
if (gbRpcGetAllProcessesOK == TRUE)
|
|
{
|
|
//
|
|
// free our own pointers, free the database and disable the checking
|
|
//
|
|
ReleaseGAPPointersDatabase();
|
|
|
|
//
|
|
// release the lock that has been held since we entered
|
|
// RpcWinStationGetAllProcesses
|
|
//
|
|
RtlLeaveCriticalSection(&gRpcGetAllProcessesLock);
|
|
}
|
|
}
|
|
|
|
|
|
/*******************************************************************************
|
|
*
|
|
* RpcWinStationGetAllProcesses_notify_flag
|
|
*
|
|
* This callback function is called by the RPC server stub at the very end
|
|
* (after all the calls to MIDL_user_free).
|
|
* This allows us to free the remaining pointers.
|
|
* We also release the lock so that a new RpcWinStationGetAllProcesses
|
|
* can be processed.
|
|
*
|
|
******************************************************************************/
|
|
void RpcWinStationGetAllProcesses_notify_flag(boolean fServerCalled)
|
|
{
|
|
// DBGPRINT(( "TERMSRV: Entering RpcWinStationGAP_notify\n"));
|
|
|
|
if (!fServerCalled)
|
|
return;
|
|
|
|
if (gbRpcGetAllProcessesOK == TRUE)
|
|
{
|
|
//
|
|
// free our own pointers, free the database and disable the checking
|
|
//
|
|
ReleaseGAPPointersDatabase();
|
|
|
|
//
|
|
// release the lock that has been held since we entered
|
|
// RpcWinStationGetAllProcesses
|
|
//
|
|
RtlLeaveCriticalSection(&gRpcGetAllProcessesLock);
|
|
}
|
|
}
|
|
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* RpcWinStationGetProcessSid
|
|
*
|
|
* RpcWinStationGetProcessSid API
|
|
*
|
|
* ENTRY:
|
|
* hServer - input, The serrver handle to work on.
|
|
* dwUniqueProcessId - input, ProcessID (its not really unique)
|
|
* ProcessStartTime - input, ProcessStartTime combined with ProcessID
|
|
* identifies a unique process
|
|
* pResult - output, error code
|
|
* pProcessUserSid - output, process user sid
|
|
* dwSidSize - input, sid size allocated.
|
|
*
|
|
* EXIT:
|
|
* TRUE - Requested Sid is in pProcessUserSid.
|
|
* FALSE - The operation failed. Status code is in pResult.
|
|
*
|
|
****************************************************************************/
|
|
BOOLEAN RpcWinStationGetProcessSid(
|
|
HANDLE hServer,
|
|
DWORD dwUniqueProcessId,
|
|
LARGE_INTEGER ProcessStartTime,
|
|
LONG *pResult, // Really an NTSTATUS
|
|
PBYTE pProcessUserSid,
|
|
DWORD dwSidSize,
|
|
DWORD *pdwSizeNeeded)
|
|
{
|
|
PSID pSid;
|
|
|
|
RtlEnterCriticalSection(&gRpcGetAllProcessesLock);
|
|
|
|
// Check if SID cache hasn't grown too much
|
|
CheckSidCacheSize();
|
|
|
|
*pResult = GetSidFromProcessId(
|
|
(HANDLE)(ULONG_PTR)dwUniqueProcessId,
|
|
ProcessStartTime,
|
|
&pSid
|
|
);
|
|
|
|
if (NT_SUCCESS(*pResult)) {
|
|
*pdwSizeNeeded = RtlLengthSid(pSid);
|
|
|
|
if (*pdwSizeNeeded <= dwSidSize) {
|
|
if (pProcessUserSid == NULL) {
|
|
*pResult = STATUS_INVALID_PARAMETER;
|
|
} else {
|
|
*pResult = RtlCopySid(
|
|
*pdwSizeNeeded,
|
|
pProcessUserSid,
|
|
pSid
|
|
);
|
|
}
|
|
} else {
|
|
*pResult = STATUS_BUFFER_TOO_SMALL;
|
|
}
|
|
} else {
|
|
*pdwSizeNeeded = 0;
|
|
}
|
|
|
|
RtlLeaveCriticalSection(&gRpcGetAllProcessesLock);
|
|
|
|
return(*pResult == STATUS_SUCCESS ? TRUE : FALSE);
|
|
}
|
|
|
|
|
|
/*******************************************************************************
|
|
*
|
|
* RpcWinStationRename
|
|
*
|
|
* Renames a window station object in the session manager.
|
|
*
|
|
* ENTRY:
|
|
*
|
|
* pWinStationNameOld (input)
|
|
* Old name of window station.
|
|
*
|
|
* pWinStationNameNew (input)
|
|
* New name of window station.
|
|
*
|
|
*
|
|
* EXIT:
|
|
*
|
|
* TRUE -- The rename operation succeeded.
|
|
*
|
|
* FALSE -- The operation failed. Extended error status is available
|
|
* using GetLastError.
|
|
*
|
|
******************************************************************************/
|
|
|
|
BOOLEAN
|
|
RpcWinStationRename(
|
|
HANDLE hServer,
|
|
DWORD *pResult,
|
|
PWCHAR pWinStationNameOld,
|
|
DWORD NameOldSize,
|
|
PWCHAR pWinStationNameNew,
|
|
DWORD NameNewSize
|
|
)
|
|
{
|
|
*pResult = WinStationRenameWorker(
|
|
pWinStationNameOld,
|
|
NameOldSize,
|
|
pWinStationNameNew,
|
|
NameNewSize
|
|
);
|
|
|
|
return( *pResult == STATUS_SUCCESS ? TRUE : FALSE );
|
|
}
|
|
|
|
|
|
/*******************************************************************************
|
|
*
|
|
* RpcWinStationQueryInformation
|
|
*
|
|
* Queries configuration information about a window station object.
|
|
*
|
|
* ENTRY:
|
|
*
|
|
* WinStationHandle (input)
|
|
* Identifies the window station object. The handle must have
|
|
* WINSTATION_QUERY access.
|
|
*
|
|
* WinStationInformationClass (input)
|
|
* Specifies the type of information to retrieve from the specified
|
|
* window station object.
|
|
*
|
|
* pWinStationInformation (output)
|
|
* A pointer to a buffer that will receive information about the
|
|
* specified window station. The format and contents of the buffer
|
|
* depend on the specified information class being queried.
|
|
*
|
|
* WinStationInformationLength (input)
|
|
* Specifies the length in bytes of the window station information
|
|
* buffer.
|
|
*
|
|
* pReturnLength (output)
|
|
* An optional parameter that if specified, receives the number of
|
|
* bytes placed in the window station information buffer.
|
|
*
|
|
* EXIT:
|
|
*
|
|
* TRUE -- The query succeeded, and the buffer contains the requested data.
|
|
*
|
|
* FALSE -- The operation failed. Extended error status is available
|
|
* using GetLastError.
|
|
*
|
|
******************************************************************************/
|
|
|
|
BOOLEAN
|
|
RpcWinStationQueryInformation(
|
|
HANDLE hServer,
|
|
DWORD *pResult,
|
|
ULONG LogonId,
|
|
DWORD WinStationInformationClass,
|
|
PCHAR pWinStationInformation,
|
|
DWORD WinStationInformationLength,
|
|
DWORD *pReturnLength
|
|
)
|
|
{
|
|
if (!pReturnLength) {
|
|
*pResult = STATUS_INVALID_USER_BUFFER;
|
|
return FALSE;
|
|
}
|
|
|
|
// Do minimal buffer validation
|
|
if ((pWinStationInformation == NULL ) && (WinStationInformationLength != 0)) {
|
|
*pResult = STATUS_INVALID_USER_BUFFER;
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
*pResult = xxxWinStationQueryInformation(
|
|
LogonId,
|
|
WinStationInformationClass,
|
|
pWinStationInformation,
|
|
WinStationInformationLength,
|
|
pReturnLength
|
|
);
|
|
|
|
|
|
return( *pResult == STATUS_SUCCESS ? TRUE : FALSE );
|
|
}
|
|
|
|
|
|
/*******************************************************************************
|
|
*
|
|
* RpcWinStationSetInformation
|
|
*
|
|
* Sets configuration information for a window station object.
|
|
*
|
|
* ENTRY:
|
|
*
|
|
* WinStationHandle (input)
|
|
* Identifies the window station object. The handle must have
|
|
* WINSTATION_SET access.
|
|
*
|
|
* WinStationInformationClass (input)
|
|
* Specifies the type of information to retrieve from the specified
|
|
* window station object.
|
|
*
|
|
* pWinStationInformation (input)
|
|
* A pointer to a buffer that contains information to set 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.
|
|
*
|
|
* EXIT:
|
|
*
|
|
* TRUE -- The set operation succeeded.
|
|
*
|
|
* FALSE -- The operation failed. Extended error status is available
|
|
* using GetLastError.
|
|
*
|
|
******************************************************************************/
|
|
|
|
BOOLEAN
|
|
RpcWinStationSetInformation(
|
|
HANDLE hServer,
|
|
DWORD *pResult,
|
|
ULONG LogonId,
|
|
DWORD WinStationInformationClass,
|
|
PCHAR pWinStationInformation,
|
|
ULONG WinStationInformationLength
|
|
)
|
|
{
|
|
// Do minimal buffer validation
|
|
if ((pWinStationInformation == NULL ) && (WinStationInformationLength != 0)) {
|
|
*pResult = STATUS_INVALID_USER_BUFFER;
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
*pResult = xxxWinStationSetInformation(
|
|
LogonId,
|
|
WinStationInformationClass,
|
|
pWinStationInformation,
|
|
WinStationInformationLength
|
|
);
|
|
|
|
|
|
return( *pResult == STATUS_SUCCESS ? TRUE : FALSE );
|
|
}
|
|
|
|
|
|
/*******************************************************************************
|
|
*
|
|
* RpcLogonIdFromWinStationName
|
|
*
|
|
* Returns the LogonId for the specified window station name.
|
|
*
|
|
* ENTRY:
|
|
*
|
|
* pWinStationName (input)
|
|
* Window station name.
|
|
*
|
|
* pLogonId (output)
|
|
* Pointer to where to place the LogonId if found
|
|
*
|
|
* EXIT:
|
|
*
|
|
* If the function succeeds, the return value is TRUE, otherwise, it is
|
|
* FALSE.
|
|
* To get extended error information, use the GetLastError function.
|
|
*
|
|
******************************************************************************/
|
|
BOOLEAN
|
|
RpcLogonIdFromWinStationName(
|
|
HANDLE hServer,
|
|
DWORD *pResult,
|
|
PWCHAR pWinStationName,
|
|
DWORD NameSize,
|
|
PULONG pLogonId
|
|
)
|
|
{
|
|
|
|
*pResult = LogonIdFromWinStationNameWorker(
|
|
pWinStationName,
|
|
NameSize,
|
|
pLogonId
|
|
);
|
|
|
|
return( *pResult == STATUS_SUCCESS ? TRUE : FALSE );
|
|
}
|
|
|
|
|
|
/*******************************************************************************
|
|
*
|
|
* RpcWinStationNameFromLogonId
|
|
*
|
|
* Returns the WinStation name for the specified LogonId.
|
|
*
|
|
* ENTRY:
|
|
*
|
|
* LogonId (output)
|
|
* LogonId to query
|
|
*
|
|
* pWinStationName (input)
|
|
* Location to return WinStation name
|
|
*
|
|
* EXIT:
|
|
*
|
|
* If the function succeeds, the return value is TRUE, otherwise, it is
|
|
* FALSE.
|
|
* To get extended error information, use the GetLastError function.
|
|
*
|
|
******************************************************************************/
|
|
BOOLEAN
|
|
RpcWinStationNameFromLogonId(
|
|
HANDLE hServer,
|
|
DWORD *pResult,
|
|
ULONG LogonId,
|
|
PWCHAR pWinStationName,
|
|
DWORD NameSize
|
|
)
|
|
{
|
|
if((NameSize < ((WINSTATIONNAME_LENGTH + 1) * sizeof(WCHAR))) ||
|
|
(IsBadWritePtr(pWinStationName, NameSize)))
|
|
{
|
|
*pResult = STATUS_INVALID_PARAMETER;
|
|
return FALSE;
|
|
}
|
|
|
|
*pResult = IcaWinStationNameFromLogonId(
|
|
LogonId,
|
|
pWinStationName
|
|
);
|
|
|
|
return( *pResult == STATUS_SUCCESS ? TRUE : FALSE );
|
|
}
|
|
|
|
|
|
/*******************************************************************************
|
|
*
|
|
* RpcWinStationDisconnect
|
|
*
|
|
* Disconects a window station object from the configured terminal and Pd.
|
|
* While disconnected all window station i/o is bit bucketed.
|
|
*
|
|
* ENTRY:
|
|
*
|
|
* LogonId (input)
|
|
* ID of window station object to disconnect.
|
|
* bWait (input)
|
|
* Specifies whether or not to wait for disconnect to complete
|
|
*
|
|
* EXIT:
|
|
*
|
|
* TRUE -- The disconnect operation succeeded.
|
|
*
|
|
* FALSE -- The operation failed. Extended error status is available
|
|
* using GetLastError.
|
|
*
|
|
******************************************************************************/
|
|
BOOLEAN
|
|
RpcWinStationDisconnect(
|
|
HANDLE hServer,
|
|
DWORD *pResult,
|
|
ULONG LogonId,
|
|
BOOLEAN bWait
|
|
)
|
|
{
|
|
*pResult = WinStationDisconnectWorker(
|
|
LogonId,
|
|
bWait,
|
|
TRUE
|
|
);
|
|
return( *pResult == STATUS_SUCCESS ? TRUE : FALSE );
|
|
}
|
|
|
|
|
|
/*******************************************************************************
|
|
*
|
|
* RpcWinStationConnect
|
|
*
|
|
* Connects a window station object to the configured terminal and Pd.
|
|
*
|
|
* ENTRY:
|
|
*
|
|
* LogonId (input)
|
|
* ID of window station object to connect.
|
|
*
|
|
* TargetLogonId (input)
|
|
* ID of target window station.
|
|
*
|
|
* pPassword (input)
|
|
* password of LogonId window station (not needed if same domain/username)
|
|
*
|
|
* bWait (input)
|
|
* Specifies whether or not to wait for connect to complete
|
|
*
|
|
* EXIT:
|
|
*
|
|
* TRUE -- The connect operation succeeded.
|
|
*
|
|
* FALSE -- The operation failed. Extended error status is available
|
|
* using GetLastError.
|
|
*
|
|
******************************************************************************/
|
|
BOOLEAN
|
|
RpcWinStationConnect(
|
|
HANDLE hServer,
|
|
DWORD *pResult,
|
|
ULONG ClientLogonId,
|
|
ULONG ConnectLogonId,
|
|
ULONG TargetLogonId,
|
|
PWCHAR pPassword,
|
|
DWORD PasswordSize,
|
|
BOOLEAN bWait
|
|
)
|
|
{
|
|
|
|
// Do some buffer Validation
|
|
|
|
|
|
*pResult = IsZeroterminateStringW(pPassword, PasswordSize );
|
|
|
|
if (*pResult != STATUS_SUCCESS) {
|
|
return FALSE;
|
|
}
|
|
*pResult = WinStationConnectWorker(
|
|
ClientLogonId,
|
|
ConnectLogonId,
|
|
TargetLogonId,
|
|
pPassword,
|
|
PasswordSize,
|
|
bWait,
|
|
FALSE
|
|
);
|
|
|
|
return( *pResult == STATUS_SUCCESS ? TRUE : FALSE );
|
|
}
|
|
|
|
|
|
/*****************************************************************************
|
|
* RpcWinStationVirtualOpen
|
|
*
|
|
* Open a virtual channel
|
|
****************************************************************************/
|
|
BOOLEAN
|
|
RpcWinStationVirtualOpen(
|
|
HANDLE hServer,
|
|
DWORD *pResult,
|
|
ULONG LogonId,
|
|
DWORD Pid,
|
|
PCHAR pVirtualName, /* ascii name */
|
|
DWORD NameSize,
|
|
DWORD *pHandle
|
|
)
|
|
{
|
|
RPC_STATUS RpcStatus;
|
|
NTSTATUS Status = STATUS_SUCCESS;
|
|
PWINSTATION pWinStation;
|
|
HANDLE pidhandle = NULL;
|
|
HANDLE handle = NULL;
|
|
|
|
// Check channel name length.
|
|
|
|
try{
|
|
ULONG ulChannelNameLength;
|
|
ulChannelNameLength = strlen( pVirtualName );
|
|
if ( !ulChannelNameLength ||
|
|
(ulChannelNameLength > VIRTUALCHANNELNAME_LENGTH) ||
|
|
ulChannelNameLength >= NameSize
|
|
) {
|
|
Status = (DWORD)STATUS_INVALID_PARAMETER;
|
|
goto done;
|
|
}
|
|
} except(EXCEPTION_EXECUTE_HANDLER){
|
|
Status = (DWORD)STATUS_INVALID_PARAMETER;
|
|
goto done;
|
|
}
|
|
|
|
/*
|
|
* Impersonate the client so that when the attempt is made to open the
|
|
* process, it will fail if the client does not have dup handle access.
|
|
*/
|
|
RpcStatus = RpcImpersonateClient( NULL );
|
|
if( RpcStatus != RPC_S_OK ) {
|
|
KdPrintEx((DPFLTR_TERMSRV_ID, DPFLTR_ERROR_LEVEL, "TERMSRV: RpcWinStationVirtualOpen: Not impersonating! RpcStatus 0x%x\n",RpcStatus));
|
|
Status = (DWORD)STATUS_CANNOT_IMPERSONATE;
|
|
goto done;
|
|
}
|
|
|
|
pidhandle = OpenProcess( PROCESS_DUP_HANDLE, FALSE, Pid );
|
|
|
|
RpcRevertToSelf();
|
|
|
|
if ( !pidhandle ) {
|
|
Status = (DWORD)STATUS_ACCESS_DENIED;
|
|
goto done;
|
|
}
|
|
|
|
/*
|
|
* Find and lock client WinStation
|
|
*/
|
|
pWinStation = FindWinStationById( LogonId, FALSE );
|
|
if ( pWinStation == NULL ) {
|
|
Status = (DWORD)STATUS_ACCESS_DENIED;
|
|
goto done;
|
|
}
|
|
|
|
/*
|
|
* Make sure the caller has WINSTATION_VIRTUAL access
|
|
*/
|
|
Status = RpcCheckClientAccess( pWinStation, WINSTATION_VIRTUAL, FALSE );
|
|
if ( !NT_SUCCESS( Status ) ) {
|
|
ReleaseWinStation( pWinStation );
|
|
goto done;
|
|
}
|
|
|
|
/*
|
|
* Don't allow VirtualChannel opens on listner or idle sessions
|
|
*/
|
|
if( pWinStation->State == State_Listen ||
|
|
pWinStation->State == State_Idle )
|
|
{
|
|
ReleaseWinStation( pWinStation);
|
|
Status = (DWORD)STATUS_ACCESS_DENIED;
|
|
goto done;
|
|
}
|
|
|
|
/*
|
|
* Duplicate the virtual channel.
|
|
*/
|
|
Status = IcaChannelOpen( pWinStation->hIca,
|
|
Channel_Virtual,
|
|
pVirtualName,
|
|
&handle );
|
|
|
|
ReleaseWinStation( pWinStation );
|
|
|
|
if ( !NT_SUCCESS( Status ) ) {
|
|
goto done;
|
|
}
|
|
|
|
Status = NtDuplicateObject( NtCurrentProcess(),
|
|
handle,
|
|
pidhandle,
|
|
(PHANDLE)pHandle,
|
|
0,
|
|
0,
|
|
DUPLICATE_SAME_ACCESS |
|
|
DUPLICATE_SAME_ATTRIBUTES );
|
|
|
|
done:
|
|
if ( handle )
|
|
IcaChannelClose( handle );
|
|
if ( pidhandle )
|
|
NtClose( pidhandle );
|
|
*pResult = Status;
|
|
|
|
if ( NT_SUCCESS(Status) )
|
|
return( TRUE );
|
|
else
|
|
return( FALSE );
|
|
}
|
|
|
|
|
|
/*****************************************************************************
|
|
* RpcWinStationBeepOpen
|
|
*
|
|
* Open a beep channel
|
|
****************************************************************************/
|
|
BOOLEAN
|
|
RpcWinStationBeepOpen(
|
|
HANDLE hServer,
|
|
DWORD *pResult,
|
|
ULONG LogonId,
|
|
DWORD Pid,
|
|
DWORD *pHandle
|
|
)
|
|
{
|
|
RPC_STATUS RpcStatus;
|
|
NTSTATUS Status = STATUS_SUCCESS;
|
|
PWINSTATION pWinStation;
|
|
HANDLE pidhandle = NULL;
|
|
HANDLE handle = NULL;
|
|
|
|
/*
|
|
* Impersonate the client so that when the attempt is made to open the
|
|
* process, it will fail if the client does not have dup handle access.
|
|
*/
|
|
RpcStatus = RpcImpersonateClient( NULL );
|
|
if( RpcStatus != RPC_S_OK ) {
|
|
KdPrintEx((DPFLTR_TERMSRV_ID, DPFLTR_ERROR_LEVEL, "TERMSRV: RpcWinStationBeepOpen: Not impersonating! RpcStatus 0x%x\n",RpcStatus));
|
|
Status = (DWORD)STATUS_CANNOT_IMPERSONATE;
|
|
goto done;
|
|
}
|
|
|
|
pidhandle = OpenProcess( PROCESS_DUP_HANDLE, FALSE, Pid );
|
|
|
|
RpcRevertToSelf();
|
|
|
|
if ( !pidhandle ) {
|
|
Status = (DWORD)STATUS_ACCESS_DENIED;
|
|
goto done;
|
|
}
|
|
|
|
/*
|
|
* Find and lock client WinStation
|
|
*/
|
|
pWinStation = FindWinStationById( LogonId, FALSE );
|
|
if ( pWinStation == NULL ) {
|
|
Status = (DWORD)STATUS_ACCESS_DENIED;
|
|
goto done;
|
|
}
|
|
|
|
/*
|
|
* Make sure the caller has WINSTATION_VIRTUAL access
|
|
*/
|
|
Status = RpcCheckClientAccess( pWinStation, WINSTATION_VIRTUAL, FALSE );
|
|
if ( !NT_SUCCESS( Status ) ) {
|
|
ReleaseWinStation( pWinStation );
|
|
goto done;
|
|
}
|
|
|
|
/*
|
|
* Duplicate the beep channel.
|
|
*/
|
|
Status = IcaChannelOpen( pWinStation->hIca,
|
|
Channel_Beep,
|
|
NULL,
|
|
&handle );
|
|
|
|
ReleaseWinStation( pWinStation );
|
|
|
|
if ( !NT_SUCCESS( Status ) ) {
|
|
goto done;
|
|
}
|
|
|
|
Status = NtDuplicateObject( NtCurrentProcess(),
|
|
handle,
|
|
pidhandle,
|
|
(PHANDLE)pHandle,
|
|
0,
|
|
0,
|
|
DUPLICATE_SAME_ACCESS |
|
|
DUPLICATE_SAME_ATTRIBUTES );
|
|
|
|
done:
|
|
if ( handle )
|
|
IcaChannelClose( handle );
|
|
if ( pidhandle )
|
|
NtClose( pidhandle );
|
|
*pResult = Status;
|
|
|
|
if ( NT_SUCCESS(Status) )
|
|
return( TRUE );
|
|
else
|
|
return( FALSE );
|
|
}
|
|
|
|
|
|
/*******************************************************************************
|
|
* RpcWinStationReset
|
|
*
|
|
* Reset the specified window station.
|
|
*
|
|
* ENTRY:
|
|
* LogonId (input)
|
|
* Identifies the window station object to reset.
|
|
* bWait (input)
|
|
* Specifies whether or not to wait for reset to complete
|
|
*
|
|
* EXIT:
|
|
* TRUE -- The reset operation succeeded.
|
|
* FALSE -- The operation failed. Extended error status is available
|
|
* using GetLastError.
|
|
******************************************************************************/
|
|
BOOLEAN
|
|
RpcWinStationReset(
|
|
HANDLE hServer,
|
|
DWORD *pResult,
|
|
ULONG LogonId,
|
|
BOOLEAN bWait
|
|
)
|
|
{
|
|
NTSTATUS Status;
|
|
|
|
*pResult = WinStationResetWorker(
|
|
LogonId,
|
|
bWait,
|
|
TRUE, // Rpc caller, must check access
|
|
TRUE // By default, recreate the WinStation
|
|
);
|
|
|
|
//
|
|
// dont return STATUS_TIMEOUT.
|
|
//
|
|
|
|
if (*pResult == STATUS_TIMEOUT) *pResult = STATUS_UNSUCCESSFUL;
|
|
return( *pResult == STATUS_SUCCESS ? TRUE : FALSE );
|
|
}
|
|
|
|
|
|
/*******************************************************************************
|
|
* RpcWinStationShadowStop
|
|
*
|
|
* Stop any shadow operation on the specified window station.
|
|
*
|
|
* ENTRY:
|
|
* LogonId (input)
|
|
* Identifies the window station object to reset.
|
|
* bWait (input)
|
|
* Specifies whether or not to wait for reset to complete
|
|
*
|
|
* EXIT:
|
|
* TRUE -- The stop shadow operation succeeded.
|
|
* FALSE -- The operation failed. Extended error status is available
|
|
* using GetLastError.
|
|
******************************************************************************/
|
|
BOOLEAN
|
|
RpcWinStationShadowStop(
|
|
HANDLE hServer,
|
|
DWORD *pResult,
|
|
ULONG LogonId,
|
|
BOOLEAN bWait // unused - later?
|
|
)
|
|
{
|
|
PWINSTATION pWinStation;
|
|
ULONG ClientLogonId;
|
|
NTSTATUS Status;
|
|
RPC_STATUS RpcStatus;
|
|
UINT LocalFlag;
|
|
|
|
|
|
TRACE((hTrace,TC_ICASRV,TT_API1, "TERMSRV: RpcWinStationShadowStop, LogonId=%d\n", LogonId ));
|
|
|
|
|
|
/*
|
|
* Find and lock the WinStation struct for the specified LogonId
|
|
*/
|
|
pWinStation = FindWinStationById( LogonId, FALSE );
|
|
if ( pWinStation == NULL ) {
|
|
Status = STATUS_CTX_WINSTATION_NOT_FOUND;
|
|
goto notfound;
|
|
}
|
|
|
|
//
|
|
// If the session is NOT being shadowed then bail out
|
|
//
|
|
if ( !( pWinStation->State == State_Active &&
|
|
!IsListEmpty(&pWinStation->ShadowHead) ) ) {
|
|
Status = STATUS_CTX_SHADOW_NOT_RUNNING;
|
|
goto done;
|
|
}
|
|
|
|
/*
|
|
* Impersonate the client
|
|
*/
|
|
RpcStatus = RpcImpersonateClient( NULL );
|
|
if( RpcStatus != RPC_S_OK ) {
|
|
KdPrintEx((DPFLTR_TERMSRV_ID, DPFLTR_ERROR_LEVEL, "TERMSRV: RpcWinStationShadowStop: Not impersonating! RpcStatus 0x%x\n",RpcStatus));
|
|
Status = STATUS_CANNOT_IMPERSONATE;
|
|
goto done;
|
|
}
|
|
|
|
//
|
|
// If its remote RPC call we should ignore ClientLogonId
|
|
//
|
|
RpcStatus = I_RpcBindingIsClientLocal(
|
|
0, // Active RPC call we are servicing
|
|
&LocalFlag
|
|
);
|
|
|
|
if( RpcStatus != RPC_S_OK ) {
|
|
KdPrintEx((DPFLTR_TERMSRV_ID, DPFLTR_ERROR_LEVEL, "ERMSRV: RpcWinStationShadowStop: I_RpcBindingIsClientLocal() failed : 0x%x\n",RpcStatus));
|
|
RpcRevertToSelf();
|
|
Status = STATUS_UNSUCCESSFUL;
|
|
goto done;
|
|
}
|
|
|
|
//
|
|
// Get the client session id if it's local
|
|
//
|
|
if (LocalFlag) {
|
|
Status = RpcGetClientLogonId( &ClientLogonId );
|
|
if ( !NT_SUCCESS( Status ) ) {
|
|
RpcRevertToSelf();
|
|
goto done;
|
|
}
|
|
}
|
|
|
|
|
|
//
|
|
// Check for Disconnect or Reset rights since these two operations
|
|
// would anyway terminate the shadow.
|
|
//
|
|
Status = RpcCheckClientAccess( pWinStation, WINSTATION_DISCONNECT | WINSTATION_RESET, TRUE );
|
|
|
|
//
|
|
// If the access is denied then see if the client is on the same session
|
|
// and check to see if the user has the veto right to being shadowed.
|
|
//
|
|
if( !NT_SUCCESS(Status) && LocalFlag && (ClientLogonId == LogonId ) ) {
|
|
|
|
switch ( pWinStation->Config.Config.User.Shadow ) {
|
|
|
|
case Shadow_EnableInputNotify :
|
|
case Shadow_EnableNoInputNotify :
|
|
|
|
Status = STATUS_SUCCESS;
|
|
break;
|
|
|
|
default :
|
|
|
|
// other cases : don't touch the Status
|
|
break;
|
|
}
|
|
|
|
} // else : The call comes from a remote machine or a different session.
|
|
|
|
RpcRevertToSelf();
|
|
|
|
if ( !NT_SUCCESS( Status ) ) {
|
|
goto done;
|
|
}
|
|
|
|
Status = WinStationStopAllShadows( pWinStation );
|
|
|
|
done:
|
|
|
|
ReleaseWinStation( pWinStation );
|
|
|
|
notfound:
|
|
|
|
*pResult = Status;
|
|
|
|
return( *pResult == STATUS_SUCCESS ? TRUE : FALSE );
|
|
}
|
|
|
|
|
|
/*******************************************************************************
|
|
* RpcWinStationShutdownSystem
|
|
*
|
|
* Shutdown the system and optionally logoff all WinStations
|
|
* and/or reboot the system.
|
|
*
|
|
* ENTRY:
|
|
* ShutdownFlags (input)
|
|
* Flags which specify shutdown options.
|
|
*
|
|
* EXIT:
|
|
* TRUE -- The shutdown operation succeeded.
|
|
* FALSE -- The operation failed. Extended error status is available
|
|
* using GetLastError.
|
|
******************************************************************************/
|
|
BOOLEAN
|
|
RpcWinStationShutdownSystem(
|
|
HANDLE hServer,
|
|
DWORD *pResult,
|
|
DWORD ClientLogonId,
|
|
ULONG ShutdownFlags
|
|
)
|
|
{
|
|
*pResult = WinStationShutdownSystemWorker( ClientLogonId, ShutdownFlags );
|
|
if (AuditingEnabled() && (ShutdownFlags & WSD_LOGOFF)
|
|
&& (*pResult == STATUS_SUCCESS))
|
|
AuditShutdownEvent();
|
|
|
|
//
|
|
// dont return STATUS_TIMEOUT.
|
|
//
|
|
|
|
if (*pResult == STATUS_TIMEOUT) *pResult = STATUS_UNSUCCESSFUL;
|
|
return( *pResult == STATUS_SUCCESS ? TRUE : FALSE );
|
|
}
|
|
|
|
|
|
/*******************************************************************************
|
|
* RpcWinStationTerminateProcess
|
|
*
|
|
* Terminate the specified process
|
|
*
|
|
* ENTRY:
|
|
* hServer (input)
|
|
* handle to winframe server
|
|
* pResult (output)
|
|
* address to return error status
|
|
* ProcessId (input)
|
|
* process id of the process to terminate
|
|
* ExitCode (input)
|
|
* Termination status for each thread in the process
|
|
*
|
|
* EXIT:
|
|
* TRUE -- The terminate operation succeeded.
|
|
* FALSE -- The operation failed. Extended error status is available
|
|
* using GetLastError.
|
|
******************************************************************************/
|
|
BOOLEAN
|
|
RpcWinStationTerminateProcess(
|
|
HANDLE hServer,
|
|
DWORD *pResult,
|
|
ULONG ProcessId,
|
|
ULONG ExitCode
|
|
)
|
|
{
|
|
RPC_STATUS RpcStatus;
|
|
|
|
/*
|
|
* Impersonate the client so that when the attempt is made to terminate
|
|
* the process, it will fail if the account is not admin.
|
|
*/
|
|
RpcStatus = RpcImpersonateClient( NULL );
|
|
if( RpcStatus != RPC_S_OK ) {
|
|
KdPrintEx((DPFLTR_TERMSRV_ID, DPFLTR_ERROR_LEVEL, "TERMSRV: RpcWinStationTerminateProcess: Not impersonating! RpcStatus 0x%x\n",RpcStatus));
|
|
*pResult = (DWORD)STATUS_CANNOT_IMPERSONATE;
|
|
return( FALSE );
|
|
}
|
|
|
|
*pResult = WinStationTerminateProcessWorker( ProcessId, ExitCode );
|
|
|
|
RpcRevertToSelf();
|
|
|
|
return( *pResult == STATUS_SUCCESS ? TRUE : FALSE );
|
|
}
|
|
|
|
|
|
/*******************************************************************************
|
|
* RpcWinStationWaitSystemEvent
|
|
*
|
|
* Waits for an event (WinStation create, delete, connect, etc) before
|
|
* returning to the caller.
|
|
*
|
|
* ENTRY:
|
|
* EventFlags (input)
|
|
* Bit mask that specifies which event(s) to wait for.
|
|
* pEventFlags (output)
|
|
* Bit mask of event(s) that occurred.
|
|
*
|
|
* EXIT:
|
|
* TRUE -- The wait event operation succeeded.
|
|
* FALSE -- The operation failed. Extended error status is available
|
|
* using GetLastError.
|
|
******************************************************************************/
|
|
BOOLEAN
|
|
RpcWinStationWaitSystemEvent(
|
|
HANDLE hServer,
|
|
DWORD *pResult,
|
|
ULONG EventMask,
|
|
PULONG pEventFlags
|
|
)
|
|
{
|
|
*pResult = WinStationWaitSystemEventWorker( hServer, EventMask, pEventFlags );
|
|
|
|
return( *pResult == STATUS_SUCCESS ? TRUE : FALSE );
|
|
}
|
|
|
|
|
|
/*****************************************************************************
|
|
* RpcWinStationShadow
|
|
*
|
|
* Start a Winstation shadow operation
|
|
*
|
|
* ENTRY:
|
|
* hServer (input)
|
|
* shadow client server handle
|
|
* pResult (output)
|
|
* address to return status
|
|
* LogonId (input)
|
|
* shadow client logon id
|
|
* pTargetServerName (input)
|
|
* shadow target server name
|
|
* NameSize (input)
|
|
* size of pTargetServerName (input)
|
|
* TargetLogonId (input)
|
|
* shadow target login id (where the app is running)
|
|
* HotkeyVk (input)
|
|
* virtual key to press to stop shadow
|
|
* HotkeyModifiers (input)
|
|
* virtual modifer to press to stop shadow (i.e. shift, control)
|
|
****************************************************************************/
|
|
BOOLEAN
|
|
RpcWinStationShadow(
|
|
HANDLE hServer,
|
|
DWORD *pResult,
|
|
ULONG LogonId,
|
|
PWSTR pTargetServerName,
|
|
ULONG NameSize,
|
|
ULONG TargetLogonId,
|
|
BYTE HotkeyVk,
|
|
USHORT HotkeyModifiers
|
|
)
|
|
{
|
|
RPC_STATUS RpcStatus;
|
|
|
|
// Do minimal buffer validation
|
|
if (pTargetServerName != NULL) {
|
|
// No of bytes is sent from the Client, so send half the length got
|
|
*pResult = (DWORD)IsZeroterminateStringW(pTargetServerName, NameSize / sizeof(WCHAR));
|
|
if (*pResult != STATUS_SUCCESS) {
|
|
return FALSE;
|
|
}
|
|
|
|
if (wcslen(pTargetServerName) > (MAX_COMPUTERNAME_LENGTH)) {
|
|
*pResult = STATUS_INVALID_USER_BUFFER;
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Impersonate the client so that when the shadow connection is
|
|
* created, it will have the correct security.
|
|
*/
|
|
RpcStatus = RpcImpersonateClient( NULL );
|
|
if( RpcStatus != RPC_S_OK ) {
|
|
KdPrintEx((DPFLTR_TERMSRV_ID, DPFLTR_ERROR_LEVEL, "TERMSRV: RpcWinStationShadow: Not impersonating! RpcStatus 0x%x\n",RpcStatus));
|
|
*pResult = (DWORD)STATUS_CANNOT_IMPERSONATE;
|
|
return( FALSE );
|
|
}
|
|
|
|
*pResult = (DWORD)WinStationShadowWorker( LogonId,
|
|
pTargetServerName,
|
|
TargetLogonId,
|
|
HotkeyVk,
|
|
HotkeyModifiers );
|
|
|
|
RpcRevertToSelf();
|
|
|
|
return( *pResult == STATUS_SUCCESS ? TRUE : FALSE );
|
|
}
|
|
|
|
|
|
/*****************************************************************************
|
|
* RpcWinStationShadowTargetSetup
|
|
*
|
|
* Setup a Winstation shadow operation
|
|
*
|
|
* ENTRY:
|
|
* hServer (input)
|
|
* target server
|
|
* pResult (output)
|
|
* address to return status
|
|
* LogonId (input)
|
|
* target logon id
|
|
****************************************************************************/
|
|
BOOLEAN
|
|
RpcWinStationShadowTargetSetup(
|
|
HANDLE hServer,
|
|
DWORD *pResult,
|
|
IN ULONG LogonId
|
|
)
|
|
{
|
|
RPC_STATUS RpcStatus;
|
|
|
|
/*
|
|
* Impersonate the client so that the shadow connect request
|
|
* will be under the correct security context.
|
|
*/
|
|
RpcStatus = RpcImpersonateClient( NULL );
|
|
if( RpcStatus != RPC_S_OK ) {
|
|
KdPrintEx((DPFLTR_TERMSRV_ID, DPFLTR_ERROR_LEVEL, "TERMSRV: RpcWinStationShadow: Not impersonating! RpcStatus 0x%x\n",RpcStatus));
|
|
*pResult = (DWORD)STATUS_CANNOT_IMPERSONATE;
|
|
return( FALSE );
|
|
}
|
|
|
|
*pResult = (DWORD)WinStationShadowTargetSetupWorker( LogonId );
|
|
|
|
RpcRevertToSelf();
|
|
|
|
return( *pResult == STATUS_SUCCESS ? TRUE : FALSE );
|
|
}
|
|
|
|
|
|
/*****************************************************************************
|
|
* RpcWinStationShadowTarget
|
|
*
|
|
* Start a Winstation shadow operation
|
|
*
|
|
* ENTRY:
|
|
* hServer (input)
|
|
* target server
|
|
* pResult (output)
|
|
* address to return status
|
|
* LogonId (input)
|
|
* target logon id
|
|
* pConfig (input)
|
|
* pointer to WinStation config data
|
|
* NameSize (input)
|
|
* length of config data
|
|
* pAddress (input)
|
|
* address of shadow client
|
|
* AddressSize (input)
|
|
* length of address
|
|
* pModuleData (input)
|
|
* pointer to client module data
|
|
* ModuleDataLength (input)
|
|
* length of client module data
|
|
* pThinwireData (input)
|
|
* pointer to thinwire module data
|
|
* ThinwireDataLength (input)
|
|
* length of thinwire module data
|
|
* pClientName (input)
|
|
* pointer to client name string (domain/username)
|
|
* ClientNameLength (input)
|
|
* length of client name string
|
|
****************************************************************************/
|
|
BOOLEAN
|
|
RpcWinStationShadowTarget(
|
|
HANDLE hServer,
|
|
DWORD *pResult,
|
|
IN ULONG LogonId,
|
|
IN PBYTE pConfig,
|
|
IN DWORD NameSize,
|
|
IN PBYTE pAddress,
|
|
IN DWORD AddressSize,
|
|
IN PBYTE pModuleData,
|
|
IN DWORD ModuleDataLength,
|
|
IN PBYTE pThinwireData,
|
|
IN DWORD ThinwireDataLength,
|
|
IN PBYTE pClientName,
|
|
IN DWORD ClientNameLength
|
|
)
|
|
{
|
|
RPC_STATUS RpcStatus;
|
|
|
|
/*
|
|
* Validate buffers .
|
|
*/
|
|
if (AddressSize < sizeof(ICA_STACK_ADDRESS) ||
|
|
pClientName == NULL ||
|
|
NameSize < sizeof(WINSTATIONCONFIG2)) {
|
|
*pResult = STATUS_INVALID_USER_BUFFER;
|
|
return FALSE;
|
|
}
|
|
// No of bytes is sent from the Client, so send half the length got
|
|
*pResult = (DWORD)IsZeroterminateStringW((PWCHAR)pClientName,ClientNameLength / sizeof(WCHAR));
|
|
if (*pResult != STATUS_SUCCESS) {
|
|
return FALSE;
|
|
}
|
|
|
|
/*
|
|
* Impersonate the client so that the shadow connect request
|
|
* will be under the correct security context.
|
|
*/
|
|
RpcStatus = RpcImpersonateClient( NULL );
|
|
if( RpcStatus != RPC_S_OK ) {
|
|
KdPrintEx((DPFLTR_TERMSRV_ID, DPFLTR_ERROR_LEVEL, "TERMSRV: RpcWinStationShadow: Not impersonating! RpcStatus 0x%x\n",RpcStatus));
|
|
*pResult = (DWORD)STATUS_CANNOT_IMPERSONATE;
|
|
return( FALSE );
|
|
}
|
|
|
|
*pResult = (DWORD)WinStationShadowTargetWorker(
|
|
FALSE, // Shadower not a Help Assistant session.
|
|
FALSE, // protocol shadow, can't be help assistant session
|
|
LogonId,
|
|
(PWINSTATIONCONFIG2) pConfig,
|
|
(PICA_STACK_ADDRESS) pAddress,
|
|
(PVOID) pModuleData,
|
|
ModuleDataLength,
|
|
(PVOID) pThinwireData,
|
|
ThinwireDataLength,
|
|
pClientName );
|
|
|
|
RpcRevertToSelf();
|
|
|
|
return( *pResult == STATUS_SUCCESS ? TRUE : FALSE );
|
|
}
|
|
|
|
|
|
/*******************************************************************************
|
|
* RpcWinStationGenerateLicense
|
|
*
|
|
* Called to generate a license from a given serial number string.
|
|
*
|
|
* ENTRY:
|
|
* hServer (input)
|
|
* Server handle
|
|
* pSerialNumberString (input)
|
|
* Pointer to a null-terminated, wide-character Serial Number string
|
|
* pLicense (output)
|
|
* Pointer to a License structure that will be filled in with
|
|
* information based on pSerialNumberString
|
|
* LicenseSize (input)
|
|
* Size in bytes of the structure pointed to by pLicense
|
|
*
|
|
* EXIT:
|
|
* TRUE -- The generate operation succeeded.
|
|
* FALSE -- The operation failed. Extended error status is available
|
|
* in pResult.
|
|
******************************************************************************/
|
|
BOOLEAN
|
|
RpcWinStationGenerateLicense(
|
|
HANDLE hServer,
|
|
DWORD *pResult,
|
|
PWCHAR pSerialNumberString,
|
|
DWORD Length,
|
|
PCHAR pLicense,
|
|
DWORD LicenseSize
|
|
)
|
|
{
|
|
RPC_STATUS RpcStatus;
|
|
|
|
//
|
|
// The WinFrame licensing API's load the Ulm DLL in the context
|
|
// of the ICASRV process, and then attempt to access the
|
|
// WinFrame licensing registry keys. By impersonating the caller,
|
|
// we can make sure that the license key operation is done under
|
|
// the callers subject context. This will fail for non-admin callers, as
|
|
// defined by the ACL placed on the licensing keys.
|
|
//
|
|
|
|
/*
|
|
* Impersonate the client
|
|
*/
|
|
RpcStatus = RpcImpersonateClient( NULL );
|
|
if( RpcStatus != RPC_S_OK ) {
|
|
KdPrintEx((DPFLTR_TERMSRV_ID, DPFLTR_ERROR_LEVEL, "TERMSRV: RpcWinStationGenerateLicense: Not impersonating! RpcStatus 0x%x\n",RpcStatus));
|
|
*pResult = ERROR_CANNOT_IMPERSONATE;
|
|
return( FALSE );
|
|
}
|
|
|
|
#ifdef not_hydrix
|
|
*pResult = xxxWinStationGenerateLicense(
|
|
pSerialNumberString,
|
|
Length,
|
|
pLicense,
|
|
LicenseSize
|
|
);
|
|
#else
|
|
{
|
|
PLIST_ENTRY Head, Next;
|
|
PWSEXTENSION pWsx;
|
|
ICASRVPROCADDR IcaSrvProcAddr;
|
|
|
|
*pResult = (DWORD)STATUS_UNSUCCESSFUL;
|
|
|
|
RtlEnterCriticalSection( &WsxListLock );
|
|
|
|
Head = &WsxListHead;
|
|
for ( Next = Head->Flink; Next != Head; Next = Next->Flink ) {
|
|
pWsx = CONTAINING_RECORD( Next, WSEXTENSION, Links );
|
|
if ( pWsx->pWsxWinStationGenerateLicense ) {
|
|
*pResult = pWsx->pWsxWinStationGenerateLicense(
|
|
pSerialNumberString,
|
|
Length,
|
|
pLicense,
|
|
LicenseSize
|
|
);
|
|
break;
|
|
}
|
|
}
|
|
|
|
RtlLeaveCriticalSection( &WsxListLock );
|
|
}
|
|
KdPrintEx((DPFLTR_TERMSRV_ID, DPFLTR_TRACE_LEVEL, "WinStationGenerateLicense: 0x%x\n", *pResult ));
|
|
#endif
|
|
|
|
RpcRevertToSelf();
|
|
|
|
return( *pResult == ERROR_SUCCESS ? TRUE : FALSE );
|
|
}
|
|
|
|
|
|
/*******************************************************************************
|
|
* RpcWinStationInstallLicense
|
|
*
|
|
* Called to install a license.
|
|
*
|
|
* ENTRY:
|
|
* hServer (input)
|
|
* Server handle
|
|
* pLicense (input)
|
|
* Pointer to a License structure containing the license to
|
|
* be installed
|
|
* LicenseSize (input)
|
|
* Size in bytes of the structure pointed to by pLicense
|
|
*
|
|
* EXIT:
|
|
* TRUE -- The install operation succeeded.
|
|
* FALSE -- The operation failed. Extended error status is available
|
|
* in pResult.
|
|
******************************************************************************/
|
|
BOOLEAN
|
|
RpcWinStationInstallLicense(
|
|
HANDLE hServer,
|
|
DWORD *pResult,
|
|
PCHAR pLicense,
|
|
DWORD LicenseSize
|
|
)
|
|
{
|
|
RPC_STATUS RpcStatus;
|
|
|
|
/*
|
|
* Impersonate the client
|
|
*/
|
|
RpcStatus = RpcImpersonateClient( NULL );
|
|
if( RpcStatus != RPC_S_OK ) {
|
|
KdPrintEx((DPFLTR_TERMSRV_ID, DPFLTR_ERROR_LEVEL, "TERMSRV: RpcWinStationInstallLicense: Not impersonating! RpcStatus 0x%x\n",RpcStatus));
|
|
*pResult = ERROR_CANNOT_IMPERSONATE;
|
|
return( FALSE );
|
|
}
|
|
|
|
#ifdef not_hydrix
|
|
*pResult = xxxWinStationInstallLicense(
|
|
pLicense,
|
|
LicenseSize
|
|
);
|
|
#else
|
|
{
|
|
PLIST_ENTRY Head, Next;
|
|
PWSEXTENSION pWsx;
|
|
ICASRVPROCADDR IcaSrvProcAddr;
|
|
|
|
*pResult = (DWORD)STATUS_UNSUCCESSFUL;
|
|
|
|
RtlEnterCriticalSection( &WsxListLock );
|
|
|
|
Head = &WsxListHead;
|
|
for ( Next = Head->Flink; Next != Head; Next = Next->Flink ) {
|
|
pWsx = CONTAINING_RECORD( Next, WSEXTENSION, Links );
|
|
if ( pWsx->pWsxWinStationInstallLicense ) {
|
|
*pResult = pWsx->pWsxWinStationInstallLicense(
|
|
pLicense,
|
|
LicenseSize
|
|
);
|
|
break;
|
|
}
|
|
}
|
|
|
|
RtlLeaveCriticalSection( &WsxListLock );
|
|
}
|
|
KdPrintEx((DPFLTR_TERMSRV_ID, DPFLTR_INFO_LEVEL, "WinStationInstallLicense: 0x%x\n", *pResult ));
|
|
#endif
|
|
|
|
RpcRevertToSelf();
|
|
|
|
return( *pResult == ERROR_SUCCESS ? TRUE : FALSE );
|
|
}
|
|
|
|
|
|
/*******************************************************************************
|
|
* RpcWinStationEnumerateLicenses
|
|
*
|
|
* Called to return the list of valid licenses.
|
|
*
|
|
* ENTRY:
|
|
* hServer (input)
|
|
* Server handle
|
|
* pIndex (input/output)
|
|
* Specifies the subkey index for the \Citrix\WinStations subkeys in the
|
|
* registry. Should be set to 0 for the initial call, and supplied
|
|
* again (as modified by this function) for multi-call enumeration.
|
|
* pEntries (input/output)
|
|
* Points to a variable specifying the number of entries requested.
|
|
* If the number requested is 0xFFFFFFFF, the function returns as
|
|
* many entries as possible. When the function finishes successfully,
|
|
* the variable pointed to by the pEntries parameter contains the
|
|
* number of entries actually read.
|
|
* pLicense (input/output)
|
|
* Points to the buffer to receive the enumeration results, which are
|
|
* returned as an array of LICENSE structures. If this parameter
|
|
* is NULL, then no data will be copied, but just an enumeration count
|
|
* will be made.
|
|
* LicenseSize (input)
|
|
* Size in bytes of the structure pointed to by pLicense
|
|
* pByteCount (input/output)
|
|
* Points to a variable that specifies the size, in bytes, of the
|
|
* pWinStationName parameter. If the buffer is too small to receive even
|
|
* one entry, the function returns an error code (ERROR_OUTOFMEMORY)
|
|
* and this variable receives the required size of the buffer for a
|
|
* single subkey. When the function finishes sucessfully, the variable
|
|
* pointed to by the pByteCount parameter contains the number of bytes
|
|
* actually stored in pLicense.
|
|
*
|
|
* EXIT:
|
|
* TRUE -- The enumerate operation succeeded.
|
|
* FALSE -- The operation failed. Extended error status is available
|
|
* in pResult.
|
|
******************************************************************************/
|
|
BOOLEAN
|
|
RpcWinStationEnumerateLicenses(
|
|
HANDLE hServer,
|
|
DWORD *pResult,
|
|
DWORD *pIndex,
|
|
DWORD *pEntries,
|
|
PCHAR pLicense,
|
|
DWORD LicenseSize,
|
|
DWORD *pByteCount
|
|
)
|
|
{
|
|
RPC_STATUS RpcStatus;
|
|
|
|
/*
|
|
* Impersonate the client
|
|
*/
|
|
RpcStatus = RpcImpersonateClient( NULL );
|
|
if( RpcStatus != RPC_S_OK ) {
|
|
KdPrintEx((DPFLTR_TERMSRV_ID, DPFLTR_ERROR_LEVEL, "TERMSRV: RpcWinStationEnumerateLicenses: Not impersonating! RpcStatus 0x%x\n",RpcStatus));
|
|
*pResult = ERROR_CANNOT_IMPERSONATE;
|
|
return( FALSE );
|
|
}
|
|
|
|
#ifdef not_hydrix
|
|
*pResult = xxxWinStationEnumerateLicenses(
|
|
pIndex,
|
|
pEntries,
|
|
pLicense,
|
|
pByteCount
|
|
);
|
|
#else
|
|
{
|
|
PLIST_ENTRY Head, Next;
|
|
PWSEXTENSION pWsx;
|
|
ICASRVPROCADDR IcaSrvProcAddr;
|
|
|
|
*pResult = (DWORD)STATUS_UNSUCCESSFUL;
|
|
|
|
RtlEnterCriticalSection( &WsxListLock );
|
|
|
|
Head = &WsxListHead;
|
|
for ( Next = Head->Flink; Next != Head; Next = Next->Flink ) {
|
|
pWsx = CONTAINING_RECORD( Next, WSEXTENSION, Links );
|
|
if ( pWsx->pWsxWinStationEnumerateLicenses ) {
|
|
*pResult = pWsx->pWsxWinStationEnumerateLicenses(
|
|
pIndex,
|
|
pEntries,
|
|
pLicense,
|
|
pByteCount
|
|
);
|
|
break;
|
|
}
|
|
}
|
|
|
|
RtlLeaveCriticalSection( &WsxListLock );
|
|
}
|
|
KdPrintEx((DPFLTR_TERMSRV_ID, DPFLTR_INFO_LEVEL, "WinStationEnumerateLicense: 0x%x\n", *pResult ));
|
|
#endif
|
|
|
|
RpcRevertToSelf();
|
|
|
|
return( *pResult == ERROR_SUCCESS ? TRUE : FALSE );
|
|
}
|
|
|
|
|
|
/*******************************************************************************
|
|
* RpcWinStationActivateLicense
|
|
*
|
|
* Called to Activate a license for a License
|
|
*
|
|
* ENTRY:
|
|
* hServer (input)
|
|
* Server handle
|
|
* pLicense (output)
|
|
* Pointer to a License structure that will be filled in with
|
|
* information based on pSerialNumberString
|
|
* LicenseSize (input)
|
|
* Size in bytes of the structure pointed to by pLicense
|
|
* pActivationCode (input)
|
|
* Pointer to a null-terminated, wide-character Activation Code string
|
|
*
|
|
* EXIT:
|
|
* TRUE -- The activate operation succeeded.
|
|
* FALSE -- The operation failed. Extended error status is available
|
|
* in pResult.
|
|
******************************************************************************/
|
|
BOOLEAN
|
|
RpcWinStationActivateLicense(
|
|
HANDLE hServer,
|
|
DWORD *pResult,
|
|
PCHAR pLicense,
|
|
DWORD LicenseSize,
|
|
PWCHAR pActivationCode,
|
|
DWORD StringSize
|
|
)
|
|
{
|
|
RPC_STATUS RpcStatus;
|
|
|
|
/*
|
|
* Impersonate the client
|
|
*/
|
|
RpcStatus = RpcImpersonateClient( NULL );
|
|
if( RpcStatus != RPC_S_OK ) {
|
|
KdPrintEx((DPFLTR_TERMSRV_ID, DPFLTR_ERROR_LEVEL, "TERMSRV: RpcWinStationActivateLicense: Not impersonating! RpcStatus 0x%x\n",RpcStatus));
|
|
*pResult = ERROR_CANNOT_IMPERSONATE;
|
|
return( FALSE );
|
|
}
|
|
|
|
#ifdef not_hydrix
|
|
*pResult = xxxWinStationActivateLicense(
|
|
pLicense,
|
|
LicenseSize,
|
|
pActivationCode,
|
|
StringSize
|
|
);
|
|
#else
|
|
{
|
|
PLIST_ENTRY Head, Next;
|
|
PWSEXTENSION pWsx;
|
|
ICASRVPROCADDR IcaSrvProcAddr;
|
|
|
|
*pResult = (DWORD)STATUS_UNSUCCESSFUL;
|
|
|
|
RtlEnterCriticalSection( &WsxListLock );
|
|
|
|
Head = &WsxListHead;
|
|
for ( Next = Head->Flink; Next != Head; Next = Next->Flink ) {
|
|
pWsx = CONTAINING_RECORD( Next, WSEXTENSION, Links );
|
|
if ( pWsx->pWsxWinStationActivateLicense ) {
|
|
*pResult = pWsx->pWsxWinStationActivateLicense(
|
|
pLicense,
|
|
LicenseSize,
|
|
pActivationCode,
|
|
StringSize
|
|
);
|
|
break;
|
|
}
|
|
}
|
|
|
|
RtlLeaveCriticalSection( &WsxListLock );
|
|
}
|
|
KdPrintEx((DPFLTR_TERMSRV_ID, DPFLTR_INFO_LEVEL, "WinStationActivateLicense: 0x%x\n", *pResult ));
|
|
#endif
|
|
|
|
RpcRevertToSelf();
|
|
|
|
return( *pResult == ERROR_SUCCESS ? TRUE : FALSE );
|
|
}
|
|
|
|
|
|
/*****************************************************************************
|
|
* RpcWinStationQueryLicense
|
|
*
|
|
* Query the license(s) on the WinFrame server and the network
|
|
*
|
|
* ENTRY:
|
|
* hServer (input)
|
|
* Server handle
|
|
* pLicenseCounts (output)
|
|
* pointer to buffer to return license count structure
|
|
* ByteCount (input)
|
|
* length of buffer in bytes
|
|
*
|
|
* EXIT:
|
|
* TRUE -- The query operation succeeded.
|
|
* FALSE -- The operation failed. Extended error status is available
|
|
* in pResult.
|
|
****************************************************************************/
|
|
BOOLEAN
|
|
RpcWinStationQueryLicense(
|
|
HANDLE hServer,
|
|
DWORD *pResult,
|
|
PCHAR pLicenseCounts,
|
|
ULONG ByteCount
|
|
)
|
|
{
|
|
RPC_STATUS RpcStatus;
|
|
|
|
/*
|
|
* Impersonate the client
|
|
*/
|
|
RpcStatus = RpcImpersonateClient( NULL );
|
|
if( RpcStatus != RPC_S_OK ) {
|
|
KdPrintEx((DPFLTR_TERMSRV_ID, DPFLTR_ERROR_LEVEL, "TERMSRV: RpcWinStationQueryLicense: Not impersonating! RpcStatus 0x%x\n",RpcStatus));
|
|
*pResult = ERROR_CANNOT_IMPERSONATE;
|
|
return( FALSE );
|
|
}
|
|
|
|
#ifdef not_hydrix
|
|
*pResult = QueryLicense(
|
|
(PLICENSE_COUNTS) pLicenseCounts,
|
|
ByteCount );
|
|
#else
|
|
{
|
|
PLIST_ENTRY Head, Next;
|
|
PWSEXTENSION pWsx;
|
|
ICASRVPROCADDR IcaSrvProcAddr;
|
|
|
|
*pResult = (DWORD)STATUS_UNSUCCESSFUL;
|
|
|
|
RtlEnterCriticalSection( &WsxListLock );
|
|
|
|
Head = &WsxListHead;
|
|
for ( Next = Head->Flink; Next != Head; Next = Next->Flink ) {
|
|
pWsx = CONTAINING_RECORD( Next, WSEXTENSION, Links );
|
|
if ( pWsx->pWsxQueryLicense ) {
|
|
*pResult = pWsx->pWsxQueryLicense(
|
|
pLicenseCounts,
|
|
ByteCount );
|
|
break;
|
|
}
|
|
}
|
|
|
|
RtlLeaveCriticalSection( &WsxListLock );
|
|
}
|
|
KdPrintEx((DPFLTR_TERMSRV_ID, DPFLTR_INFO_LEVEL, "WinStationQueryLicense: 0x%x\n", *pResult ));
|
|
#endif
|
|
|
|
RpcRevertToSelf();
|
|
|
|
return( *pResult == ERROR_SUCCESS ? TRUE : FALSE );
|
|
}
|
|
|
|
|
|
/*****************************************************************************
|
|
* RpcWinStationQueryUpdateRequired
|
|
*
|
|
* Query the license(s) on the WinFrame server and determine if an
|
|
* update is required.
|
|
*
|
|
* ENTRY:
|
|
* hServer (input)
|
|
* Server handle
|
|
* pUpdateFlag (output)
|
|
* Update flag, set if an update is required
|
|
*
|
|
* EXIT:
|
|
* TRUE -- The query operation succeeded.
|
|
* FALSE -- The operation failed. Extended error status is available
|
|
* in pResult.
|
|
****************************************************************************/
|
|
BOOLEAN
|
|
RpcWinStationQueryUpdateRequired(
|
|
HANDLE hServer,
|
|
DWORD *pResult,
|
|
PULONG pUpdateFlag
|
|
)
|
|
{
|
|
RPC_STATUS RpcStatus;
|
|
|
|
/*
|
|
* Impersonate the client
|
|
*/
|
|
RpcStatus = RpcImpersonateClient( NULL );
|
|
if( RpcStatus != RPC_S_OK ) {
|
|
KdPrintEx((DPFLTR_TERMSRV_ID, DPFLTR_ERROR_LEVEL, "TERMSRV: RpcWinStationQueryUpdateRequired: Not impersonating! RpcStatus 0x%x\n",RpcStatus));
|
|
*pResult = ERROR_CANNOT_IMPERSONATE;
|
|
return( FALSE );
|
|
}
|
|
|
|
#ifdef not_hydrix
|
|
*pResult = xxxWinStationQueryUpdateRequired( pUpdateFlag );
|
|
#else
|
|
{
|
|
PLIST_ENTRY Head, Next;
|
|
PWSEXTENSION pWsx;
|
|
ICASRVPROCADDR IcaSrvProcAddr;
|
|
|
|
*pResult = (DWORD)STATUS_UNSUCCESSFUL;
|
|
|
|
RtlEnterCriticalSection( &WsxListLock );
|
|
|
|
Head = &WsxListHead;
|
|
for ( Next = Head->Flink; Next != Head; Next = Next->Flink ) {
|
|
pWsx = CONTAINING_RECORD( Next, WSEXTENSION, Links );
|
|
if ( pWsx->pWsxWinStationQueryUpdateRequired ) {
|
|
*pResult = pWsx->pWsxWinStationQueryUpdateRequired( pUpdateFlag );
|
|
break;
|
|
}
|
|
}
|
|
|
|
RtlLeaveCriticalSection( &WsxListLock );
|
|
}
|
|
#endif
|
|
|
|
RpcRevertToSelf();
|
|
|
|
return( *pResult == ERROR_SUCCESS ? TRUE : FALSE );
|
|
}
|
|
|
|
|
|
/*******************************************************************************
|
|
* RpcWinStationRemoveLicense
|
|
*
|
|
* Called to remove a license diskette.
|
|
*
|
|
* ENTRY:
|
|
* hServer (input)
|
|
* Server handle
|
|
* pLicense (input)
|
|
* Pointer to a License structure containing the license to
|
|
* be removed
|
|
* LicenseSize (input)
|
|
* Size in bytes of the structure pointed to by pLicense
|
|
*
|
|
* EXIT:
|
|
* TRUE -- The remove operation succeeded.
|
|
* FALSE -- The operation failed. Extended error status is available
|
|
* in pResult.
|
|
******************************************************************************/
|
|
BOOLEAN
|
|
RpcWinStationRemoveLicense(
|
|
HANDLE hServer,
|
|
DWORD *pResult,
|
|
PCHAR pLicense,
|
|
DWORD LicenseSize
|
|
)
|
|
{
|
|
RPC_STATUS RpcStatus;
|
|
|
|
/*
|
|
* Impersonate the client
|
|
*/
|
|
RpcStatus = RpcImpersonateClient( NULL );
|
|
if( RpcStatus != RPC_S_OK ) {
|
|
KdPrintEx((DPFLTR_TERMSRV_ID, DPFLTR_ERROR_LEVEL, "TERMSRV: RpcWinStationRemoveLicense: Not impersonating! RpcStatus 0x%x\n",RpcStatus));
|
|
*pResult = ERROR_CANNOT_IMPERSONATE;
|
|
return( FALSE );
|
|
}
|
|
|
|
#ifdef not_hydrix
|
|
*pResult = xxxWinStationRemoveLicense(
|
|
pLicense,
|
|
LicenseSize
|
|
);
|
|
#else
|
|
{
|
|
PLIST_ENTRY Head, Next;
|
|
PWSEXTENSION pWsx;
|
|
ICASRVPROCADDR IcaSrvProcAddr;
|
|
|
|
*pResult = (DWORD)STATUS_UNSUCCESSFUL;
|
|
|
|
RtlEnterCriticalSection( &WsxListLock );
|
|
|
|
Head = &WsxListHead;
|
|
for ( Next = Head->Flink; Next != Head; Next = Next->Flink ) {
|
|
pWsx = CONTAINING_RECORD( Next, WSEXTENSION, Links );
|
|
if ( pWsx->pWsxWinStationRemoveLicense ) {
|
|
*pResult = pWsx->pWsxWinStationRemoveLicense(
|
|
pLicense,
|
|
LicenseSize
|
|
);
|
|
break;
|
|
}
|
|
}
|
|
|
|
RtlLeaveCriticalSection( &WsxListLock );
|
|
}
|
|
KdPrintEx((DPFLTR_TERMSRV_ID, DPFLTR_INFO_LEVEL, "WinStationRemoveLicense: 0x%x\n", *pResult ));
|
|
#endif
|
|
|
|
RpcRevertToSelf();
|
|
|
|
return( *pResult == ERROR_SUCCESS ? TRUE : FALSE );
|
|
}
|
|
|
|
|
|
/*******************************************************************************
|
|
* RpcWinStationSetPoolCount
|
|
*
|
|
* Called to change the PoolCount for the given license
|
|
*
|
|
* ENTRY:
|
|
* hServer (input)
|
|
* Server handle
|
|
* pLicense (input)
|
|
* Pointer to a License structure containing the license to
|
|
* be removed
|
|
* LicenseSize (input)
|
|
* Size in bytes of the structure pointed to by pLicense
|
|
*
|
|
* EXIT:
|
|
* TRUE -- The change operation succeeded.
|
|
* FALSE -- The operation failed. Extended error status is available
|
|
* in pResult.
|
|
******************************************************************************/
|
|
BOOLEAN
|
|
RpcWinStationSetPoolCount(
|
|
HANDLE hServer,
|
|
DWORD *pResult,
|
|
PCHAR pLicense,
|
|
DWORD LicenseSize
|
|
)
|
|
{
|
|
RPC_STATUS RpcStatus;
|
|
|
|
/*
|
|
* Impersonate the client
|
|
*/
|
|
RpcStatus = RpcImpersonateClient( NULL );
|
|
if( RpcStatus != RPC_S_OK ) {
|
|
KdPrintEx((DPFLTR_TERMSRV_ID, DPFLTR_ERROR_LEVEL, "TERMSRV: RpcWinStationSetPoolCount: Not impersonating! RpcStatus 0x%x\n",RpcStatus));
|
|
*pResult = ERROR_CANNOT_IMPERSONATE;
|
|
return( FALSE );
|
|
}
|
|
|
|
#ifdef not_hydrix
|
|
*pResult = xxxWinStationSetPoolCount(
|
|
pLicense,
|
|
LicenseSize
|
|
);
|
|
#else
|
|
{
|
|
PLIST_ENTRY Head, Next;
|
|
PWSEXTENSION pWsx;
|
|
ICASRVPROCADDR IcaSrvProcAddr;
|
|
|
|
*pResult = (DWORD)STATUS_UNSUCCESSFUL;
|
|
|
|
RtlEnterCriticalSection( &WsxListLock );
|
|
|
|
Head = &WsxListHead;
|
|
for ( Next = Head->Flink; Next != Head; Next = Next->Flink ) {
|
|
pWsx = CONTAINING_RECORD( Next, WSEXTENSION, Links );
|
|
if ( pWsx->pWsxWinStationSetPoolCount ) {
|
|
*pResult = pWsx->pWsxWinStationSetPoolCount(
|
|
pLicense,
|
|
LicenseSize
|
|
);
|
|
break;
|
|
}
|
|
}
|
|
|
|
RtlLeaveCriticalSection( &WsxListLock );
|
|
}
|
|
KdPrintEx((DPFLTR_TERMSRV_ID, DPFLTR_INFO_LEVEL, "WinStationSetPoolCount: 0x%x\n", *pResult ));
|
|
#endif
|
|
|
|
RpcRevertToSelf();
|
|
|
|
return( *pResult == ERROR_SUCCESS ? TRUE : FALSE );
|
|
}
|
|
|
|
|
|
/*****************************************************************************
|
|
* RpcWinStationAnnoyancePopup
|
|
****************************************************************************/
|
|
BOOLEAN
|
|
RpcWinStationAnnoyancePopup(
|
|
HANDLE hServer,
|
|
DWORD *pResult,
|
|
ULONG LogonId
|
|
)
|
|
{
|
|
PWINSTATION pWinStation;
|
|
NTSTATUS Status;
|
|
|
|
/*
|
|
* Make sure the caller is SYSTEM (WinLogon)
|
|
*/
|
|
Status = RpcCheckSystemClient( LogonId );
|
|
if ( !NT_SUCCESS( Status ) ) {
|
|
*pResult = Status;
|
|
return( FALSE );
|
|
}
|
|
|
|
#ifdef not_hydrix
|
|
*pResult = WinStationLogonAnnoyance( LogonId );
|
|
#else
|
|
// Assume the worst
|
|
*pResult = (DWORD)STATUS_UNSUCCESSFUL;
|
|
|
|
// Check for compatibility
|
|
pWinStation = FindWinStationById( LogonId, FALSE );
|
|
if ( pWinStation != NULL ) {
|
|
if ( pWinStation->pWsx &&
|
|
pWinStation->pWsx->pWsxWinStationLogonAnnoyance ) {
|
|
UnlockWinStation( pWinStation );
|
|
*pResult = pWinStation->pWsx->pWsxWinStationLogonAnnoyance( LogonId );
|
|
RelockWinStation( pWinStation );
|
|
/*
|
|
* If WinStation has no Wsx structure, wen try to annoy it anyway.
|
|
* This will have the effect of sending annoy msgs to the console.
|
|
*/
|
|
} else if ( pWinStation->pWsx == NULL ) {
|
|
PLIST_ENTRY Head, Next;
|
|
PWSEXTENSION pWsx;
|
|
ICASRVPROCADDR IcaSrvProcAddr;
|
|
|
|
RtlEnterCriticalSection( &WsxListLock );
|
|
|
|
Head = &WsxListHead;
|
|
for ( Next = Head->Flink; Next != Head; Next = Next->Flink ) {
|
|
pWsx = CONTAINING_RECORD( Next, WSEXTENSION, Links );
|
|
if ( pWsx->pWsxWinStationLogonAnnoyance ) {
|
|
UnlockWinStation( pWinStation );
|
|
*pResult = pWsx->pWsxWinStationLogonAnnoyance( LogonId );
|
|
RelockWinStation( pWinStation );
|
|
break;
|
|
}
|
|
}
|
|
|
|
RtlLeaveCriticalSection( &WsxListLock );
|
|
}
|
|
ReleaseWinStation( pWinStation );
|
|
}
|
|
KdPrintEx((DPFLTR_TERMSRV_ID, DPFLTR_INFO_LEVEL, "WinStationAnnoyancePopup: 0x%x\n", *pResult ));
|
|
#endif
|
|
|
|
return( *pResult == STATUS_SUCCESS ? TRUE : FALSE );
|
|
}
|
|
|
|
|
|
/*****************************************************************************
|
|
* RpcWinStationCallback
|
|
****************************************************************************/
|
|
BOOLEAN
|
|
RpcWinStationCallback(
|
|
HANDLE hServer,
|
|
DWORD *pResult,
|
|
ULONG LogonId,
|
|
PWCHAR pPhoneNumber,
|
|
DWORD PhoneNumberSize
|
|
)
|
|
{
|
|
NTSTATUS Status;
|
|
|
|
/*
|
|
* Make sure the caller is SYSTEM (WinLogon)
|
|
*/
|
|
Status = RpcCheckSystemClient( LogonId );
|
|
if ( !NT_SUCCESS( Status ) ) {
|
|
*pResult = Status;
|
|
return( FALSE );
|
|
}
|
|
|
|
*pResult = WinStationCallbackWorker( LogonId, pPhoneNumber );
|
|
|
|
return( *pResult == STATUS_SUCCESS ? TRUE : FALSE );
|
|
}
|
|
|
|
|
|
/*****************************************************************************
|
|
* RpcWinStationBreakPoint
|
|
****************************************************************************/
|
|
BOOLEAN
|
|
RpcWinStationBreakPoint(
|
|
HANDLE hServer,
|
|
DWORD *pResult,
|
|
ULONG LogonId,
|
|
BOOLEAN KernelFlag
|
|
)
|
|
{
|
|
/*
|
|
* This is obsolete and should be removed from the RPC code.
|
|
*/
|
|
*pResult = STATUS_NOT_IMPLEMENTED;
|
|
RpcRaiseException(ERROR_INVALID_FUNCTION);
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
/*****************************************************************************
|
|
* RpcWinStationReadRegistry
|
|
****************************************************************************/
|
|
BOOLEAN
|
|
RpcWinStationReadRegistry(
|
|
HANDLE hServer,
|
|
DWORD *pResult
|
|
)
|
|
{
|
|
/*
|
|
* This API is not secured, since it is harmless. It tells the system
|
|
* to make sure all winstations are up to date with the registry. You
|
|
* must be system to write the keys, so the info would match for a normal
|
|
* user poking this call.
|
|
*/
|
|
|
|
return RpcWinStationUpdateSettings(hServer, pResult, WINSTACFG_LEGACY, 0);
|
|
}
|
|
|
|
|
|
/*****************************************************************************
|
|
* RpcWinStationUpdateSettings
|
|
****************************************************************************/
|
|
BOOLEAN
|
|
RpcWinStationUpdateSettings(
|
|
HANDLE hServer,
|
|
DWORD *pResult,
|
|
DWORD SettingsClass,
|
|
DWORD SettingsParameters
|
|
)
|
|
{
|
|
/*
|
|
* This API is not secured, since it is harmless. It tells the system
|
|
* to make sure all winstations are up to date with the registry. You
|
|
* must be system to write the keys, so the info would match for a normal
|
|
* user poking this call.
|
|
*/
|
|
|
|
switch (SettingsClass) {
|
|
|
|
case WINSTACFG_SESSDIR:
|
|
{
|
|
if (!g_bPersonalTS && g_fAppCompat) {
|
|
*pResult = UpdateSessionDirectory();
|
|
}
|
|
else {
|
|
// Invalid in remote admin or on PTS
|
|
*pResult = STATUS_INVALID_PARAMETER;
|
|
}
|
|
}
|
|
break;
|
|
|
|
case WINSTACFG_LEGACY:
|
|
{
|
|
*pResult = WinStationReadRegistryWorker();
|
|
}
|
|
break;
|
|
|
|
default:
|
|
{
|
|
*pResult = STATUS_INVALID_PARAMETER;
|
|
}
|
|
}
|
|
|
|
return( *pResult == STATUS_SUCCESS ? TRUE : FALSE );
|
|
}
|
|
|
|
|
|
|
|
/*****************************************************************************
|
|
* RpcReInitializeSecurity
|
|
****************************************************************************/
|
|
BOOLEAN
|
|
RpcWinStationReInitializeSecurity(
|
|
HANDLE hServer,
|
|
DWORD *pResult
|
|
)
|
|
{
|
|
/*
|
|
* This API is not secured, since it is harmless. It tells the system
|
|
* to update all winstations security configuration. You
|
|
* must be system to write the keys, so the info would match for a normal
|
|
* user poking this call.
|
|
*/
|
|
|
|
*pResult = ReInitializeSecurityWorker();
|
|
|
|
return( *pResult == STATUS_SUCCESS ? TRUE : FALSE );
|
|
}
|
|
|
|
|
|
/*****************************************************************************
|
|
* RpcWinStationWaitForConnect
|
|
****************************************************************************/
|
|
BOOLEAN
|
|
RpcWinStationWaitForConnect(
|
|
HANDLE hServer,
|
|
DWORD *pResult,
|
|
DWORD ClientLogonId,
|
|
DWORD ClientProcessId
|
|
)
|
|
{
|
|
PWINSTATION pWinStation;
|
|
NTSTATUS Status = STATUS_SUCCESS;
|
|
HANDLE WinlogonStartHandle = NULL;
|
|
WCHAR szWinlogonStartEvent[MAX_PATH];
|
|
UNICODE_STRING WinlogonEventName;
|
|
OBJECT_ATTRIBUTES ObjA;
|
|
LARGE_INTEGER TimeOut ;
|
|
ULONG SleepDuration = 90 * 1000; // 90 seconds
|
|
|
|
//
|
|
// Winlogon can call into Terminal Server, before we store the session id in its internal structure
|
|
// To prevent this, RpcWinStationWaitForConnect (which is called by Winlogon) will wait for a named event
|
|
// This is the same Named event (CsrStartEvent) which the Csr also waits for before calling into TermSrv
|
|
//
|
|
|
|
if (ClientLogonId != 0) {
|
|
|
|
wsprintf(szWinlogonStartEvent,
|
|
L"\\Sessions\\%d\\BaseNamedObjects\\CsrStartEvent",ClientLogonId);
|
|
|
|
RtlInitUnicodeString( &WinlogonEventName, szWinlogonStartEvent );
|
|
InitializeObjectAttributes( &ObjA, &WinlogonEventName, OBJ_OPENIF, NULL, NULL );
|
|
|
|
Status = NtCreateEvent( &WinlogonStartHandle,
|
|
EVENT_ALL_ACCESS,
|
|
&ObjA,
|
|
NotificationEvent,
|
|
FALSE );
|
|
|
|
if (!WinlogonStartHandle) {
|
|
Status = STATUS_INSUFFICIENT_RESOURCES;
|
|
goto error_exit;
|
|
} else {
|
|
|
|
TimeOut = RtlEnlargedIntegerMultiply( SleepDuration, -10000);
|
|
Status = NtWaitForSingleObject(WinlogonStartHandle, FALSE, &TimeOut);
|
|
|
|
NtClose(WinlogonStartHandle);
|
|
WinlogonStartHandle = NULL;
|
|
|
|
if (!NT_SUCCESS(Status) || (Status == STATUS_TIMEOUT)) {
|
|
// We timed out waiting for Session Creation to get complete - cant connect now, just exit
|
|
goto error_exit;
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Make sure the caller is SYSTEM (WinLogon)
|
|
*/
|
|
Status = RpcCheckSystemClient( ClientLogonId );
|
|
if ( !NT_SUCCESS( Status ) ) {
|
|
*pResult = Status;
|
|
return( FALSE );
|
|
}
|
|
|
|
TRACE((hTrace,TC_ICASRV,TT_API1, "TERMSRV: WinStationWaitForConnect, LogonId=%d\n",ClientLogonId ));
|
|
|
|
/*
|
|
* Find and lock client WinStation
|
|
*/
|
|
pWinStation = FindWinStationById( ClientLogonId, FALSE );
|
|
if ( pWinStation == NULL ) {
|
|
*pResult = (DWORD)STATUS_ACCESS_DENIED;
|
|
return( FALSE );
|
|
}
|
|
|
|
*pResult = (DWORD)WaitForConnectWorker( pWinStation, (HANDLE)(ULONG_PTR)ClientProcessId );
|
|
|
|
return( *pResult == STATUS_SUCCESS ? TRUE : FALSE );
|
|
|
|
error_exit:
|
|
*pResult = Status;
|
|
|
|
//
|
|
// dont return STATUS_TIMEOUT.
|
|
//
|
|
|
|
if (*pResult == STATUS_TIMEOUT) *pResult = STATUS_UNSUCCESSFUL;
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
|
|
/*****************************************************************************
|
|
* RpcWinStationNotifyLogon
|
|
****************************************************************************/
|
|
BOOLEAN
|
|
RpcWinStationNotifyLogon(
|
|
HANDLE hServer,
|
|
DWORD *pResult,
|
|
DWORD ClientLogonId,
|
|
DWORD ClientProcessId,
|
|
BOOLEAN fUserIsAdmin,
|
|
DWORD UserToken,
|
|
PWCHAR pDomain,
|
|
DWORD DomainSize,
|
|
PWCHAR pUserName,
|
|
DWORD UserNameSize,
|
|
PWCHAR pPassword,
|
|
DWORD PasswordSize,
|
|
UCHAR Seed,
|
|
PCHAR pUserConfig,
|
|
DWORD ConfigSize
|
|
)
|
|
{
|
|
NTSTATUS Status;
|
|
|
|
|
|
/*
|
|
* Do some buffer validation
|
|
* No of bytes is sent from the Client, so send half the length got
|
|
*/
|
|
|
|
*pResult = IsZeroterminateStringW(pPassword, PasswordSize / sizeof(WCHAR) );
|
|
if (*pResult != STATUS_SUCCESS) {
|
|
return FALSE;
|
|
}
|
|
*pResult = IsZeroterminateStringW(pUserName, UserNameSize / sizeof(WCHAR) );
|
|
if (*pResult != STATUS_SUCCESS) {
|
|
return FALSE;
|
|
}
|
|
*pResult = IsZeroterminateStringW(pDomain, DomainSize / sizeof(WCHAR) );
|
|
if (*pResult != STATUS_SUCCESS) {
|
|
return FALSE;
|
|
}
|
|
|
|
/*
|
|
* Make sure the caller is SYSTEM (WinLogon)
|
|
*/
|
|
Status = RpcCheckSystemClient( ClientLogonId );
|
|
if ( !NT_SUCCESS( Status ) ) {
|
|
*pResult = Status;
|
|
return( FALSE );
|
|
}
|
|
|
|
*pResult = WinStationNotifyLogonWorker(
|
|
ClientLogonId,
|
|
ClientProcessId,
|
|
fUserIsAdmin,
|
|
UserToken,
|
|
pDomain,
|
|
DomainSize,
|
|
pUserName,
|
|
UserNameSize,
|
|
pPassword,
|
|
PasswordSize,
|
|
Seed,
|
|
pUserConfig,
|
|
ConfigSize
|
|
);
|
|
|
|
return( *pResult == STATUS_SUCCESS ? TRUE : FALSE );
|
|
}
|
|
|
|
|
|
/*****************************************************************************
|
|
* RpcWinStationNotifyLogoff
|
|
****************************************************************************/
|
|
BOOLEAN
|
|
RpcWinStationNotifyLogoff(
|
|
HANDLE hServer,
|
|
DWORD ClientLogonId,
|
|
DWORD ClientProcessId,
|
|
DWORD *pResult
|
|
)
|
|
{
|
|
NTSTATUS Status;
|
|
|
|
/*
|
|
* Make sure the caller is SYSTEM (WinLogon)
|
|
*/
|
|
Status = RpcCheckSystemClient( ClientLogonId );
|
|
if ( !NT_SUCCESS( Status ) ) {
|
|
*pResult = Status;
|
|
return( FALSE );
|
|
}
|
|
|
|
*pResult = WinStationNotifyLogoffWorker(
|
|
ClientLogonId,
|
|
ClientProcessId
|
|
);
|
|
|
|
return( *pResult == STATUS_SUCCESS ? TRUE : FALSE );
|
|
}
|
|
|
|
|
|
/*****************************************************************************
|
|
* RpcWinStationNotifyNewSession
|
|
*
|
|
* THIS FUNCTION IS OBSOLETE AND SHOULD BE REMOVED FROM FUTURE VERSIONS OF
|
|
* INTERFACE.
|
|
****************************************************************************/
|
|
BOOLEAN
|
|
RpcWinStationNotifyNewSession(
|
|
HANDLE hServer,
|
|
DWORD *pResult,
|
|
DWORD ClientLogonId
|
|
)
|
|
{
|
|
*pResult = STATUS_SUCCESS;
|
|
|
|
return(TRUE);
|
|
}
|
|
|
|
|
|
/*******************************************************************************
|
|
* RpcWinStationSendMessage
|
|
*
|
|
* Sends a message to the specified window station object and optionally
|
|
* waits for a reply. The reply is returned to the caller of
|
|
* WinStationSendMessage.
|
|
*
|
|
* ENTRY:
|
|
* WinStationHandle (input)
|
|
* Specifies the window station object to send a message to.
|
|
* pTitle (input)
|
|
* Pointer to title for message box to display.
|
|
* TitleLength (input)
|
|
* Length of title to display in bytes.
|
|
* pMessage (input)
|
|
* Pointer to message to display.
|
|
* MessageLength (input)
|
|
* Length of message in bytes to display at the specified window station.
|
|
* Style (input)
|
|
* Standard Windows MessageBox() style parameter.
|
|
* Timeout (input)
|
|
* Response timeout in seconds. If message is not responded to in
|
|
* Timeout seconds then a response code of IDTIMEOUT (cwin.h) is
|
|
* returned to signify the message timed out.
|
|
* pResponse (output)
|
|
* Address to return selected response.
|
|
* DoNotWait (input)
|
|
* Do not wait for the response. Causes pResponse to be set to
|
|
* IDASYNC (cwin.h) if no errors queueing the message.
|
|
*
|
|
* EXIT:
|
|
* TRUE -- The send message operation succeeded.
|
|
* FALSE -- The operation failed. Extended error status is available
|
|
* using GetLastError.
|
|
******************************************************************************/
|
|
BOOLEAN
|
|
RpcWinStationSendMessage(
|
|
HANDLE hServer,
|
|
DWORD *pResult,
|
|
ULONG LogonId,
|
|
PWCHAR pTitle,
|
|
ULONG TitleLength,
|
|
PWCHAR pMessage,
|
|
ULONG MessageLength,
|
|
ULONG Style,
|
|
ULONG Timeout,
|
|
PULONG pResponse,
|
|
BOOLEAN DoNotWait
|
|
)
|
|
{
|
|
PWINSTATION pWinStation;
|
|
WINSTATION_APIMSG WMsg;
|
|
OBJECT_ATTRIBUTES ObjA;
|
|
NTSTATUS Status;
|
|
|
|
/*
|
|
* Find and lock client WinStation
|
|
*/
|
|
pWinStation = FindWinStationById( LogonId, FALSE );
|
|
if ( pWinStation == NULL ) {
|
|
*pResult = (DWORD)STATUS_CTX_WINSTATION_NOT_FOUND;
|
|
return( FALSE );
|
|
}
|
|
|
|
Status = RpcCheckClientAccess( pWinStation, WINSTATION_MSG, FALSE );
|
|
if ( !NT_SUCCESS( Status ) ) {
|
|
*pResult = Status;
|
|
ReleaseWinStation( pWinStation );
|
|
return( FALSE );
|
|
}
|
|
|
|
TRACE((hTrace,TC_ICASRV,TT_API1, "RpcWinStationSendMessage: pTitle %S\n", pTitle ));
|
|
TRACE((hTrace,TC_ICASRV,TT_API1, "RpcWinStationSendMessage: pMessage %S\n", pMessage ));
|
|
|
|
/*
|
|
* Marshall in the [in] parameters
|
|
*
|
|
* pTitle and pMessage will be mapped into client
|
|
* view at apropriate time and place
|
|
*/
|
|
|
|
WMsg.u.SendMessage.pTitle = pTitle;
|
|
WMsg.u.SendMessage.TitleLength = TitleLength;
|
|
WMsg.u.SendMessage.pMessage = pMessage;
|
|
WMsg.u.SendMessage.MessageLength = MessageLength;
|
|
WMsg.u.SendMessage.Style = Style;
|
|
WMsg.u.SendMessage.Timeout = Timeout;
|
|
WMsg.u.SendMessage.DoNotWait = DoNotWait;
|
|
WMsg.u.SendMessage.pResponse = pResponse;
|
|
|
|
WMsg.ApiNumber = SMWinStationDoMessage;
|
|
|
|
/*
|
|
* Create wait event
|
|
*/
|
|
if( !DoNotWait ) {
|
|
InitializeObjectAttributes( &ObjA, NULL, 0, NULL, NULL );
|
|
Status = NtCreateEvent( &WMsg.u.SendMessage.hEvent, EVENT_ALL_ACCESS, &ObjA,
|
|
NotificationEvent, FALSE );
|
|
if ( !NT_SUCCESS(Status) ) {
|
|
*pResult = Status;
|
|
goto done;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Initialize response to IDTIMEOUT or IDASYNC
|
|
*/
|
|
|
|
if (DoNotWait) {
|
|
*pResponse = IDASYNC;
|
|
} else {
|
|
*pResponse = IDTIMEOUT;
|
|
}
|
|
|
|
/*
|
|
* Tell the WinStation to display the message box
|
|
*/
|
|
*pResult = SendWinStationCommand( pWinStation, &WMsg, 0 );
|
|
|
|
/*
|
|
* Wait for response, pResponse filled in WinStationIcaReplyMessage
|
|
*/
|
|
if( !DoNotWait ) {
|
|
if (*pResult == STATUS_SUCCESS) {
|
|
TRACE((hTrace,TC_ICASRV,TT_API1, "RpcWinStationSendMessage: wait for response\n" ));
|
|
UnlockWinStation( pWinStation );
|
|
Status = NtWaitForSingleObject( WMsg.u.SendMessage.hEvent, FALSE, NULL );
|
|
if ( !RelockWinStation( pWinStation ) ) {
|
|
Status = STATUS_CTX_CLOSE_PENDING;
|
|
}
|
|
*pResult = Status;
|
|
TRACE((hTrace,TC_ICASRV,TT_API1, "RpcWinStationSendMessage: got response %u\n", *pResponse ));
|
|
}
|
|
NtClose( WMsg.u.SendMessage.hEvent );
|
|
}
|
|
|
|
done:
|
|
|
|
ReleaseWinStation( pWinStation );
|
|
|
|
return( *pResult == STATUS_SUCCESS ? TRUE : FALSE );
|
|
}
|
|
|
|
|
|
/*****************************************************************************
|
|
* SERVER_HANDLE_rundown
|
|
*
|
|
* Required RPC context run down routine.
|
|
*
|
|
* This gets called when the RPC client drops or closes
|
|
* the connection and allows us to cleanup any state
|
|
* information.
|
|
*
|
|
* ENTRY:
|
|
* phContext (input)
|
|
* Context handle being rundown
|
|
****************************************************************************/
|
|
VOID
|
|
SERVER_HANDLE_rundown(
|
|
HANDLE hContext
|
|
)
|
|
{
|
|
DWORD Result;
|
|
|
|
TRACE((hTrace,TC_ICASRV,TT_API1,"TERMSRV: Context rundown, %p\n", hContext));
|
|
|
|
//RpcWinStationCloseServerEx( &hContext, &Result );
|
|
//ASSERT(hContext == NULL);
|
|
|
|
midl_user_free(hContext);
|
|
hContext = NULL;
|
|
|
|
return;
|
|
}
|
|
|
|
|
|
/*
|
|
* The following functions allow us to control the
|
|
* memory allocation and free functions of the RPC.
|
|
*/
|
|
|
|
void __RPC_FAR * __RPC_USER
|
|
MIDL_user_allocate( size_t Size )
|
|
{
|
|
return( LocalAlloc(LMEM_FIXED,Size) );
|
|
}
|
|
|
|
|
|
void __RPC_USER
|
|
MIDL_user_free( void __RPC_FAR *p )
|
|
{
|
|
if (!PointerIsInGAPDatabase(p))
|
|
{
|
|
// KdPrintEx((DPFLTR_TERMSRV_ID, DPFLTR_ERROR_LEVEL, "TERMSRV: MIDL_user_free for 0x%x....FREE it\n",p));
|
|
LocalFree( p );
|
|
}
|
|
else
|
|
{
|
|
// KdPrintEx((DPFLTR_TERMSRV_ID, DPFLTR_ERROR_LEVEL, "TERMSRV: MIDL_user_free for 0x%x..................DON'T FREE IT\n",p));
|
|
}
|
|
}
|
|
|
|
|
|
/*******************************************************************************
|
|
* NotifySystemEvent
|
|
*
|
|
* Notify clients that a system event occured.
|
|
*
|
|
* ENTRY:
|
|
* EventMask (input)
|
|
* mask of event(s) that have occured
|
|
******************************************************************************/
|
|
VOID
|
|
NotifySystemEvent( ULONG EventMask )
|
|
{
|
|
PLIST_ENTRY Head, Next;
|
|
PEVENT pWaitEvent;
|
|
NTSTATUS Status;
|
|
|
|
if ( IsListEmpty( &SystemEventHead ) ) {
|
|
return;
|
|
}
|
|
|
|
TRACE((hTrace,TC_ICAAPI,TT_API3, "TERMSRV: NotifySystemEvent, Event=0x%08x\n", EventMask ));
|
|
|
|
RtlEnterCriticalSection( &WinStationListLock );
|
|
Head = &SystemEventHead;
|
|
for ( Next = Head->Flink; Next != Head; Next = Next->Flink ) {
|
|
pWaitEvent = CONTAINING_RECORD( Next, EVENT, EventListEntry );
|
|
if ( pWaitEvent->EventMask & EventMask ) {
|
|
pWaitEvent->EventFlags |= EventMask;
|
|
if ( pWaitEvent->fWaiter ) {
|
|
pWaitEvent->WaitResult = STATUS_SUCCESS;
|
|
NtSetEvent( pWaitEvent->Event, NULL );
|
|
}
|
|
}
|
|
}
|
|
|
|
RtlLeaveCriticalSection( &WinStationListLock );
|
|
}
|
|
|
|
|
|
/*****************************************************************************
|
|
* WinStationDisconnectWorker
|
|
*
|
|
* Function to disconnect a Winstation based on an RPC API request.
|
|
*
|
|
* ENTRY:
|
|
* pContext (input)
|
|
* Pointer to our context structure describing the connection.
|
|
* pMsg (input/output)
|
|
* Pointer to the API message, a superset of NT LPC PORT_MESSAGE.
|
|
****************************************************************************/
|
|
NTSTATUS
|
|
WinStationDisconnectWorker(
|
|
ULONG LogonId,
|
|
BOOLEAN bWait,
|
|
BOOLEAN CallerIsRpc
|
|
)
|
|
{
|
|
PWINSTATION pWinStation;
|
|
ULONG ClientLogonId;
|
|
ULONG PdFlag;
|
|
WINSTATIONNAME WinStationName;
|
|
WINSTATIONNAME ListenName;
|
|
NTSTATUS Status;
|
|
BOOLEAN bConsoleSession = FALSE;
|
|
UINT LocalFlag = FALSE;
|
|
BOOLEAN bRelock;
|
|
BOOLEAN bIncrementFlag = FALSE;
|
|
|
|
TRACE((hTrace,TC_ICASRV,TT_API1, "TERMSRV: WinStationDisconnect, LogonId=%d\n", LogonId ));
|
|
|
|
//
|
|
//There are some bad ginas that break
|
|
//console disconnection bug 345286
|
|
//
|
|
if(LogonId == 0 && !IsGinaVersionCurrent()) {
|
|
Status = STATUS_CTX_CONSOLE_DISCONNECT;
|
|
goto done;
|
|
}
|
|
|
|
/*
|
|
* Find and lock the WinStation struct for the specified LogonId
|
|
*/
|
|
pWinStation = FindWinStationById( LogonId, FALSE );
|
|
if ( pWinStation == NULL ) {
|
|
Status = STATUS_CTX_WINSTATION_NOT_FOUND;
|
|
goto done;
|
|
}
|
|
|
|
if (LogonId == 0 && !bConsoleConnected){
|
|
|
|
Status = WaitForConsoleConnectWorker( pWinStation );
|
|
if (NT_SUCCESS(Status)) {
|
|
bConsoleConnected=TRUE;
|
|
} else {
|
|
ReleaseWinStation( pWinStation );
|
|
goto done;
|
|
}
|
|
|
|
}
|
|
|
|
/*
|
|
* Note if we are disconnecting a session that is connected to the console terminal.
|
|
*/
|
|
|
|
bConsoleSession = pWinStation->fOwnsConsoleTerminal;
|
|
|
|
/*
|
|
* Verify that client has DISCONNECT access if its an RPC (external) caller.
|
|
*
|
|
* When ICASRV calls this function internally, it is not impersonating
|
|
* and fails the RpcCheckClientAccess() call. Internal calls are
|
|
* not a security problem since they come in as LPC messages on a secured
|
|
* port.
|
|
*/
|
|
if ( CallerIsRpc ) {
|
|
RPC_STATUS RpcStatus;
|
|
|
|
/*
|
|
* Impersonate the client
|
|
*/
|
|
RpcStatus = RpcImpersonateClient( NULL );
|
|
if ( RpcStatus != RPC_S_OK ) {
|
|
ReleaseWinStation( pWinStation );
|
|
KdPrintEx((DPFLTR_TERMSRV_ID, DPFLTR_ERROR_LEVEL, "TERMSRV: WinStationDisconnectWorker: Not impersonating! RpcStatus 0x%x\n",RpcStatus));
|
|
Status = STATUS_CANNOT_IMPERSONATE;
|
|
goto done;
|
|
}
|
|
|
|
Status = RpcCheckClientAccess( pWinStation, WINSTATION_DISCONNECT, TRUE );
|
|
if ( !NT_SUCCESS( Status ) ) {
|
|
RpcRevertToSelf();
|
|
ReleaseWinStation( pWinStation );
|
|
goto done;
|
|
}
|
|
|
|
//
|
|
// If its remote RPC call we should ignore ClientLogonId
|
|
//
|
|
RpcStatus = I_RpcBindingIsClientLocal(
|
|
0, // Active RPC call we are servicing
|
|
&LocalFlag
|
|
);
|
|
|
|
if( RpcStatus != RPC_S_OK ) {
|
|
RpcRevertToSelf();
|
|
ReleaseWinStation( pWinStation );
|
|
KdPrintEx((DPFLTR_TERMSRV_ID, DPFLTR_ERROR_LEVEL, "TERMSRV: WinStationDisconnectWorker: IsClientLocal failed! RpcStatus 0x%x\n",RpcStatus));
|
|
Status = STATUS_UNSUCCESSFUL;
|
|
goto done;
|
|
}
|
|
|
|
if ( LocalFlag ) {
|
|
Status = RpcGetClientLogonId( &ClientLogonId );
|
|
if ( !NT_SUCCESS( Status ) ) {
|
|
RpcRevertToSelf();
|
|
ReleaseWinStation( pWinStation );
|
|
goto done;
|
|
}
|
|
}
|
|
|
|
RpcRevertToSelf();
|
|
}
|
|
|
|
/*
|
|
* If WinStation is already disconnected, then we're done
|
|
*/
|
|
if ( !pWinStation->WinStationName[0] ) {
|
|
ReleaseWinStation( pWinStation );
|
|
return (STATUS_SUCCESS);
|
|
}
|
|
|
|
/*
|
|
* If we are disconnecting the console session, we want to make sure
|
|
* that we can precreate a session that would become the console session.
|
|
*/
|
|
if (bConsoleSession && !ShutdownInProgress) {
|
|
UnlockWinStation(pWinStation);
|
|
Status = CheckIdleWinstation();
|
|
bRelock = RelockWinStation(pWinStation);
|
|
if (!NT_SUCCESS(Status) || !bRelock) {
|
|
if (NT_SUCCESS(Status)) {
|
|
Status = STATUS_CTX_WINSTATION_NOT_FOUND;
|
|
}
|
|
ReleaseWinStation( pWinStation );
|
|
goto done;
|
|
}
|
|
|
|
}
|
|
|
|
/*
|
|
* If busy with something already, don't do this
|
|
*/
|
|
if ( pWinStation->NeverConnected || pWinStation->Flags ) {
|
|
|
|
ReleaseWinStation( pWinStation );
|
|
Status = STATUS_CTX_WINSTATION_BUSY;
|
|
goto done;
|
|
}
|
|
|
|
|
|
if (bConsoleSession) {
|
|
InterlockedIncrement(&gConsoleCreationDisable);
|
|
bIncrementFlag = TRUE;
|
|
}
|
|
|
|
/*
|
|
* If no broken reason/source have been set, then set them here.
|
|
*
|
|
* BrokenReason is Disconnect. BrokenSource is User if we are
|
|
* called via RPC and caller is disconnecting his own LogonId.
|
|
*/
|
|
if ( pWinStation->BrokenReason == 0 ) {
|
|
pWinStation->BrokenReason = Broken_Disconnect;
|
|
if ( CallerIsRpc && LocalFlag && ClientLogonId == pWinStation->LogonId ) {
|
|
pWinStation->BrokenSource = BrokenSource_User;
|
|
} else {
|
|
pWinStation->BrokenSource = BrokenSource_Server;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* If it's an external request (RPC) then set the last error
|
|
* state to the client to indicate what the reason for the
|
|
* disconnection was.
|
|
*
|
|
*/
|
|
if( CallerIsRpc && pWinStation )
|
|
{
|
|
if(pWinStation->pWsx &&
|
|
pWinStation->pWsx->pWsxSetErrorInfo &&
|
|
pWinStation->pWsxContext)
|
|
{
|
|
pWinStation->pWsx->pWsxSetErrorInfo(
|
|
pWinStation->pWsxContext,
|
|
TS_ERRINFO_RPC_INITIATED_DISCONNECT,
|
|
FALSE); //stack lock not held
|
|
}
|
|
}
|
|
|
|
/*
|
|
* If the RPC caller did not wish to wait for this disconnect,
|
|
* then queue an internal call for this to be done.
|
|
* This is safe now that we have done all of the above checks
|
|
* to determine that the caller has access to perform the
|
|
* disconnect and have set BrokenSource/Reason above.
|
|
*/
|
|
if ( CallerIsRpc && !bWait ) {
|
|
ReleaseWinStation( pWinStation );
|
|
QueueWinStationDisconnect( LogonId );
|
|
Status = STATUS_SUCCESS;
|
|
goto done;
|
|
}
|
|
|
|
/*
|
|
* Preserve some of this WinStation's state in case it's
|
|
* needed after we disconnect and release it.
|
|
*/
|
|
PdFlag = pWinStation->Config.Pd[0].Create.PdFlag;
|
|
wcscpy( WinStationName, pWinStation->WinStationName );
|
|
if ( !gbServer ) {
|
|
wcscpy( ListenName, pWinStation->ListenName );
|
|
}
|
|
|
|
/*
|
|
* Notify the licensing core of the disconnect. Failures are ignored.
|
|
*/
|
|
|
|
(VOID)LCProcessConnectionDisconnect(pWinStation);
|
|
|
|
/*
|
|
* Disconnect the WinStation
|
|
*/
|
|
pWinStation->Flags |= WSF_DISCONNECT;
|
|
Status = WinStationDoDisconnect( pWinStation, NULL, FALSE );
|
|
pWinStation->Flags &= ~WSF_DISCONNECT;
|
|
|
|
/*
|
|
* If there is no user logged on (logon time is 0),
|
|
* then queue a reset for this WinStation.
|
|
*
|
|
* We don't want to do this here directly since the RPC client may
|
|
* NOT have reset access. However, the behavior we want is that if
|
|
* a WinStation with no user logged on is disconnected, it gets reset.
|
|
* This is consistent with how we handle broken connections
|
|
* (see WinStationBrokenConnection() in wstlpc.c).
|
|
*/
|
|
if ( RtlLargeIntegerEqualToZero( pWinStation->LogonTime ) ) {
|
|
QueueWinStationReset( pWinStation->LogonId);
|
|
}
|
|
|
|
ReleaseWinStation( pWinStation );
|
|
|
|
// Increment the total number of disconnected sessions
|
|
if (Status == STATUS_SUCCESS) {
|
|
InterlockedIncrement(&g_TermSrvDiscSessions);
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
* For single-instance transports a listener must be re-created
|
|
* upon disconnection
|
|
*/
|
|
KdPrintEx((DPFLTR_TERMSRV_ID, DPFLTR_INFO_LEVEL, "TERMSRV: WinStationDisconnect: after disconnecting\n" ));
|
|
|
|
|
|
if ( PdFlag & PD_SINGLE_INST ) {
|
|
|
|
Status = QueueWinStationCreate( WinStationName );
|
|
KdPrintEx((DPFLTR_TERMSRV_ID, DPFLTR_INFO_LEVEL, "TERMSRV: WinStationDisconnect: QueueWinStationCreate returned 0x%x\n", Status ));
|
|
if ( !NT_SUCCESS( Status ) ) {
|
|
goto done;
|
|
}
|
|
}
|
|
|
|
if ( !gbServer && ListenName[0]) {
|
|
|
|
StartStopListeners( ListenName, FALSE );
|
|
}
|
|
|
|
/*
|
|
* Determine return status and cleanup
|
|
*/
|
|
done:
|
|
TRACE((hTrace,TC_ICASRV,TT_API1, "TERMSRV: WinStationDisconnect, Status=0x%x\n", Status ));
|
|
|
|
if (bIncrementFlag) {
|
|
InterlockedDecrement(&gConsoleCreationDisable);
|
|
bIncrementFlag = FALSE;
|
|
}
|
|
|
|
// If we disconnected a Session owning the console terminal, go an create a new one
|
|
// to own it.
|
|
|
|
if (bConsoleSession) {
|
|
ENTERCRIT(&ConsoleLock);
|
|
if (!WinStationCheckConsoleSession()) {
|
|
/*
|
|
* Wake up the WinStationIdleControlThread
|
|
*/
|
|
NtSetEvent(WinStationIdleControlEvent, NULL);
|
|
|
|
}
|
|
LEAVECRIT(&ConsoleLock);
|
|
}
|
|
return( Status );
|
|
}
|
|
|
|
|
|
|
|
/*****************************************************************************
|
|
* WinStationConnectWorker
|
|
*
|
|
* Worker for handling WinStation connection called from RPC server.
|
|
*
|
|
* ENTRY:
|
|
* pContext (input)
|
|
* Pointer to our context structure describing the connection.
|
|
* bAutoReconnecting (input)
|
|
* Boolean set to TRUE to indicate that this is a connection for the
|
|
* purposes of autoreconnection. This is important to allow an atomic
|
|
* autoreconnection by properly handling the WSF_AUTORECONNECTING flag
|
|
* that otherwise guards against race conditions while autoreconnecting.
|
|
*
|
|
* pMsg (input/output)
|
|
* Pointer to the API message, a superset of NT LPC PORT_MESSAGE.
|
|
****************************************************************************/
|
|
NTSTATUS
|
|
WinStationConnectWorker(
|
|
ULONG ClientLogonId,
|
|
ULONG ConnectLogonId,
|
|
ULONG TargetLogonId,
|
|
PWCHAR pPassword,
|
|
DWORD PasswordSize,
|
|
BOOLEAN bWait,
|
|
BOOLEAN bAutoReconnecting
|
|
)
|
|
{
|
|
PWINSTATION pClientWinStation;
|
|
PWINSTATION pSourceWinStation;
|
|
PWINSTATION pTargetWinStation;
|
|
PWINSTATION pWinStation;
|
|
PSID pClientSid;
|
|
UNICODE_STRING PasswordString;
|
|
BOOLEAN fWrongPassword;
|
|
PRECONNECT_INFO pTargetReconnectInfo = NULL;
|
|
PRECONNECT_INFO pSourceReconnectInfo = NULL;
|
|
BOOLEAN SourceConnected = FALSE;
|
|
WINSTATIONNAME SourceWinStationName;
|
|
NTSTATUS Status;
|
|
ULONG SidLength;
|
|
PTOKEN_USER TokenInfo = NULL;
|
|
HANDLE CurrentThreadToken = NULL;
|
|
BOOLEAN bIsImpersonating = FALSE;
|
|
ULONG Length;
|
|
RPC_STATUS RpcStatus;
|
|
BOOLEAN bConsoleSession = FALSE;
|
|
ULONG ulIndex;
|
|
LONG lActiveCount = 0;
|
|
PLIST_ENTRY Head, Next;
|
|
BOOLEAN fSourceAutoReconnecting = FALSE;
|
|
BOOLEAN fTargetAutoReconnecting = FALSE;
|
|
|
|
|
|
// Do not allow reconnection to the same session, for any session.
|
|
// BUG 506808
|
|
// This problem was only happening on a non-logged in console (bConsoleConnected=FALSE), when from
|
|
// some other session, TsCon was used to connection session0 to console session:
|
|
// tscon 0 /dest:console
|
|
//
|
|
|
|
if (TargetLogonId == ConnectLogonId)
|
|
{
|
|
Status = STATUS_CTX_WINSTATION_ACCESS_DENIED;
|
|
return Status;
|
|
}
|
|
|
|
|
|
|
|
//
|
|
//On session 0 it might happen that user already logged on,
|
|
//but termsrv is not notified yet.
|
|
//in this case "Local\\WinlogonTSSynchronizeEvent" event will be in
|
|
//nonsignaled state. We need to refuse all attempts to connect to
|
|
//session 0 untill this event is signaled.
|
|
//
|
|
if (ConnectLogonId == 0) {
|
|
|
|
|
|
HANDLE hSyncEvent;
|
|
DWORD dwWaitResult;
|
|
|
|
hSyncEvent = OpenEventW(SYNCHRONIZE, FALSE, L"Local\\WinlogonTSSynchronizeEvent");
|
|
if ( !hSyncEvent){
|
|
KdPrintEx((DPFLTR_TERMSRV_ID, DPFLTR_ERROR_LEVEL,
|
|
"TERMSRV: Cannot open WinlogonTSSynchronizeEvent event. ERROR: %d\n",GetLastError()));
|
|
return STATUS_OBJECT_NAME_NOT_FOUND;
|
|
}
|
|
|
|
dwWaitResult = WaitForSingleObject(hSyncEvent,0);
|
|
|
|
CloseHandle(hSyncEvent);
|
|
|
|
if(dwWaitResult != WAIT_OBJECT_0) {
|
|
TRACE((hTrace,TC_ICASRV,TT_API1,
|
|
"TERMSRV: WinStationConnectWorker. WinlogonTSSynchronizeEvent is not signaled.\n"));
|
|
return STATUS_CTX_WINSTATION_BUSY;
|
|
}
|
|
|
|
}
|
|
|
|
TRACE((hTrace,TC_ICASRV,TT_API1, "TERMSRV: WinStationConnect, LogonId=%u, Target LogonId=%u, (%S)\n",
|
|
ConnectLogonId, TargetLogonId, pPassword ));
|
|
|
|
/*
|
|
* Allocate RECONNECT_INFO structures
|
|
*/
|
|
|
|
if ((pTargetReconnectInfo = MemAlloc(sizeof(*pTargetReconnectInfo))) == NULL) {
|
|
return STATUS_NO_MEMORY;
|
|
}
|
|
|
|
if ((pSourceReconnectInfo = MemAlloc(sizeof(*pSourceReconnectInfo))) == NULL) {
|
|
MemFree(pTargetReconnectInfo);
|
|
return STATUS_NO_MEMORY;
|
|
}
|
|
|
|
/*
|
|
* Impersonate the client and find the SID of the session initiating the
|
|
* connect operation
|
|
*/
|
|
RpcStatus = RpcImpersonateClient( NULL );
|
|
if ( RpcStatus != RPC_S_OK ) {
|
|
KdPrintEx((DPFLTR_TERMSRV_ID, DPFLTR_ERROR_LEVEL, "TERMSRV: CheckClientAccess: Not impersonating! RpcStatus 0x%x\n",RpcStatus));
|
|
Status = STATUS_CANNOT_IMPERSONATE ;
|
|
goto done;
|
|
} else {
|
|
bIsImpersonating = TRUE;
|
|
}
|
|
|
|
Status = NtOpenThreadToken(
|
|
NtCurrentThread(),
|
|
TOKEN_QUERY,
|
|
TRUE, // Use service's security
|
|
// context to open thread token
|
|
&CurrentThreadToken
|
|
);
|
|
|
|
if (! NT_SUCCESS(Status)) {
|
|
KdPrintEx((DPFLTR_TERMSRV_ID, DPFLTR_ERROR_LEVEL, "Termsrv: Cannot open the current thread token %08lx\n",
|
|
Status));
|
|
goto done;
|
|
}
|
|
/*
|
|
* Determine size needed for token info buffer and allocate it
|
|
*/
|
|
Status = NtQueryInformationToken( CurrentThreadToken, TokenUser,
|
|
NULL, 0, &Length );
|
|
if ( Status != STATUS_BUFFER_TOO_SMALL ) {
|
|
goto done;
|
|
}
|
|
TokenInfo = MemAlloc( Length );
|
|
if ( TokenInfo == NULL ) {
|
|
Status = STATUS_NO_MEMORY;
|
|
goto done;
|
|
}
|
|
|
|
/*
|
|
* Query token information to get client user's SID
|
|
*/
|
|
Status = NtQueryInformationToken( CurrentThreadToken, TokenUser,
|
|
TokenInfo, Length, &Length );
|
|
if ( !NT_SUCCESS( Status ) ) {
|
|
MemFree( TokenInfo );
|
|
goto done;
|
|
}
|
|
|
|
|
|
SidLength = RtlLengthSid( TokenInfo->User.Sid );
|
|
pClientSid = (PSID) MemAlloc( SidLength );
|
|
if (!pClientSid) {
|
|
MemFree( TokenInfo );
|
|
Status = STATUS_NO_MEMORY;
|
|
goto done;
|
|
}
|
|
|
|
Status = RtlCopySid(SidLength, pClientSid, TokenInfo->User.Sid);
|
|
if (!NT_SUCCESS(Status)) {
|
|
MemFree( TokenInfo );
|
|
MemFree( pClientSid );
|
|
goto done;
|
|
}
|
|
|
|
NtClose( CurrentThreadToken );
|
|
CurrentThreadToken = NULL;
|
|
RpcRevertToSelf();
|
|
bIsImpersonating = FALSE;
|
|
MemFree( TokenInfo );
|
|
|
|
/*
|
|
* on non server make sure to fail reconnect if it is going to endup in more than one active session.
|
|
*/
|
|
|
|
|
|
|
|
|
|
if (!gbServer) {
|
|
Head = &WinStationListHead;
|
|
ENTERCRIT( &WinStationListLock );
|
|
for ( Next = Head->Flink; Next != Head; Next = Next->Flink) {
|
|
pWinStation = CONTAINING_RECORD( Next, WINSTATION, Links );
|
|
if ( (pWinStation->State == State_Active) || (pWinStation->State == State_Shadow) ){
|
|
if (pWinStation->LogonId != ConnectLogonId && pWinStation->LogonId != TargetLogonId ) {
|
|
if (!TSIsSessionHelpSession(pWinStation, NULL)) {
|
|
lActiveCount ++;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
LEAVECRIT( &WinStationListLock );
|
|
if (lActiveCount != 0) {
|
|
Status = STATUS_CTX_WINSTATION_NOT_FOUND;
|
|
MemFree( pClientSid );
|
|
goto done;
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
* Find and lock the WinStation (source) for the specified LogonId
|
|
*/
|
|
|
|
pSourceWinStation = FindWinStationById( ConnectLogonId, FALSE );
|
|
if ( pSourceWinStation == NULL ) {
|
|
Status = STATUS_CTX_WINSTATION_NOT_FOUND;
|
|
MemFree( pClientSid );
|
|
goto done;
|
|
}
|
|
if (ConnectLogonId == 0 && !bConsoleConnected ){
|
|
|
|
Status = WaitForConsoleConnectWorker( pSourceWinStation );
|
|
if (NT_SUCCESS(Status)) {
|
|
bConsoleConnected=TRUE;
|
|
} else{
|
|
ReleaseWinStation( pSourceWinStation );
|
|
MemFree( pClientSid );
|
|
goto done;
|
|
}
|
|
|
|
}
|
|
|
|
/*
|
|
* Verify that there is someone logged on (SALIMC)
|
|
*/
|
|
if ( (ConnectLogonId != 0) && !pSourceWinStation->pUserSid ) {
|
|
Status = STATUS_CTX_WINSTATION_ACCESS_DENIED;
|
|
ReleaseWinStation( pSourceWinStation );
|
|
MemFree( pClientSid );
|
|
goto done;
|
|
}
|
|
|
|
/*
|
|
* Verify that client has CONNECT access
|
|
*
|
|
* NOTE: This function clears pPassword whether successful or not
|
|
* to prevent its being paged out as clear text.
|
|
*/
|
|
Status = _CheckConnectAccess(
|
|
pSourceWinStation,
|
|
pClientSid,
|
|
ClientLogonId,
|
|
pPassword,
|
|
PasswordSize
|
|
);
|
|
MemFree( pClientSid );
|
|
if ( !NT_SUCCESS( Status ) ) {
|
|
ReleaseWinStation( pSourceWinStation );
|
|
goto done;
|
|
}
|
|
|
|
//
|
|
// Deterime if source is part of an autoreconnection
|
|
//
|
|
fSourceAutoReconnecting = bAutoReconnecting &&
|
|
(pSourceWinStation->Flags & WSF_AUTORECONNECTING);
|
|
|
|
|
|
/*
|
|
* Mark the winstation as being connected. (SALIMC)
|
|
* If any operation (create/delete/reset/...) is already in progress
|
|
* on this winstation, then don't proceed with the connect.
|
|
* unless that operation is an autoreconnect and we are autoreconnecting
|
|
*/
|
|
if (pSourceWinStation->LogonId == 0) {
|
|
if (pSourceWinStation->Flags && !fSourceAutoReconnecting) {
|
|
if ((pSourceWinStation->Flags & WSF_DISCONNECT) && (pSourceWinStation->UserName[0] == L'\0')) {
|
|
/* Let us wait for sometime here before setting Busy flag and exiting */
|
|
for (ulIndex=0; ulIndex < WINSTATION_WAIT_RETRIES; ulIndex++) {
|
|
if ( pSourceWinStation->Flags ) {
|
|
LARGE_INTEGER Timeout;
|
|
Timeout = RtlEnlargedIntegerMultiply( WINSTATION_WAIT_DURATION, -10000 );
|
|
UnlockWinStation( pSourceWinStation );
|
|
NtDelayExecution( FALSE, &Timeout );
|
|
if ( !RelockWinStation( pSourceWinStation ) ) {
|
|
ReleaseWinStation( pSourceWinStation );
|
|
Status = STATUS_CTX_WINSTATION_BUSY;
|
|
goto done;
|
|
}
|
|
} else {
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
if (pSourceWinStation->Flags && !fSourceAutoReconnecting) {
|
|
#if DBG
|
|
DbgPrint("WinstationConnectWorker : Even after waiting for 2 mins,Winstation flag is not clear. Sending STATUS_CTX_WINSTATION_BUSY.\n");
|
|
#endif
|
|
Status = STATUS_CTX_WINSTATION_BUSY;
|
|
ReleaseWinStation( pSourceWinStation );
|
|
goto done;
|
|
}
|
|
}
|
|
|
|
} else if ( pSourceWinStation->NeverConnected ||
|
|
(pSourceWinStation->Flags && !fSourceAutoReconnecting) ||
|
|
(pSourceWinStation->State != State_Active &&
|
|
pSourceWinStation->State != State_Disconnected) ) {
|
|
Status = STATUS_CTX_WINSTATION_BUSY;
|
|
ReleaseWinStation( pSourceWinStation );
|
|
goto done;
|
|
}
|
|
|
|
pSourceWinStation->Flags |= WSF_CONNECT;
|
|
|
|
/*
|
|
* Unlock the source WinStation but keep a reference to it.
|
|
*/
|
|
UnlockWinStation( pSourceWinStation );
|
|
|
|
/*
|
|
* Now find and lock the target WinStation
|
|
*/
|
|
pTargetWinStation = FindWinStationById( TargetLogonId, FALSE );
|
|
if ( pTargetWinStation == NULL ) {
|
|
Status = STATUS_CTX_WINSTATION_NOT_FOUND;
|
|
goto badname;
|
|
}
|
|
if (TargetLogonId == 0 && !bConsoleConnected){
|
|
|
|
Status = WaitForConsoleConnectWorker( pTargetWinStation );
|
|
if (NT_SUCCESS(Status)) {
|
|
bConsoleConnected=TRUE;
|
|
} else {
|
|
ReleaseWinStation( pTargetWinStation );
|
|
goto badname;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Verify that client has DISCONNECT access
|
|
*/
|
|
Status = RpcCheckClientAccess( pTargetWinStation, WINSTATION_DISCONNECT, FALSE );
|
|
if ( !NT_SUCCESS( Status ) )
|
|
goto targetnoaccess;
|
|
|
|
|
|
/*
|
|
* Do not allow a client running on the same machine to reconnect a the console session to its own session.
|
|
*/
|
|
|
|
if (pSourceWinStation->fOwnsConsoleTerminal && IsValidLoopBack(pTargetWinStation, ConnectLogonId, pTargetWinStation->Client.ClientSessionId)) {
|
|
Status = STATUS_CTX_CONSOLE_CONNECT;
|
|
goto targetnoconsole;
|
|
}
|
|
|
|
|
|
/*
|
|
* On server do not allow reconnecting a non zero session to the iconsole.
|
|
*/
|
|
|
|
if (pTargetWinStation->fOwnsConsoleTerminal && gbServer && (ConnectLogonId != 0)) {
|
|
Status = STATUS_CTX_CONSOLE_DISCONNECT;
|
|
goto targetnoconsole;
|
|
}
|
|
|
|
// Whistler supports reconnecting a session from Console to a given Remote Protocol
|
|
// But Whistler does not support direct reconnect from one remote protocol to a different remote protocol
|
|
// Check for the above condition and block this scenario
|
|
|
|
if ( (pSourceWinStation->Client.ProtocolType != PROTOCOL_CONSOLE) && (pTargetWinStation->Client.ProtocolType != PROTOCOL_CONSOLE) ) {
|
|
// This is not a Direct Console Disconnect/Reconnect Scenario
|
|
if (pSourceWinStation->Client.ProtocolType != pTargetWinStation->Client.ProtocolType) {
|
|
Status = STATUS_CTX_BAD_VIDEO_MODE ;
|
|
goto targetnoaccess;
|
|
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Make sure the reconnected session is licensed.
|
|
*/
|
|
|
|
Status = LCProcessConnectionReconnect(pSourceWinStation, pTargetWinStation);
|
|
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
goto badlicense;
|
|
}
|
|
|
|
fTargetAutoReconnecting = bAutoReconnecting &&
|
|
(pTargetWinStation->Flags & WSF_AUTORECONNECTING);
|
|
/*
|
|
* Mark the winstation as being disconnected.
|
|
* If any operation (create/delete/reset/...) is already in progress
|
|
* on this winstation, then don't proceed with the connect.
|
|
*/
|
|
if ( pTargetWinStation->NeverConnected ||
|
|
(pTargetWinStation->Flags && !fTargetAutoReconnecting)) {
|
|
Status = STATUS_CTX_WINSTATION_BUSY;
|
|
goto targetbusy;
|
|
}
|
|
|
|
|
|
pTargetWinStation->Flags |= WSF_DISCONNECT;
|
|
|
|
|
|
/*
|
|
* Disconnect the target WinStation
|
|
*/
|
|
KdPrintEx((DPFLTR_TERMSRV_ID, DPFLTR_INFO_LEVEL, "WinStationConnectWorker Disconnecting target!\n"));
|
|
|
|
/*
|
|
* Note if we are disconnecting the session that owns the console
|
|
*/
|
|
if (pTargetWinStation->fOwnsConsoleTerminal) {
|
|
|
|
bConsoleSession = TRUE;
|
|
UnlockWinStation( pTargetWinStation );
|
|
ENTERCRIT(&ConsoleLock);
|
|
InterlockedIncrement(&gConsoleCreationDisable);
|
|
LEAVECRIT(&ConsoleLock);
|
|
if (!RelockWinStation( pTargetWinStation )) {
|
|
Status = STATUS_CTX_WINSTATION_NOT_FOUND;
|
|
goto baddisconnecttarget;
|
|
}
|
|
|
|
}
|
|
|
|
Status = WinStationDoDisconnect( pTargetWinStation, pTargetReconnectInfo, FALSE );
|
|
|
|
if ( !NT_SUCCESS( Status ) ) {
|
|
KdPrintEx((DPFLTR_TERMSRV_ID, DPFLTR_ERROR_LEVEL, "WinStationConnectWorker Disconnecting target failed Status = %x!\n", Status));
|
|
goto baddisconnecttarget;
|
|
}
|
|
|
|
/*
|
|
* Unlock target WinStation but leave it referenced
|
|
*/
|
|
UnlockWinStation( pTargetWinStation );
|
|
|
|
|
|
/*
|
|
* Relock the source WinStation
|
|
*/
|
|
if ( !RelockWinStation( pSourceWinStation ) )
|
|
goto sourcedeleted;
|
|
|
|
/*
|
|
* The source Winstation might have been deleted while it was unlocked.
|
|
* Let's check this didn't happen because we don't want to reconnect to a
|
|
* going away session (Bug#206614).
|
|
*/
|
|
if (pSourceWinStation->Terminating || pSourceWinStation->StateFlags & WSF_ST_WINSTATIONTERMINATE)
|
|
goto sourcedeleted;
|
|
|
|
/*
|
|
* If the source WinStation is currently connected, then disconnect it.
|
|
*/
|
|
if ( pSourceWinStation->WinStationName[0] ) {
|
|
SourceConnected = TRUE;
|
|
|
|
/*
|
|
* For single-instance transports a listener must be re-created upon disconnection
|
|
* so we remember the source WinStation name.
|
|
*/
|
|
if ( pSourceWinStation->Config.Pd[0].Create.PdFlag & PD_SINGLE_INST ) {
|
|
wcscpy( SourceWinStationName, pSourceWinStation->WinStationName );
|
|
}
|
|
|
|
KdPrintEx((DPFLTR_TERMSRV_ID, DPFLTR_INFO_LEVEL, "WinStationConnectWorker Disconnecting Source!\n"));
|
|
|
|
|
|
/*
|
|
* Note if we are disconnecting the session that owns the console
|
|
*/
|
|
if (pSourceWinStation->fOwnsConsoleTerminal) {
|
|
/*
|
|
* If we are disconnecting the console session, we want to make sure
|
|
* that we can precreate a session that would become the console session.
|
|
*/
|
|
|
|
UnlockWinStation( pSourceWinStation );
|
|
if ( !ShutdownInProgress) {
|
|
Status = CheckIdleWinstation();
|
|
if (!NT_SUCCESS(Status)) {
|
|
RelockWinStation(pSourceWinStation);
|
|
goto baddisconnectsource;
|
|
}
|
|
|
|
}
|
|
|
|
bConsoleSession = TRUE;
|
|
ENTERCRIT(&ConsoleLock);
|
|
InterlockedIncrement(&gConsoleCreationDisable);
|
|
LEAVECRIT(&ConsoleLock);
|
|
if (!RelockWinStation( pSourceWinStation )) {
|
|
Status = STATUS_CTX_WINSTATION_NOT_FOUND;
|
|
goto baddisconnectsource;
|
|
}
|
|
|
|
}
|
|
|
|
|
|
if(pSourceWinStation->pWsx &&
|
|
pSourceWinStation->pWsx->pWsxSetErrorInfo &&
|
|
pSourceWinStation->pWsxContext)
|
|
{
|
|
//
|
|
// Extended error reporting, set status to client.
|
|
//
|
|
pSourceWinStation->pWsx->pWsxSetErrorInfo(
|
|
pSourceWinStation->pWsxContext,
|
|
TS_ERRINFO_DISCONNECTED_BY_OTHERCONNECTION,
|
|
FALSE); //stack lock not held
|
|
}
|
|
|
|
|
|
Status = WinStationDoDisconnect( pSourceWinStation, pSourceReconnectInfo, TRUE );
|
|
if ( !NT_SUCCESS( Status ) ) {
|
|
KdPrintEx((DPFLTR_TERMSRV_ID, DPFLTR_ERROR_LEVEL, "WinStationConnectWorker Disconnecting source failed Status = %x!\n", Status));
|
|
goto baddisconnectsource;
|
|
}
|
|
|
|
|
|
}
|
|
|
|
/*
|
|
* Cause the source WinStation to connect using the stack state
|
|
* obtained from the target WinStation.
|
|
*/
|
|
|
|
|
|
Status = WinStationDoReconnect( pSourceWinStation, pTargetReconnectInfo );
|
|
|
|
|
|
|
|
if ( !NT_SUCCESS( Status ) )
|
|
goto badconnectsource;
|
|
|
|
/*
|
|
* Indicate source WinStation connect is complete and unlock it.
|
|
*/
|
|
pSourceWinStation->Flags &= ~WSF_CONNECT;
|
|
|
|
/*
|
|
* Set Last Reconnect Type for the Source WinStation
|
|
*/
|
|
|
|
if (bAutoReconnecting) {
|
|
pSourceWinStation->LastReconnectType = AutoReconnect;
|
|
} else {
|
|
pSourceWinStation->LastReconnectType = ManualReconnect;
|
|
}
|
|
|
|
ReleaseWinStation( pSourceWinStation );
|
|
|
|
|
|
/*
|
|
* Indicate target WinStation disconnect is complete and unlock it.
|
|
*/
|
|
if ( RelockWinStation( pTargetWinStation ) ) {
|
|
pTargetWinStation->Flags &= ~WSF_DISCONNECT;
|
|
|
|
/*
|
|
* Clear all client license data and indicate
|
|
* this WinStaion no longer holds a license.
|
|
*/
|
|
if ( pTargetWinStation->pWsx &&
|
|
pTargetWinStation->pWsx->pWsxClearContext ) {
|
|
pTargetWinStation->pWsx->pWsxClearContext( pTargetWinStation->pWsxContext );
|
|
}
|
|
}
|
|
|
|
ReleaseWinStation( pTargetWinStation );
|
|
|
|
/*
|
|
* If the source WinStation was connected and we disconnected it above,
|
|
* then make sure we cleanup the reconnect structure.
|
|
* (This will also complete the disconnect by closing the endpoint
|
|
* that was connected to the source WinStation).
|
|
* Also, if the source WinStation was a single-instance transport,
|
|
* then we must re-create the listener.
|
|
*/
|
|
if ( SourceConnected ) {
|
|
CleanupReconnect( pSourceReconnectInfo );
|
|
if ( (pSourceReconnectInfo->Config.Pd[0].Create.PdFlag & PD_SINGLE_INST) ) {
|
|
KdPrintEx((DPFLTR_TERMSRV_ID, DPFLTR_ERROR_LEVEL, "TERMSRV: WinstationConnectWorker create new winstation: %S \n ", SourceWinStationName));
|
|
QueueWinStationCreate( SourceWinStationName );
|
|
}
|
|
}
|
|
|
|
// Stop the listener if the target was the last WinStation.
|
|
if ( !gbServer ) {
|
|
|
|
StartStopListeners( NULL, FALSE );
|
|
}
|
|
|
|
goto done;
|
|
|
|
/*=============================================================================
|
|
== Error returns
|
|
=============================================================================*/
|
|
|
|
/*
|
|
* Could not connect source WinStation
|
|
*/
|
|
badconnectsource:
|
|
/*
|
|
* If source WinStation was connected, try to reconnect if it is
|
|
* NOT currently terminating. If the reconnect does not succeed,
|
|
* there's nothing else we can do.
|
|
*/
|
|
if ( SourceConnected ) {
|
|
CleanupReconnect( pSourceReconnectInfo );
|
|
if ( !pSourceWinStation->Terminating &&
|
|
!pSourceWinStation->WinStationName[0] ) {
|
|
if ( pSourceReconnectInfo->Config.Pd[0].Create.PdFlag & PD_SINGLE_INST ) {
|
|
QueueWinStationCreate( pSourceReconnectInfo->WinStationName );
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Could not disconnect source WinStation
|
|
*/
|
|
baddisconnectsource:
|
|
|
|
/*
|
|
* Source WinStation was deleted
|
|
*/
|
|
sourcedeleted:
|
|
pSourceWinStation->Flags &= ~WSF_CONNECT;
|
|
ReleaseWinStation( pSourceWinStation );
|
|
pSourceWinStation = NULL; // indicate source WinStation is released
|
|
|
|
/*
|
|
* Try to relock and reconnect the target WinStation.
|
|
*/
|
|
if ( RelockWinStation( pTargetWinStation ) &&
|
|
!pTargetWinStation->Terminating &&
|
|
!pTargetWinStation->WinStationName[0] ) {
|
|
NTSTATUS st;
|
|
|
|
st = WinStationDoReconnect( pTargetWinStation, pTargetReconnectInfo );
|
|
if ( !NT_SUCCESS( st ) ) {
|
|
CleanupReconnect( pTargetReconnectInfo );
|
|
if ( pTargetReconnectInfo->Config.Pd[0].Create.PdFlag & PD_SINGLE_INST ) {
|
|
QueueWinStationCreate( pTargetReconnectInfo->WinStationName );
|
|
}
|
|
}
|
|
} else {
|
|
CleanupReconnect( pTargetReconnectInfo );
|
|
}
|
|
|
|
/*
|
|
* Could not disconnect target WinStation
|
|
* Could not query target WinStation stack state
|
|
*/
|
|
baddisconnecttarget:
|
|
/* clear disconnect flag, unlock/derererence target WinStation */
|
|
pTargetWinStation->Flags &= ~WSF_DISCONNECT;
|
|
|
|
/*
|
|
* Target WinStation is busy or is the console
|
|
*/
|
|
targetbusy:
|
|
badlicense:
|
|
targetnoconsole:
|
|
targetnoaccess:
|
|
ReleaseWinStation( pTargetWinStation );
|
|
|
|
badname:
|
|
/* clear connect flag, unlock/derererence source WinStation */
|
|
if ( pSourceWinStation ) {
|
|
if ( RelockWinStation( pSourceWinStation ) )
|
|
pSourceWinStation->Flags &= ~WSF_CONNECT;
|
|
ReleaseWinStation( pSourceWinStation );
|
|
}
|
|
|
|
done:
|
|
TRACE((hTrace,TC_ICASRV,TT_API1, "TERMSRV: WinStationConnect, Status=0x%x\n", Status ));
|
|
|
|
// If we disconnected a Session owning the console terminal, go an create a new one
|
|
// to own it.
|
|
|
|
if (bConsoleSession) {
|
|
ENTERCRIT(&ConsoleLock);
|
|
|
|
InterlockedDecrement(&gConsoleCreationDisable);
|
|
|
|
|
|
|
|
|
|
|
|
if (!WinStationCheckConsoleSession()) {
|
|
/*
|
|
* Wake up the WinStationIdleControlThread
|
|
*/
|
|
NtSetEvent(WinStationIdleControlEvent, NULL);
|
|
|
|
}
|
|
LEAVECRIT(&ConsoleLock);
|
|
}
|
|
|
|
// Increment total number of reconnected sessions
|
|
if (Status == STATUS_SUCCESS) {
|
|
InterlockedIncrement(&g_TermSrvReconSessions);
|
|
}
|
|
if (bIsImpersonating) {
|
|
RpcRevertToSelf();
|
|
}
|
|
if (CurrentThreadToken) {
|
|
NtClose( CurrentThreadToken );
|
|
}
|
|
|
|
// free RECONNECT_INFO structures
|
|
|
|
MemFree(pTargetReconnectInfo);
|
|
MemFree(pSourceReconnectInfo);
|
|
|
|
return( Status );
|
|
}
|
|
|
|
|
|
/*****************************************************************************
|
|
* WinStationResetWorker
|
|
*
|
|
* Function to reset a Winstation based on an RPC API request.
|
|
*
|
|
* ENTRY:
|
|
* pContext (input)
|
|
* Pointer to our context structure describing the connection.
|
|
* pMsg (input/output)
|
|
* Pointer to the API message, a superset of NT LPC PORT_MESSAGE.
|
|
****************************************************************************/
|
|
NTSTATUS
|
|
WinStationResetWorker(
|
|
ULONG LogonId,
|
|
BOOLEAN bWait,
|
|
BOOLEAN CallerIsRpc,
|
|
BOOLEAN bRecreate
|
|
)
|
|
{
|
|
PWINSTATION pWinStation;
|
|
ULONG ClientLogonId;
|
|
WINSTATIONNAME ListenName;
|
|
NTSTATUS Status;
|
|
ULONG ulIndex;
|
|
BOOL bConnectDisconnectPending = TRUE;
|
|
BOOL bConsoleSession = FALSE;
|
|
BOOL bListener = FALSE;
|
|
UINT LocalFlag = 0;
|
|
BOOLEAN bRelock;
|
|
|
|
TRACE((hTrace,TC_ICASRV,TT_API1, "TERMSRV: WinStationReset, LogonId=%d\n", LogonId ));
|
|
|
|
/*
|
|
* Find and lock the WinStation struct for the specified LogonId
|
|
*/
|
|
pWinStation = FindWinStationById( LogonId, FALSE );
|
|
if ( pWinStation == NULL ) {
|
|
Status = STATUS_CTX_WINSTATION_NOT_FOUND;
|
|
goto done;
|
|
}
|
|
|
|
/*
|
|
* Note if we are disconnecting a session that is connected to the console terminal.
|
|
*/
|
|
bConsoleSession = pWinStation->fOwnsConsoleTerminal;
|
|
|
|
|
|
/*
|
|
* If we are Resetting a non-zero console session, we want to make sure
|
|
* that we can precreate a session that would become the console session.
|
|
*/
|
|
if (bConsoleSession && !ShutdownInProgress && (pWinStation->LogonId != 0)) {
|
|
UnlockWinStation(pWinStation);
|
|
Status = CheckIdleWinstation();
|
|
bRelock = RelockWinStation(pWinStation);
|
|
if (!NT_SUCCESS(Status) || !bRelock) {
|
|
if (NT_SUCCESS(Status)) {
|
|
Status = STATUS_CTX_WINSTATION_NOT_FOUND;
|
|
}
|
|
ReleaseWinStation( pWinStation );
|
|
goto done;
|
|
}
|
|
|
|
}
|
|
|
|
|
|
/*
|
|
* Save off the listen name if needed later.
|
|
*/
|
|
if ( pWinStation->Flags & WSF_LISTEN ) {
|
|
wcscpy(ListenName, pWinStation->WinStationName);
|
|
bListener = TRUE;
|
|
} else if (!gbServer) {
|
|
wcscpy(ListenName, pWinStation->ListenName);
|
|
}
|
|
|
|
/*
|
|
* Verify that client has RESET access if its an RPC (external) caller.
|
|
*
|
|
* When ICASRV calls this function internally, it is not impersonating
|
|
* and fails the RpcCheckClientAccess() call. Internal calls are
|
|
* not a security problem since they come in as LPC messages on a secured
|
|
* port.
|
|
*/
|
|
if ( CallerIsRpc ) {
|
|
RPC_STATUS RpcStatus;
|
|
|
|
/*
|
|
* Impersonate the client
|
|
*/
|
|
RpcStatus = RpcImpersonateClient( NULL );
|
|
if ( RpcStatus != RPC_S_OK ) {
|
|
ReleaseWinStation( pWinStation );
|
|
KdPrintEx((DPFLTR_TERMSRV_ID, DPFLTR_ERROR_LEVEL, "TERMSRV: WinStationResetWorker: Not impersonating! RpcStatus 0x%x\n",RpcStatus));
|
|
Status = STATUS_CANNOT_IMPERSONATE;
|
|
goto done;
|
|
}
|
|
|
|
Status = RpcCheckClientAccess( pWinStation, WINSTATION_RESET, TRUE );
|
|
if ( !NT_SUCCESS( Status ) ) {
|
|
RpcRevertToSelf();
|
|
ReleaseWinStation( pWinStation );
|
|
goto done;
|
|
}
|
|
|
|
//
|
|
// If its remote RPC call we should ignore ClientLogonId
|
|
//
|
|
RpcStatus = I_RpcBindingIsClientLocal(
|
|
0, // Active RPC call we are servicing
|
|
&LocalFlag
|
|
);
|
|
|
|
if( RpcStatus != RPC_S_OK ) {
|
|
RpcRevertToSelf();
|
|
ReleaseWinStation( pWinStation );
|
|
KdPrintEx((DPFLTR_TERMSRV_ID, DPFLTR_ERROR_LEVEL, "TERMSRV: WinStationResetWorker: IsClientLocal failed! RpcStatus 0x%x\n",RpcStatus));
|
|
Status = STATUS_UNSUCCESSFUL;
|
|
goto done;
|
|
}
|
|
|
|
if ( LocalFlag ) {
|
|
Status = RpcGetClientLogonId( &ClientLogonId );
|
|
if ( !NT_SUCCESS( Status ) ) {
|
|
RpcRevertToSelf();
|
|
ReleaseWinStation( pWinStation );
|
|
goto done;
|
|
}
|
|
}
|
|
|
|
RpcRevertToSelf();
|
|
|
|
if(pWinStation->WinStationName[0] &&
|
|
pWinStation->pWsx &&
|
|
pWinStation->pWsx->pWsxSetErrorInfo &&
|
|
pWinStation->pWsxContext)
|
|
{
|
|
pWinStation->pWsx->pWsxSetErrorInfo(
|
|
pWinStation->pWsxContext,
|
|
TS_ERRINFO_RPC_INITIATED_LOGOFF,
|
|
FALSE); //stack lock not held
|
|
}
|
|
}
|
|
|
|
/*
|
|
* For console reset, logoff (SALIMC)
|
|
*/
|
|
if ( LogonId == 0 ) {
|
|
DWORD dwTimeOut = 120*1000;
|
|
|
|
#if DBG
|
|
dwTimeOut = 240*1000;
|
|
#endif
|
|
|
|
|
|
Status = LogoffWinStation( pWinStation,EWX_FORCE | EWX_LOGOFF);
|
|
ReleaseWinStation( pWinStation );
|
|
if (NT_SUCCESS(Status) && bWait) {
|
|
DWORD dwRet;
|
|
dwRet = WaitForSingleObject(ConsoleLogoffEvent,120*1000);
|
|
if (dwRet == WAIT_TIMEOUT)
|
|
{
|
|
KdPrintEx((DPFLTR_TERMSRV_ID, DPFLTR_ERROR_LEVEL, "TERMSRV: TimedOut wait for ConsoleLogoffEvent\n"));
|
|
Status = STATUS_TIMEOUT;
|
|
}
|
|
}
|
|
goto done;
|
|
}
|
|
|
|
/*
|
|
* Mark the winstation as being reset.
|
|
* If a reset/delete operation is already in progress
|
|
* on this winstation, then don't proceed with the delete.
|
|
* If the WinStation is currently in the process of connecting or
|
|
* disconnecting, then give it some time to complete before we continue.
|
|
* If connect/disconnect doesn't complete whithin timeout duration
|
|
* do not proceed with termination (bug#204614).
|
|
*/
|
|
for (ulIndex=0; ulIndex < WINSTATION_WAIT_COMPLETE_RETRIES; ulIndex++) {
|
|
if ( pWinStation->Flags & (WSF_RESET | WSF_DELETE) ) {
|
|
ReleaseWinStation( pWinStation );
|
|
Status = STATUS_CTX_WINSTATION_BUSY;
|
|
goto done;
|
|
}
|
|
|
|
if ( pWinStation->Flags & (WSF_CONNECT | WSF_DISCONNECT |
|
|
WSF_AUTORECONNECTING) ) {
|
|
LARGE_INTEGER Timeout;
|
|
Timeout = RtlEnlargedIntegerMultiply( WINSTATION_WAIT_COMPLETE_DURATION, -10000 );
|
|
UnlockWinStation( pWinStation );
|
|
NtDelayExecution( FALSE, &Timeout );
|
|
if ( !RelockWinStation( pWinStation ) ) {
|
|
ReleaseWinStation( pWinStation );
|
|
Status = STATUS_SUCCESS;
|
|
goto done;
|
|
}
|
|
} else {
|
|
bConnectDisconnectPending = FALSE;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if ( bConnectDisconnectPending ) {
|
|
ReleaseWinStation( pWinStation );
|
|
Status = STATUS_CTX_WINSTATION_BUSY;
|
|
goto done;
|
|
}
|
|
|
|
pWinStation->Flags |= WSF_RESET;
|
|
|
|
/*
|
|
* If no broken reason/source have been set, then set them here.
|
|
*
|
|
* BrokenReason is Terminate. BrokenSource is User if we are
|
|
* called via RPC and caller is resetting his own LogonId, or if
|
|
* the "Terminating" field is already set, then this is a reset
|
|
* from the WinStationTerminateThread after seeing WinLogon/CSR exit.
|
|
* Otherwise, this reset is the result of a call from another
|
|
* WinStation or a QueueWinStationReset call from within ICASRV.
|
|
*/
|
|
if ( pWinStation->BrokenReason == 0 ) {
|
|
pWinStation->BrokenReason = Broken_Terminate;
|
|
if ( CallerIsRpc && LocalFlag && ClientLogonId == pWinStation->LogonId
|
|
|| pWinStation->Terminating ) {
|
|
pWinStation->BrokenSource = BrokenSource_User;
|
|
} else {
|
|
pWinStation->BrokenSource = BrokenSource_Server;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* If the RPC caller did not wish to wait for this reset,
|
|
* then queue an internal call for this to be done.
|
|
* This is safe now that we have done all of the above checks
|
|
* to determine that the caller has access to perform the
|
|
* reset and have set BrokenSource/Reason above.
|
|
*/
|
|
if ( CallerIsRpc && !bWait ) {
|
|
// clear reset flag so the internal reset will proceed
|
|
pWinStation->Flags &= ~WSF_RESET;
|
|
ReleaseWinStation( pWinStation );
|
|
QueueWinStationReset( LogonId);
|
|
Status = STATUS_SUCCESS;
|
|
goto done;
|
|
}
|
|
|
|
/*
|
|
* Make sure this WinStation is ready to reset
|
|
*/
|
|
WinStationTerminate( pWinStation );
|
|
|
|
/*
|
|
* If it's a listener, reset all active winstations of the same type
|
|
*/
|
|
if ((pWinStation->Flags & WSF_LISTEN) && ListenName[0] && bRecreate) {
|
|
ResetGroupByListener(ListenName);
|
|
}
|
|
|
|
/*
|
|
* If WinStation is marked DownPending (and is not disconnected),
|
|
* then set it to the Down state, clear the DownPending and Reset flags,
|
|
* and release the WinStation.
|
|
*/
|
|
if ( (pWinStation->Flags & WSF_DOWNPENDING) && pWinStation->WinStationName[0] ) {
|
|
pWinStation->State = State_Down;
|
|
pWinStation->Flags &= ~(WSF_DOWNPENDING | WSF_RESET);
|
|
ReleaseWinStation( pWinStation );
|
|
Status = STATUS_SUCCESS;
|
|
|
|
/*
|
|
* The WinStation is not DownPending so complete deleting it
|
|
* and then recreate it.
|
|
*/
|
|
} else {
|
|
ULONG PdFlag;
|
|
ULONG WinStationFlags;
|
|
WINSTATIONNAME WinStationName;
|
|
|
|
/*
|
|
* Save WinStation name for later create call
|
|
*/
|
|
WinStationFlags = pWinStation->Flags;
|
|
PdFlag = pWinStation->Config.Pd[0].Create.PdFlag;
|
|
wcscpy( WinStationName, pWinStation->WinStationName );
|
|
|
|
/*
|
|
* Call the WinStationDelete worker
|
|
*/
|
|
WinStationDeleteWorker( pWinStation );
|
|
|
|
/*
|
|
* Now recreate the WinStation
|
|
*/
|
|
if ( WinStationName[0] &&
|
|
bRecreate &&
|
|
((WinStationFlags & WSF_LISTEN) || (PdFlag & PD_SINGLE_INST)) ) {
|
|
Status = WinStationCreateWorker( WinStationName, NULL );
|
|
} else if ( WinStationFlags & WSF_IDLE ) {
|
|
// wake up WinStationIdleControlThread so that it recreates an idle session
|
|
NtSetEvent(WinStationIdleControlEvent, NULL);
|
|
} else {
|
|
Status = STATUS_SUCCESS;
|
|
}
|
|
}
|
|
|
|
// If we disconnected a Session owning the console terminal, go an create a new one
|
|
// to own it.
|
|
if (bConsoleSession) {
|
|
ENTERCRIT(&ConsoleLock);
|
|
if (!WinStationCheckConsoleSession()) {
|
|
/*
|
|
* Wake up the WinStationIdleControlThread
|
|
*/
|
|
NtSetEvent(WinStationIdleControlEvent, NULL);
|
|
|
|
}
|
|
LEAVECRIT(&ConsoleLock);
|
|
}
|
|
|
|
if ( !gbServer && !bListener && ListenName[0] ) {
|
|
|
|
StartStopListeners( ListenName, FALSE );
|
|
}
|
|
/*
|
|
* Save return status
|
|
*/
|
|
done:
|
|
TRACE((hTrace,TC_ICASRV,TT_API1, "TERMSRV: WinStationReset, Status=0x%x\n", Status ));
|
|
return( Status );
|
|
}
|
|
|
|
|
|
/*****************************************************************************
|
|
* WinStationShutdownSystemWorker
|
|
*
|
|
* Function to shutdown the system from an RPC API request
|
|
*
|
|
* ENTRY:
|
|
* pContext (input)
|
|
* Pointer to our context structure describing the connection.
|
|
* pMsg (input/output)
|
|
* Pointer to the API message, a superset of NT LPC PORT_MESSAGE.
|
|
****************************************************************************/
|
|
NTSTATUS
|
|
WinStationShutdownSystemWorker(
|
|
ULONG ClientLogonId,
|
|
ULONG ShutdownFlags
|
|
)
|
|
{
|
|
BOOL rc;
|
|
BOOLEAN WasEnabled;
|
|
NTSTATUS Status = 0;
|
|
NTSTATUS Status2;
|
|
PWINSTATION pWinStation;
|
|
UINT ExitWindowsFlags;
|
|
RPC_STATUS RpcStatus;
|
|
UINT LocalFlag;
|
|
|
|
|
|
TRACE((hTrace,TC_ICASRV,TT_API1, "TERMSRV: WinStationShutdownSystem, Flags=%d\n", ShutdownFlags ));
|
|
|
|
/*
|
|
* Impersonate the client so that when the attempt is made to enable
|
|
* the SE_SHUTDOWN_PRIVILEGE, it will fail if the account is not admin.
|
|
*/
|
|
RpcStatus = RpcImpersonateClient( NULL );
|
|
if( RpcStatus != RPC_S_OK ) {
|
|
KdPrintEx((DPFLTR_TERMSRV_ID, DPFLTR_ERROR_LEVEL, "TERMSRV: WinStationShutdownSystemWorker: Not impersonating! RpcStatus 0x%x\n",RpcStatus));
|
|
Status = STATUS_CANNOT_IMPERSONATE;
|
|
goto done;
|
|
}
|
|
|
|
//
|
|
// If its remote RPC call we should ignore ClientLogonId
|
|
//
|
|
RpcStatus = I_RpcBindingIsClientLocal(
|
|
0, // Active RPC call we are servicing
|
|
&LocalFlag
|
|
);
|
|
|
|
if( RpcStatus != RPC_S_OK ) {
|
|
KdPrintEx((DPFLTR_TERMSRV_ID, DPFLTR_ERROR_LEVEL, " I_RpcBindingIsClientLocal() failed : 0x%x\n",RpcStatus));
|
|
RpcRevertToSelf();
|
|
Status = STATUS_UNSUCCESSFUL;
|
|
goto done;
|
|
}
|
|
|
|
//
|
|
// we dont care about client logon id if this is called from remote machine.
|
|
// so lets set it zero. So its treated as shutdown from session 0.
|
|
//
|
|
if (!LocalFlag) {
|
|
ClientLogonId = 0;
|
|
}
|
|
|
|
|
|
/*
|
|
* We are called under RPC impersonation so that the current
|
|
* thread token represents the RPC client. If the RPC client
|
|
* does not have SE_SHUTDOWN_PRIVILEGE, the RtlAdjustPrivilege()
|
|
* will fail.
|
|
*/
|
|
Status = RtlAdjustPrivilege(
|
|
SE_SHUTDOWN_PRIVILEGE,
|
|
TRUE, // Enable the PRIVILEGE
|
|
TRUE, // Use Thread token (under impersonation)
|
|
&WasEnabled
|
|
);
|
|
if( NT_SUCCESS( Status ) && !WasEnabled ) {
|
|
/*
|
|
* Principle of least rights says to not go around with privileges
|
|
* held you do not need. So we must disable the shutdown privilege
|
|
* if it was just a logoff force.
|
|
*/
|
|
Status2 = RtlAdjustPrivilege(
|
|
SE_SHUTDOWN_PRIVILEGE,
|
|
FALSE, // Disable the PRIVILEGE
|
|
TRUE, // Use Thread token (under impersonation)
|
|
&WasEnabled
|
|
);
|
|
|
|
ASSERT( NT_SUCCESS(Status2) );
|
|
}
|
|
|
|
RpcRevertToSelf();
|
|
|
|
if ( Status == STATUS_NO_TOKEN ) {
|
|
KdPrintEx((DPFLTR_TERMSRV_ID, DPFLTR_ERROR_LEVEL, "TERMSRV: WinStationShutdownSystemWorker: No Thread token!\n"));
|
|
goto done;
|
|
}
|
|
|
|
if ( !NT_SUCCESS( Status ) ) {
|
|
KdPrintEx((DPFLTR_TERMSRV_ID, DPFLTR_ERROR_LEVEL, "TERMSRV: WinStationShutdownSystemWorker: RtlAdjustPrivilege failure 0x%x\n",Status));
|
|
goto done;
|
|
}
|
|
|
|
if ( ShutdownFlags == 0 )
|
|
goto done;
|
|
|
|
//
|
|
// At this point we know that the client has access to shutdown the machine.
|
|
// Now enable Shutdown privilege for the termsrv.exe process. This is done
|
|
// because winlogon only allow system processes to shutdown the machine if
|
|
// if no one is logged on the console session. If we just enable privilige
|
|
// for the impersonating thread winlogon will not consider it as a system
|
|
// process
|
|
//
|
|
Status = RtlAdjustPrivilege(
|
|
SE_SHUTDOWN_PRIVILEGE,
|
|
TRUE, // Enable the PRIVILEGE
|
|
FALSE, // Use Process Token
|
|
&WasEnabled
|
|
);
|
|
if ( !NT_SUCCESS( Status ) ) {
|
|
KdPrintEx((DPFLTR_TERMSRV_ID, DPFLTR_ERROR_LEVEL, "TERMSRV: WinStationShutdownSystemWorker: RtlAdjustPrivilege failure 0x%x\n",Status));
|
|
goto done;
|
|
}
|
|
|
|
/*
|
|
* Set global shutdown flag
|
|
*/
|
|
ShutdownInProgress = TRUE;
|
|
|
|
/*
|
|
* If logoff option is specified, then cause all WinStations
|
|
* to logoff now and don't restart them.
|
|
*/
|
|
if ( ShutdownFlags & (WSD_SHUTDOWN | WSD_LOGOFF) ) {
|
|
Status = ShutdownLogoff( ClientLogonId, ShutdownFlags );
|
|
}
|
|
|
|
if ( ShutdownFlags & (WSD_SHUTDOWN | WSD_REBOOT | WSD_POWEROFF) ) {
|
|
/*
|
|
* If system will be rebooted or powered off, then cause
|
|
* the client WinStation that called us to logoff now.
|
|
* If shutdown from non-console, close the connection (self) here.
|
|
*/
|
|
if ( (ShutdownFlags & (WSD_REBOOT | WSD_POWEROFF)) || ClientLogonId != 0) {
|
|
|
|
if (!ShutDownFromSessionID)
|
|
ShutDownFromSessionID = ClientLogonId;
|
|
|
|
// ShutdownTerminateNoWait = TRUE;
|
|
KdPrintEx((DPFLTR_TERMSRV_ID, DPFLTR_ERROR_LEVEL, "TERMSRV: Start reset of last WinStation\n" ));
|
|
(VOID) DoForWinStationGroup( &ClientLogonId, 1,
|
|
(LPTHREAD_START_ROUTINE) WinStationShutdownReset );
|
|
KdPrintEx((DPFLTR_TERMSRV_ID, DPFLTR_ERROR_LEVEL, "TERMSRV: Last WinStation reset\n" ));
|
|
|
|
|
|
if ( ClientLogonId == 0 ) {
|
|
|
|
DWORD dwRet;
|
|
dwRet = WaitForSingleObject(ConsoleLogoffEvent,120*1000);
|
|
if (dwRet == WAIT_TIMEOUT)
|
|
{
|
|
KdPrintEx((DPFLTR_TERMSRV_ID, DPFLTR_ERROR_LEVEL, "TERMSRV: WinStationShutdownSystemWorker: Timedout waiting for ConsoleLogoffEvent\n"));
|
|
Status = STATUS_TIMEOUT;
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Now complete system shutdown.
|
|
*
|
|
* Use ExitWindowsEx() so that console winlogon completes
|
|
* our shutdown. This is so that services get shutdown
|
|
* properly.
|
|
*/
|
|
if (ClientLogonId == (USER_SHARED_DATA->ActiveConsoleId) ) {
|
|
ExitWindowsFlags = 0;
|
|
} else {
|
|
ExitWindowsFlags = EWX_FORCE;
|
|
}
|
|
if ( ShutdownFlags & WSD_REBOOT )
|
|
ExitWindowsFlags |= EWX_REBOOT;
|
|
else if ( ShutdownFlags & WSD_POWEROFF )
|
|
ExitWindowsFlags |= EWX_POWEROFF;
|
|
else
|
|
ExitWindowsFlags |= EWX_SHUTDOWN;
|
|
|
|
/*
|
|
* Need to pass the EWX_TERMSRV_INITIATED to let winlogon know
|
|
* that the shutdown was initiated by termsrv.
|
|
*/
|
|
rc = ExitWindowsEx( ExitWindowsFlags | EWX_TERMSRV_INITIATED, 0 );
|
|
if( !rc ) {
|
|
KdPrintEx((DPFLTR_TERMSRV_ID, DPFLTR_ERROR_LEVEL, "TERMSRV: ExitWindowsEx failed %d\n",GetLastError() ));
|
|
}
|
|
}
|
|
|
|
if( !WasEnabled ) {
|
|
|
|
/*
|
|
* Principle of least rights says to not go around with privileges
|
|
* held you do not need. So we must disable the shutdown privilege
|
|
* if it was just a logoff force.
|
|
*/
|
|
Status2 = RtlAdjustPrivilege(
|
|
SE_SHUTDOWN_PRIVILEGE,
|
|
FALSE, // Disable the PRIVILEGE
|
|
FALSE, // Use Process Token
|
|
&WasEnabled
|
|
);
|
|
ASSERT( NT_SUCCESS(Status2) );
|
|
}
|
|
|
|
/*
|
|
* Save return status in API message
|
|
*/
|
|
done:
|
|
TRACE((hTrace,TC_ICASRV,TT_API1, "TERMSRV: WinStationShutdownSystem, Status=0x%x\n", Status ));
|
|
return( Status );
|
|
}
|
|
|
|
|
|
/*****************************************************************************
|
|
* WinStationTerminateProcessWorker
|
|
*
|
|
* Terminate the specified process
|
|
*
|
|
* ENTRY:
|
|
* ProcessId (input)
|
|
* process id of the process to terminate
|
|
* ExitCode (input)
|
|
* Termination status for each thread in the process
|
|
****************************************************************************/
|
|
NTSTATUS
|
|
WinStationTerminateProcessWorker(
|
|
ULONG ProcessId,
|
|
ULONG ExitCode
|
|
)
|
|
{
|
|
OBJECT_ATTRIBUTES Obja;
|
|
CLIENT_ID ClientId;
|
|
BOOLEAN fWasEnabled = FALSE;
|
|
NTSTATUS Status;
|
|
NTSTATUS Status2;
|
|
SID_IDENTIFIER_AUTHORITY NtSidAuthority = SECURITY_NT_AUTHORITY;
|
|
HANDLE ProcHandle = NULL;
|
|
HANDLE TokenHandle = NULL;
|
|
PTOKEN_USER pTokenInfo = NULL;
|
|
ULONG TokenInfoLength;
|
|
ULONG ReturnLength;
|
|
int rc;
|
|
|
|
TRACE(( hTrace,TC_ICASRV,TT_API1, "TERMSRV: WinStationTerminateProcess, PID=%d, ExitCode %u\n",
|
|
ProcessId, ExitCode ));
|
|
|
|
/*
|
|
* If possible, enable the debug privilege
|
|
*/
|
|
(void) RtlAdjustPrivilege( SE_DEBUG_PRIVILEGE,
|
|
TRUE, // Enable the PRIVILEGE
|
|
TRUE, // Use Thread token (under impersonation)
|
|
&fWasEnabled );
|
|
|
|
/*
|
|
* Attempt to open the process for query and terminate access.
|
|
*/
|
|
ClientId.UniqueThread = (HANDLE) NULL;
|
|
ClientId.UniqueProcess = (HANDLE) LongToHandle( ProcessId );
|
|
InitializeObjectAttributes( &Obja, NULL, 0, NULL, NULL );
|
|
Status = NtOpenProcess( &ProcHandle,
|
|
PROCESS_QUERY_INFORMATION | PROCESS_TERMINATE,
|
|
&Obja,
|
|
&ClientId );
|
|
if ( !NT_SUCCESS(Status) )
|
|
goto restore;
|
|
|
|
/*
|
|
* Open the process token so we can query the user SID. If the user
|
|
* SID for this process is the SYSTEM SID, we will deny access to it.
|
|
* This is to prevent ADMINs from killing system processes.
|
|
*/
|
|
Status = NtOpenProcessToken( ProcHandle, TOKEN_QUERY, &TokenHandle );
|
|
|
|
/*
|
|
* Its possible for OpenProcess above to succeed and NtOpenProcessToken
|
|
* to fail. One scenario is an ADMIN user trying to kill a USER process.
|
|
* Standard security allows ADMIN terminate access to the process but
|
|
* does not allow any access to the process token. In this case we
|
|
* will just skip the SID check and do the terminate below.
|
|
*/
|
|
if ( NT_SUCCESS( Status ) ) {
|
|
/*
|
|
* Allocate buffer for reading user SID
|
|
*/
|
|
TokenInfoLength = sizeof(TOKEN_USER) +
|
|
RtlLengthRequiredSid( SID_MAX_SUB_AUTHORITIES );
|
|
pTokenInfo = MemAlloc( TokenInfoLength );
|
|
if ( pTokenInfo == NULL ) {
|
|
Status = STATUS_NO_MEMORY;
|
|
goto freeit;
|
|
}
|
|
|
|
/*
|
|
* Query the user SID in the token
|
|
*/
|
|
Status = NtQueryInformationToken( TokenHandle, TokenUser, pTokenInfo,
|
|
TokenInfoLength, &ReturnLength );
|
|
if ( !NT_SUCCESS( Status ) )
|
|
goto freeit;
|
|
|
|
/*
|
|
* If user SID for this process is the SYSTEM SID,
|
|
* then we don't allow it to be terminated.
|
|
*/
|
|
if ( RtlEqualSid( gSystemSid, pTokenInfo->User.Sid ) ) {
|
|
Status = STATUS_ACCESS_DENIED;
|
|
goto freeit;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Now try to terminate the process
|
|
*/
|
|
Status = NtTerminateProcess( ProcHandle, (NTSTATUS)ExitCode );
|
|
|
|
freeit:
|
|
if ( pTokenInfo )
|
|
MemFree( pTokenInfo );
|
|
|
|
if ( TokenHandle )
|
|
CloseHandle( TokenHandle );
|
|
|
|
if ( ProcHandle )
|
|
CloseHandle( ProcHandle );
|
|
|
|
restore:
|
|
if( !fWasEnabled ) {
|
|
|
|
/*
|
|
* Principle of least rights says to not go around with privileges
|
|
* held you do not need. So we must disable the debug privilege
|
|
* if it was not enabled on entry to this routine.
|
|
*/
|
|
Status2 = RtlAdjustPrivilege(
|
|
SE_DEBUG_PRIVILEGE,
|
|
FALSE, // Disable the PRIVILEGE
|
|
TRUE, // Use Thread token (under impersonation)
|
|
&fWasEnabled
|
|
);
|
|
|
|
ASSERT( NT_SUCCESS(Status2) );
|
|
}
|
|
|
|
TRACE((hTrace,TC_ICASRV,TT_API1, "TERMSRV: WinStationTerminateProcess, Status=0x%x\n", Status ));
|
|
return( Status );
|
|
}
|
|
|
|
|
|
/*****************************************************************************
|
|
* WinStationWaitSystemEventWorker
|
|
*
|
|
* Function to wait for a system event from an RPC API request.
|
|
*
|
|
* Only one event wait may be posted per server handle at a time. The
|
|
* code protects itself from misuse by returning STATUS_PIPE_BUSY if
|
|
* an eventwait is already outstanding, and the request is not a cancel.
|
|
*
|
|
* ENTRY:
|
|
* pContext (input)
|
|
* Pointer to our context structure describing the connection.
|
|
* pMsg (input/output)
|
|
* Pointer to the API message, a superset of NT LPC PORT_MESSAGE.
|
|
****************************************************************************/
|
|
NTSTATUS
|
|
WinStationWaitSystemEventWorker(
|
|
HANDLE hServer,
|
|
ULONG EventMask,
|
|
PULONG pEventFlags
|
|
)
|
|
{
|
|
NTSTATUS Status;
|
|
PEVENT pWaitEvent;
|
|
OBJECT_ATTRIBUTES ObjA;
|
|
PRPC_CLIENT_CONTEXT pContext = (PRPC_CLIENT_CONTEXT)hServer;
|
|
|
|
TRACE((hTrace,TC_ICASRV,TT_API1, "TERMSRV: WinStationWaitSystemEventWorker, Mask=0x%08x\n", EventMask ));
|
|
|
|
/*
|
|
* Protect ourselves from multiple threads calling in at once
|
|
*/
|
|
RtlEnterCriticalSection( &WinStationListLock );
|
|
|
|
/*
|
|
* If client doesn't already have an event block,
|
|
* then allocate and initialize one now.
|
|
*/
|
|
if ( pContext->pWaitEvent == NULL ) {
|
|
|
|
// If event mask is null or flush specified, then nothing to do
|
|
if ( EventMask == WEVENT_NONE || (EventMask & WEVENT_FLUSH) ) {
|
|
Status = STATUS_SUCCESS;
|
|
RtlLeaveCriticalSection( &WinStationListLock );
|
|
goto done;
|
|
}
|
|
|
|
/*
|
|
* Allocate event block and initialize it
|
|
*/
|
|
if ( (pWaitEvent = MemAlloc( sizeof(EVENT) )) == NULL ) {
|
|
Status = STATUS_NO_MEMORY;
|
|
RtlLeaveCriticalSection( &WinStationListLock );
|
|
goto done;
|
|
}
|
|
RtlZeroMemory( pWaitEvent, sizeof(EVENT) );
|
|
|
|
pWaitEvent->fWaiter = FALSE;
|
|
pWaitEvent->EventMask = EventMask;
|
|
pWaitEvent->EventFlags = 0;
|
|
|
|
/*
|
|
* Create an event to wait on
|
|
*/
|
|
InitializeObjectAttributes( &ObjA, NULL, 0, NULL, NULL );
|
|
Status = NtCreateEvent( &pWaitEvent->Event, EVENT_ALL_ACCESS, &ObjA,
|
|
NotificationEvent, FALSE );
|
|
|
|
if( !NT_SUCCESS(Status) ) {
|
|
MemFree( pWaitEvent );
|
|
RtlLeaveCriticalSection( &WinStationListLock );
|
|
goto done;
|
|
}
|
|
TRACE((hTrace,TC_ICASRV,TT_API1, "TERMSRV: WinStationWaitSystemEvent, Event=%p\n", pWaitEvent->Event ));
|
|
|
|
TRACE((hTrace,TC_ICAAPI,TT_API3, "TERMSRV: WinStationWaitSystemEvent, Event block=%p\n", pWaitEvent ));
|
|
|
|
/*
|
|
* Save event block pointer in RPC client context structure
|
|
* and insert in system event list.
|
|
*/
|
|
pContext->pWaitEvent = pWaitEvent;
|
|
InsertTailList( &SystemEventHead, &pWaitEvent->EventListEntry );
|
|
|
|
/*
|
|
* Wait for the event to be signaled
|
|
*/
|
|
pWaitEvent->fWaiter = TRUE;
|
|
RtlLeaveCriticalSection( &WinStationListLock );
|
|
Status = WaitForSingleObject( pWaitEvent->Event, (DWORD)-1 );
|
|
RtlEnterCriticalSection( &WinStationListLock );
|
|
pWaitEvent->fWaiter = FALSE;
|
|
|
|
if ( NT_SUCCESS(Status) ) {
|
|
Status = pWaitEvent->WaitResult;
|
|
if( NT_SUCCESS(Status) ) {
|
|
*pEventFlags = pWaitEvent->EventFlags;
|
|
/*
|
|
* makarp - Fix For . (#21929)
|
|
*/
|
|
pWaitEvent->EventFlags = 0;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* If fClosing is set, then cleanup the eventwait entry and free it.
|
|
*/
|
|
if ( pWaitEvent->fClosing ) {
|
|
pContext->pWaitEvent = NULL;
|
|
RemoveEntryList( &pWaitEvent->EventListEntry );
|
|
RtlLeaveCriticalSection( &WinStationListLock );
|
|
NtClose( pWaitEvent->Event );
|
|
pWaitEvent->Event = NULL;
|
|
MemFree( pWaitEvent );
|
|
} else {
|
|
RtlLeaveCriticalSection( &WinStationListLock );
|
|
}
|
|
TRACE((hTrace,TC_ICASRV,TT_API1, "TERMSRV: WinStationWaitSystemEvent, Status=0x%x\n", Status ));
|
|
return( Status );
|
|
|
|
/*
|
|
* Client has an event block but wants to remove it
|
|
*/
|
|
} else if ( EventMask == WEVENT_NONE ) {
|
|
|
|
pWaitEvent = pContext->pWaitEvent;
|
|
|
|
// If we have a waiter, mark the eventwait struct as closing
|
|
// and let the waiter clean up.
|
|
if ( pWaitEvent->fWaiter ) {
|
|
pWaitEvent->fClosing = TRUE;
|
|
pWaitEvent->WaitResult = STATUS_CANCELLED;
|
|
NtSetEvent( pWaitEvent->Event, NULL );
|
|
RtlLeaveCriticalSection( &WinStationListLock );
|
|
Status = STATUS_SUCCESS;
|
|
TRACE((hTrace,TC_ICASRV,TT_ERROR, "TERMSRV: WinStationWaitSystemEvent, Status=0x%x\n", Status ));
|
|
return( Status );
|
|
}
|
|
pContext->pWaitEvent = NULL;
|
|
RemoveEntryList( &pWaitEvent->EventListEntry );
|
|
RtlLeaveCriticalSection( &WinStationListLock );
|
|
NtClose( pWaitEvent->Event );
|
|
pWaitEvent->Event = NULL;
|
|
MemFree( pWaitEvent );
|
|
Status = STATUS_SUCCESS;
|
|
TRACE((hTrace,TC_ICASRV,TT_API1, "TERMSRV: WinStationWaitSystemEvent, Status=0x%x\n", Status ));
|
|
return( Status );
|
|
|
|
/*
|
|
* Flush specified so we must release waiting client
|
|
*/
|
|
} else if ( EventMask & WEVENT_FLUSH ) {
|
|
pWaitEvent = pContext->pWaitEvent;
|
|
if ( pWaitEvent->fWaiter ) {
|
|
pWaitEvent->WaitResult = STATUS_CANCELLED;
|
|
NtSetEvent( pWaitEvent->Event, NULL );
|
|
TRACE((hTrace,TC_ICAAPI,TT_API3, "TERMSRV: WinStationWaitSystemEvent, event wait cancelled\n" ));
|
|
}
|
|
RtlLeaveCriticalSection( &WinStationListLock );
|
|
Status = STATUS_SUCCESS;
|
|
TRACE((hTrace,TC_ICASRV,TT_API1, "TERMSRV: WinStationWaitSystemEvent, Status=0x%x\n", Status ));
|
|
return( Status );
|
|
|
|
/*
|
|
* Client already has an event block and is calling again
|
|
* to wait for another event. Update the EventMask in case it
|
|
* changed from the original call.
|
|
*/
|
|
} else {
|
|
|
|
pWaitEvent = pContext->pWaitEvent;
|
|
|
|
// Only allow one waiter
|
|
if ( pWaitEvent->fWaiter ) {
|
|
RtlLeaveCriticalSection( &WinStationListLock );
|
|
Status = STATUS_PIPE_BUSY;
|
|
TRACE((hTrace,TC_ICASRV,TT_ERROR, "TERMSRV: WinStationWaitSystemEvent, Status=0x%x\n", Status ));
|
|
return( Status );
|
|
}
|
|
|
|
pWaitEvent->EventMask = EventMask;
|
|
|
|
/*
|
|
* If additional events occured while client was processing
|
|
* previous events, then just return to the client now.
|
|
*/
|
|
if ( pWaitEvent->EventFlags &= EventMask ) {
|
|
*pEventFlags = pWaitEvent->EventFlags;
|
|
pWaitEvent->EventFlags = 0;
|
|
Status = STATUS_SUCCESS;
|
|
RtlLeaveCriticalSection( &WinStationListLock );
|
|
TRACE((hTrace,TC_ICAAPI,TT_API3, "TERMSRV: WinStationWaitSystemEvent, returning immediately\n" ));
|
|
return( Status );
|
|
} else {
|
|
|
|
TRACE((hTrace,TC_ICASRV,TT_API1, "TERMSRV: WinStationWaitSystemEvent, waiting for event\n" ));
|
|
|
|
// Reset the event
|
|
NtResetEvent( pWaitEvent->Event, NULL );
|
|
|
|
/*
|
|
* Wait for the event to be signaled
|
|
*/
|
|
pWaitEvent->fWaiter = TRUE;
|
|
RtlLeaveCriticalSection( &WinStationListLock );
|
|
Status = WaitForSingleObject( pWaitEvent->Event, (DWORD)-1 );
|
|
RtlEnterCriticalSection( &WinStationListLock );
|
|
pWaitEvent->fWaiter = FALSE;
|
|
|
|
if( NT_SUCCESS(Status) ) {
|
|
Status = pWaitEvent->WaitResult;
|
|
if( NT_SUCCESS(Status) ) {
|
|
*pEventFlags = pWaitEvent->EventFlags;
|
|
/*
|
|
* makarp - Fix For . (#21929)
|
|
*/
|
|
pWaitEvent->EventFlags = 0;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* If fClosing is set, then cleanup the eventwait entry and free it.
|
|
*/
|
|
if ( pWaitEvent->fClosing ) {
|
|
pContext->pWaitEvent = NULL;
|
|
RemoveEntryList( &pWaitEvent->EventListEntry );
|
|
RtlLeaveCriticalSection( &WinStationListLock );
|
|
NtClose( pWaitEvent->Event );
|
|
pWaitEvent->Event = NULL;
|
|
MemFree( pWaitEvent );
|
|
} else {
|
|
RtlLeaveCriticalSection( &WinStationListLock );
|
|
}
|
|
TRACE((hTrace,TC_ICASRV,TT_API1, "TERMSRV: WinStationWaitSystemEvent, Status=0x%x\n", Status ));
|
|
return( Status );
|
|
}
|
|
}
|
|
|
|
done:
|
|
TRACE((hTrace,TC_ICASRV,TT_API1, "TERMSRV: WinStationWaitSystemEvent, Status=0x%x\n", Status ));
|
|
return( Status );
|
|
}
|
|
|
|
|
|
/*****************************************************************************
|
|
* WinStationCallbackWorker
|
|
*
|
|
* Perform callback processing for the specified WinStation.
|
|
*
|
|
* ENTRY:
|
|
* LogonId (input)
|
|
* Logon Id of WinStation
|
|
* pPhoneNumber (input)
|
|
* Phone number string suitable for processing by TAPI
|
|
****************************************************************************/
|
|
NTSTATUS
|
|
WinStationCallbackWorker(
|
|
ULONG LogonId,
|
|
PWCHAR pPhoneNumber
|
|
)
|
|
{
|
|
PWINSTATION pWinStation;
|
|
NTSTATUS Status;
|
|
|
|
/*
|
|
* Find and lock client WinStation
|
|
*/
|
|
pWinStation = FindWinStationById( LogonId, FALSE );
|
|
if ( pWinStation == NULL ) {
|
|
Status = STATUS_CTX_WINSTATION_NOT_FOUND;
|
|
return( Status );
|
|
}
|
|
|
|
/*
|
|
* Unlock WinStation while Callback is in progress
|
|
*/
|
|
UnlockWinStation( pWinStation );
|
|
|
|
if (pWinStation->hStack != NULL) {
|
|
Status = IcaStackCallback( pWinStation->hStack,
|
|
&pWinStation->Config,
|
|
pPhoneNumber,
|
|
pWinStation->pEndpoint,
|
|
pWinStation->EndpointLength,
|
|
&pWinStation->EndpointLength );
|
|
} else {
|
|
Status = STATUS_INVALID_PARAMETER;
|
|
|
|
}
|
|
|
|
return( Status );
|
|
}
|
|
|
|
|
|
/*****************************************************************************
|
|
* WinStationBreakPointWorker
|
|
*
|
|
* Message parameter unmarshalling function for WinStation API.
|
|
*
|
|
* ENTRY:
|
|
* pContext (input)
|
|
* Pointer to our context structure describing the connection.
|
|
*
|
|
* pMsg (input/output)
|
|
* Pointer to the API message, a superset of NT LPC PORT_MESSAGE.
|
|
****************************************************************************/
|
|
NTSTATUS
|
|
WinStationBreakPointWorker(
|
|
ULONG LogonId,
|
|
BOOLEAN KernelFlag
|
|
)
|
|
{
|
|
NTSTATUS Status;
|
|
NTSTATUS Status2;
|
|
BOOLEAN WasEnabled;
|
|
WINSTATION_APIMSG WMsg;
|
|
PWINSTATION pWinStation;
|
|
|
|
/*
|
|
* We are called under RPC impersonation so that the current
|
|
* thread token represents the RPC client. If the RPC client
|
|
* does not have SE_SHUTDOWN_PRIVILEGE, the RtlAdjustPrivilege()
|
|
* will fail.
|
|
*
|
|
* SE_SHUTDOWN_PRIVILEGE is used for breakpoints because that is
|
|
* effectively what a break point does to the system.
|
|
*/
|
|
Status = RtlAdjustPrivilege(
|
|
SE_SHUTDOWN_PRIVILEGE,
|
|
TRUE, // Enable the PRIVILEGE
|
|
TRUE, // Use Thread token (under impersonation)
|
|
&WasEnabled
|
|
);
|
|
|
|
if ( Status == STATUS_NO_TOKEN ) {
|
|
KdPrintEx((DPFLTR_TERMSRV_ID, DPFLTR_ERROR_LEVEL, "TERMSRV: WinStationBreakPointWorker: No Thread token!\n"));
|
|
return( Status );
|
|
}
|
|
|
|
if ( !NT_SUCCESS( Status ) ) {
|
|
KdPrintEx((DPFLTR_TERMSRV_ID, DPFLTR_ERROR_LEVEL, "TERMSRV: WinStationBreakPointWorker: RtlAdjustPrivilege failure 0x%x\n",Status));
|
|
return( Status );
|
|
}
|
|
|
|
/*
|
|
* Stop here if that's what was requested
|
|
*/
|
|
if ( LogonId == (ULONG)-2 ) {
|
|
DbgBreakPoint();
|
|
Status = STATUS_SUCCESS;
|
|
goto Done;
|
|
}
|
|
|
|
/*
|
|
* Find and lock client WinStation
|
|
*/
|
|
pWinStation = FindWinStationById( LogonId, FALSE );
|
|
if ( pWinStation == NULL ) {
|
|
Status = STATUS_CTX_WINSTATION_NOT_FOUND;
|
|
goto Done;
|
|
}
|
|
|
|
/*
|
|
* Tell the WinStation to breakpoint
|
|
*/
|
|
|
|
WMsg.ApiNumber = SMWinStationDoBreakPoint;
|
|
WMsg.u.BreakPoint.KernelFlag = KernelFlag;
|
|
Status = SendWinStationCommand( pWinStation, &WMsg, 0 );
|
|
|
|
ReleaseWinStation( pWinStation );
|
|
|
|
Done:
|
|
|
|
if( !WasEnabled ) {
|
|
/*
|
|
* Principle of least rights says to not go around with privileges
|
|
* held you do not need.
|
|
*/
|
|
Status2 = RtlAdjustPrivilege(
|
|
SE_SHUTDOWN_PRIVILEGE,
|
|
FALSE, // Disable the PRIVILEGE
|
|
TRUE, // Use Thread token (under impersonation)
|
|
&WasEnabled
|
|
);
|
|
|
|
ASSERT( NT_SUCCESS(Status2) );
|
|
}
|
|
|
|
return( Status );
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
WinStationEnableSessionIo(
|
|
PWINSTATION pWinStation,
|
|
BOOL bEnable
|
|
)
|
|
/*++
|
|
|
|
Description:
|
|
|
|
Funtion to disable keyboard and mouse input from session, this is to prevent
|
|
security hole that a hacker can send keystrock to bring up utility manager
|
|
before shadowing.
|
|
|
|
Parameters:
|
|
|
|
pWinStation (INPUT) : Pointer to winstation, function will ignore if session is
|
|
not a help session.
|
|
bEnable (INPUT) : TRUE to enable keyboard/mouse, FALSE otherwise.
|
|
|
|
Returns:
|
|
|
|
...
|
|
|
|
Note:
|
|
|
|
WINSTATION structure must be locked. Two new IOCTL code so we don't introduce any regression.
|
|
|
|
|
|
--*/
|
|
{
|
|
HANDLE ChannelHandle;
|
|
NTSTATUS Status;
|
|
|
|
if( pWinStation->fOwnsConsoleTerminal )
|
|
{
|
|
//
|
|
// Don't want to disable mouse/keyboard input on active console session,
|
|
//
|
|
Status = STATUS_SUCCESS;
|
|
}
|
|
else
|
|
{
|
|
Status = IcaChannelOpen(
|
|
pWinStation->hIca,
|
|
Channel_Keyboard,
|
|
NULL,
|
|
&ChannelHandle
|
|
);
|
|
if ( NT_SUCCESS( Status ) ) {
|
|
Status = IcaChannelIoControl(
|
|
ChannelHandle,
|
|
(bEnable) ? IOCTL_ICA_CHANNEL_ENABLE_SESSION_IO : IOCTL_ICA_CHANNEL_DISABLE_SESSION_IO,
|
|
NULL, 0, NULL, 0, NULL
|
|
);
|
|
IcaChannelClose( ChannelHandle );
|
|
}
|
|
|
|
Status = IcaChannelOpen(
|
|
pWinStation->hIca,
|
|
Channel_Mouse,
|
|
NULL,
|
|
&ChannelHandle
|
|
);
|
|
if ( NT_SUCCESS( Status ) ) {
|
|
Status = IcaChannelIoControl( ChannelHandle,
|
|
(bEnable) ? IOCTL_ICA_CHANNEL_ENABLE_SESSION_IO : IOCTL_ICA_CHANNEL_DISABLE_SESSION_IO,
|
|
NULL, 0, NULL, 0, NULL );
|
|
IcaChannelClose( ChannelHandle );
|
|
}
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
|
|
/*****************************************************************************
|
|
* WinStationNotifyLogonWorker
|
|
*
|
|
* Message parameter unmarshalling function for WinStation API.
|
|
****************************************************************************/
|
|
NTSTATUS WinStationNotifyLogonWorker(
|
|
DWORD ClientLogonId,
|
|
DWORD ClientProcessId,
|
|
BOOLEAN fUserIsAdmin,
|
|
DWORD UserToken,
|
|
PWCHAR pDomain,
|
|
DWORD DomainSize,
|
|
PWCHAR pUserName,
|
|
DWORD UserNameSize,
|
|
PWCHAR pPassword,
|
|
DWORD PasswordSize,
|
|
UCHAR Seed,
|
|
PCHAR pUserConfig,
|
|
DWORD ConfigSize)
|
|
{
|
|
extern GENERIC_MAPPING WinStaMapping;
|
|
extern LPCWSTR szTermsrv;
|
|
extern LPCWSTR szTermsrvSession;
|
|
|
|
PWINSTATION pWinStation;
|
|
HANDLE ClientToken, NewToken;
|
|
SECURITY_QUALITY_OF_SERVICE SecurityQualityOfService;
|
|
OBJECT_ATTRIBUTES ObjA;
|
|
HANDLE ImpersonationToken;
|
|
PTOKEN_USER TokenInfo;
|
|
ULONG Length;
|
|
NTSTATUS Status = STATUS_SUCCESS;
|
|
ULONG UserNameLength = USERNAME_LENGTH;
|
|
ULONG DomainLength = DOMAIN_LENGTH;
|
|
BOOL bAccessCheckOk = FALSE;
|
|
DWORD GrantedAccess;
|
|
BOOL AccessStatus;
|
|
BOOL fGenerateOnClose;
|
|
PTSSD_CreateSessionInfo pCreateInfo = NULL;
|
|
BOOL bHaveCreateInfo = FALSE;
|
|
BOOL bQueueReset = FALSE;
|
|
BOOL bRedirect = FALSE; // TRUE: redirect this connection
|
|
|
|
BOOL bValidHelpSession;
|
|
|
|
TRACE((hTrace,TC_ICASRV,TT_API1, "TERMSRV: WinStationNotifyLogon, LogonId=%d\n", ClientLogonId ));
|
|
|
|
if( sizeof( USERCONFIGW ) > ConfigSize ) {
|
|
return( STATUS_ACCESS_VIOLATION );
|
|
}
|
|
|
|
//
|
|
// If this is a notification from Session 0, then clear the Mpr notification information
|
|
// Termsrv already erases this after query, but since this is critical information, lets Erase everything again
|
|
// This is to take care of the case where reconnect to Session 0 failed for some reason and we do not Erase these after Query
|
|
//
|
|
|
|
if (ClientLogonId == 0) {
|
|
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) );
|
|
}
|
|
|
|
if ( ShutdownInProgress ) {
|
|
return ( STATUS_CTX_WINSTATION_ACCESS_DENIED );
|
|
}
|
|
|
|
pCreateInfo = MemAlloc(sizeof(TSSD_CreateSessionInfo));
|
|
if (NULL == pCreateInfo) {
|
|
return ( STATUS_NO_MEMORY );
|
|
}
|
|
|
|
/*
|
|
* Find and lock client WinStation
|
|
*/
|
|
pWinStation = FindWinStationById( ClientLogonId, FALSE );
|
|
if ( pWinStation == NULL ) {
|
|
Status = STATUS_CTX_WINSTATION_NOT_FOUND;
|
|
goto done;
|
|
}
|
|
|
|
|
|
//
|
|
// Release filtered address
|
|
//
|
|
if (pWinStation->pRememberedAddress != NULL) {
|
|
Filter_RemoveOutstandingConnection( &pWinStation->pRememberedAddress->addr[0], pWinStation->pRememberedAddress->length );
|
|
MemFree(pWinStation->pRememberedAddress);
|
|
pWinStation->pRememberedAddress = NULL;
|
|
if( (ULONG)InterlockedDecrement( &NumOutStandingConnect ) == MaxOutStandingConnect )
|
|
{
|
|
if (hConnectEvent != NULL)
|
|
{
|
|
SetEvent(hConnectEvent);
|
|
}
|
|
}
|
|
|
|
|
|
}
|
|
|
|
// The client has communicated its initial configuration
|
|
// information. Check if we need to redirect the client for
|
|
// load balancing. Ignore the console!
|
|
// Note: Only go through this if SD is enabled, i.e. GetTSSD() returns valid value
|
|
if (ClientLogonId != 0 && !g_bPersonalTS && g_fAppCompat && GetTSSD()) {
|
|
PTS_LOAD_BALANCE_INFO pLBInfo = NULL;
|
|
PWINSTATION pTargetWinStation = pWinStation;
|
|
ULONG ReturnLength;
|
|
BOOL bSuccess = FALSE;
|
|
|
|
pLBInfo = MemAlloc(sizeof(TS_LOAD_BALANCE_INFO));
|
|
if (NULL == pLBInfo) {
|
|
Status = STATUS_NO_MEMORY;
|
|
goto done;
|
|
}
|
|
|
|
// need to release it
|
|
ReleaseTSSD();
|
|
|
|
// Get the client load balance capability info. We continue onward
|
|
// to do a session directory query only when the client supports
|
|
// redirection and has not already been redirected to this server.
|
|
memset(pLBInfo, 0, sizeof(TS_LOAD_BALANCE_INFO));
|
|
Status = IcaStackIoControl(pTargetWinStation->hStack,
|
|
IOCTL_TS_STACK_QUERY_LOAD_BALANCE_INFO,
|
|
NULL, 0,
|
|
pLBInfo, sizeof(TS_LOAD_BALANCE_INFO),
|
|
&ReturnLength);
|
|
|
|
// Only check for possible redirection if this is the initial request
|
|
if (NT_SUCCESS(Status) && !pLBInfo->bRequestedSessionIDFieldValid) {
|
|
// On non-success, we'll have FALSE for all of our entries, on
|
|
// success valid values. So, save off the cluster info into the
|
|
// WinStation struct now.
|
|
pTargetWinStation->bClientSupportsRedirection =
|
|
pLBInfo->bClientSupportsRedirection;
|
|
pTargetWinStation->bRequestedSessionIDFieldValid =
|
|
pLBInfo->bRequestedSessionIDFieldValid;
|
|
pTargetWinStation->bClientRequireServerAddr =
|
|
pLBInfo->bClientRequireServerAddr;
|
|
pTargetWinStation->RequestedSessionID = pLBInfo->RequestedSessionID;
|
|
|
|
// Use the name & domain they used to actually log on
|
|
memset(pLBInfo->Domain, 0, sizeof(pLBInfo->Domain));
|
|
memset(pLBInfo->UserName, 0, sizeof(pLBInfo->UserName));
|
|
wcsncpy(pLBInfo->Domain, pDomain, DomainLength);
|
|
wcsncpy(pLBInfo->UserName, pUserName, UserNameLength);
|
|
|
|
TRACE((hTrace,TC_LOAD,TT_API1,
|
|
"Client LBInfo: Supports Redirect [%lx], "
|
|
"Session Id valid [%lx]:%lx, "
|
|
"Creds [%S\\%S]\n",
|
|
pLBInfo->bClientSupportsRedirection,
|
|
pLBInfo->bRequestedSessionIDFieldValid,
|
|
pLBInfo->RequestedSessionID,
|
|
pLBInfo->UserName, pLBInfo->Domain));
|
|
|
|
wcsncpy(pLBInfo->Password, pPassword, PasswordSize);
|
|
|
|
bSuccess = SessDirCheckRedirectClient(pTargetWinStation, pLBInfo);
|
|
|
|
// Clear password
|
|
if (0 != PasswordSize)
|
|
memset(pLBInfo->Password, 0, PasswordSize);
|
|
|
|
if (bSuccess) {
|
|
// The client should drop the socket, and we'll
|
|
// go ahead and drop this ongoing connection.
|
|
// Set an error status.
|
|
Status = STATUS_UNSUCCESSFUL;
|
|
bRedirect = TRUE;
|
|
|
|
TRACE((hTrace,TC_LOAD,TT_API1,
|
|
"Disconnected session found: redirecting client!\n"));
|
|
|
|
if (pLBInfo != NULL) {
|
|
MemFree(pLBInfo);
|
|
pLBInfo = NULL;
|
|
}
|
|
|
|
goto release;
|
|
}
|
|
else
|
|
TRACE((hTrace,TC_LOAD,TT_API1,
|
|
"Disconnected session not found: status [%lx], pers [%ld], appcompat [%ld]\n",
|
|
Status, g_bPersonalTS, g_fAppCompat));
|
|
}
|
|
|
|
if (pLBInfo != NULL) {
|
|
MemFree(pLBInfo);
|
|
pLBInfo = NULL;
|
|
}
|
|
}
|
|
|
|
|
|
if (ClientLogonId == 0) {
|
|
//
|
|
//ReSet the Console Logon Event
|
|
//
|
|
KdPrintEx((DPFLTR_TERMSRV_ID, DPFLTR_INFO_LEVEL, "TERMSRV: WinStationNotifyLogon, ReSetting ConsoleLogoffEvent\n"));
|
|
NtResetEvent(ConsoleLogoffEvent, NULL);
|
|
}
|
|
/*
|
|
* Do not do anything if the session is not connected. The processing in this API assumes we are connected and have a
|
|
* valid stack.
|
|
*/
|
|
|
|
if ((ClientLogonId != 0) && ((pWinStation->State != State_Connected) || pWinStation->StateFlags & WSF_ST_IN_DISCONNECT)) {
|
|
Status = STATUS_CTX_CLOSE_PENDING;
|
|
ReleaseWinStation( pWinStation );
|
|
goto done;
|
|
}
|
|
|
|
|
|
if (ClientLogonId == 0 && !bConsoleConnected ){
|
|
|
|
Status = WaitForConsoleConnectWorker( pWinStation );
|
|
if (NT_SUCCESS(Status)) {
|
|
bConsoleConnected=TRUE;
|
|
} else {
|
|
ReleaseWinStation( pWinStation );
|
|
goto done;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Upper level code has verified that this RPC
|
|
* is coming from a local client with SYSTEM access.
|
|
*
|
|
* We should be able to trust the ClientProcessId from
|
|
* SYSTEM code.
|
|
*/
|
|
|
|
/*
|
|
* Ensure this is WinLogon calling
|
|
*/
|
|
if ( (HANDLE)(ULONG_PTR)ClientProcessId != pWinStation->InitialCommandProcessId ) {
|
|
/*
|
|
* The console has a special problem with NTSD starting winlogon.
|
|
* It doesn't get notified until now what the PID if winlogon.exe
|
|
* instead of ntsd.exe is.
|
|
*/
|
|
if ( !pWinStation->LogonId && !pWinStation->InitialProcessSet ) {
|
|
pWinStation->InitialCommandProcess = OpenProcess(
|
|
PROCESS_ALL_ACCESS,
|
|
FALSE,
|
|
(DWORD) ClientProcessId );
|
|
|
|
if ( pWinStation->InitialCommandProcess == NULL ) {
|
|
Status = STATUS_ACCESS_DENIED;
|
|
goto done;
|
|
}
|
|
pWinStation->InitialCommandProcessId = (HANDLE)(ULONG_PTR)ClientProcessId;
|
|
pWinStation->InitialProcessSet = TRUE;
|
|
|
|
}
|
|
else {
|
|
//Set flag saying that WinStationNotifyLogonWorker
|
|
//was successfully completed
|
|
pWinStation->StateFlags |= WSF_ST_LOGON_NOTIFIED;
|
|
|
|
ReleaseWinStation( pWinStation );
|
|
Status = STATUS_SUCCESS;
|
|
goto done;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Verify the client license if appropriate
|
|
*/
|
|
if ( pWinStation->pWsx && pWinStation->pWsx->pWsxVerifyClientLicense ) {
|
|
Status = pWinStation->pWsx->pWsxVerifyClientLicense(
|
|
pWinStation->pWsxContext,
|
|
pWinStation->Config.Pd[0].Create.SdClass);
|
|
}
|
|
|
|
if ( Status != STATUS_SUCCESS) {
|
|
ReleaseWinStation( pWinStation );
|
|
goto done;
|
|
}
|
|
|
|
//
|
|
// DON'T CHECK RpcClientAccess. The client is always winlogon (verified
|
|
// before the call to this function) so this call does not check against
|
|
// the actual user logging in. This is done further down from here.
|
|
//
|
|
#if 0
|
|
if (ClientLogonId != 0)
|
|
{
|
|
Status = RpcCheckClientAccess( pWinStation, WINSTATION_LOGON, FALSE );
|
|
if ( !NT_SUCCESS( Status ) ) {
|
|
ReleaseWinStation( pWinStation );
|
|
KdPrintEx((DPFLTR_TERMSRV_ID, DPFLTR_ERROR_LEVEL, "TERMSRV: WinStationNotifyLogon, RpcCheckClientAccess failed=%x\n", Status ));
|
|
|
|
goto done;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
/*
|
|
* Save user status
|
|
*
|
|
* NOTE: This flag should only be used by the annoyance thread,
|
|
* and not for any security sensitive operations. All
|
|
* security sensitive operations need to go through the
|
|
* NT SeAccessCheck so that auditing is done properly.
|
|
*/
|
|
|
|
pWinStation->fUserIsAdmin = fUserIsAdmin;
|
|
|
|
if (!ClientLogonId && !pWinStation->pWsx) {
|
|
PLIST_ENTRY Head, Next;
|
|
PWSEXTENSION pWsx;
|
|
ICASRVPROCADDR IcaSrvProcAddr;
|
|
|
|
RtlEnterCriticalSection( &WsxListLock );
|
|
|
|
Head = &WsxListHead;
|
|
for ( Next = Head->Flink; Next != Head; Next = Next->Flink ) {
|
|
pWsx = CONTAINING_RECORD( Next, WSEXTENSION, Links );
|
|
if ( pWsx->pWsxGetLicense ) {
|
|
if (!pWinStation->pWsxContext && pWsx->pWsxWinStationInitialize) {
|
|
Status = pWsx->pWsxWinStationInitialize(&pWinStation->pWsxContext);
|
|
}
|
|
Status = pWsx->pWsxGetLicense(pWinStation->pWsxContext,
|
|
pWinStation->hStack,
|
|
pWinStation->LogonId,
|
|
fUserIsAdmin);
|
|
break;
|
|
}
|
|
}
|
|
|
|
RtlLeaveCriticalSection( &WsxListLock );
|
|
} else {
|
|
if ( pWinStation->pWsx && pWinStation->pWsx->pWsxGetLicense ) {
|
|
Status = pWinStation->pWsx->pWsxGetLicense( pWinStation->pWsxContext,
|
|
pWinStation->hStack,
|
|
pWinStation->LogonId,
|
|
fUserIsAdmin );
|
|
}
|
|
}
|
|
|
|
if ( Status != STATUS_SUCCESS) {
|
|
HANDLE h;
|
|
PWSTR Strings[2];
|
|
|
|
/*
|
|
* Send event to event log
|
|
*/
|
|
h = RegisterEventSource(NULL, gpszServiceName);
|
|
if (h) {
|
|
//
|
|
// Would have used UserName and Domain in this error message,
|
|
// but they aren't set yet.
|
|
//
|
|
Strings[0] = pUserName;
|
|
Strings[1] = pDomain;
|
|
ReportEvent(h, EVENTLOG_WARNING_TYPE, 0, EVENT_NO_LICENSES, NULL, 2, 0, Strings, NULL);
|
|
DeregisterEventSource(h);
|
|
}
|
|
|
|
ReleaseWinStation( pWinStation );
|
|
goto done;
|
|
}
|
|
|
|
/*
|
|
* Get a valid copy of the clients token handle
|
|
*/
|
|
Status = NtDuplicateObject( pWinStation->InitialCommandProcess,
|
|
(HANDLE)LongToHandle( UserToken ),
|
|
NtCurrentProcess(),
|
|
&ClientToken,
|
|
0, 0,
|
|
DUPLICATE_SAME_ACCESS |
|
|
DUPLICATE_SAME_ATTRIBUTES );
|
|
if ( !NT_SUCCESS( Status ) )
|
|
goto baddupobject;
|
|
|
|
/*
|
|
* ClientToken is a primary token - create an impersonation token
|
|
* version of it so we can set it on our thread
|
|
*/
|
|
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( ClientToken,
|
|
TOKEN_IMPERSONATE,
|
|
&ObjA,
|
|
FALSE,
|
|
TokenImpersonation,
|
|
&ImpersonationToken );
|
|
if ( !NT_SUCCESS( Status ) )
|
|
goto badduptoken;
|
|
|
|
/*
|
|
* Impersonate the client
|
|
*/
|
|
Status = NtSetInformationThread( NtCurrentThread(),
|
|
ThreadImpersonationToken,
|
|
(PVOID)&ImpersonationToken,
|
|
(ULONG)sizeof(HANDLE) );
|
|
if ( !NT_SUCCESS( Status ) )
|
|
goto badimpersonate;
|
|
|
|
//
|
|
// security check
|
|
//
|
|
Status = ApplyWinStaMapping( pWinStation );
|
|
if( !NT_SUCCESS( Status ) )
|
|
{
|
|
KdPrintEx((DPFLTR_TERMSRV_ID, DPFLTR_ERROR_LEVEL, "TERMSRV: WinStationNotifyLogon, ApplyWinStaMapping failed=%x\n", Status ));
|
|
goto noaccess;
|
|
}
|
|
|
|
if (ClientLogonId != (USER_SHARED_DATA->ActiveConsoleId))
|
|
//
|
|
// Since for PTS the remote session could have an ID of 0 or (1),
|
|
// we check for access only if session is not on console.
|
|
//
|
|
{
|
|
|
|
bAccessCheckOk = AccessCheckAndAuditAlarm(szTermsrv,
|
|
NULL,
|
|
(LPWSTR)szTermsrvSession,
|
|
(LPWSTR)szTermsrvSession,
|
|
WinStationGetSecurityDescriptor(pWinStation),
|
|
WINSTATION_LOGON,
|
|
&WinStaMapping,
|
|
FALSE,
|
|
&GrantedAccess,
|
|
&AccessStatus,
|
|
&fGenerateOnClose);
|
|
|
|
if (bAccessCheckOk)
|
|
{
|
|
if (AccessStatus == FALSE)
|
|
{
|
|
TRACE((hTrace,TC_ICASRV,TT_ERROR, "TERMSRV: WinStationNotifyLogon, AccessCheckAndAuditAlarm(%u) returned error 0x%x\n",
|
|
pWinStation->LogonId, GetLastError() ));
|
|
Status = STATUS_CTX_WINSTATION_ACCESS_DENIED;
|
|
goto noaccess;
|
|
}
|
|
else
|
|
{
|
|
TRACE((hTrace,TC_ICASRV,TT_ERROR, "TERMSRV: WinStationNotifyLogon, AccessCheckAndAuditAlarm(%u) returned no error \n",
|
|
pWinStation->LogonId));
|
|
}
|
|
}
|
|
else
|
|
{
|
|
TRACE((hTrace,TC_ICASRV,TT_ERROR, "TERMSRV: WinStationNotifyLogon, AccessCheckAndAuditAlarm(%u) failed 0x%x\n",
|
|
pWinStation->LogonId, GetLastError() ));
|
|
goto noaccess;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Revert back to our threads default token.
|
|
*/
|
|
NewToken = NULL;
|
|
NtSetInformationThread( NtCurrentThread(),
|
|
ThreadImpersonationToken,
|
|
(PVOID)&NewToken,
|
|
(ULONG)sizeof(HANDLE) );
|
|
|
|
/*
|
|
* See if OpenWinStation was successful
|
|
*/
|
|
if ( !NT_SUCCESS( Status ) ) {
|
|
TRACE((hTrace,TC_ICASRV,TT_ERROR, "TERMSRV: WinStationNotifyLogon, OpenWinStation(%u) failed 0x%x\n",
|
|
pWinStation->LogonId, Status ));
|
|
goto noaccess;
|
|
}
|
|
|
|
/*
|
|
* Save User Name and Domain Name. Do this before the call into the
|
|
* license core so that the core knows who is calling.
|
|
*/
|
|
|
|
wcsncpy( pWinStation->Domain, pDomain, DomainLength );
|
|
wcsncpy( pWinStation->UserName, pUserName, UserNameLength );
|
|
|
|
/*
|
|
* Call into the licensing core.
|
|
*/
|
|
|
|
Status = LCProcessConnectionPostLogon(pWinStation);
|
|
|
|
if (Status != STATUS_SUCCESS)
|
|
{
|
|
goto nolicense;
|
|
}
|
|
|
|
|
|
if (pWinStation->pWsx &&
|
|
pWinStation->pWsx->pWsxLogonNotify) {
|
|
if ((ClientLogonId != 0) && (pWinStation->State != State_Connected || pWinStation->StateFlags & WSF_ST_IN_DISCONNECT)) {
|
|
Status = STATUS_CTX_CLOSE_PENDING;
|
|
} else {
|
|
|
|
PWCHAR pDomainToSend, pUserNameToSend ;
|
|
|
|
// Use the Notification given by GINA (WinStationUpdateClientCachedCredentials) if they are available
|
|
// This is because the credentials got in this call are not UPN names
|
|
if (pWinStation->pNewNotificationCredentials) {
|
|
pDomainToSend = pWinStation->pNewNotificationCredentials->Domain;
|
|
pUserNameToSend = pWinStation->pNewNotificationCredentials->UserName;
|
|
} else {
|
|
pDomainToSend = pDomain;
|
|
pUserNameToSend = pUserName;
|
|
}
|
|
|
|
/*
|
|
* Reset any autoreconnect information prior to reconnection
|
|
* as it is stale. New information will be generated by the stack
|
|
* when login completes.
|
|
*/
|
|
ResetAutoReconnectInfo(pWinStation);
|
|
|
|
Status = pWinStation->pWsx->pWsxLogonNotify(pWinStation->pWsxContext,
|
|
pWinStation->LogonId,
|
|
ClientToken,
|
|
pDomainToSend,
|
|
pUserNameToSend);
|
|
|
|
if (pWinStation->pNewNotificationCredentials != NULL) {
|
|
MemFree(pWinStation->pNewNotificationCredentials);
|
|
pWinStation->pNewNotificationCredentials = NULL;
|
|
}
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if( !NT_SUCCESS(Status) ) {
|
|
KdPrintEx((DPFLTR_TERMSRV_ID, DPFLTR_ERROR_LEVEL, "TERMSRV: pWsxLogonNotify rejected logon status 0x%x\n",Status));
|
|
goto badwsxnotify;
|
|
}
|
|
|
|
/*
|
|
* Determine size needed for token info buffer and allocate it
|
|
*/
|
|
Status = NtQueryInformationToken( ClientToken, TokenUser,
|
|
NULL, 0, &Length );
|
|
if ( Status != STATUS_BUFFER_TOO_SMALL )
|
|
goto badquerytoken;
|
|
TokenInfo = MemAlloc( Length );
|
|
if ( TokenInfo == NULL ) {
|
|
Status = STATUS_NO_MEMORY;
|
|
goto badquerytoken;
|
|
}
|
|
|
|
/*
|
|
* Query token information to get user's SID
|
|
*/
|
|
Status = NtQueryInformationToken( ClientToken, TokenUser,
|
|
TokenInfo, Length, &Length );
|
|
if ( !NT_SUCCESS( Status ) ) {
|
|
MemFree( TokenInfo );
|
|
goto badquerytoken;
|
|
}
|
|
|
|
/*
|
|
* Save copy of user's SID and password in WinStation
|
|
*/
|
|
Length = RtlLengthSid( TokenInfo->User.Sid );
|
|
|
|
if (pWinStation->pUserSid) {
|
|
MemFree(pWinStation->pUserSid);
|
|
}
|
|
// clean up usertoken
|
|
if ( pWinStation->UserToken ) {
|
|
NtClose( pWinStation->UserToken );
|
|
pWinStation->UserToken = NULL;
|
|
}
|
|
|
|
pWinStation->pUserSid = MemAlloc( Length );
|
|
|
|
/* makarp; check for allocation failure. #182624 */
|
|
if (!pWinStation->pUserSid) {
|
|
Status = STATUS_NO_MEMORY;
|
|
goto badusersid;
|
|
}
|
|
|
|
RtlCopySid( Length, pWinStation->pUserSid, TokenInfo->User.Sid );
|
|
MemFree( TokenInfo );
|
|
NtClose( ImpersonationToken );
|
|
|
|
//For console session profile cleanup is done at next
|
|
//logon because the session is never unloaded.
|
|
if (pWinStation->pProfileSid != NULL) {
|
|
ASSERT(pWinStation->LogonId == 0);
|
|
if (pWinStation->LogonId == 0) {
|
|
if (!RtlEqualSid(pWinStation->pProfileSid, pWinStation->pUserSid )) {
|
|
WinstationUnloadProfile(pWinStation);
|
|
}
|
|
}
|
|
MemFree(pWinStation->pProfileSid);
|
|
pWinStation->pProfileSid = NULL;
|
|
}
|
|
|
|
|
|
/*
|
|
* Save copy of Client token in WinStation
|
|
*/
|
|
pWinStation->UserToken = ClientToken;
|
|
|
|
#if 0
|
|
//
|
|
// C2 WARNING - WARNING - WARNING
|
|
//
|
|
// This is no longer done, or needed. See comments in acl.c
|
|
//
|
|
// C2 WARNING - WARNING - WARNING
|
|
//
|
|
RtlCopyMemory( pWinStation->Password, pPassword,
|
|
sizeof(pWinStation->Password) );
|
|
|
|
pWinStation->Seed = Seed;
|
|
#endif
|
|
|
|
/*
|
|
* Fixup the security descriptors for the session
|
|
* so this user has access to their named WIN32
|
|
* object directory's.
|
|
*/
|
|
ConfigurePerSessionSecurity( pWinStation );
|
|
|
|
/*
|
|
* Add ACE for logged on user to the WinStation object SD
|
|
*/
|
|
AddUserAce( pWinStation );
|
|
|
|
/*
|
|
* Notify clients of WinStation logon
|
|
*/
|
|
NotifySystemEvent( WEVENT_LOGON );
|
|
NotifyLogon(pWinStation);
|
|
|
|
/*
|
|
* State is now active
|
|
*/
|
|
if ( pWinStation->State != (ULONG) State_Active ) {
|
|
pWinStation->State = State_Active;
|
|
NotifySystemEvent( WEVENT_STATECHANGE );
|
|
}
|
|
|
|
(VOID) NtQuerySystemTime( &(pWinStation->LogonTime) );
|
|
|
|
// 2nd and 3rd parama are NULL, since we don't yet have policy data.
|
|
// policy data will be aquired when user hive is loaded and winlogin fires
|
|
// a shell-startup notify.
|
|
MergeUserConfigData(pWinStation, NULL, NULL, (PUSERCONFIGW)pUserConfig ) ;
|
|
|
|
/*
|
|
* Save User Name and Domain Name into the USERCONFIG of the WINSTATION
|
|
*/
|
|
wcsncpy( pWinStation->Config.Config.User.UserName, pUserName, UserNameLength );
|
|
wcsncpy( pWinStation->Config.Config.User.Domain, pDomain, DomainLength );
|
|
|
|
/*
|
|
* Convert any "published app" to absolute path. This will also return
|
|
* failure if a non-published app is trying to run on a WinStation that
|
|
* is configured to only run published apps.
|
|
*/
|
|
if ( pWinStation->pWsx &&
|
|
pWinStation->pWsx->pWsxConvertPublishedApp ) {
|
|
if ((Status = pWinStation->pWsx->pWsxConvertPublishedApp(
|
|
pWinStation->pWsxContext, &pWinStation->Config.Config.User)) !=
|
|
STATUS_SUCCESS)
|
|
goto release;
|
|
}
|
|
|
|
// Now that we have all the WinStation data, notify the session directory.
|
|
// Copy off the pertinent info, which we will send to the directory after
|
|
// we release the WinStation lock.
|
|
wcsncpy(pCreateInfo->UserName, pWinStation->UserName,
|
|
sizeof(pCreateInfo->UserName) / sizeof(WCHAR) - 1);
|
|
wcsncpy(pCreateInfo->Domain, pWinStation->Domain,
|
|
sizeof(pCreateInfo->Domain) / sizeof(WCHAR) - 1);
|
|
pCreateInfo->SessionID = pWinStation->LogonId;
|
|
pCreateInfo->TSProtocol = pWinStation->Client.ProtocolType;
|
|
wcsncpy(pCreateInfo->ApplicationType, pWinStation->Client.InitialProgram,
|
|
sizeof(pCreateInfo->ApplicationType) / sizeof(WCHAR) - 1);
|
|
pCreateInfo->ResolutionWidth = pWinStation->Client.HRes;
|
|
pCreateInfo->ResolutionHeight = pWinStation->Client.VRes;
|
|
pCreateInfo->ColorDepth = pWinStation->Client.ColorDepth;
|
|
memcpy(&(pCreateInfo->CreateTime), &pWinStation->LogonTime,
|
|
sizeof(pCreateInfo->CreateTime));
|
|
bHaveCreateInfo = TRUE;
|
|
|
|
|
|
if(Status == STATUS_SUCCESS)
|
|
{
|
|
//Set flag saying that WinStationNotifyLogonWorker
|
|
//was successfully completed
|
|
pWinStation->StateFlags |= WSF_ST_LOGON_NOTIFIED;
|
|
}
|
|
|
|
|
|
if( TSIsSessionHelpSession(pWinStation, &bValidHelpSession) )
|
|
{
|
|
// we disconnect RA if ticket is invalid at conntion time so assert
|
|
// if we ever come to this.
|
|
ASSERT( TRUE == bValidHelpSession );
|
|
|
|
//
|
|
// Disable IO from Help Session
|
|
//
|
|
WinStationEnableSessionIo( pWinStation, FALSE );
|
|
|
|
//
|
|
// Verify help session ticket and log event if ticket is
|
|
// invalid, sessmgr is the centralize event logging
|
|
// component, in the case that we are going to log an event,
|
|
// call inside sessmgr CAN NOT invoke any winstation API
|
|
// to retrieve/set any information in this winstation or will
|
|
// have circular deadlock since we are holding winstation lock
|
|
// here
|
|
//
|
|
if( FALSE == TSVerifyHelpSessionAndLogSalemEvent( pWinStation ) )
|
|
{
|
|
// Help Session is either invalid or can't
|
|
// startup session manager, return error code to client
|
|
if(pWinStation->WinStationName[0] &&
|
|
pWinStation->pWsx &&
|
|
pWinStation->pWsx->pWsxSetErrorInfo &&
|
|
pWinStation->pWsxContext)
|
|
{
|
|
pWinStation->pWsx->pWsxSetErrorInfo(
|
|
pWinStation->pWsxContext,
|
|
TS_ERRINFO_SERVER_DENIED_CONNECTION,
|
|
FALSE); //stack lock not held
|
|
}
|
|
|
|
//
|
|
// We have queued a reset on winstation, don't bother about
|
|
// notifying sessdir
|
|
bQueueReset = TRUE;
|
|
|
|
//
|
|
// Disconnect client
|
|
//
|
|
QueueWinStationReset( pWinStation->LogonId);
|
|
}
|
|
else
|
|
{
|
|
WINSTATION_APIMSG msg;
|
|
msg.ApiNumber = SMWinStationNotify;
|
|
msg.WaitForReply = FALSE;
|
|
msg.u.DoNotify.NotifyEvent = WinStation_Notify_DisableScrnSaver;
|
|
Status = SendWinStationCommand( pWinStation, &msg, 0 );
|
|
|
|
ASSERT( NT_SUCCESS(Status) );
|
|
|
|
// ignore this error, help can still proceed.
|
|
Status = STATUS_SUCCESS;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Release the winstation lock
|
|
*/
|
|
release:
|
|
ReleaseWinStation( pWinStation );
|
|
|
|
// Now inform the session directory while we're not holding the lock.
|
|
if (!bQueueReset && bHaveCreateInfo && !g_bPersonalTS && g_fAppCompat && !bRedirect)
|
|
SessDirNotifyLogon(pCreateInfo);
|
|
|
|
/*
|
|
* Save return status in API message
|
|
*/
|
|
done:
|
|
// Clear password
|
|
if (0 != PasswordSize)
|
|
memset(pPassword, 0, PasswordSize);
|
|
TRACE((hTrace,TC_ICASRV,TT_API1, "TERMSRV: WinStationNotifyLogon, Status=0x%x\n", Status ));
|
|
|
|
if (pCreateInfo != NULL) {
|
|
MemFree(pCreateInfo);
|
|
pCreateInfo = NULL;
|
|
}
|
|
|
|
if (pWinStation->pNewNotificationCredentials != NULL) {
|
|
MemFree(pWinStation->pNewNotificationCredentials);
|
|
pWinStation->pNewNotificationCredentials = NULL;
|
|
}
|
|
|
|
return Status;
|
|
|
|
/*=============================================================================
|
|
== Error returns
|
|
=============================================================================*/
|
|
|
|
/* Could not allocate for pWinStation->pUserSid, makarp #182624 */
|
|
badusersid:
|
|
MemFree( TokenInfo );
|
|
|
|
/*
|
|
* Could not query token info
|
|
* WinStation Open failed (no access)
|
|
* Could not impersonate client token
|
|
* Could not duplicate client token
|
|
*/
|
|
badquerytoken:
|
|
badwsxnotify:
|
|
nolicense:
|
|
noaccess:
|
|
badimpersonate:
|
|
NtClose( ImpersonationToken );
|
|
badduptoken:
|
|
NtClose( ClientToken );
|
|
|
|
/*
|
|
* Could not duplicate client token handle
|
|
*/
|
|
baddupobject:
|
|
#ifdef not_hydrix
|
|
pWinStation->HasLicense = FALSE;
|
|
RtlZeroMemory( pWinStation->ClientLicense,
|
|
sizeof(pWinStation->ClientLicense) );
|
|
#else
|
|
if ( pWinStation->pWsx &&
|
|
pWinStation->pWsx->pWsxClearContext ) {
|
|
pWinStation->pWsx->pWsxClearContext( pWinStation->pWsxContext );
|
|
}
|
|
#endif
|
|
ReleaseWinStation( pWinStation );
|
|
TRACE((hTrace,TC_ICASRV,TT_API1, "TERMSRV: WinStationNotifyLogon, Status=0x%x\n", Status ));
|
|
|
|
// Clear password
|
|
if (0 != PasswordSize)
|
|
memset(pPassword, 0, PasswordSize);
|
|
|
|
if (pCreateInfo != NULL) {
|
|
MemFree(pCreateInfo);
|
|
pCreateInfo = NULL;
|
|
}
|
|
|
|
if (pWinStation->pNewNotificationCredentials != NULL) {
|
|
MemFree(pWinStation->pNewNotificationCredentials);
|
|
pWinStation->pNewNotificationCredentials = NULL;
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
|
|
|
|
/*****************************************************************************
|
|
* WinStationNotifyLogoffWorker
|
|
*
|
|
* Message parameter unmarshalling function for WinStation API.
|
|
****************************************************************************/
|
|
NTSTATUS WinStationNotifyLogoffWorker(
|
|
DWORD ClientLogonId,
|
|
DWORD ClientProcessId)
|
|
{
|
|
|
|
NTSTATUS Status;
|
|
PWINSTATION pWinStation;
|
|
DWORD SessionID;
|
|
|
|
|
|
TRACE((hTrace,TC_ICASRV,TT_API1, "TERMSRV: WinStationNotifyLogoff, LogonId=%d\n", ClientLogonId ));
|
|
KdPrintEx((DPFLTR_TERMSRV_ID, DPFLTR_ERROR_LEVEL, "TERMSRV: WinStationNotifyLogoff, LogonId=%d\n", ClientLogonId ));
|
|
|
|
Status = STATUS_SUCCESS;
|
|
|
|
/*
|
|
* Find and lock client WinStation
|
|
*/
|
|
pWinStation = FindWinStationById( ClientLogonId, FALSE );
|
|
if ( pWinStation == NULL ) {
|
|
Status = STATUS_CTX_WINSTATION_NOT_FOUND;
|
|
goto done;
|
|
}
|
|
|
|
//See if WinStation was notified of logon
|
|
if(pWinStation->StateFlags & WSF_ST_LOGON_NOTIFIED)
|
|
{
|
|
//Clear the flag
|
|
pWinStation->StateFlags &= ~WSF_ST_LOGON_NOTIFIED;
|
|
}
|
|
else
|
|
{
|
|
//WinStation was not notified of logon.
|
|
//do nothing;
|
|
|
|
KdPrint(("TERMSRV: WinStationNotifyLogoff FAILED, WinStation was not notified of logon!\n"));
|
|
ReleaseWinStation( pWinStation );
|
|
Status = STATUS_INVALID_PARAMETER; //probably need some special error code
|
|
goto done;
|
|
}
|
|
|
|
|
|
/*
|
|
* The upper level has verified the client caller
|
|
* is local and has SYSTEM access. So we can trust
|
|
* the parameters passed.
|
|
*/
|
|
|
|
/*
|
|
* Ensure this is WinLogon calling
|
|
*/
|
|
if ( (HANDLE)(ULONG_PTR)ClientProcessId != pWinStation->InitialCommandProcessId ) {
|
|
ReleaseWinStation( pWinStation );
|
|
goto done;
|
|
}
|
|
|
|
/*
|
|
* Stop the shadow on console if needed.
|
|
*/
|
|
if ( pWinStation->fOwnsConsoleTerminal ) {
|
|
WinStationStopAllShadows( pWinStation );
|
|
}
|
|
|
|
/*
|
|
* Remove ACE for logged on user to the WinStation object SD
|
|
*/
|
|
RemoveUserAce( pWinStation );
|
|
if ( pWinStation->pUserSid ) {
|
|
ASSERT(pWinStation->pProfileSid == NULL);
|
|
pWinStation->pProfileSid = pWinStation->pUserSid;
|
|
pWinStation->pUserSid = NULL;
|
|
}
|
|
|
|
/*
|
|
* Cleanup UserToken
|
|
*/
|
|
if ( pWinStation->UserToken ) {
|
|
NtClose( pWinStation->UserToken );
|
|
pWinStation->UserToken = NULL;
|
|
}
|
|
|
|
/*
|
|
* Indicate this WinStation no longer has a license
|
|
*/
|
|
#ifdef not_hydrix
|
|
pWinStation->HasLicense = FALSE;
|
|
RtlZeroMemory( pWinStation->ClientLicense,
|
|
sizeof(pWinStation->ClientLicense) );
|
|
#else
|
|
if ( pWinStation->pWsx &&
|
|
pWinStation->pWsx->pWsxClearContext ) {
|
|
pWinStation->pWsx->pWsxClearContext( pWinStation->pWsxContext );
|
|
}
|
|
#endif
|
|
|
|
/*
|
|
* do needed data cleanup.
|
|
*/
|
|
|
|
RtlZeroMemory( pWinStation->Domain,
|
|
sizeof( pWinStation->Domain ) );
|
|
RtlZeroMemory( pWinStation->UserName,
|
|
sizeof( pWinStation->UserName ) );
|
|
RtlZeroMemory( &pWinStation->LogonTime,
|
|
sizeof( pWinStation->LogonTime ) );
|
|
|
|
ResetUserConfigData( pWinStation );
|
|
|
|
pWinStation->Config.Config.User.UserName[0] = L'\0';
|
|
pWinStation->Config.Config.User.Domain[0] = L'\0';
|
|
pWinStation->Config.Config.User.Password[0] = L'\0';
|
|
|
|
if ( pWinStation->LogonId == 0 ) {
|
|
|
|
|
|
/*
|
|
* No need to do anything else for console state change.
|
|
*/
|
|
|
|
if ( pWinStation->State != (ULONG) State_Connected &&
|
|
pWinStation->State != (ULONG) State_Disconnected) {
|
|
pWinStation->State = State_Connected;
|
|
NotifySystemEvent( WEVENT_STATECHANGE );
|
|
}
|
|
|
|
pWinStation->Config.Config.User.fPromptForPassword = TRUE;
|
|
//
|
|
//Set the Console Logon Event
|
|
//
|
|
|
|
KdPrintEx((DPFLTR_TERMSRV_ID, DPFLTR_ERROR_LEVEL, "TERMSRV: WinStationNotifyLogoff, Setting ConsoleLogoffEvent\n"));
|
|
NtSetEvent(ConsoleLogoffEvent, NULL);
|
|
|
|
/*
|
|
* For non-Console WinStations, mark this WinStation as terminating
|
|
* and set the broken reason and source for later use.
|
|
*/
|
|
} else {
|
|
//pWinStation->Terminating = TRUE;
|
|
pWinStation->BrokenReason = Broken_Terminate;
|
|
pWinStation->BrokenSource = BrokenSource_User;
|
|
|
|
// Save off the session dir for sending to the session directory
|
|
// below.
|
|
SessionID = pWinStation->LogonId;
|
|
}
|
|
|
|
// Clean up the New Client Credentials struct for Long UserName
|
|
|
|
if (pWinStation->pNewClientCredentials != NULL) {
|
|
|
|
MemFree(pWinStation->pNewClientCredentials);
|
|
pWinStation->pNewClientCredentials = NULL;
|
|
}
|
|
|
|
/*
|
|
* Call into licensing core for logoff. Ignore errors.
|
|
*/
|
|
(VOID)LCProcessConnectionLogoff(pWinStation);
|
|
|
|
|
|
NotifyLogoff(pWinStation);
|
|
ReleaseWinStation( pWinStation );
|
|
|
|
// Notify the session directory.
|
|
if (!g_bPersonalTS && g_fAppCompat)
|
|
SessDirNotifyLogoff(SessionID);
|
|
|
|
/*
|
|
* Notify clients of WinStation logoff
|
|
*/
|
|
NotifySystemEvent(WEVENT_LOGOFF);
|
|
|
|
done:
|
|
TRACE((hTrace,TC_ICASRV,TT_API1, "TERMSRV: WinStationNotifyLogoff, Status=0x%x\n", Status ));
|
|
return Status;
|
|
}
|
|
|
|
|
|
/*****************************************************************************
|
|
* OldRpcWinStationEnumerateProcesses
|
|
*
|
|
* WinStationEnumerateProcesses API for Beta servers
|
|
*
|
|
* The format changed after Beta. New winsta.dll's were trapping when talking
|
|
* to old hosts.
|
|
****************************************************************************/
|
|
BOOLEAN
|
|
OldRpcWinStationEnumerateProcesses(
|
|
HANDLE hServer,
|
|
DWORD *pResult,
|
|
PBYTE pProcessBuffer,
|
|
DWORD ByteCount
|
|
)
|
|
{
|
|
return ( RpcWinStationEnumerateProcesses( hServer, pResult, pProcessBuffer, ByteCount ) );
|
|
}
|
|
|
|
|
|
/*******************************************************************************
|
|
* RpcWinStationCheckForApplicationName
|
|
*
|
|
* Handles published applications.
|
|
*
|
|
* EXIT:
|
|
* TRUE -- The query succeeded, and the buffer contains the requested data.
|
|
* FALSE -- The operation failed. Extended error status is available
|
|
* using GetLastError.
|
|
******************************************************************************/
|
|
BOOLEAN
|
|
RpcWinStationCheckForApplicationName(
|
|
HANDLE hServer,
|
|
DWORD *pResult,
|
|
ULONG LogonId,
|
|
PWCHAR pUserName,
|
|
DWORD UserNameSize,
|
|
PWCHAR pDomain,
|
|
DWORD DomainSize,
|
|
PWCHAR pPassword,
|
|
DWORD *pPasswordSize,
|
|
DWORD MaxPasswordSize,
|
|
PCHAR pSeed,
|
|
PBOOLEAN pfPublished,
|
|
PBOOLEAN pfAnonymous
|
|
)
|
|
{
|
|
/*
|
|
* This is obsolete and should be removed from the RPC
|
|
*/
|
|
*pResult = STATUS_NOT_IMPLEMENTED;
|
|
RpcRaiseException(ERROR_INVALID_FUNCTION);
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
/*******************************************************************************
|
|
*
|
|
* RpcWinStationGetApplicationInfo
|
|
*
|
|
* Gets info about published applications.
|
|
*
|
|
* ENTRY:
|
|
*
|
|
* EXIT:
|
|
*
|
|
* TRUE -- The query succeeded, and the buffer contains the requested data.
|
|
*
|
|
* FALSE -- The operation failed. Extended error status is available
|
|
* using GetLastError.
|
|
*
|
|
******************************************************************************/
|
|
|
|
BOOLEAN
|
|
RpcWinStationGetApplicationInfo(
|
|
HANDLE hServer,
|
|
DWORD *pResult,
|
|
ULONG LogonId,
|
|
PBOOLEAN pfPublished,
|
|
PBOOLEAN pfAnonymous
|
|
)
|
|
{
|
|
/*
|
|
* This is obsolete and should be removed from the RPC code.
|
|
*/
|
|
*pResult = STATUS_NOT_IMPLEMENTED;
|
|
RpcRaiseException(ERROR_INVALID_FUNCTION);
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
/*******************************************************************************
|
|
*
|
|
* RpcWinStationNtsdDebug
|
|
*
|
|
* Sets up connection for Ntsd to debug processes belonging to another
|
|
* CSR.
|
|
*
|
|
* ENTRY:
|
|
*
|
|
* EXIT:
|
|
*
|
|
* TRUE -- The query succeeded, and the buffer contains the requested data.
|
|
*
|
|
* FALSE -- The operation failed. Extended error status is available
|
|
* using GetLastError.
|
|
*
|
|
******************************************************************************/
|
|
|
|
BOOLEAN
|
|
RpcWinStationNtsdDebug(
|
|
HANDLE hServer,
|
|
DWORD *pResult,
|
|
ULONG LogonId,
|
|
LONG ProcessId,
|
|
ULONG DbgProcessId,
|
|
ULONG DbgThreadId,
|
|
DWORD_PTR AttachCompletionRoutine
|
|
)
|
|
{
|
|
|
|
*pResult = STATUS_NOT_IMPLEMENTED;
|
|
RpcRaiseException(ERROR_INVALID_FUNCTION);
|
|
return FALSE;
|
|
}
|
|
|
|
/*******************************************************************************
|
|
*
|
|
* RpcWinStationGetTermSrvCountersValue
|
|
*
|
|
* Gets TermSrv Counters value
|
|
*
|
|
* ENTRY:
|
|
*
|
|
* EXIT:
|
|
*
|
|
* TRUE -- The query succeeded, and the buffer contains the requested data.
|
|
*
|
|
* FALSE -- The operation failed. Extended error status is available
|
|
* using GetLastError.
|
|
*
|
|
******************************************************************************/
|
|
|
|
BOOLEAN
|
|
RpcWinStationGetTermSrvCountersValue(
|
|
HANDLE hServer,
|
|
DWORD *pResult,
|
|
DWORD dwEntries,
|
|
PTS_COUNTER pCounter
|
|
)
|
|
{
|
|
UINT i;
|
|
PLIST_ENTRY Head, Next;
|
|
PWINSTATION pWinStation;
|
|
BOOLEAN bWalkedList = FALSE;
|
|
ULONG cActive, cDisconnected;
|
|
|
|
*pResult = STATUS_UNSUCCESSFUL;
|
|
|
|
if (pCounter != NULL) {
|
|
for (i = 0; i < dwEntries; i++) {
|
|
// set the TermSrv counter value. Currently, startTime is always
|
|
// set to 0 because we don't support time stamp.
|
|
pCounter[i].startTime.QuadPart = 0;
|
|
|
|
switch (pCounter[i].counterHead.dwCounterID) {
|
|
|
|
case TERMSRV_TOTAL_SESSIONS:
|
|
{
|
|
pCounter[i].counterHead.bResult = TRUE;
|
|
pCounter[i].dwValue = g_TermSrvTotalSessions;
|
|
|
|
*pResult = STATUS_SUCCESS;
|
|
}
|
|
break;
|
|
|
|
case TERMSRV_DISC_SESSIONS:
|
|
{
|
|
pCounter[i].counterHead.bResult = TRUE;
|
|
pCounter[i].dwValue = g_TermSrvDiscSessions;
|
|
|
|
*pResult = STATUS_SUCCESS;
|
|
}
|
|
break;
|
|
|
|
case TERMSRV_RECON_SESSIONS:
|
|
{
|
|
pCounter[i].counterHead.bResult = TRUE;
|
|
pCounter[i].dwValue = g_TermSrvReconSessions;
|
|
|
|
*pResult = STATUS_SUCCESS;
|
|
}
|
|
break;
|
|
|
|
case TERMSRV_CURRENT_ACTIVE_SESSIONS:
|
|
case TERMSRV_CURRENT_DISC_SESSIONS:
|
|
{
|
|
if ( !bWalkedList ) {
|
|
|
|
cActive = cDisconnected = 0;
|
|
|
|
Head = &WinStationListHead;
|
|
ENTERCRIT( &WinStationListLock );
|
|
for ( Next = Head->Flink; Next != Head; Next = Next->Flink ) {
|
|
|
|
pWinStation = CONTAINING_RECORD( Next, WINSTATION, Links );
|
|
|
|
if ( (pWinStation->State == State_Active) ||
|
|
(pWinStation->State == State_Shadow) )
|
|
cActive ++;
|
|
|
|
if ( pWinStation->State == State_Disconnected )
|
|
if ( pWinStation->LogonId != 0 )
|
|
cDisconnected ++;
|
|
else if ( pWinStation->UserName[0] )
|
|
// For session 0, test if a user is logged on.
|
|
cDisconnected ++;
|
|
}
|
|
LEAVECRIT( &WinStationListLock );
|
|
|
|
bWalkedList = TRUE;
|
|
}
|
|
|
|
pCounter[i].counterHead.bResult = TRUE;
|
|
|
|
if ( pCounter[i].counterHead.dwCounterID == TERMSRV_CURRENT_ACTIVE_SESSIONS )
|
|
pCounter[i].dwValue = cActive;
|
|
else
|
|
pCounter[i].dwValue = cDisconnected;
|
|
|
|
*pResult = STATUS_SUCCESS;
|
|
}
|
|
break;
|
|
|
|
default:
|
|
{
|
|
pCounter[i].counterHead.bResult = FALSE;
|
|
pCounter[i].dwValue = 0;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return ( *pResult == STATUS_SUCCESS );
|
|
}
|
|
|
|
/******************************************************************************
|
|
*
|
|
* RpcServerGetInternetConnectorStatus
|
|
*
|
|
* Returns whether Internet Connector licensing is being used
|
|
*
|
|
* ENTRY:
|
|
*
|
|
* EXIT:
|
|
*
|
|
* TRUE -- The query succeeded, and pfEnabled contains the requested data.
|
|
*
|
|
* FALSE -- The operation failed. Extended error status is in pResult
|
|
*
|
|
*****************************************************************************/
|
|
|
|
BOOLEAN
|
|
RpcServerGetInternetConnectorStatus(
|
|
HANDLE hServer,
|
|
DWORD *pResult,
|
|
PBOOLEAN pfEnabled
|
|
)
|
|
{
|
|
#if 0
|
|
if (pResult != NULL)
|
|
{
|
|
*pResult = STATUS_NOT_IMPLEMENTED;
|
|
}
|
|
|
|
if (pfEnabled != NULL)
|
|
{
|
|
*pfEnabled = FALSE;
|
|
}
|
|
|
|
return(FALSE);
|
|
#else
|
|
//
|
|
// TEMPORARY FUNCTION! THIS WILL GET NUKED ONCE THE LCRPC
|
|
// INTERFACE IS UP AND RUNNING AND TSCC CHANGES HAVE BEEN
|
|
// MADE!
|
|
//
|
|
|
|
if (pResult == NULL)
|
|
{
|
|
return(FALSE);
|
|
}
|
|
|
|
if (pfEnabled == NULL)
|
|
{
|
|
*pResult = STATUS_INVALID_PARAMETER;
|
|
return(FALSE);
|
|
}
|
|
|
|
*pfEnabled = (LCGetPolicy() == (ULONG)3);
|
|
|
|
*pResult = STATUS_SUCCESS;
|
|
return(TRUE);
|
|
#endif
|
|
}
|
|
|
|
/******************************************************************************
|
|
*
|
|
* RpcServerSetInternetConnectorStatus
|
|
*
|
|
* This function will (if fEnabled has changed from its previous setting):
|
|
* Check that the caller has administrative privileges,
|
|
* Modify the corresponding value in the registry,
|
|
* Change licensing mode (between normal per-seat and Internet Connector.
|
|
* Enable/Disable the TsInternetUser account appropriately
|
|
*
|
|
* ENTRY:
|
|
*
|
|
* EXIT:
|
|
*
|
|
* TRUE -- The operation succeeded.
|
|
*
|
|
* FALSE -- The operation failed. Extended error status is in pResult
|
|
*
|
|
******************************************************************************/
|
|
|
|
BOOLEAN
|
|
RpcServerSetInternetConnectorStatus(
|
|
HANDLE hServer,
|
|
DWORD *pResult,
|
|
BOOLEAN fEnabled
|
|
)
|
|
{
|
|
#if 0
|
|
if (pResult != NULL)
|
|
{
|
|
*pResult = STATUS_NOT_IMPLEMENTED;
|
|
}
|
|
|
|
return(FALSE);
|
|
#else
|
|
//
|
|
// TEMPORARY FUNCTION! THIS WILL GET NUKED ONCE THE LCRPC
|
|
// INTERFACE IS UP AND RUNNING AND TSCC CHANGES HAVE BEEN
|
|
// MADE!
|
|
//
|
|
|
|
NTSTATUS NewStatus;
|
|
RPC_STATUS RpcStatus;
|
|
|
|
if (pResult == NULL)
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
RpcStatus = RpcImpersonateClient( NULL );
|
|
if( RpcStatus != RPC_S_OK ) {
|
|
*pResult = STATUS_CANNOT_IMPERSONATE;
|
|
return( FALSE );
|
|
}
|
|
|
|
if (!IsCallerAdmin())
|
|
{
|
|
RpcRevertToSelf();
|
|
*pResult = STATUS_PRIVILEGE_NOT_HELD;
|
|
return FALSE;
|
|
}
|
|
|
|
RpcRevertToSelf();
|
|
|
|
*pResult = LCSetPolicy(fEnabled ? 3 : 2, &NewStatus);
|
|
|
|
if ((*pResult == STATUS_SUCCESS) && (NewStatus == STATUS_SUCCESS))
|
|
{
|
|
return(TRUE);
|
|
}
|
|
|
|
//
|
|
// If there was an error, it was either in the core or the new policy.
|
|
// If its in the core, NewStatus will be success. If its in the new
|
|
// policy, *pResult will be unsuccessful, and the real error will be in
|
|
// NewStatus. Return the real error.
|
|
//
|
|
|
|
if (NewStatus != STATUS_SUCCESS)
|
|
{
|
|
*pResult = NewStatus;
|
|
}
|
|
|
|
return(FALSE);
|
|
#endif
|
|
}
|
|
|
|
/******************************************************************************
|
|
*
|
|
* RpcServerQueryInetConnectorInformation
|
|
*
|
|
* Queries configuration information about a Internet Connector licensing.
|
|
*
|
|
* ENTRY:
|
|
*
|
|
* pWinStationInformation (output)
|
|
* A pointer to a buffer that will receive information about the
|
|
* specified window station. The format and contents of the buffer
|
|
* depend on the specified information class being queried.
|
|
*
|
|
* WinStationInformationLength (input)
|
|
* Specifies the length in bytes of the window station information
|
|
* buffer.
|
|
*
|
|
* pReturnLength (output)
|
|
* An optional parameter that if specified, receives the number of
|
|
* bytes placed in the window station information buffer.
|
|
*
|
|
* EXIT:
|
|
*
|
|
* TRUE -- The query succeeded, and the buffer contains the requested data.
|
|
*
|
|
* False -- The operation failed or Internet Connector licensing isn't
|
|
* turned on. Extended error status is in pResult
|
|
*
|
|
*****************************************************************************/
|
|
|
|
BOOLEAN
|
|
RpcServerQueryInetConnectorInformation(
|
|
HANDLE hServer,
|
|
DWORD *pResult,
|
|
PCHAR pWinStationInformation,
|
|
DWORD WinStationInformationLength,
|
|
DWORD *pReturnLength
|
|
)
|
|
{
|
|
// doesn't return
|
|
RpcRaiseException(RPC_S_CANNOT_SUPPORT);
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
/******************************************************************************
|
|
*
|
|
* RpcWinStationQueryLogonCredentials
|
|
*
|
|
* Queries autologon credentials for use in Winlogon/GINA.
|
|
*
|
|
*****************************************************************************/
|
|
|
|
BOOLEAN
|
|
RpcWinStationQueryLogonCredentials(
|
|
HANDLE hServer,
|
|
ULONG LogonId,
|
|
PCHAR *ppWire,
|
|
PULONG pcbWire
|
|
)
|
|
{
|
|
BOOL fRet;
|
|
BOOL fUseLcCredentials;
|
|
LCCREDENTIALS LcCredentials;
|
|
NTSTATUS Status;
|
|
PWINSTATION pWinStation;
|
|
RPC_STATUS RpcStatus;
|
|
WLX_CLIENT_CREDENTIALS_INFO_V2_0 WlxCredentials;
|
|
BOOL fHelpAssistant = FALSE;
|
|
BOOL bValidHelpSession;
|
|
|
|
pExtendedClientCredentials pHelpAssistantCredential = NULL;
|
|
|
|
//
|
|
// Impersonate client.
|
|
//
|
|
|
|
RpcStatus = RpcImpersonateClient(NULL);
|
|
|
|
if (RpcStatus != RPC_S_OK)
|
|
{
|
|
return(FALSE);
|
|
}
|
|
|
|
//
|
|
// Check for administration privileges.
|
|
//
|
|
|
|
if (!IsCallerSystem())
|
|
{
|
|
RpcRevertToSelf();
|
|
return(FALSE);
|
|
}
|
|
|
|
RpcRevertToSelf();
|
|
|
|
pWinStation = FindWinStationById(LogonId, FALSE);
|
|
|
|
if (pWinStation == NULL)
|
|
{
|
|
return(FALSE);
|
|
}
|
|
|
|
pHelpAssistantCredential = MemAlloc(sizeof(ExtendedClientCredentials));
|
|
if (pHelpAssistantCredential == NULL) {
|
|
ReleaseWinStation(pWinStation);
|
|
return FALSE;
|
|
}
|
|
|
|
ZeroMemory(&WlxCredentials, sizeof(WLX_CLIENT_CREDENTIALS_INFO_V2_0));
|
|
WlxCredentials.dwType = WLX_CREDENTIAL_TYPE_V2_0;
|
|
|
|
if( TSIsSessionHelpSession( pWinStation, &bValidHelpSession ) )
|
|
{
|
|
//
|
|
// We should not hit this since we will disconnect at winstation transfer time
|
|
//
|
|
ASSERT( TRUE == bValidHelpSession );
|
|
|
|
Status = TSHelpAssistantQueryLogonCredentials(pHelpAssistantCredential);
|
|
|
|
if( STATUS_SUCCESS == Status )
|
|
{
|
|
WlxCredentials.fDisconnectOnLogonFailure = TRUE;
|
|
WlxCredentials.fPromptForPassword = FALSE;
|
|
WlxCredentials.pszUserName = pHelpAssistantCredential->UserName;
|
|
WlxCredentials.pszDomain = pHelpAssistantCredential->Domain;
|
|
WlxCredentials.pszPassword = pHelpAssistantCredential->Password;
|
|
|
|
fUseLcCredentials = FALSE;
|
|
|
|
fHelpAssistant = TRUE;
|
|
}
|
|
}
|
|
|
|
if( FALSE == fHelpAssistant )
|
|
{
|
|
//
|
|
// Not Help Assistant, use whatever send in from client.
|
|
//
|
|
ZeroMemory(&LcCredentials, sizeof(LCCREDENTIALS));
|
|
|
|
Status = LCProvideAutoLogonCredentials(
|
|
pWinStation,
|
|
&fUseLcCredentials,
|
|
&LcCredentials
|
|
);
|
|
|
|
|
|
if (Status == STATUS_SUCCESS)
|
|
{
|
|
if (fUseLcCredentials)
|
|
{
|
|
WlxCredentials.fDisconnectOnLogonFailure = TRUE;
|
|
WlxCredentials.fPromptForPassword = FALSE;
|
|
WlxCredentials.pszUserName = LcCredentials.pUserName;
|
|
WlxCredentials.pszDomain = LcCredentials.pDomain;
|
|
WlxCredentials.pszPassword = LcCredentials.pPassword;
|
|
}
|
|
else
|
|
{
|
|
WlxCredentials.fDisconnectOnLogonFailure = FALSE;
|
|
WlxCredentials.fPromptForPassword = pWinStation->Config.Config.User.fPromptForPassword;
|
|
|
|
// If it's an app server, check if it's a SD redirected connection
|
|
// If yes, ignore fPromptForPassword setting and allow auto-logon
|
|
// Note: Only do it if SD is enabled, i.e. GetTSSD() returns a valide value
|
|
if (!g_bPersonalTS && g_fAppCompat && GetTSSD()) {
|
|
TS_LOAD_BALANCE_INFO LBInfo;
|
|
ULONG ReturnLength;
|
|
|
|
// need to release it
|
|
ReleaseTSSD();
|
|
|
|
memset(&LBInfo, 0, sizeof(LBInfo));
|
|
Status = IcaStackIoControl(pWinStation->hStack,
|
|
IOCTL_TS_STACK_QUERY_LOAD_BALANCE_INFO,
|
|
NULL, 0,
|
|
&LBInfo, sizeof(LBInfo),
|
|
&ReturnLength);
|
|
|
|
if (NT_SUCCESS(Status)) {
|
|
if (LBInfo.RequestedSessionID &&
|
|
(LBInfo.ClientRedirectionVersion >= TS_CLUSTER_REDIRECTION_VERSION3)) {
|
|
WlxCredentials.fPromptForPassword = FALSE;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Check if we have to use New Credentials for long UserName and copy accordingly
|
|
if (pWinStation->pNewClientCredentials != NULL) {
|
|
WlxCredentials.pszUserName = pWinStation->pNewClientCredentials->UserName;
|
|
WlxCredentials.pszDomain = pWinStation->pNewClientCredentials->Domain;
|
|
WlxCredentials.pszPassword = pWinStation->pNewClientCredentials->Password;
|
|
} else {
|
|
WlxCredentials.pszUserName = pWinStation->Config.Config.User.UserName ;
|
|
WlxCredentials.pszDomain = pWinStation->Config.Config.User.Domain ;
|
|
WlxCredentials.pszPassword = pWinStation->Config.Config.User.Password ;
|
|
}
|
|
}
|
|
|
|
}
|
|
else
|
|
{
|
|
fRet = FALSE;
|
|
goto exit;
|
|
}
|
|
}
|
|
|
|
ASSERT(WlxCredentials.pszUserName != NULL);
|
|
ASSERT(WlxCredentials.pszDomain != NULL);
|
|
ASSERT(WlxCredentials.pszPassword != NULL);
|
|
|
|
*pcbWire = AllocateAndCopyCredToWire((PWLXCLIENTCREDWIREW*)ppWire, &WlxCredentials);
|
|
|
|
fRet = *pcbWire > 0;
|
|
|
|
//
|
|
// The values in LcCredentials are LocalAlloc-ed by the core.
|
|
//
|
|
|
|
if (fUseLcCredentials)
|
|
{
|
|
if (LcCredentials.pUserName != NULL)
|
|
{
|
|
LocalFree(LcCredentials.pUserName);
|
|
}
|
|
|
|
if (LcCredentials.pDomain != NULL)
|
|
{
|
|
LocalFree(LcCredentials.pDomain);
|
|
}
|
|
|
|
if (LcCredentials.pPassword != NULL)
|
|
{
|
|
LocalFree(LcCredentials.pPassword);
|
|
}
|
|
}
|
|
|
|
if( TRUE == fHelpAssistant )
|
|
{
|
|
// Zero out memory that contains password
|
|
ZeroMemory( pHelpAssistantCredential, sizeof(ExtendedClientCredentials));
|
|
}
|
|
|
|
exit:
|
|
ReleaseWinStation(pWinStation);
|
|
|
|
if (pHelpAssistantCredential != NULL) {
|
|
MemFree(pHelpAssistantCredential);
|
|
pHelpAssistantCredential = NULL;
|
|
}
|
|
|
|
|
|
return((BOOLEAN)fRet);
|
|
}
|
|
|
|
/*****************************************************************************
|
|
* RPcWinStationBroadcastSystemMessage
|
|
* This is the server side for cleint's WinStationBroadcastSystemMessage
|
|
*
|
|
* Perform the the equivalent to BroadcastSystemMessage to each specified sessions
|
|
*
|
|
* Limittations:
|
|
* Caller must be system or Admin, and lparam can not be zero unless
|
|
* msg is WM_DEVICECHANGE
|
|
* Error checking is done on the clinet side (winsta\client\winsta.c)
|
|
*
|
|
* ENTRY:
|
|
* hServer
|
|
* this is a handle which identifies a Hydra server. For the local server, hServer
|
|
* should be set to SERVERNAME_CURRENT
|
|
* sessionID
|
|
* this idefntifies the hydra session to which message is being sent
|
|
* timeOut
|
|
* set this to the amount of time you are willing to wait to get a response
|
|
* from the specified winstation. Even though Window's SendMessage API
|
|
* is blocking, the call from this side MUST choose how long it is willing to
|
|
* wait for a response.
|
|
* dwFlags
|
|
* Option flags. Can be a combination of the following values: Value Meaning
|
|
* BSF_ALLOWSFW Windows NT 5.0 and later: Enables the recipient to set the foreground window while
|
|
* processing the message.
|
|
* BSF_FLUSHDISK Flush the disk after each recipient processes the message.
|
|
* BSF_FORCEIFHUNG Continue to broadcast the message, even if the time-out period elapses or one of
|
|
* the recipients is hung..
|
|
* BSF_IGNORECURRENTTASK Do not send the message to windows that belong to the current task.
|
|
* This prevents an application from receiving its own message.
|
|
* BSF_NOHANG Force a hung application to time out. If one of the recipients times out, do not continue
|
|
* broadcasting the message.
|
|
* BSF_NOTIMEOUTIFNOTHUNG Wait for a response to the message, as long as the recipient is not hung.
|
|
* Do not time out.
|
|
* ***
|
|
* *** DO NOT USE *** BSF_POSTMESSAGE Post the message. Do not use in combination with BSF_QUERY.
|
|
* ***
|
|
* BSF_QUERY Send the message to one recipient at a time, sending to a subsequent recipient only if the
|
|
* current recipient returns TRUE.
|
|
* lpdwRecipients
|
|
* Pointer to a variable that contains and receives information about the recipients of the message. The variable can be a combination of the following values: Value Meaning
|
|
* BSM_ALLCOMPONENTS Broadcast to all system components.
|
|
* BSM_ALLDESKTOPS Windows NT: Broadcast to all desktops. Requires the SE_TCB_NAME privilege.
|
|
* BSM_APPLICATIONS Broadcast to applications.
|
|
* BSM_INSTALLABLEDRIVERS Windows 95 and Windows 98: Broadcast to installable drivers.
|
|
* BSM_NETDRIVER Windows 95 and Windows 98: Broadcast to network drivers.
|
|
* BSM_VXDS Windows 95 and Windows 98: Broadcast to all system-level device drivers.
|
|
* When the function returns, this variable receives a combination of these values identifying which recipients actually received the message.
|
|
* If this parameter is NULL, the function broadcasts to all components.
|
|
* uiMessage
|
|
* the window's message to send
|
|
* wParam
|
|
* first message param
|
|
* lParam
|
|
* second message parameter
|
|
*
|
|
* pResponse
|
|
* This is the response to the broadcasted message
|
|
* If the function succeeds, the value is a positive value.
|
|
* If the function is unable to broadcast the message, the value is ?1.
|
|
* If the dwFlags parameter is BSF_QUERY and at least one recipient returned
|
|
* BROADCAST_QUERY_DENY to the corresponding message, the return value is zero
|
|
*
|
|
* EXIT:
|
|
* TRUE if all went well or
|
|
* FALSE if something went wrong.
|
|
*
|
|
* WARNING:
|
|
* Do not use flag BSF_POSTMESSAGE, since an app/window on a winstation is not setup to send back
|
|
* a response to the query in an asynchronous fashion. You must wait for the response (until the time out period).
|
|
*
|
|
* Comments:
|
|
* For more info, please see MSDN for BroadcastSystemMessage()
|
|
*
|
|
****************************************************************************/
|
|
LONG RpcWinStationBroadcastSystemMessage(
|
|
HANDLE hServer,
|
|
ULONG sessionID,
|
|
ULONG waitTime,
|
|
DWORD dwFlags,
|
|
DWORD *lpdwRecipients,
|
|
ULONG uiMessage,
|
|
WPARAM wParam,
|
|
LPARAM lParam,
|
|
PBYTE rpcBuffer,
|
|
ULONG bufferSize,
|
|
BOOLEAN fBufferHasValidData,
|
|
LONG *pResponse
|
|
)
|
|
{
|
|
// Broadcast system message to all winstations.
|
|
|
|
PWINSTATION pWinStation=NULL;
|
|
WINSTATION_APIMSG WMsg;
|
|
OBJECT_ATTRIBUTES ObjA;
|
|
NTSTATUS Status;
|
|
LONG rc;
|
|
int i;
|
|
RPC_STATUS RpcStatus;
|
|
UINT LocalFlag;
|
|
|
|
// KdPrintEx((DPFLTR_TERMSRV_ID, DPFLTR_ERROR_LEVEL, "in RpcWinStationBroadcastSystemMessage()\n"));
|
|
|
|
//
|
|
//
|
|
//
|
|
RpcStatus = RpcImpersonateClient( NULL );
|
|
if( RpcStatus != RPC_S_OK ) {
|
|
KdPrintEx((DPFLTR_TERMSRV_ID, DPFLTR_ERROR_LEVEL, " RpcImpersonateClient() failed : 0x%x\n",RpcStatus));
|
|
SetLastError(ERROR_CANNOT_IMPERSONATE );
|
|
return( FALSE );
|
|
}
|
|
|
|
//
|
|
// Inquire if local RPC call
|
|
//
|
|
RpcStatus = I_RpcBindingIsClientLocal(
|
|
0, // Active RPC call we are servicing
|
|
&LocalFlag
|
|
);
|
|
|
|
if( RpcStatus != RPC_S_OK ) {
|
|
KdPrintEx((DPFLTR_TERMSRV_ID, DPFLTR_ERROR_LEVEL, " I_RpcBindingIsClientLocal() failed : 0x%x\n",RpcStatus));
|
|
RpcRevertToSelf();
|
|
SetLastError(ERROR_ACCESS_DENIED);
|
|
return( FALSE );
|
|
}
|
|
|
|
if( !LocalFlag ) {
|
|
KdPrintEx((DPFLTR_TERMSRV_ID, DPFLTR_ERROR_LEVEL, " Not a local client call\n"));
|
|
RpcRevertToSelf();
|
|
return( FALSE );
|
|
}
|
|
|
|
// if the caller is system or admin, then let it thru, else, return
|
|
if( !(IsCallerSystem() | IsCallerAdmin() ) )
|
|
{
|
|
KdPrintEx((DPFLTR_TERMSRV_ID, DPFLTR_ERROR_LEVEL, " Caller must be system or admin \n"));
|
|
SetLastError(ERROR_ACCESS_DENIED);
|
|
RpcRevertToSelf();
|
|
return( FALSE );
|
|
}
|
|
|
|
//
|
|
// if we got this far, then client is admin or system
|
|
//
|
|
|
|
//
|
|
// done with the impersonation, we must have succeeded, otherwise, we would have exited above
|
|
//
|
|
RpcRevertToSelf();
|
|
|
|
|
|
/*
|
|
* Marshall in the [in] parameters
|
|
*
|
|
*/
|
|
|
|
WMsg.WaitForReply=TRUE;
|
|
WMsg.u.bMsg.dwFlags = dwFlags;
|
|
WMsg.u.bMsg.dwRecipients= *lpdwRecipients; //note that we are passing the value, and we will get a new value back
|
|
WMsg.u.bMsg.uiMessage = uiMessage;
|
|
WMsg.u.bMsg.wParam = wParam;
|
|
WMsg.u.bMsg.lParam = lParam;
|
|
WMsg.u.bMsg.dataBuffer = NULL;
|
|
WMsg.u.bMsg.bufferSize = 0;
|
|
WMsg.u.bMsg.Response = *pResponse;
|
|
|
|
WMsg.ApiNumber = SMWinStationBroadcastSystemMessage ;
|
|
|
|
/*
|
|
* Find and lock client WinStation
|
|
*/
|
|
// KdPrintEx((DPFLTR_TERMSRV_ID, DPFLTR_ERROR_LEVEL, "in RpcWinStationBroadcastSystemMessage() for sessionID = %d \n", sessionID ));
|
|
pWinStation = FindWinStationById( sessionID, FALSE );
|
|
|
|
if ( pWinStation == NULL )
|
|
{
|
|
KdPrintEx((DPFLTR_TERMSRV_ID, DPFLTR_ERROR_LEVEL, "Winstation not found \n"));
|
|
return( FALSE );
|
|
}
|
|
|
|
if ( pWinStation->Flags & WSF_LISTEN )
|
|
{
|
|
// this is a "listen" winstation, not an interactive one, so return an empty response.
|
|
*pResponse = 0;
|
|
ReleaseWinStation( pWinStation );
|
|
return( TRUE );
|
|
}
|
|
|
|
if ( !((pWinStation->State == State_Active) ||
|
|
(pWinStation->State == State_Disconnected) ) )
|
|
{
|
|
// This is something that winsta.C checks for, but it's possibele that from the time it
|
|
// took to get here, a winstation was logged out from.
|
|
KdPrintEx((DPFLTR_TERMSRV_ID, DPFLTR_ERROR_LEVEL, "\t request aborted, winstation State should be Active or Disconnected, neither in this case. \n"));
|
|
*pResponse = 0;
|
|
ReleaseWinStation( pWinStation );
|
|
return( TRUE );
|
|
}
|
|
|
|
// if we have valid data, then set the msg dataBuffer to point to it
|
|
if ( fBufferHasValidData )
|
|
{
|
|
WMsg.u.bMsg.dataBuffer = rpcBuffer;
|
|
WMsg.u.bMsg.bufferSize = bufferSize;
|
|
}
|
|
|
|
/*
|
|
* send message to winstation
|
|
*/
|
|
rc = SendWinStationCommand( pWinStation, &WMsg, waitTime );
|
|
|
|
ReleaseWinStation( pWinStation );
|
|
|
|
if ( NT_SUCCESS( rc ) )
|
|
{
|
|
*pResponse = WMsg.u.bMsg.Response;
|
|
*lpdwRecipients = WMsg.u.bMsg.dwRecipients;
|
|
return (TRUE);
|
|
}
|
|
else
|
|
return ( FALSE );
|
|
|
|
}
|
|
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* RpcWinStationSendWindowMessage
|
|
*
|
|
* Perform the the equivalent to SendMessage to a specific winstation as
|
|
* identified by the session ID. This is an exported function, at least used
|
|
* by the PNP manager to send a device change message (or any other window's message)
|
|
*
|
|
* Limitations:
|
|
* Caller must be system or Admin, and lparam can not be zero unless
|
|
* msg is WM_DEVICECHANGE
|
|
* Error checking is done on the clinet side (winsta\client\winsta.c)
|
|
*
|
|
* ENTRY:
|
|
* hServer
|
|
* this is a handle which identifies a Hydra server. For the local server, hServer
|
|
* should be set to SERVERNAME_CURRENT
|
|
* sessionID
|
|
* this idefntifies the hydra session to which message is being sent
|
|
* timeOut
|
|
* set this to the amount of time you are willing to wait to get a response
|
|
* from the specified winstation. Even though Window's SendMessage API
|
|
* is blocking, the call from this side MUST choose how long it is willing to
|
|
* wait for a response.
|
|
* hWnd
|
|
* This is the HWND of the target window in the specified session that
|
|
* a message will be sent to.
|
|
* Msg
|
|
* the window's message to send
|
|
* wParam
|
|
* first message param
|
|
* lParam
|
|
* second message parameter
|
|
* pResponse
|
|
* this is the response to the message sent, it depends on the type of message sent, see MSDN
|
|
*
|
|
*
|
|
* EXIT:
|
|
* TRUE if all went well , check presponse for the actual response to the send message
|
|
* FALSE if something went wrong, the value of pResponse is not altered.
|
|
*
|
|
* WARNINGs:
|
|
* since the RPC call never blocks, you need to specify a reasonable timeOut if you want to wait for
|
|
* a response. Please remember that since this message is being sent to all winstations, the timeOut value
|
|
* will be on per-winstation.
|
|
*
|
|
*
|
|
* Comments:
|
|
* For more info, please see MSDN for SendMessage()
|
|
*
|
|
****************************************************************************/
|
|
LONG
|
|
RpcWinStationSendWindowMessage(
|
|
HANDLE hServer,
|
|
ULONG sessionID,
|
|
ULONG waitTime,
|
|
ULONG hWnd, // handle of destination window
|
|
ULONG Msg, // message to send
|
|
WPARAM wParam, // first message parameter
|
|
LPARAM lParam, // second message parameter
|
|
PBYTE rpcBuffer,
|
|
ULONG bufferSize,
|
|
BOOLEAN fBufferHasValidData,
|
|
LONG *pResponse // reply to the message sent
|
|
)
|
|
{
|
|
PWINSTATION pWinStation=NULL;
|
|
WINSTATION_APIMSG WMsg;
|
|
OBJECT_ATTRIBUTES ObjA;
|
|
NTSTATUS Status;
|
|
LONG rc;
|
|
int i;
|
|
PVOID pData;
|
|
RPC_STATUS RpcStatus;
|
|
UINT LocalFlag;
|
|
|
|
|
|
// KdPrintEx((DPFLTR_TERMSRV_ID, DPFLTR_ERROR_LEVEL, "in RpcWinStationSendWindowMessage()\n"));
|
|
|
|
/*
|
|
* Impersonate the client
|
|
*/
|
|
RpcStatus = RpcImpersonateClient( NULL );
|
|
if( RpcStatus != RPC_S_OK ) {
|
|
KdPrintEx((DPFLTR_TERMSRV_ID, DPFLTR_ERROR_LEVEL, "\tNot impersonating! RpcStatus 0x%x\n",RpcStatus));
|
|
SetLastError(ERROR_CANNOT_IMPERSONATE );
|
|
return( FALSE );
|
|
}
|
|
|
|
//
|
|
// Inquire if local RPC call
|
|
//
|
|
RpcStatus = I_RpcBindingIsClientLocal(
|
|
0, // Active RPC call we are servicing
|
|
&LocalFlag
|
|
);
|
|
|
|
if( RpcStatus != RPC_S_OK ) {
|
|
KdPrintEx((DPFLTR_TERMSRV_ID, DPFLTR_ERROR_LEVEL, "\tI_RpcBindingIsClientLocal() failed : 0x%x\n",RpcStatus));
|
|
RpcRevertToSelf();
|
|
SetLastError(ERROR_ACCESS_DENIED);
|
|
return( FALSE );
|
|
}
|
|
|
|
if( !LocalFlag ) {
|
|
KdPrintEx((DPFLTR_TERMSRV_ID, DPFLTR_ERROR_LEVEL, "\tNot a local client call\n"));
|
|
RpcRevertToSelf();
|
|
return( FALSE );
|
|
}
|
|
|
|
// if the caller is system or admin, then let it thru, else, return
|
|
if( !(IsCallerSystem() | IsCallerAdmin() ) )
|
|
{
|
|
KdPrintEx((DPFLTR_TERMSRV_ID, DPFLTR_ERROR_LEVEL, "\tCaller must be system or admin \n"));
|
|
SetLastError(ERROR_ACCESS_DENIED);
|
|
RpcRevertToSelf();
|
|
return( FALSE );
|
|
}
|
|
|
|
//
|
|
// if we got this far, then client is admin or system
|
|
//
|
|
|
|
//
|
|
// done with the impersonation, we must have succeeded, otherwise, we would have exited above
|
|
//
|
|
RpcRevertToSelf();
|
|
|
|
|
|
/*
|
|
* Marshall in the [in] parameters
|
|
*
|
|
*/
|
|
|
|
WMsg.WaitForReply=TRUE;
|
|
WMsg.u.sMsg.hWnd = (HWND)LongToHandle( hWnd );
|
|
WMsg.u.sMsg.Msg = Msg;
|
|
WMsg.u.sMsg.wParam = wParam;
|
|
WMsg.u.sMsg.lParam = lParam;
|
|
WMsg.u.sMsg.dataBuffer = NULL;
|
|
WMsg.u.sMsg.bufferSize = 0;
|
|
WMsg.u.sMsg.Response = *pResponse;
|
|
|
|
WMsg.ApiNumber = SMWinStationSendWindowMessage ;
|
|
|
|
|
|
/*
|
|
* Find and lock client WinStation
|
|
*/
|
|
// KdPrintEx((DPFLTR_TERMSRV_ID, DPFLTR_ERROR_LEVEL, "in RpcWinStationSendWindowMessage() for essionID = %d \n", sessionID ));
|
|
pWinStation = FindWinStationById( sessionID, FALSE );
|
|
|
|
if ( pWinStation == NULL )
|
|
{
|
|
KdPrintEx((DPFLTR_TERMSRV_ID, DPFLTR_ERROR_LEVEL, "Winstation not found \n"));
|
|
return( FALSE );
|
|
}
|
|
|
|
if ( pWinStation->Flags & WSF_LISTEN )
|
|
{
|
|
// this is a "listen" winstation, not an interactive one
|
|
ReleaseWinStation( pWinStation );
|
|
return( FALSE ); // this is normal situation, not an error, but no caller of this func should send a message to this w/s
|
|
}
|
|
|
|
if ( !((pWinStation->State == State_Active) ||
|
|
(pWinStation->State == State_Disconnected) ) )
|
|
{
|
|
// This is something that winsta.C checks for, but it's possibele that from the time it
|
|
// took to get here, a winstation was logged out from.
|
|
KdPrintEx((DPFLTR_TERMSRV_ID, DPFLTR_ERROR_LEVEL, "\t request aborted, winstation State should be Active or Disconnected, neither in this case. \n"));
|
|
*pResponse = 0;
|
|
ReleaseWinStation( pWinStation );
|
|
return( TRUE );
|
|
}
|
|
|
|
// if we have valid data, then set the msg dataBuffer to point to it
|
|
if ( fBufferHasValidData )
|
|
{
|
|
WMsg.u.sMsg.dataBuffer = rpcBuffer;
|
|
WMsg.u.sMsg.bufferSize = bufferSize;
|
|
|
|
}
|
|
|
|
// KdPrintEx((DPFLTR_TERMSRV_ID, DPFLTR_ERROR_LEVEL, " dataBuffer = 0x%lx \n", WMsg.u.sMsg.dataBuffer ));
|
|
// KdPrintEx((DPFLTR_TERMSRV_ID, DPFLTR_ERROR_LEVEL, " bufferSize = %d \n", WMsg.u.sMsg.bufferSize ));
|
|
|
|
/*
|
|
* send message to winstation
|
|
*/
|
|
rc = SendWinStationCommand( pWinStation, &WMsg, waitTime );
|
|
|
|
ReleaseWinStation( pWinStation );
|
|
|
|
if ( NT_SUCCESS( rc ) )
|
|
{
|
|
*pResponse = WMsg.u.sMsg.Response;
|
|
return (TRUE);
|
|
}
|
|
else
|
|
return ( FALSE );
|
|
}
|
|
|
|
NTSTATUS
|
|
IsZeroterminateStringA(
|
|
PBYTE pString,
|
|
DWORD dwLength
|
|
)
|
|
{
|
|
NTSTATUS Status = STATUS_SUCCESS;
|
|
|
|
|
|
|
|
if ( (pString == NULL) || (memchr(pString, '\0', dwLength) == NULL)) {
|
|
Status = STATUS_INVALID_PARAMETER;
|
|
}
|
|
return Status;
|
|
|
|
}
|
|
|
|
NTSTATUS
|
|
IsZeroterminateStringW(
|
|
PWCHAR pwString,
|
|
DWORD dwLength
|
|
)
|
|
{
|
|
|
|
|
|
if (pwString == NULL) {
|
|
return STATUS_INVALID_PARAMETER;
|
|
}
|
|
for (; 0 < dwLength; ++pwString, --dwLength ) {
|
|
if (*pwString == (WCHAR)0) {
|
|
return STATUS_SUCCESS;
|
|
}
|
|
}
|
|
return STATUS_INVALID_PARAMETER;
|
|
|
|
|
|
}
|
|
|
|
|
|
/*****************************************************************************
|
|
* GetTextualSid()
|
|
* given a sid, return the text of that sid
|
|
*
|
|
* Params:
|
|
* [in] user sid binary
|
|
* [out] text of user sid stuffed in the buffer which was passed in by the caller
|
|
* [in] the size of buffer passed in.
|
|
*
|
|
* Return:
|
|
* TRUE if no errors.
|
|
*
|
|
******************************************************************************/
|
|
BOOL
|
|
GetTextualSid(
|
|
PSID pSid, // binary Sid
|
|
LPTSTR TextualSid, // buffer for Textual representaion of Sid
|
|
LPDWORD cchSidSize // required/provided TextualSid buffersize
|
|
)
|
|
{
|
|
PSID_IDENTIFIER_AUTHORITY psia;
|
|
DWORD dwSubAuthorities;
|
|
DWORD dwCounter;
|
|
DWORD cchSidCopy;
|
|
|
|
//
|
|
// test if Sid passed in is valid
|
|
//
|
|
if(!IsValidSid(pSid)) return FALSE;
|
|
|
|
// obtain SidIdentifierAuthority
|
|
psia = GetSidIdentifierAuthority(pSid);
|
|
|
|
// obtain sidsubauthority count
|
|
dwSubAuthorities = *GetSidSubAuthorityCount(pSid);
|
|
|
|
//
|
|
// compute approximate buffer length
|
|
// S-SID_REVISION- + identifierauthority- + subauthorities- + NULL
|
|
//
|
|
cchSidCopy = (15 + 12 + (12 * dwSubAuthorities) + 1) * sizeof(TCHAR);
|
|
|
|
//
|
|
// check provided buffer length.
|
|
// If not large enough, indicate proper size and setlasterror
|
|
//
|
|
if(*cchSidSize < cchSidCopy) {
|
|
*cchSidSize = cchSidCopy;
|
|
SetLastError(ERROR_INSUFFICIENT_BUFFER);
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// prepare S-SID_REVISION-
|
|
//
|
|
cchSidCopy = wsprintf(TextualSid, TEXT("S-%lu-"), SID_REVISION );
|
|
|
|
//
|
|
// prepare SidIdentifierAuthority
|
|
//
|
|
if ( (psia->Value[0] != 0) || (psia->Value[1] != 0) ) {
|
|
cchSidCopy += wsprintf(TextualSid + cchSidCopy,
|
|
TEXT("0x%02hx%02hx%02hx%02hx%02hx%02hx"),
|
|
(USHORT)psia->Value[0],
|
|
(USHORT)psia->Value[1],
|
|
(USHORT)psia->Value[2],
|
|
(USHORT)psia->Value[3],
|
|
(USHORT)psia->Value[4],
|
|
(USHORT)psia->Value[5]);
|
|
} else {
|
|
cchSidCopy += wsprintf(TextualSid + cchSidCopy,
|
|
TEXT("%lu"),
|
|
(ULONG)(psia->Value[5] ) +
|
|
(ULONG)(psia->Value[4] << 8) +
|
|
(ULONG)(psia->Value[3] << 16) +
|
|
(ULONG)(psia->Value[2] << 24) );
|
|
}
|
|
|
|
//
|
|
// loop through SidSubAuthorities
|
|
//
|
|
for(dwCounter = 0 ; dwCounter < dwSubAuthorities ; dwCounter++) {
|
|
cchSidCopy += wsprintf(TextualSid + cchSidCopy, TEXT("-%lu"),
|
|
*GetSidSubAuthority(pSid, dwCounter) );
|
|
}
|
|
|
|
//
|
|
// tell the caller how many chars we provided, not including NULL
|
|
//
|
|
*cchSidSize = cchSidCopy;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
/*****************************************************************************
|
|
* RpcWinStationUpdateUserConfig()
|
|
* Called by notify when winlogon tells notify that shell startup is about to happen
|
|
* We open the user profile and get the policy data, and then override any data found
|
|
* in user's sessions's USERCONFIGW
|
|
*
|
|
* Params:
|
|
* [in] hServer,
|
|
* [in] ClientLogonId,
|
|
* [in] ClientProcessId,
|
|
* [in] UserToken,
|
|
* [in] pDomain,
|
|
* [in] DomainSize,
|
|
* [in] pUserName,
|
|
* [in] UserNameSize,
|
|
* [out] *pResult
|
|
*
|
|
* Return:
|
|
* TRUE if no errors, FALSE otherwise, see pResult for the NTSTATUS of the error
|
|
*
|
|
*
|
|
*
|
|
* *****************************************************************************/
|
|
BOOLEAN
|
|
RpcWinStationUpdateUserConfig(
|
|
HANDLE hServer,
|
|
DWORD ClientLogonId,
|
|
DWORD ClientProcessId,
|
|
DWORD UserToken,
|
|
DWORD *pResult
|
|
)
|
|
{
|
|
NTSTATUS Status = STATUS_SUCCESS;
|
|
ULONG Length;
|
|
DWORD rc = ERROR_SUCCESS;
|
|
ULONG Error;
|
|
USERCONFIGW *pTmpUserConfig;
|
|
POLICY_TS_USER userPolicy;
|
|
USERCONFIGW userPolicyData;
|
|
HANDLE NewToken;
|
|
|
|
PWINSTATION pWinStation;
|
|
OBJECT_ATTRIBUTES ObjA;
|
|
|
|
PTOKEN_USER pTokenInfo = NULL;
|
|
HANDLE hClientToken=NULL;
|
|
|
|
// @@@ make these dynamic later
|
|
TCHAR szSidText [MAX_PATH ];
|
|
DWORD sidTextSize = MAX_PATH;
|
|
|
|
|
|
*pResult=0;
|
|
|
|
/*
|
|
* Make sure the caller is SYSTEM (WinLogon)
|
|
*/
|
|
Status = RpcCheckSystemClient( ClientLogonId );
|
|
if ( !NT_SUCCESS( Status ) ) {
|
|
*pResult = Status;
|
|
return( FALSE );
|
|
}
|
|
|
|
TRACE((hTrace,TC_ICASRV,TT_API1, "TERMSRV: WinStationUpdateUserConfig, LogonId=%d\n", ClientLogonId ));
|
|
|
|
/*
|
|
* Find and lock client WinStation
|
|
*/
|
|
pWinStation = FindWinStationById( ClientLogonId, FALSE );
|
|
if ( pWinStation == NULL )
|
|
{
|
|
Status = STATUS_CTX_WINSTATION_NOT_FOUND;
|
|
*pResult = Status;
|
|
return( FALSE );
|
|
}
|
|
|
|
/*
|
|
* Get a valid copy of the clients token handle
|
|
*/
|
|
Status = NtDuplicateObject( pWinStation->InitialCommandProcess,
|
|
(HANDLE)LongToHandle( UserToken ),
|
|
NtCurrentProcess(),
|
|
&hClientToken,
|
|
0, 0,
|
|
DUPLICATE_SAME_ACCESS |
|
|
DUPLICATE_SAME_ATTRIBUTES );
|
|
|
|
if ( !NT_SUCCESS( Status ) )
|
|
goto done;
|
|
|
|
/*
|
|
* Determine size needed for token info buffer and allocate it
|
|
*/
|
|
Status = NtQueryInformationToken ( hClientToken, TokenUser,
|
|
NULL, 0, &Length );
|
|
|
|
if ( Status != STATUS_BUFFER_TOO_SMALL )
|
|
{
|
|
goto done;
|
|
}
|
|
|
|
pTokenInfo = MemAlloc( Length );
|
|
if ( pTokenInfo == NULL )
|
|
{
|
|
Status = STATUS_NO_MEMORY;
|
|
goto done;
|
|
}
|
|
|
|
/*
|
|
* Query token information to get client user's SID
|
|
*/
|
|
Status = NtQueryInformationToken ( hClientToken, TokenUser,
|
|
pTokenInfo, Length, &Length );
|
|
if ( NT_SUCCESS( Status ) )
|
|
{
|
|
|
|
if ( GetTextualSid( pTokenInfo->User.Sid , szSidText, &sidTextSize ) )
|
|
{
|
|
|
|
// We now have the user SID
|
|
|
|
/*
|
|
* Get User policy from HKCU
|
|
*/
|
|
if ( RegGetUserPolicy( szSidText, &userPolicy, & userPolicyData ) )
|
|
{
|
|
// 4th param is NULL, since config data is already part of pWinstation
|
|
// due to the call into NotifyLogonWorker from Winlogon. We are now
|
|
// going to override data by any/all user group policy data.
|
|
MergeUserConfigData( pWinStation, &userPolicy, &userPolicyData, NULL );
|
|
}
|
|
// else we were not able to get user policy, so no merge was done.
|
|
|
|
}
|
|
}
|
|
|
|
done:
|
|
|
|
if (pTokenInfo )
|
|
{
|
|
MemFree( pTokenInfo );
|
|
}
|
|
|
|
if (hClientToken)
|
|
{
|
|
NtClose( hClientToken );
|
|
}
|
|
|
|
/*
|
|
* Start logon timers anyway, in case of any errors, we should still start the timers since
|
|
* some machine policy might have set them up.
|
|
*/
|
|
StartLogonTimers( pWinStation );
|
|
|
|
ReleaseWinStation( pWinStation );
|
|
|
|
TRACE((hTrace,TC_ICASRV,TT_API1, "TERMSRV: RpcWinStationUpdateUserConfig, Status=0x%x\n", Status ));
|
|
|
|
*pResult = Status ;
|
|
|
|
return( *pResult == STATUS_SUCCESS ? TRUE : FALSE );
|
|
}
|
|
|
|
|
|
BOOLEAN RpcWinStationRegisterConsoleNotification (
|
|
HANDLE hServer,
|
|
DWORD *pResult,
|
|
ULONG SessionId,
|
|
ULONG hWnd, // handle of destination window
|
|
DWORD dwFlags
|
|
)
|
|
{
|
|
|
|
RPC_STATUS RpcStatus;
|
|
UINT LocalFlag;
|
|
|
|
if (pResult == NULL) {
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// Calling this function from the remote machine, does not make sense, and
|
|
// we should not allow it.
|
|
//
|
|
RpcStatus = RpcImpersonateClient( NULL );
|
|
if( RpcStatus != RPC_S_OK ) {
|
|
DBGPRINT((" RpcImpersonateClient() failed : 0x%x\n",RpcStatus));
|
|
*pResult = STATUS_CANNOT_IMPERSONATE;
|
|
return( FALSE );
|
|
}
|
|
|
|
//
|
|
// Inquire if local RPC call
|
|
//
|
|
RpcStatus = I_RpcBindingIsClientLocal(
|
|
0, // Active RPC call we are servicing
|
|
&LocalFlag
|
|
);
|
|
|
|
if( RpcStatus != RPC_S_OK ) {
|
|
DBGPRINT((" I_RpcBindingIsClientLocal() failed : 0x%x\n",RpcStatus));
|
|
RpcRevertToSelf();
|
|
*pResult = STATUS_ACCESS_DENIED;
|
|
return( FALSE );
|
|
}
|
|
|
|
if( !LocalFlag ) {
|
|
DBGPRINT((" Not a local client call\n"));
|
|
*pResult = STATUS_ACCESS_DENIED;
|
|
RpcRevertToSelf();
|
|
return( FALSE );
|
|
}
|
|
|
|
//
|
|
// done with the impersonation, we must have succeeded, otherwise, we would have exited above
|
|
//
|
|
RpcRevertToSelf();
|
|
|
|
|
|
// BUGBUG : MakarP, can we check if the calling process owns this hWnd ?
|
|
// using GetWindowThreadProcessId ? Does it work across the sessions ?
|
|
|
|
*pResult = RegisterConsoleNotification ( hWnd, SessionId, dwFlags );
|
|
return (*pResult == STATUS_SUCCESS);
|
|
}
|
|
|
|
BOOLEAN RpcWinStationUnRegisterConsoleNotification (
|
|
HANDLE hServer,
|
|
DWORD *pResult,
|
|
ULONG SessionId,
|
|
ULONG hWnd // handle of destination window
|
|
)
|
|
{
|
|
RPC_STATUS RpcStatus;
|
|
UINT LocalFlag;
|
|
|
|
if (pResult == NULL) {
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// Calling this function from the remote machine, does not make sense, and
|
|
// we should not allow it.
|
|
//
|
|
RpcStatus = RpcImpersonateClient( NULL );
|
|
if( RpcStatus != RPC_S_OK ) {
|
|
DBGPRINT((" RpcImpersonateClient() failed : 0x%x\n",RpcStatus));
|
|
*pResult = STATUS_CANNOT_IMPERSONATE;
|
|
return( FALSE );
|
|
}
|
|
|
|
//
|
|
// Inquire if local RPC call
|
|
//
|
|
RpcStatus = I_RpcBindingIsClientLocal(
|
|
0, // Active RPC call we are servicing
|
|
&LocalFlag
|
|
);
|
|
|
|
if( RpcStatus != RPC_S_OK ) {
|
|
DBGPRINT((" I_RpcBindingIsClientLocal() failed : 0x%x\n",RpcStatus));
|
|
RpcRevertToSelf();
|
|
*pResult = STATUS_ACCESS_DENIED;
|
|
return( FALSE );
|
|
}
|
|
|
|
if( !LocalFlag ) {
|
|
DBGPRINT((" Not a local client call\n"));
|
|
*pResult = STATUS_ACCESS_DENIED;
|
|
RpcRevertToSelf();
|
|
return( FALSE );
|
|
}
|
|
|
|
//
|
|
// done with the impersonation, we must have succeeded, otherwise, we would have exited above
|
|
//
|
|
RpcRevertToSelf();
|
|
|
|
|
|
// BUGBUG : MakarP, can we check if the calling process owns this hWnd ?
|
|
// using GetWindowThreadProcessId ? Does it work across the sessions ?
|
|
|
|
*pResult = UnRegisterConsoleNotification ( hWnd, SessionId);
|
|
|
|
return (*pResult == STATUS_SUCCESS);
|
|
}
|
|
|
|
BOOLEAN RpcWinStationIsHelpAssistantSession (
|
|
HANDLE hServer,
|
|
DWORD *pResult, // function status
|
|
ULONG SessionId // user logon id
|
|
)
|
|
/*++
|
|
|
|
RpcWinStationIsSessionHelpAssistantSession returns if a
|
|
given session is created by Salem HelpAssistant account.
|
|
|
|
Parameters:
|
|
|
|
hServer : Handle to server, unused, just to match all
|
|
other RPC function.
|
|
SessionId : User session ID.
|
|
|
|
Returns:
|
|
|
|
TRUE/FALSE
|
|
|
|
--*/
|
|
{
|
|
RPC_STATUS RpcStatus;
|
|
UINT LocalFlag;
|
|
PWINSTATION pWinStation=NULL;
|
|
BOOLEAN bReturn;
|
|
BOOL bValidHelpSession;
|
|
|
|
*pResult=0;
|
|
|
|
//
|
|
// Do we need to check system or only LPC call?
|
|
//
|
|
|
|
//
|
|
// Find and lock client WinStation
|
|
//
|
|
pWinStation = FindWinStationById( SessionId, FALSE );
|
|
if ( pWinStation == NULL )
|
|
{
|
|
*pResult = STATUS_CTX_WINSTATION_NOT_FOUND;
|
|
return( FALSE );
|
|
}
|
|
|
|
bReturn = (BOOLEAN)TSIsSessionHelpSession( pWinStation, &bValidHelpSession );
|
|
|
|
if( TRUE == bReturn )
|
|
{
|
|
if( FALSE == bValidHelpSession )
|
|
{
|
|
*pResult = STATUS_WRONG_PASSWORD;
|
|
}
|
|
}
|
|
|
|
|
|
ReleaseWinStation( pWinStation );
|
|
return bReturn;
|
|
}
|
|
|
|
BOOLEAN RpcRemoteAssistancePrepareSystemRestore (
|
|
HANDLE hServer,
|
|
DWORD *pResult // function status
|
|
)
|
|
/*++
|
|
|
|
Prepare TermSrv/Salem for system restore.
|
|
|
|
Parameters:
|
|
|
|
hServer : Handle to server, unused, just to match all
|
|
other RPC function.
|
|
pResult : Pointer to DWORD to receive status of function.
|
|
|
|
Returns:
|
|
|
|
TRUE/FALSE
|
|
|
|
--*/
|
|
{
|
|
BOOLEAN bReturn = TRUE;
|
|
RPC_STATUS RpcStatus;
|
|
BOOL LocalFlag;
|
|
HRESULT hRes;
|
|
|
|
if (pResult == NULL) {
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// Calling this function from the remote machine, does not make sense, and
|
|
// we should not allow it.
|
|
//
|
|
RpcStatus = RpcImpersonateClient( NULL );
|
|
if( RpcStatus != RPC_S_OK ) {
|
|
DBGPRINT((" RpcImpersonateClient() failed : 0x%x\n",RpcStatus));
|
|
*pResult = ERROR_CANNOT_IMPERSONATE;
|
|
return( FALSE );
|
|
}
|
|
|
|
//
|
|
// Allow ony local admin to invoke this call.
|
|
//
|
|
if( !IsCallerAdmin() ) {
|
|
RpcRevertToSelf();
|
|
*pResult = ERROR_ACCESS_DENIED;
|
|
return (FALSE);
|
|
}
|
|
|
|
//
|
|
// Inquire if local RPC call
|
|
//
|
|
RpcStatus = I_RpcBindingIsClientLocal(
|
|
0, // Active RPC call we are servicing
|
|
&LocalFlag
|
|
);
|
|
|
|
if( RpcStatus != RPC_S_OK ) {
|
|
DBGPRINT((" I_RpcBindingIsClientLocal() failed : 0x%x\n",RpcStatus));
|
|
RpcRevertToSelf();
|
|
*pResult = ERROR_ACCESS_DENIED;
|
|
return( FALSE );
|
|
}
|
|
|
|
if( !LocalFlag ) {
|
|
DBGPRINT((" Not a local client call\n"));
|
|
*pResult = ERROR_ACCESS_DENIED;
|
|
RpcRevertToSelf();
|
|
return( FALSE );
|
|
}
|
|
|
|
//
|
|
// done with the impersonation, we must have succeeded, otherwise, we would have exited above
|
|
//
|
|
RpcRevertToSelf();
|
|
|
|
*pResult=0;
|
|
|
|
hRes = TSRemoteAssistancePrepareSystemRestore();
|
|
if( S_OK != hRes ) {
|
|
bReturn = FALSE;
|
|
*pResult = hRes;
|
|
}
|
|
|
|
return bReturn;
|
|
}
|
|
|
|
|
|
/*
|
|
* RpcWinStationGetMachinePolicy
|
|
*
|
|
* Return (a copye of ) the machine policy to the caller
|
|
*
|
|
* Parameters:
|
|
*
|
|
* hServer : Handle to server, unused, just to match all
|
|
* other RPC function.
|
|
*
|
|
* pointer to the policy struct
|
|
*
|
|
* size of the policy struct.
|
|
*
|
|
*/
|
|
BOOLEAN
|
|
RpcWinStationGetMachinePolicy(
|
|
SERVER_HANDLE hServer,
|
|
PBYTE pPolicy,
|
|
ULONG bufferSize )
|
|
{
|
|
if (pPolicy == NULL) {
|
|
return FALSE;
|
|
}
|
|
// Check if BufferSize if big enough
|
|
if (bufferSize < sizeof(POLICY_TS_MACHINE)) {
|
|
return FALSE;
|
|
}
|
|
RtlCopyMemory( pPolicy , & g_MachinePolicy, sizeof( POLICY_TS_MACHINE ) );
|
|
return TRUE;
|
|
}
|
|
|
|
/*++
|
|
|
|
RpcWinStationUpdateClientCachedCredentials is used to store the ACTUAL credentials
|
|
used to log on by a client. This is stored in WINSTATION struct which is later
|
|
on used to notify the client about the logon credentials. This API is called
|
|
from MSGINA.
|
|
|
|
Parameters:
|
|
|
|
ClientLogonId : Logon Id of the new session opened by the client
|
|
|
|
pDomian, pUserName : Credentials used by the client to log on
|
|
|
|
Returns:
|
|
|
|
TRUE if the call succeeded in updating the credentials.
|
|
|
|
--*/
|
|
|
|
BOOLEAN
|
|
RpcWinStationUpdateClientCachedCredentials(
|
|
HANDLE hServer,
|
|
DWORD *pResult,
|
|
DWORD ClientLogonId,
|
|
ULONG_PTR ClientProcessId,
|
|
PWCHAR pDomain,
|
|
DWORD DomainSize,
|
|
PWCHAR pUserName,
|
|
DWORD UserNameSize
|
|
)
|
|
{
|
|
NTSTATUS Status;
|
|
|
|
|
|
/*
|
|
* Do some buffer validation
|
|
*/
|
|
*pResult = IsZeroterminateStringW(pUserName, UserNameSize );
|
|
if (*pResult != STATUS_SUCCESS) {
|
|
return FALSE;
|
|
}
|
|
*pResult = IsZeroterminateStringW(pDomain, DomainSize );
|
|
if (*pResult != STATUS_SUCCESS) {
|
|
return FALSE;
|
|
}
|
|
|
|
/*
|
|
* Make sure the caller is SYSTEM (WinLogon)
|
|
*/
|
|
Status = RpcCheckSystemClient( ClientLogonId );
|
|
if ( !NT_SUCCESS( Status ) ) {
|
|
*pResult = Status;
|
|
return( FALSE );
|
|
}
|
|
|
|
*pResult = WinStationUpdateClientCachedCredentialsWorker(
|
|
ClientLogonId,
|
|
ClientProcessId,
|
|
pDomain,
|
|
DomainSize,
|
|
pUserName,
|
|
UserNameSize
|
|
);
|
|
|
|
return( *pResult == STATUS_SUCCESS ? TRUE : FALSE );
|
|
}
|
|
|
|
/*****************************************************************************
|
|
* WinStationUpdateClientCachedCredentialsWorker
|
|
*
|
|
* Worker function for RPCWinStationUpdateClientCachedCredentials
|
|
****************************************************************************/
|
|
NTSTATUS WinStationUpdateClientCachedCredentialsWorker(
|
|
DWORD ClientLogonId,
|
|
ULONG_PTR ClientProcessId,
|
|
PWCHAR pDomain,
|
|
DWORD DomainSize,
|
|
PWCHAR pUserName,
|
|
DWORD UserNameSize)
|
|
{
|
|
|
|
PWINSTATION pWinStation;
|
|
ULONG Length;
|
|
NTSTATUS Status = STATUS_SUCCESS;
|
|
|
|
TRACE((hTrace,TC_ICASRV,TT_API1, "TERMSRV: WinStationUpdateClientCachedCredentialsWorker, LogonId=%d\n", ClientLogonId ));
|
|
|
|
if ( ShutdownInProgress ) {
|
|
return ( STATUS_CTX_WINSTATION_ACCESS_DENIED );
|
|
}
|
|
|
|
/*
|
|
* Find and lock client WinStation
|
|
*/
|
|
pWinStation = FindWinStationById( ClientLogonId, FALSE );
|
|
if ( pWinStation == NULL ) {
|
|
Status = STATUS_CTX_WINSTATION_NOT_FOUND;
|
|
goto done;
|
|
}
|
|
|
|
/*
|
|
* Do not do anything if the session is not connected. The processing in this API assumes we are connected and have a
|
|
* valid stack.
|
|
*/
|
|
if ((ClientLogonId != 0) && ((pWinStation->State != State_Connected) || pWinStation->StateFlags & WSF_ST_IN_DISCONNECT)) {
|
|
Status = STATUS_CTX_CLOSE_PENDING;
|
|
ReleaseWinStation( pWinStation );
|
|
goto done;
|
|
}
|
|
|
|
/*
|
|
* Upper level code has verified that this RPC
|
|
* is coming from a local client with SYSTEM access.
|
|
*
|
|
* We should be able to trust the ClientProcessId from
|
|
* SYSTEM code.
|
|
*/
|
|
|
|
/*
|
|
* Ensure this is WinLogon calling
|
|
*/
|
|
if ( (HANDLE)(ULONG_PTR)ClientProcessId != pWinStation->InitialCommandProcessId ) {
|
|
ReleaseWinStation( pWinStation );
|
|
goto done;
|
|
}
|
|
|
|
/*
|
|
* Save User Name and Domain Name which we will later use to send Notification to the Client
|
|
*/
|
|
pWinStation->pNewNotificationCredentials = MemAlloc(sizeof(CLIENTNOTIFICATIONCREDENTIALS));
|
|
if (pWinStation->pNewNotificationCredentials == NULL) {
|
|
Status = STATUS_NO_MEMORY ;
|
|
ReleaseWinStation( pWinStation );
|
|
goto done ;
|
|
}
|
|
|
|
// pDomain and pUserName are sent from Winlogon - these cannot exceed the size of the pWinstation buffers
|
|
// because of the restrictions in the credentials lengths imposed by winlogon
|
|
// But anyway check for their length and truncate them if they are more than the length of pWinstation buffers
|
|
|
|
if ( wcslen(pDomain) > EXTENDED_DOMAIN_LEN ) {
|
|
pDomain[EXTENDED_DOMAIN_LEN] = L'\0';
|
|
}
|
|
|
|
if ( wcslen(pUserName) > EXTENDED_USERNAME_LEN ) {
|
|
pUserName[EXTENDED_USERNAME_LEN] = L'\0';
|
|
}
|
|
|
|
wcscpy( pWinStation->pNewNotificationCredentials->Domain, pDomain);
|
|
wcscpy( pWinStation->pNewNotificationCredentials->UserName, pUserName);
|
|
|
|
/*
|
|
* Release the winstation lock
|
|
*/
|
|
ReleaseWinStation( pWinStation );
|
|
|
|
done:
|
|
/*
|
|
* Save return status in API message
|
|
*/
|
|
TRACE((hTrace,TC_ICASRV,TT_API1, "TERMSRV: WinStationUpdateClientCachedCredentialsWorker, Status=0x%x\n", Status ));
|
|
return Status;
|
|
|
|
}
|
|
|
|
/*++
|
|
|
|
RpcWinStationFUSCanRemoteUserDisconnect - this API is used in a FUS Scenario
|
|
when there is someone at the console and another user tries to open a remote
|
|
session. This API askes the local user if it is ok to disconnect his/her session
|
|
and allow the remote user to connect.
|
|
|
|
Parameters:
|
|
|
|
TargetLogonId : Session ID which is being requested for connection
|
|
|
|
ClientLogonId : Session ID of the temperory new session
|
|
|
|
pDomain : Domain Name of the user who is trying to connect from remote
|
|
|
|
pUserName : UserName of the user who is trying to connect from remote
|
|
|
|
|
|
Returns:
|
|
|
|
TRUE - The local user has agreed to connect the remote user - so this user's session
|
|
will be disconnected
|
|
|
|
FALSE - Local user does not allow the remote user to connect
|
|
|
|
--*/
|
|
|
|
BOOLEAN
|
|
RpcWinStationFUSCanRemoteUserDisconnect(
|
|
HANDLE hServer,
|
|
DWORD *pResult,
|
|
DWORD TargetLogonId,
|
|
DWORD ClientLogonId,
|
|
ULONG_PTR ClientProcessId,
|
|
PWCHAR pDomain,
|
|
DWORD DomainSize,
|
|
PWCHAR pUserName,
|
|
DWORD UserNameSize
|
|
)
|
|
{
|
|
NTSTATUS Status;
|
|
|
|
/*
|
|
* Do some buffer validation
|
|
*/
|
|
*pResult = IsZeroterminateStringW(pUserName, UserNameSize );
|
|
if (*pResult != STATUS_SUCCESS) {
|
|
return FALSE;
|
|
}
|
|
*pResult = IsZeroterminateStringW(pDomain, DomainSize );
|
|
if (*pResult != STATUS_SUCCESS) {
|
|
return FALSE;
|
|
}
|
|
|
|
/*
|
|
* Make sure the caller is SYSTEM (WinLogon)
|
|
*/
|
|
Status = RpcCheckSystemClient( ClientLogonId );
|
|
if ( !NT_SUCCESS( Status ) ) {
|
|
*pResult = Status;
|
|
return( FALSE );
|
|
}
|
|
|
|
*pResult = WinStationFUSCanRemoteUserDisconnectWorker(
|
|
TargetLogonId,
|
|
ClientLogonId,
|
|
ClientProcessId,
|
|
pDomain,
|
|
DomainSize,
|
|
pUserName,
|
|
UserNameSize
|
|
);
|
|
|
|
return( *pResult == STATUS_SUCCESS ? TRUE : FALSE );
|
|
}
|
|
|
|
/*****************************************************************************
|
|
* WinStationFUSCanRemoteUserDisconnectWorker
|
|
*
|
|
* Worker function for WinStationFUSCanRemoteUserDisconnect
|
|
****************************************************************************/
|
|
NTSTATUS WinStationFUSCanRemoteUserDisconnectWorker(
|
|
DWORD TargetLogonId,
|
|
DWORD ClientLogonId,
|
|
ULONG_PTR ClientProcessId,
|
|
PWCHAR pDomain,
|
|
DWORD DomainSize,
|
|
PWCHAR pUserName,
|
|
DWORD UserNameSize)
|
|
{
|
|
|
|
PWINSTATION pTargetWinStation, pClientWinStation;
|
|
ULONG Length;
|
|
NTSTATUS Status = STATUS_SUCCESS;
|
|
|
|
WINSTATION_APIMSG msg;
|
|
ULONG DisconnectResponse;
|
|
OBJECT_ATTRIBUTES ObjA;
|
|
int cchTitle, cchMessage;
|
|
|
|
WCHAR *szTitle = NULL;
|
|
WCHAR *szMsg = NULL;
|
|
WCHAR *FUSDisconnectMsg = NULL;
|
|
|
|
TRACE((hTrace,TC_ICASRV,TT_API1, "TERMSRV: WinStationFUSCanRemoteUserDisconnect, LogonId=%d\n", ClientLogonId ));
|
|
|
|
if ( ShutdownInProgress ) {
|
|
return ( STATUS_CTX_WINSTATION_ACCESS_DENIED );
|
|
}
|
|
|
|
// Allocate the strings needed to display the Popup MessageBox
|
|
if ((szTitle = LocalAlloc(LMEM_FIXED, MAX_STRING_BYTES * sizeof(WCHAR))) == NULL) {
|
|
Status = STATUS_NO_MEMORY ;
|
|
goto done;
|
|
}
|
|
|
|
if ((szMsg = LocalAlloc(LMEM_FIXED, MAX_STRING_BYTES * sizeof(WCHAR))) == NULL) {
|
|
Status = STATUS_NO_MEMORY ;
|
|
goto done;
|
|
}
|
|
|
|
if ((FUSDisconnectMsg = LocalAlloc(LMEM_FIXED, MAX_STRING_BYTES * sizeof(WCHAR))) == NULL) {
|
|
Status = STATUS_NO_MEMORY ;
|
|
goto done;
|
|
}
|
|
|
|
/*
|
|
* Find and lock client WinStation
|
|
*/
|
|
pClientWinStation = FindWinStationById( ClientLogonId, FALSE );
|
|
if ( pClientWinStation == NULL ) {
|
|
Status = STATUS_CTX_WINSTATION_NOT_FOUND;
|
|
goto done;
|
|
}
|
|
|
|
/*
|
|
* Do not do anything if the session is not connected. The processing in this API assumes we are connected and have a
|
|
* valid stack.
|
|
*/
|
|
if ((ClientLogonId != 0) && ((pClientWinStation->State != State_Connected) || pClientWinStation->StateFlags & WSF_ST_IN_DISCONNECT)) {
|
|
Status = STATUS_CTX_CLOSE_PENDING;
|
|
ReleaseWinStation(pClientWinStation);
|
|
goto done;
|
|
}
|
|
|
|
/*
|
|
* Upper level code has verified that this RPC
|
|
* is coming from a local client with SYSTEM access.
|
|
*
|
|
* We should be able to trust the ClientProcessId from
|
|
* SYSTEM code.
|
|
*/
|
|
|
|
/*
|
|
* Ensure this is WinLogon calling
|
|
*/
|
|
if ( (HANDLE)(ULONG_PTR)ClientProcessId != pClientWinStation->InitialCommandProcessId ) {
|
|
ReleaseWinStation(pClientWinStation);
|
|
goto done;
|
|
}
|
|
|
|
/*
|
|
* Client WinStation is not needed anymore, so release that lock
|
|
*/
|
|
ReleaseWinStation(pClientWinStation);
|
|
|
|
/*
|
|
* Find and lock target WinStation
|
|
*/
|
|
pTargetWinStation = FindWinStationById( TargetLogonId, FALSE );
|
|
if ( pTargetWinStation == NULL ) {
|
|
Status = STATUS_CTX_WINSTATION_NOT_FOUND;
|
|
goto done;
|
|
}
|
|
|
|
/*
|
|
* Display a MessageBox to the User in the present session - the main purpose of this API
|
|
*/
|
|
cchTitle = LoadString( hModuleWin, STR_FUS_REMOTE_DISCONNECT_TITLE, szTitle, MAX_STRING_BYTES);
|
|
|
|
LoadString( hModuleWin, STR_FUS_REMOTE_DISCONNECT_MSG, szMsg, MAX_STRING_BYTES);
|
|
|
|
cchMessage = wsprintf( FUSDisconnectMsg, L"%s\\%s %s", pDomain, pUserName, szMsg );
|
|
/*
|
|
* Send message and wait for reply
|
|
*/
|
|
msg.u.SendMessage.pTitle = szTitle;
|
|
msg.u.SendMessage.TitleLength = (cchTitle+1) * sizeof(WCHAR);
|
|
msg.u.SendMessage.pMessage = FUSDisconnectMsg;
|
|
msg.u.SendMessage.MessageLength = (cchMessage+1) * sizeof(WCHAR);
|
|
msg.u.SendMessage.Style = MB_YESNO | MB_DEFBUTTON1 | MB_ICONQUESTION;
|
|
msg.u.SendMessage.Timeout = 20;
|
|
msg.u.SendMessage.DoNotWait = FALSE;
|
|
msg.u.SendMessage.pResponse = &DisconnectResponse;
|
|
|
|
msg.ApiNumber = SMWinStationDoMessage;
|
|
|
|
/*
|
|
* Create wait event
|
|
*/
|
|
InitializeObjectAttributes( &ObjA, NULL, 0, NULL, NULL );
|
|
Status = NtCreateEvent( &msg.u.SendMessage.hEvent, EVENT_ALL_ACCESS, &ObjA,
|
|
NotificationEvent, FALSE );
|
|
if ( !NT_SUCCESS(Status) ) {
|
|
ReleaseWinStation(pTargetWinStation);
|
|
goto done;
|
|
}
|
|
|
|
/*
|
|
* Initialize response to IDTIMEOUT
|
|
*/
|
|
DisconnectResponse = IDTIMEOUT;
|
|
|
|
/*
|
|
* Tell the WinStation to display the message box
|
|
*/
|
|
Status = SendWinStationCommand( pTargetWinStation, &msg, 0 );
|
|
|
|
/*
|
|
* Wait for response
|
|
*/
|
|
if ( Status == STATUS_SUCCESS ) {
|
|
TRACE((hTrace,TC_ICASRV,TT_API1, "WinStationSendMessage: wait for response\n" ));
|
|
UnlockWinStation( pTargetWinStation );
|
|
Status = NtWaitForSingleObject( msg.u.SendMessage.hEvent, FALSE, NULL );
|
|
if ( !RelockWinStation( pTargetWinStation ) ) {
|
|
Status = STATUS_CTX_CLOSE_PENDING;
|
|
}
|
|
TRACE((hTrace,TC_ICASRV,TT_API1, "WinStationSendMessage: got response %u\n", DisconnectResponse ));
|
|
NtClose( msg.u.SendMessage.hEvent );
|
|
} else {
|
|
/* close the event in case of SendWinStationCommand failure */
|
|
NtClose( msg.u.SendMessage.hEvent );
|
|
}
|
|
|
|
if (Status == STATUS_SUCCESS && DisconnectResponse == IDNO) {
|
|
Status = STATUS_CTX_WINSTATION_ACCESS_DENIED;
|
|
}
|
|
|
|
/*
|
|
* Release the target winstation lock
|
|
*/
|
|
ReleaseWinStation( pTargetWinStation );
|
|
|
|
done:
|
|
/*
|
|
* Do some memory cleanup
|
|
*/
|
|
if (szTitle != NULL) {
|
|
LocalFree(szTitle);
|
|
szTitle = NULL;
|
|
}
|
|
|
|
if (szMsg != NULL) {
|
|
LocalFree(szMsg);
|
|
szMsg = NULL;
|
|
}
|
|
|
|
if (FUSDisconnectMsg != NULL) {
|
|
LocalFree(FUSDisconnectMsg);
|
|
FUSDisconnectMsg = NULL;
|
|
}
|
|
|
|
/*
|
|
* Save return status in API message
|
|
*/
|
|
TRACE((hTrace,TC_ICASRV,TT_API1, "TERMSRV: WinStationFUSCanRemoteUserDisconnectWorker, Status=0x%x\n", Status ));
|
|
return Status;
|
|
|
|
}
|
|
|
|
/*++
|
|
|
|
RPCWinStationCheckLoopBack checks for loopback during connections. This API
|
|
is called from Winlogon.
|
|
|
|
Parameters:
|
|
|
|
ClientSessionId : Session ID from where the TS Client was started
|
|
|
|
TargetLogonId : Session ID to which we are trying to connect
|
|
|
|
pTargetServerName : Name of the Server we are trying to connect to
|
|
|
|
Returns:
|
|
|
|
TRUE if there is a loopBack, FALSE otherwise
|
|
|
|
--*/
|
|
|
|
BOOLEAN
|
|
RpcWinStationCheckLoopBack(
|
|
HANDLE hServer,
|
|
DWORD *pResult,
|
|
DWORD ClientSessionId,
|
|
DWORD TargetLogonId,
|
|
PWCHAR pTargetServerName,
|
|
DWORD NameSize
|
|
)
|
|
{
|
|
|
|
NTSTATUS Status;
|
|
|
|
|
|
/*
|
|
* Do some buffer validation
|
|
*/
|
|
*pResult = IsZeroterminateStringW(pTargetServerName, NameSize );
|
|
if (*pResult != STATUS_SUCCESS) {
|
|
return FALSE;
|
|
}
|
|
|
|
*pResult = WinStationCheckLoopBackWorker(
|
|
TargetLogonId,
|
|
ClientSessionId,
|
|
pTargetServerName,
|
|
NameSize
|
|
);
|
|
|
|
// NOTE - If Worker function returns STATUS_SUCCESS that means there is no loopback, so return FALSE
|
|
return( *pResult == STATUS_SUCCESS ? FALSE : TRUE );
|
|
}
|
|
|
|
/*****************************************************************************
|
|
* WinStationCheckLoopBackWorker
|
|
*
|
|
* Worker function for RPCWinStationCheckLoopBack
|
|
****************************************************************************/
|
|
NTSTATUS WinStationCheckLoopBackWorker(
|
|
DWORD TargetLogonId,
|
|
DWORD ClientSessionId,
|
|
PWCHAR pTargetServerName,
|
|
DWORD NameSize)
|
|
{
|
|
PWINSTATION pWinStation;
|
|
ULONG Length;
|
|
NTSTATUS Status = STATUS_SUCCESS;
|
|
|
|
TRACE((hTrace,TC_ICASRV,TT_API1, "TERMSRV: WinStationCheckLoopBackWorker, ClientSessionId=%d, TargetLogonId = %d\n", ClientSessionId, TargetLogonId ));
|
|
|
|
if ( ShutdownInProgress ) {
|
|
return ( STATUS_CTX_WINSTATION_ACCESS_DENIED );
|
|
}
|
|
|
|
// Do the actual processing to call the CheckLoopBack routine here
|
|
// Use the _CheckShadowLoop function which already detects for a loop during Shadowing
|
|
|
|
Status = _CheckShadowLoop(ClientSessionId, pTargetServerName, TargetLogonId);
|
|
|
|
TRACE((hTrace,TC_ICASRV,TT_API1, "TERMSRV: WinStationCheckLoopBackWorker, Status=0x%x\n", Status ));
|
|
return Status;
|
|
|
|
}
|
|
|
|
/****************************************************************************\
|
|
*
|
|
* IsValidLoopBack first checks if the client is running on the same machine
|
|
* If client is running on the same machine, it makes a call to
|
|
* WinStationCheckLoopBackWorker to detect if this is a loopback
|
|
*
|
|
\****************************************************************************/
|
|
|
|
BOOL IsValidLoopBack(PWINSTATION pWinStation, ULONG TargetSessionId, ULONG ClientSessionId)
|
|
{
|
|
BOOL IsClientOnSameMachine_Result = FALSE ;
|
|
BOOL IsLoopBack = FALSE ;
|
|
DWORD NameSize ;
|
|
PWCHAR pComputerName = NULL;
|
|
DWORD dwComputerNameSize;
|
|
|
|
pComputerName = MemAlloc( MAX_STRING_BYTES * sizeof(WCHAR));
|
|
if (pComputerName == NULL) {
|
|
return FALSE;
|
|
}
|
|
|
|
dwComputerNameSize = MAX_STRING_BYTES ;
|
|
if (!GetComputerName(pComputerName,&dwComputerNameSize)) {
|
|
return FALSE;
|
|
}
|
|
|
|
IsClientOnSameMachine_Result = IsClientOnSameMachine(pWinStation);
|
|
|
|
// If Client is not on the same machine, then we need not go through this API to termsrv
|
|
// There are some valid cases where Client is on the same machine but there is no loopback - this call checks that
|
|
|
|
if (IsClientOnSameMachine_Result == TRUE) {
|
|
NameSize = lstrlenW(pComputerName) + 1;
|
|
IsLoopBack = WinStationCheckLoopBackWorker(
|
|
TargetSessionId,
|
|
ClientSessionId,
|
|
pComputerName,
|
|
NameSize);
|
|
}
|
|
|
|
if (pComputerName != NULL) {
|
|
MemFree( pComputerName );
|
|
pComputerName = NULL;
|
|
}
|
|
#if DBG
|
|
if (TRUE == IsLoopBack) {
|
|
DbgPrint("TermSrv - loop back detected in WinstationConnectWorker.");
|
|
}
|
|
#endif
|
|
return IsLoopBack ;
|
|
}
|
|
|
|
/*******************************************************************************
|
|
* RpcConnectCallBack
|
|
*
|
|
* Initiate connect back to TS client. For Whistler, this is Salem only call.
|
|
*
|
|
*
|
|
* Returns :
|
|
* TRUE --
|
|
* FALSE -- The operation failed. Extended error status is available
|
|
* using GetLastError.
|
|
******************************************************************************/
|
|
BOOLEAN
|
|
RpcConnectCallback(
|
|
HANDLE hServer, // needed?
|
|
DWORD *pResult,
|
|
DWORD Timeout,
|
|
ULONG AddressType, // should be one of the TDI_ADDRESS_TYPE_XXX
|
|
PBYTE pAddress, // should be one of the TDI_ADDRESS_XXX
|
|
ULONG AddressLength
|
|
)
|
|
{
|
|
NTSTATUS Status;
|
|
RPC_STATUS RpcStatus;
|
|
ICA_STACK_ADDRESS StackAddress;
|
|
PICA_STACK_ADDRESS pStackAddress = &StackAddress;
|
|
PTDI_ADDRESS_IP pTargetIpAddress;
|
|
|
|
TRACE((hTrace,TC_ICASRV,TT_API1, "TERMSRV: RpcConnectCallBack" ));
|
|
|
|
if( ShutdownInProgress )
|
|
{
|
|
*pResult = STATUS_SYSTEM_SHUTDOWN;
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// We only support IPV4 address, Need to modify tdtcp.sys to support IPv6
|
|
//
|
|
if( AddressType != TDI_ADDRESS_TYPE_IP )
|
|
{
|
|
*pResult = STATUS_NOT_SUPPORTED;
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// Extra checking, making sure we gets everything.
|
|
//
|
|
if( AddressLength != TDI_ADDRESS_LENGTH_IP )
|
|
{
|
|
*pResult = STATUS_INVALID_PARAMETER;
|
|
return FALSE;
|
|
}
|
|
|
|
ZeroMemory( &StackAddress, 0 );
|
|
|
|
|
|
//
|
|
// ??? What other security we want to apply here
|
|
//
|
|
|
|
/*
|
|
* Impersonate the client
|
|
*/
|
|
RpcStatus = RpcImpersonateClient( NULL );
|
|
if( RpcStatus != RPC_S_OK ) {
|
|
KdPrintEx((DPFLTR_TERMSRV_ID, DPFLTR_ERROR_LEVEL, "TERMSRV: RpcWinStationShadowStop: Not impersonating! RpcStatus 0x%x\n",RpcStatus));
|
|
Status = STATUS_CANNOT_IMPERSONATE;
|
|
goto done;
|
|
}
|
|
|
|
//
|
|
// For Whistler, this call is Salem rdshost.exe only, rdshost.exe is kick off by helpctr
|
|
// running under system context.
|
|
//
|
|
if( !IsCallerSystem() ) {
|
|
|
|
#if DISABLESECURITYCHECKS
|
|
|
|
//
|
|
// Private testing only, allowing administrator to make this call.
|
|
//
|
|
if( !IsCallerAdmin() ) {
|
|
RpcRevertToSelf();
|
|
Status = STATUS_ACCESS_DENIED;
|
|
goto done;
|
|
}
|
|
#else
|
|
|
|
RpcRevertToSelf();
|
|
Status = STATUS_ACCESS_DENIED;
|
|
goto done;
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
RpcRevertToSelf();
|
|
|
|
|
|
//
|
|
// winsta.h, byte 0, 1, family, 2-n address.
|
|
//
|
|
*(PUSHORT)pStackAddress = (USHORT)AddressType;
|
|
if ( AddressLength <= (sizeof(ICA_STACK_ADDRESS) - 2) ) {
|
|
//
|
|
// Refer to TDI_ADDRESS_IP, last 8 char is not use.
|
|
// Adding a timeout to pass into TD will require changes to ICA_STACK_ADDRESS
|
|
// too risky for Whister but needed on next.
|
|
//
|
|
pTargetIpAddress = (PTDI_ADDRESS_IP)pAddress;
|
|
RtlCopyMemory( &(pTargetIpAddress->sin_zero[0]), &Timeout, sizeof(Timeout) );
|
|
|
|
RtlCopyMemory( &((PCHAR)pStackAddress)[2], pAddress, AddressLength );
|
|
} else {
|
|
Status = STATUS_INVALID_PARAMETER;
|
|
goto done;
|
|
}
|
|
|
|
#if DBG
|
|
{
|
|
PULONG pData = (PULONG)pStackAddress;
|
|
PTDI_ADDRESS_IP pIpAddress = (PTDI_ADDRESS_IP)&((PCHAR)pStackAddress)[2];
|
|
|
|
DbgPrint(
|
|
"TERMSRV: Connect API: address port %u address 0x%x (%u.%u.%u.%u)\n",
|
|
ntohs(pIpAddress->sin_port),
|
|
pIpAddress->in_addr,
|
|
(pIpAddress->in_addr & 0xff000000) >> 24,
|
|
(pIpAddress->in_addr & 0x00ff0000) >> 16,
|
|
(pIpAddress->in_addr & 0x0000ff00) >> 8,
|
|
(pIpAddress->in_addr & 0x000000ff)
|
|
);
|
|
}
|
|
#endif
|
|
|
|
Status = TransferConnectionToIdleWinStation( NULL, // no listen winstation
|
|
NULL, // no Endpoint,
|
|
0, // EndpointLength,
|
|
&StackAddress );
|
|
|
|
done:
|
|
|
|
*pResult = Status;
|
|
|
|
return( NT_SUCCESS(*pResult) ? TRUE : FALSE );
|
|
}
|
|
|
|
//*************************************************************
|
|
//
|
|
// IsGinaVersionCurrent()
|
|
//
|
|
// Purpose: Loads the gina DLL and negotiates the version number
|
|
//
|
|
// Parameters: NONE
|
|
//
|
|
// Return: TRUE if gina is of current version
|
|
// FALSE if version of gina is not current
|
|
// or in case of any error
|
|
//
|
|
//*************************************************************
|
|
|
|
BOOL
|
|
IsGinaVersionCurrent()
|
|
{
|
|
HMODULE hGina = NULL;
|
|
LPWSTR wszGinaName = NULL;
|
|
PWLX_NEGOTIATE pWlxNegotiate = NULL;
|
|
DWORD dwGinaLevel = 0;
|
|
BOOL bResult = FALSE;
|
|
|
|
wszGinaName = (LPWSTR) LocalAlloc(LPTR,(MAX_PATH+1)*sizeof(WCHAR));
|
|
|
|
if(!wszGinaName)
|
|
{
|
|
//
|
|
//Not enough memory
|
|
//
|
|
return FALSE;
|
|
}
|
|
|
|
GetProfileStringW(
|
|
L"WINLOGON",
|
|
L"GinaDll",
|
|
L"msgina.dll",
|
|
wszGinaName,
|
|
MAX_PATH);
|
|
|
|
|
|
if(!_wcsicmp(L"msgina.dll",wszGinaName))
|
|
{
|
|
//
|
|
//If it is "msgina.dll",
|
|
//assume that it is a Windows native gina.
|
|
//
|
|
LocalFree(wszGinaName);
|
|
return TRUE;
|
|
}
|
|
|
|
//
|
|
//Load gina
|
|
//if we cannot load gina, assume that it is incompatible
|
|
//with TS
|
|
//
|
|
hGina = LoadLibraryW(wszGinaName);
|
|
|
|
if (hGina)
|
|
{
|
|
//
|
|
// Get the "WlxNegotiate" function pointer
|
|
//
|
|
pWlxNegotiate = (PWLX_NEGOTIATE) GetProcAddress(hGina, WLX_NEGOTIATE_NAME);
|
|
|
|
if (pWlxNegotiate)
|
|
{
|
|
//
|
|
// Negotiate a version number with the gina
|
|
//
|
|
|
|
if ( pWlxNegotiate(WLX_CURRENT_VERSION, &dwGinaLevel) &&
|
|
(dwGinaLevel == WLX_CURRENT_VERSION) )
|
|
{
|
|
bResult = TRUE;
|
|
}
|
|
|
|
}
|
|
|
|
FreeLibrary(hGina);
|
|
}
|
|
|
|
LocalFree(wszGinaName);
|
|
return bResult;
|
|
}
|
|
|
|
/*++
|
|
|
|
RPCWinStationNotifyDisconnectPipe notifies session 0 Winlogon to disconnect from the autologon named pipe
|
|
|
|
Parameters:
|
|
|
|
ClientSessionId : Session ID of the calling process
|
|
|
|
ClientProcessId : Process ID of the calling process
|
|
|
|
|
|
Returns:
|
|
|
|
TRUE if notification is successful, FALSE otherwise
|
|
|
|
--*/
|
|
|
|
BOOLEAN
|
|
RpcWinStationNotifyDisconnectPipe(
|
|
HANDLE hServer,
|
|
DWORD *pResult,
|
|
DWORD ClientLogonId,
|
|
ULONG_PTR ClientProcessId
|
|
)
|
|
{
|
|
|
|
NTSTATUS Status;
|
|
|
|
/*
|
|
* Make sure the caller is SYSTEM (WinLogon)
|
|
*/
|
|
Status = RpcCheckSystemClient( ClientLogonId );
|
|
if ( !NT_SUCCESS( Status ) ) {
|
|
*pResult = Status;
|
|
return( FALSE );
|
|
}
|
|
|
|
|
|
*pResult = WinStationNotifyDisconnectPipeWorker(
|
|
ClientLogonId,
|
|
ClientProcessId
|
|
);
|
|
|
|
return( *pResult == STATUS_SUCCESS ? TRUE : FALSE );
|
|
}
|
|
|
|
/*****************************************************************************
|
|
* WinStationNotifyDisconnectPipeWorker
|
|
*
|
|
* Worker function for RpcWinstationNotifyDisconnectPipe
|
|
****************************************************************************/
|
|
NTSTATUS WinStationNotifyDisconnectPipeWorker(
|
|
DWORD ClientLogonId,
|
|
ULONG_PTR ClientProcessId
|
|
)
|
|
|
|
{
|
|
PWINSTATION pWinStation, pTargetWinStation;
|
|
ULONG Length;
|
|
NTSTATUS Status = STATUS_SUCCESS;
|
|
WINSTATION_APIMSG DisconnectPipeMsg;
|
|
DWORD SessionZeroLogonId = 0;
|
|
|
|
TRACE((hTrace,TC_ICASRV,TT_API1, "TERMSRV: WinStationNotifyDisconnectPipeWorker, ClientLogonId=%d \n", ClientLogonId));
|
|
|
|
if ( ShutdownInProgress ) {
|
|
return ( STATUS_CTX_WINSTATION_ACCESS_DENIED );
|
|
}
|
|
|
|
/*
|
|
* Find and lock client WinStation
|
|
*/
|
|
pWinStation = FindWinStationById( ClientLogonId, FALSE );
|
|
if ( pWinStation == NULL ) {
|
|
Status = STATUS_CTX_WINSTATION_NOT_FOUND;
|
|
goto done;
|
|
}
|
|
|
|
/*
|
|
* Upper level code has verified that this RPC
|
|
* is coming from a local client with SYSTEM access.
|
|
*
|
|
* We should be able to trust the ClientProcessId from
|
|
* SYSTEM code.
|
|
*/
|
|
|
|
/*
|
|
* Ensure this is WinLogon calling
|
|
*/
|
|
if ( (HANDLE)(ULONG_PTR)ClientProcessId != pWinStation->InitialCommandProcessId ) {
|
|
Status = STATUS_ACCESS_DENIED;
|
|
ReleaseWinStation( pWinStation );
|
|
goto done;
|
|
}
|
|
|
|
ReleaseWinStation(pWinStation);
|
|
|
|
/*
|
|
* Send the Notification to disconnect the autologon Pipe to Winlogon in session 0
|
|
*/
|
|
pTargetWinStation = FindWinStationById( SessionZeroLogonId, FALSE );
|
|
if ( pTargetWinStation == NULL ) {
|
|
Status = STATUS_CTX_WINSTATION_NOT_FOUND;
|
|
goto done;
|
|
}
|
|
|
|
DisconnectPipeMsg.ApiNumber = SMWinStationNotify;
|
|
DisconnectPipeMsg.WaitForReply = FALSE;
|
|
DisconnectPipeMsg.u.DoNotify.NotifyEvent = WinStation_Notify_DisconnectPipe;
|
|
Status = SendWinStationCommand( pTargetWinStation, &DisconnectPipeMsg, 0 );
|
|
|
|
/*
|
|
* Release the winstation lock
|
|
*/
|
|
ReleaseWinStation( pTargetWinStation );
|
|
|
|
done:
|
|
/*
|
|
* Save return status in API message
|
|
*/
|
|
TRACE((hTrace,TC_ICASRV,TT_API1, "TERMSRV: WinstationNotifyDisconnectPipeWorker, Status=0x%x\n", Status ));
|
|
return Status;
|
|
|
|
}
|
|
|
|
/*++
|
|
|
|
RpcWinstationSessionInitialized informs termsrv that winlogon has created the window station and desktop for this session
|
|
|
|
Parameters:
|
|
|
|
ClientSessionId : Session ID of the calling process
|
|
|
|
ClientProcessId : Process ID of the calling process
|
|
|
|
|
|
Returns:
|
|
|
|
TRUE if everything went well. FALSE otherwise
|
|
|
|
--*/
|
|
|
|
BOOLEAN
|
|
RpcWinStationSessionInitialized(
|
|
HANDLE hServer,
|
|
DWORD *pResult,
|
|
DWORD ClientLogonId,
|
|
ULONG_PTR ClientProcessId
|
|
)
|
|
{
|
|
return( FALSE );
|
|
}
|
|
|
|
|
|
/*******************************************************************************
|
|
* RpcWinStationAutoReconnect
|
|
*
|
|
* Does atomic autoreconnection policy work
|
|
*
|
|
* ENTRY:
|
|
* IN hServer - RPC caller handle - API restricted to local use
|
|
* OUT pResult - Result code in NTSTATUS format with information class set
|
|
* IN LogonId - Session to autoreconnect
|
|
* IN flags - extra options (currently unused)
|
|
*
|
|
* EXIT:
|
|
* STATUS_SUCCESS - if we successfully autoreconnected
|
|
* STATUS_CTX_WINSTATION_BUSY - if session is already disconnected, or busy
|
|
* STATUS_ACCESS_DENIED - if the target winstation was not found
|
|
* or if the autoreconnection check failed
|
|
* STATUS_NOT_FOUND - autoreconnect info was not specified
|
|
******************************************************************************/
|
|
BOOLEAN
|
|
RpcWinStationAutoReconnect(
|
|
SERVER_HANDLE hServer,
|
|
DWORD *pResult,
|
|
DWORD LogonId,
|
|
DWORD flags
|
|
)
|
|
{
|
|
RPC_STATUS RpcStatus;
|
|
UINT LocalFlag = 0;
|
|
NTSTATUS Status = STATUS_UNSUCCESSFUL;
|
|
PWINSTATION pSourceWinStation = NULL;
|
|
PWINSTATION pTargetWinStation = NULL;
|
|
TS_AUTORECONNECTINFO autoReconnectInfo;
|
|
BYTE abClientRandom[512];
|
|
LONG cbClientRandomLen = 0;
|
|
ULONG BytesGot = 0;
|
|
DWORD SourceID;
|
|
DWORD TargetID;
|
|
|
|
TRACE((hTrace,TC_ICASRV,TT_API2,"RPC RpcWinStationAutoReconnect for %d\n",
|
|
LogonId));
|
|
|
|
//
|
|
// Only allow system caller for security reasons
|
|
//
|
|
|
|
RpcStatus = RpcImpersonateClient(NULL);
|
|
if (RpcStatus != RPC_S_OK) {
|
|
Status = STATUS_UNSUCCESSFUL;
|
|
goto rpcaccessdenied;
|
|
}
|
|
|
|
//
|
|
// Check for System caller
|
|
//
|
|
if (!IsCallerSystem()) {
|
|
RpcRevertToSelf();
|
|
Status = STATUS_ACCESS_DENIED;
|
|
goto rpcaccessdenied;
|
|
}
|
|
RpcRevertToSelf();
|
|
|
|
|
|
//
|
|
// Only allow local access for security reasons
|
|
//
|
|
RpcStatus = I_RpcBindingIsClientLocal(
|
|
0, // Active RPC call we are servicing
|
|
&LocalFlag
|
|
);
|
|
|
|
if( RpcStatus != RPC_S_OK ) {
|
|
KdPrintEx((DPFLTR_TERMSRV_ID, DPFLTR_ERROR_LEVEL,
|
|
"TERMSRV: RpcWinStationAutoReconnect:" \
|
|
"I_RpcBindingIsClientLocal() failed: 0x%x\n", RpcStatus));
|
|
Status = STATUS_UNSUCCESSFUL;
|
|
goto rpcaccessdenied;
|
|
}
|
|
|
|
//
|
|
// Do the check for local access
|
|
//
|
|
if (!LocalFlag) {
|
|
Status = (DWORD)STATUS_INVALID_PARAMETER;
|
|
goto rpcaccessdenied;
|
|
}
|
|
|
|
pSourceWinStation = FindWinStationById( LogonId, FALSE);
|
|
if (pSourceWinStation == NULL) {
|
|
|
|
TRACE((hTrace,TC_ICASRV,TT_ERROR,
|
|
"RpcWinStationAutoReconnect source session not found: %d\n",
|
|
LogonId));
|
|
|
|
Status = STATUS_ACCESS_DENIED;
|
|
goto badconnectsource;
|
|
}
|
|
|
|
if (pSourceWinStation->Terminating ||
|
|
pSourceWinStation->StateFlags & WSF_ST_WINSTATIONTERMINATE ||
|
|
!pSourceWinStation->WinStationName[0]) {
|
|
|
|
TRACE((hTrace,TC_ICASRV,TT_ERROR,
|
|
"RpcWinStationAutoReconnect source session disconnected %d\n",
|
|
LogonId));
|
|
|
|
Status = STATUS_ACCESS_DENIED;
|
|
goto badconnectsource;
|
|
}
|
|
|
|
if (pSourceWinStation->Flags) {
|
|
Status = STATUS_CTX_WINSTATION_BUSY;
|
|
goto badconnectsource;
|
|
}
|
|
|
|
//
|
|
// Check if Winstation logons are disabled and prevent ARC from
|
|
// happening in this case. Bug#532238
|
|
//
|
|
if (GetProfileInt(APPLICATION_NAME, WINSTATIONS_DISABLED, 0) == 1) {
|
|
|
|
//
|
|
// Fail the ARC and tell the client
|
|
//
|
|
Status = STATUS_ACCESS_DENIED;
|
|
pSourceWinStation->pWsx->pWsxSendAutoReconnectStatus(
|
|
pSourceWinStation->pWsxContext,
|
|
0,
|
|
FALSE); //stack lock held
|
|
|
|
ReleaseWinStation(pSourceWinStation);
|
|
pSourceWinStation = NULL;
|
|
goto done;
|
|
}
|
|
|
|
//
|
|
// Get the client autoreconnect info
|
|
//
|
|
if (pSourceWinStation->pWsx &&
|
|
pSourceWinStation->pWsx->pWsxEscape) {
|
|
Status = pSourceWinStation->pWsx->pWsxEscape(
|
|
pSourceWinStation->pWsxContext,
|
|
GET_CS_AUTORECONNECT_INFO,
|
|
NULL,
|
|
0,
|
|
&autoReconnectInfo,
|
|
sizeof(autoReconnectInfo),
|
|
&BytesGot);
|
|
}
|
|
TRACE((hTrace,TC_ICASRV,TT_API3,
|
|
"RpcWinStationAutoReconnect get GET_CS_AUTORECONNECT_INFO: 0x%x\n",
|
|
Status));
|
|
|
|
if (0 == BytesGot ) {
|
|
|
|
//
|
|
// Skip the rest of the processing if we didn't get any arc info
|
|
//
|
|
|
|
//
|
|
// This is not strictly an error condition, the client could have
|
|
// just not sent up any autoreconnect info
|
|
//
|
|
|
|
Status = STATUS_NOT_FOUND;
|
|
goto badconnectsource;
|
|
}
|
|
|
|
//
|
|
// Get the client random
|
|
//
|
|
if (NT_SUCCESS(Status)) {
|
|
if (pSourceWinStation->pWsx &&
|
|
pSourceWinStation->pWsx->pWsxEscape) {
|
|
Status = pSourceWinStation->pWsx->pWsxEscape(
|
|
pSourceWinStation->pWsxContext,
|
|
GET_CLIENT_RANDOM,
|
|
NULL,
|
|
0,
|
|
&abClientRandom,
|
|
sizeof(abClientRandom),
|
|
&BytesGot);
|
|
|
|
TRACE((hTrace,TC_ICASRV,TT_API3,
|
|
"RpcWinStationAutoReconnect get GET_CLIENT_RANDOM: 0x%x\n",
|
|
Status));
|
|
|
|
}
|
|
}
|
|
|
|
//
|
|
// Source winstation locked here
|
|
// Target not set yet
|
|
//
|
|
|
|
if (NT_SUCCESS(Status)) {
|
|
|
|
cbClientRandomLen = BytesGot;
|
|
|
|
//
|
|
// Flag the winstation as part of the autoreconnection
|
|
// to prevent a race where it might be reconnected somewhere
|
|
// else while we unlock it
|
|
//
|
|
pSourceWinStation->Flags |= WSF_AUTORECONNECTING;
|
|
SourceID = pSourceWinStation->LogonId;
|
|
|
|
//
|
|
// Unlock the winstation because we are about to try to
|
|
// lock the target winstation
|
|
//
|
|
UnlockWinStation(pSourceWinStation);
|
|
|
|
//
|
|
// Use the autoreconnect info to find the target winstation
|
|
// Returns a LOCKED winstation on success
|
|
//
|
|
pTargetWinStation = GetWinStationFromArcInfo(
|
|
(PBYTE)abClientRandom,
|
|
cbClientRandomLen,
|
|
(PTS_AUTORECONNECTINFO)&autoReconnectInfo
|
|
);
|
|
|
|
if (pTargetWinStation) {
|
|
|
|
//
|
|
// Check if the target is busy or if AutoReconnect is disallowed
|
|
//
|
|
if (pTargetWinStation->Flags || pTargetWinStation->fDisallowAutoReconnect) {
|
|
|
|
if (pTargetWinStation->fDisallowAutoReconnect) {
|
|
Status = STATUS_ACCESS_DENIED;
|
|
} else {
|
|
Status = STATUS_CTX_WINSTATION_BUSY;
|
|
}
|
|
|
|
ReleaseWinStation(pTargetWinStation);
|
|
pTargetWinStation = NULL;
|
|
|
|
RelockWinStation(pSourceWinStation);
|
|
|
|
//
|
|
// Tell the client that ARC failed
|
|
//
|
|
if (pSourceWinStation->pWsx &&
|
|
pSourceWinStation->pWsx->pWsxSendAutoReconnectStatus) {
|
|
|
|
pSourceWinStation->pWsx->pWsxSendAutoReconnectStatus(
|
|
pSourceWinStation->pWsxContext,
|
|
0,
|
|
FALSE); //stack lock held
|
|
}
|
|
|
|
pSourceWinStation->Flags &= ~WSF_AUTORECONNECTING;
|
|
ReleaseWinStation(pSourceWinStation);
|
|
|
|
goto done;
|
|
}
|
|
|
|
//
|
|
// Success! We found a winstation to autoreconnect to
|
|
// flag it and unlock it so we can make the connect call
|
|
//
|
|
TargetID = pTargetWinStation->LogonId;
|
|
pTargetWinStation->Flags |= WSF_AUTORECONNECTING;
|
|
UnlockWinStation(pTargetWinStation);
|
|
|
|
}
|
|
else {
|
|
|
|
TRACE((hTrace,TC_ICASRV,TT_ERROR,
|
|
"TERMSRV: GetWinStationFromArcInfo failed\n"));
|
|
|
|
//
|
|
// Relock the source and cancel the autoreconnect flag
|
|
//
|
|
if (RelockWinStation(pSourceWinStation)) {
|
|
|
|
//
|
|
// Tell the client that ARC failed
|
|
//
|
|
if (pSourceWinStation->pWsx &&
|
|
pSourceWinStation->pWsx->pWsxSendAutoReconnectStatus) {
|
|
|
|
Status = pSourceWinStation->pWsx->pWsxSendAutoReconnectStatus(
|
|
pSourceWinStation->pWsxContext,
|
|
0,
|
|
FALSE); //stack lock held
|
|
}
|
|
}
|
|
else {
|
|
//
|
|
// This is a failure path anyway so it doesn't
|
|
// matter if the winstation was deleted. It just means
|
|
// we can't send status to the client
|
|
//
|
|
}
|
|
|
|
pSourceWinStation->Flags &= ~WSF_AUTORECONNECTING;
|
|
ReleaseWinStation(pSourceWinStation);
|
|
pSourceWinStation = NULL;
|
|
|
|
Status = STATUS_ACCESS_DENIED;
|
|
|
|
goto done;
|
|
}
|
|
}
|
|
else {
|
|
goto badconnectsource;
|
|
}
|
|
|
|
//
|
|
// At this point neither winstation is locked
|
|
//
|
|
|
|
if (NT_SUCCESS(Status)) {
|
|
|
|
ASSERT(pTargetWinStation);
|
|
|
|
//
|
|
// Trigger an autoreconnection from Source->Target
|
|
//
|
|
TRACE((hTrace,TC_ICASRV,TT_API1,
|
|
"RpcWinStationAutoReconnect doing ARC from %d to %d\n",
|
|
SourceID, TargetID));
|
|
|
|
//
|
|
// Do the reconnection
|
|
//
|
|
// The autoreconnect flag allows the connect worker to properly
|
|
// handle the WSF_AUTORECONNECTING flag whose purpose is to
|
|
// prevent a race where the sessions could be reconnected while
|
|
// the winstations are unlocked
|
|
//
|
|
Status = WinStationConnectWorker(
|
|
LOGONID_CURRENT,
|
|
TargetID,
|
|
SourceID,
|
|
NULL,
|
|
0,
|
|
TRUE,
|
|
TRUE //flag that this is an autoreconnection
|
|
);
|
|
|
|
//
|
|
// Relock and then release the source
|
|
//
|
|
if (!RelockWinStation(pSourceWinStation)) {
|
|
Status = STATUS_CTX_WINSTATION_NOT_FOUND;
|
|
}
|
|
pSourceWinStation->Flags &= ~WSF_AUTORECONNECTING;
|
|
ReleaseWinStation(pSourceWinStation);
|
|
pSourceWinStation = NULL;
|
|
|
|
//
|
|
// Relock and then release the target
|
|
//
|
|
if (!RelockWinStation(pTargetWinStation)) {
|
|
Status = STATUS_CTX_WINSTATION_NOT_FOUND;
|
|
}
|
|
pTargetWinStation->Flags &= ~WSF_AUTORECONNECTING;
|
|
ReleaseWinStation(pTargetWinStation);
|
|
pTargetWinStation = NULL;
|
|
|
|
|
|
TRACE((hTrace,TC_ICASRV,TT_API1,
|
|
"RpcWinStationAutoReconnect ARC ConnectWorker result: 0x%x\n",
|
|
Status));
|
|
|
|
if (NT_SUCCESS(Status)) {
|
|
|
|
//
|
|
// Call succeeded AND we autoreconnected
|
|
//
|
|
TRACE((hTrace,TC_ICASRV,TT_API1,
|
|
"RpcWinStationAutoReconnect ARC Succeeded\n"));
|
|
}
|
|
|
|
}
|
|
goto done;
|
|
|
|
badconnectsource:
|
|
if (pSourceWinStation) {
|
|
ReleaseWinStation(pSourceWinStation);
|
|
}
|
|
rpcaccessdenied:
|
|
done:
|
|
*pResult = Status;
|
|
|
|
return NT_SUCCESS(Status);
|
|
}
|