/************************************************************************* * * winsta.c * * Client side APIs for window stations objects * * Copyright Microsoft Corporation, 1998 * *************************************************************************/ /* * Includes */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* * Include the RPC generated common header */ #include "tsrpc.h" #include "rpcwire.h" #ifdef NTSDDEBUG #define NTSDDBGPRINT(x) DbgPrint x #else #define NTSDDBGPRINT(x) #endif #if DBG #define VERIFY(x) ASSERT(x) // we already have ASSERT; #else #define VERIFY(x) (x) #endif #if DBG ULONG DbgPrint( PCH Format, ... ); #define DBGPRINT(x) DbgPrint x #if DBGTRACE #define TRACE0(x) DbgPrint x #define TRACE1(x) DbgPrint x #else #define TRACE0(x) #define TRACE1(x) #endif #else #define DBGPRINT(x) #define TRACE0(x) #define TRACE1(x) #endif /* * This handle is returned when there is no terminal * server present on the system. (Non-Hydra) */ #define RPC_HANDLE_NO_SERVER (HANDLE)IntToPtr( 0xFFFFFFFD ) /* * Private Procedures defined here */ BOOLEAN DllInitialize(IN PVOID, IN ULONG, IN PCONTEXT OPTIONAL); RPC_STATUS RpcWinStationBind( LPWSTR pszUuid, LPWSTR pszProtocolSequence, LPWSTR pszNetworkAddress, LPWSTR pszEndPoint, LPWSTR pszOptions, RPC_BINDING_HANDLE *pHandle ); RPC_STATUS RpcWinStationBindSecure( LPWSTR pszUuid, LPWSTR pszProtocolSequence, LPWSTR pszNetworkAddress, LPWSTR pszEndPoint, LPWSTR pszOptions, RPC_BINDING_HANDLE *pHandle ); BOOLEAN RpcLocalAutoBind( VOID ); /* * Global data */ // Critical section to protect the handlelist from multiple threads RTL_CRITICAL_SECTION WstHandleLock; /* * RPC program identifier and security options */ LPWSTR pszUuid = L"5ca4a760-ebb1-11cf-8611-00a0245420ed"; // From ICAAPI.IDL LPWSTR pszOptions = L"Security=Impersonation Dynamic False"; /* * RPC over LPC binding information */ LPWSTR pszProtocolSequence = L"ncalrpc"; // RPC over LPC LPWSTR pszEndPoint = L"IcaApi"; /* * RPC over named pipes binding information */ LPWSTR pszRemoteProtocolSequence = L"ncacn_np"; // RPC over Named pipes LPWSTR pszRemoteEndPoint = L"\\pipe\\Ctx_WinStation_API_service"; /* * other internal Procedures used (not defined here) */ VOID UnicodeToAnsi( CHAR *, ULONG, WCHAR * ); VOID AnsiToUnicode( WCHAR *, ULONG, CHAR * ); VOID PdConfig2U2A( PPDCONFIG2A, PPDCONFIG2W ); VOID PdConfig2A2U( PPDCONFIG2W, PPDCONFIG2A ); VOID PdParamsU2A( PPDPARAMSA, PPDPARAMSW ); VOID PdParamsA2U( PPDPARAMSW, PPDPARAMSA ); VOID WdConfigU2A( PWDCONFIGA, PWDCONFIGW ); VOID WdConfigA2U( PWDCONFIGW, PWDCONFIGA ); VOID WinStationCreateU2A( PWINSTATIONCREATEA, PWINSTATIONCREATEW ); VOID WinStationCreateA2U( PWINSTATIONCREATEW, PWINSTATIONCREATEA ); VOID WinStationConfigU2A( PWINSTATIONCONFIGA, PWINSTATIONCONFIGW ); VOID WinStationConfigA2U( PWINSTATIONCONFIGW, PWINSTATIONCONFIGA ); VOID WinStationPrinterU2A( PWINSTATIONPRINTERA, PWINSTATIONPRINTERW ); VOID WinStationPrinterA2U( PWINSTATIONPRINTERW, PWINSTATIONPRINTERA ); VOID WinStationInformationU2A( PWINSTATIONINFORMATIONA, PWINSTATIONINFORMATIONW ); VOID WinStationInformationA2U( PWINSTATIONINFORMATIONW, PWINSTATIONINFORMATIONA ); VOID WinStationClientU2A( PWINSTATIONCLIENTA, PWINSTATIONCLIENTW ); VOID WinStationProductIdU2A( PWINSTATIONPRODIDA, PWINSTATIONPRODIDW ); ULONG CheckUserBuffer(WINSTATIONINFOCLASS, PVOID, ULONG, PVOID *, PULONG, BOOLEAN *); BOOLEAN CloseContextHandle(HANDLE *pHandle, DWORD *pdwResult); /* * Check to see that caller does not hold the loader critsec. * WinStation APIs must NOT be called while holding the loader critsec * since deadlock may occur. */ #define CheckLoaderLock() \ ASSERT( NtCurrentTeb()->ClientId.UniqueThread != \ ((PRTL_CRITICAL_SECTION)(NtCurrentPeb()->LoaderLock))->OwningThread ); /* * Handle the SERVERNAME_CURRENT for auto local binding. */ #define HANDLE_CURRENT_BINDING( hServer ) \ CheckLoaderLock(); \ if( hServer == SERVERNAME_CURRENT ) { \ if( IcaApi_IfHandle == NULL ) { \ if( !RpcLocalAutoBind() ) { \ return FALSE; \ } \ } \ hServer = IcaApi_IfHandle; \ } \ if( hServer == RPC_HANDLE_NO_SERVER ) { \ SetLastError( ERROR_APP_WRONG_OS ); \ return FALSE; \ } #define HANDLE_CURRENT_BINDING_BUFFER( hServer, pBuffer ) \ CheckLoaderLock(); \ if( hServer == SERVERNAME_CURRENT ) { \ if( IcaApi_IfHandle == NULL ) { \ if( !RpcLocalAutoBind() ) { \ if (pBuffer != NULL) { \ LocalFree(pBuffer); \ } \ return FALSE; \ } \ } \ hServer = IcaApi_IfHandle; \ } \ if( hServer == RPC_HANDLE_NO_SERVER ) { \ if (pBuffer != NULL) { \ LocalFree(pBuffer); \ } \ SetLastError( ERROR_APP_WRONG_OS ); \ return FALSE; \ } /* * Handle the SERVERNAME_CURRENT for auto local binding that * allows the RPC_HANDLE_NO_SERVER handle. */ #define HANDLE_CURRENT_BINDING_NO_SERVER( hServer ) \ CheckLoaderLock(); \ if( hServer == SERVERNAME_CURRENT ) { \ if( IcaApi_IfHandle == NULL ) { \ if( !RpcLocalAutoBind() ) { \ return FALSE; \ } \ } \ hServer = IcaApi_IfHandle; \ } /**************************************************************************** * * DllInitialize * * Function is called when the DLL is loaded. The only work we do here * is initialize our CriticalSection. * * ENTRY: * * DllHandle * Loaded handle to our DLL image * * Reason * Reason for notifying us * * Context * Reason specific parameter from NT * ****************************************************************************/ BOOLEAN DllInitialize( IN PVOID DllHandle, IN ULONG Reason, IN PCONTEXT Context OPTIONAL ) { BOOLEAN rc; DWORD Result; RPC_STATUS Status; BOOLEAN Success; NTSTATUS ntStatus; static BOOLEAN sbIniOK = FALSE; (VOID)Context; Success = TRUE; switch ( Reason ) { case DLL_PROCESS_ATTACH: /* // some instrumentation for catching the bug # // 145378 TRACKING: Winsta.dll getting loaded into csrss DBGPRINT(("Checking if winsta is being loaded into csrss.exe\n")); if(NULL != wcsstr(GetCommandLine(), TEXT("csrss.exe"))) { DBGPRINT(("**** will break because csrss.exe loaded winsta.dll ***** \n")); DebugBreak(); } */ ntStatus = RtlInitializeCriticalSection( &WstHandleLock ); IcaApi_IfHandle = NULL; if (!NT_SUCCESS(ntStatus)) { Success = FALSE; }else { sbIniOK = TRUE; } break; case DLL_PROCESS_DETACH: if (sbIniOK) { if( (IcaApi_IfHandle != NULL) && (IcaApi_IfHandle != RPC_HANDLE_NO_SERVER) ) { HANDLE hTmp = InterlockedExchangePointer(&IcaApi_IfHandle,NULL); if( hTmp && !IcaApi_IfHandle ) { // // making RPC call in DLL_PROCESS_DETACH is bad. // threrefore we cannot do CloseContextHandle(&hTmp, &Result); // lets just call RpcSsDestroyClientContext, which will cause // rundown to run at server end. RpcTryExcept { RpcSsDestroyClientContext(&hTmp); } RpcExcept(I_RpcExceptionFilter(RpcExceptionCode())) { ASSERT(FALSE); } RpcEndExcept } } RtlDeleteCriticalSection( &WstHandleLock ); } break; default: break; } return Success; } /***************************************************************************** * * RpcWinStationBind * * Perform the RPC binding sequence. * * This is an internal function. * * ENTRY: * Param1 (input/output) * Comments * * EXIT: * ERROR_SUCCESS - no error * ****************************************************************************/ RPC_STATUS RpcWinStationBind( LPWSTR pszUuid, LPWSTR pszProtocolSequence, LPWSTR pszNetworkAddress, LPWSTR pszEndPoint, LPWSTR pszOptions, RPC_BINDING_HANDLE *pHandle ) { RPC_STATUS Status; LPWSTR pszString = NULL; /* * Compose the binding string using the helper routine * and our protocol sequence, security options, UUID, etc. */ Status = RpcStringBindingCompose( pszUuid, pszProtocolSequence, pszNetworkAddress, pszEndPoint, pszOptions, &pszString ); if( Status != RPC_S_OK ) { DBGPRINT(("Error %d in RpcStringBindingCompose\n",Status)); return( Status ); } /* * Now generate the RPC binding from the cononical RPC * binding string. */ Status = RpcBindingFromStringBinding( pszString, pHandle ); if( Status != RPC_S_OK ) { DBGPRINT(("Error %d in RpcBindingFromStringBinding\n",Status)); RpcStringFree( &pszString ); return( Status ); } /* * Free the memory returned from RpcStringBindingCompose() */ RpcStringFree( &pszString ); return( Status ); } BOOL PrepareServerSPN( LPWSTR pszNetworkAddress, LPWSTR *wszServerSPN) { PWKSTA_INFO_100 pwi; NET_API_STATUS net_status; LPWSTR wszTemplate = L"%s\\%s$"; *wszServerSPN = NULL; net_status = NetWkstaGetInfo( pszNetworkAddress, 100, (LPBYTE *)&pwi); if(net_status == NERR_Success) { if(pwi->wki100_computername && pwi->wki100_langroup) { *wszServerSPN = (LPWSTR)LocalAlloc(LPTR, (wcslen(wszTemplate)+wcslen(pwi->wki100_computername)+wcslen(pwi->wki100_langroup))*sizeof(WCHAR)); if(*wszServerSPN) { swprintf(*wszServerSPN, wszTemplate, pwi->wki100_langroup, pwi->wki100_computername); NetApiBufferFree(pwi); return TRUE; } } NetApiBufferFree(pwi); } return FALSE; } /***************************************************************************** * * RpcWinStationBindSecure * * Performs the RPC binding sequence. * It also specifies authentication level and SSP used. * * This is an internal function. * * ENTRY: * * Comments * * EXIT: * ERROR_SUCCESS - no error * ****************************************************************************/ RPC_STATUS RpcWinStationBindSecure( LPWSTR pszUuid, LPWSTR pszProtocolSequence, LPWSTR pszNetworkAddress, LPWSTR pszEndPoint, LPWSTR pszOptions, RPC_BINDING_HANDLE *pHandle ) { RPC_STATUS Status; RPC_SECURITY_QOS qos; LPWSTR wszServerSPN = NULL; Status = RpcWinStationBind( pszUuid, pszProtocolSequence, pszNetworkAddress, pszEndPoint, pszOptions, pHandle); if( Status != RPC_S_OK ) { DBGPRINT(("Error %d in RpcWinStationBind\n",Status)); return Status; } qos.Capabilities = RPC_C_QOS_CAPABILITIES_MUTUAL_AUTH; qos.IdentityTracking = RPC_C_QOS_IDENTITY_DYNAMIC; qos.ImpersonationType = RPC_C_IMP_LEVEL_IMPERSONATE; qos.Version = RPC_C_SECURITY_QOS_VERSION; if(PrepareServerSPN(pszNetworkAddress,&wszServerSPN)) { Status = RpcBindingSetAuthInfoEx( *pHandle, wszServerSPN, RPC_C_AUTHN_LEVEL_PKT_PRIVACY, RPC_C_AUTHN_GSS_NEGOTIATE, NULL, RPC_C_AUTHZ_NAME, &qos); LocalFree(wszServerSPN); } else { Status = RpcBindingSetAuthInfoEx( *pHandle, pszNetworkAddress, RPC_C_AUTHN_LEVEL_PKT_PRIVACY, RPC_C_AUTHN_GSS_NEGOTIATE, NULL, RPC_C_AUTHZ_NAME, &qos); } if(Status != RPC_S_OK) { DBGPRINT(("Error %d in RpcBindingSetAuthInfoEx\n",Status)); RpcBindingFree(pHandle); return Status; } return Status; } /***************************************************************************** * * WinStationOpenLocalServer (Private) * * Connect to the local RPC over LPC server for WINSTATION API's. * * On non-terminal server machines, it returns a handle that allows * a subset of the DLL's functions to operate locally. * * ENTRY: * * EXIT: * ****************************************************************************/ HANDLE WINAPI WinStationOpenLocalServer( ) { RPC_STATUS Status; DWORD Result; BOOLEAN rc; RPC_BINDING_HANDLE RpcHandle; HANDLE ContextHandle; RPC_SECURITY_QOS RpcSecQos; if( !(USER_SHARED_DATA->SuiteMask & (1 << TerminalServer)) ) { return( RPC_HANDLE_NO_SERVER ); } /* * Do the RPC bind to the local server. * * We use explict binding handles since we want * to allow a single application to talk to multiple * WinFrame servers at a time. * * NOTE: We use the auto handle from the .ACF file * for our local connections. */ Status = RpcWinStationBind( NULL, pszProtocolSequence, NULL, // ServerName pszEndPoint, pszOptions, &RpcHandle ); if( Status != RPC_S_OK ) { SetLastError( RtlNtStatusToDosError(RPC_NT_SERVER_UNAVAILABLE) ); return( NULL ); } // //Demand mutual authentication //We only want to work with service running by LocalSystem // RpcSecQos.Capabilities= RPC_C_QOS_CAPABILITIES_MUTUAL_AUTH; RpcSecQos.IdentityTracking= RPC_C_QOS_IDENTITY_DYNAMIC; RpcSecQos.ImpersonationType= RPC_C_IMP_LEVEL_IMPERSONATE; RpcSecQos.Version= RPC_C_SECURITY_QOS_VERSION; Status= RpcBindingSetAuthInfoExW(RpcHandle, L"NT AUTHORITY\\SYSTEM", RPC_C_AUTHN_LEVEL_PKT_PRIVACY, RPC_C_AUTHN_WINNT, NULL, RPC_C_AUTHZ_NONE, &RpcSecQos); if( Status != RPC_S_OK ) { RpcBindingFree( &RpcHandle ); SetLastError( Status ); DBGPRINT(("RpcBindingSetAuthInfoExW failed %d\n", Status)); return( NULL ); } // // Get a context handle from the server so it can // manage the connections state // // NOTE: This can fail due to authentication failure. // RpcTryExcept { rc = RpcWinStationOpenServer( RpcHandle, &Result, &ContextHandle ); } RpcExcept(I_RpcExceptionFilter(RpcExceptionCode())) { Result = RpcExceptionCode(); rc = FALSE; #if DBG if ( Result != RPC_S_SERVER_UNAVAILABLE ) { DBGPRINT(("RPC Exception %d\n",Result)); } #endif } RpcEndExcept if( rc ) { // // Close the server binding handle now that we // have a client specific context handle // RpcBindingFree( &RpcHandle ); return( (HANDLE)ContextHandle ); } else { #if DBG if ( Result != RPC_S_SERVER_UNAVAILABLE ) { DBGPRINT(("WinStationOpenLocalServer: Error %d getting context handle\n",Result)); } #endif RpcBindingFree( &RpcHandle ); SetLastError( Result ); return( NULL ); } } /***************************************************************************** * * RpcLocalAutoBind * * Handle auto binding to the local server. * * ENTRY: * * EXIT: * TRUE - Success * FALSE - Error, Use GetLastError() to retrieve reason. * ****************************************************************************/ BOOLEAN RpcLocalAutoBind(void) { if( IcaApi_IfHandle == NULL ) { DWORD Result; HANDLE hTmp = WinStationOpenLocalServer(); if( hTmp == NULL ) { SetLastError( RPC_S_INVALID_BINDING ); return( FALSE ); } InterlockedCompareExchangePointer(&IcaApi_IfHandle,hTmp,NULL); if(IcaApi_IfHandle != hTmp) { CloseContextHandle(&hTmp, &Result); } } return( TRUE ); } /***************************************************************************** * * WinStationOpenServerA * * Connect to a WinFrame computer in order to issue * ICA API's * * NULL for machine name means local system. * * ENTRY: * Machine (input) * Name of WinFrame computer to connect to * * EXIT: * handle to server (or NULL on error) * ****************************************************************************/ HANDLE WINAPI WinStationOpenServerA( LPSTR pServerName ) { HANDLE hServer; ULONG NameLength; PWCHAR pServerNameW = NULL; if( pServerName == NULL ) { return( WinStationOpenServerW( NULL ) ); } NameLength = strlen( pServerName ) + 1; pServerNameW = LocalAlloc( 0, NameLength * sizeof(WCHAR) ); if( pServerNameW == NULL ) { SetLastError( ERROR_NOT_ENOUGH_MEMORY ); return( NULL ); } AnsiToUnicode( pServerNameW, NameLength*sizeof(WCHAR), pServerName ); hServer = WinStationOpenServerW( pServerNameW ); LocalFree( pServerNameW ); return( hServer ); } /***************************************************************************** * * WinStationOpenServerW * * Connect to a WinFrame computer in order to issue * ICA API's * * NULL for machine name means local system. * * ENTRY: * Machine (input) * Name of WinFrame computer to connect to * * EXIT: * handle to server (or NULL on error) * ****************************************************************************/ HANDLE WINAPI WinStationOpenServerW( LPWSTR pServerName ) { DWORD Result = ERROR_ACCESS_DENIED; BOOLEAN rc; RPC_STATUS Status; RPC_BINDING_HANDLE RpcHandle; HANDLE ContextHandle; BOOL bTryAgain = TRUE; /* * If the server name is NULL, attempt to open * the local machines ICA server over LPC. */ if( pServerName == NULL ) { return( WinStationOpenLocalServer() ); } /* * Do the RPC bind to the server. * * We use explict binding handles since we want * to allow a single application to talk to multiple * WinFrame servers at a time. */ Status = RpcWinStationBindSecure( pszUuid, pszRemoteProtocolSequence, pServerName, pszRemoteEndPoint, pszOptions, &RpcHandle ); if( Status != RPC_S_OK ) { SetLastError( RtlNtStatusToDosError(RPC_NT_SERVER_UNAVAILABLE) ); return( NULL ); } for(;;) { // // Get a context handle from the server so it can // manage the connections state // // NOTE: This can fail due to authentication failure. // RpcTryExcept { rc = RpcWinStationOpenServer( RpcHandle, &Result, &ContextHandle ); } RpcExcept(I_RpcExceptionFilter(RpcExceptionCode())) { Result = RpcExceptionCode(); rc = FALSE; DBGPRINT(("RPC Exception %d\n",Result)); } RpcEndExcept // // Close the server binding handle now that we // have a client specific context handle // RpcBindingFree( &RpcHandle ); //RPC_S_UNKNOWN_AUTHN_SERVICE - it's an old server and does not use Kerberos //for authentication //We get ERROR_ACCESS_DENIED if the client runs under wrong (local) user account, //but we can still succeed if we drop authentication if we have a net session //opened on the target computer if( !rc && (Result == RPC_S_UNKNOWN_AUTHN_SERVICE || Result == ERROR_ACCESS_DENIED) && bTryAgain ) { bTryAgain = FALSE; //Try again with no security set Status = RpcWinStationBind( pszUuid, pszRemoteProtocolSequence, pServerName, pszRemoteEndPoint, pszOptions, &RpcHandle ); if(Status == RPC_S_OK){ DBGPRINT(("Using nonsecure connection!!!\n")); continue; } } break; } if( rc ) { return( (HANDLE)ContextHandle ); } else { DBGPRINT(("WinStationOpenServerW: Error %d getting context handle\n",Result)); SetLastError( Result ); return( NULL ); } } /***************************************************************************** * * WinStationCloseServer * * Close a connection to a WinFrame computer. * * ENTRY: * hServer (input) * Handle to close * * EXIT: * ERROR_SUCCESS - no error * ****************************************************************************/ BOOLEAN WINAPI WinStationCloseServer( HANDLE hServer ) { BOOLEAN rc; DWORD Result; // // Do not close the implicit handles // if( (hServer == IcaApi_IfHandle) || (hServer == RPC_HANDLE_NO_SERVER) ) { return( TRUE ); } // // Send the close to the remote side so it clean // cleanup its context // rc = CloseContextHandle(&hServer, &Result); if( rc ) { return( TRUE ); } else { DBGPRINT(("WinStationCloseServer: Error %d closing context handle\n",Result)); SetLastError( Result ); return( FALSE ); } } /***************************************************************************** * * MIDL_user_allocate * * Handles RPC's allocation of argument data structures * * ENTRY: * Param1 (input/output) * Comments * * EXIT: * ERROR_SUCCESS - no error * ****************************************************************************/ void __RPC_FAR * __RPC_USER MIDL_user_allocate( size_t Size ) { return( LocalAlloc(LMEM_FIXED,Size) ); } /***************************************************************************** * * MIDL_user_allocate * * Handles RPC's de-allocation of argument data structures * * ENTRY: * Param1 (input/output) * Comments * * EXIT: * ERROR_SUCCESS - no error * ****************************************************************************/ void __RPC_USER MIDL_user_free( void __RPC_FAR *p ) { LocalFree( p ); } /***************************************************************************** * * WinStationServerPing * * Ping the given WinFrame server handle to see if it is still up. * * ENTRY: * hServer (input) * Open RPC server handle * * EXIT: * ERROR_SUCCESS - no error * ****************************************************************************/ BOOLEAN WINAPI WinStationServerPing( HANDLE hServer ) { DWORD Result; BOOLEAN rc; HANDLE_CURRENT_BINDING( hServer ); /* * Do the RPC * * NOTE: This must be done under an RPC exception handler, * since the RPC runtime code throws exceptions if * network errors occur, or the server can not be * reached. */ RpcTryExcept { rc = RpcIcaServerPing( hServer, &Result ); Result = RtlNtStatusToDosError( Result ); if( !rc ) SetLastError(Result); TRACE0(("RpcIcaServerPing rc 0x%x, Result 0x%x\n",rc, Result)); } RpcExcept(I_RpcExceptionFilter(RpcExceptionCode())) { Result = RpcExceptionCode(); SetLastError( Result ); DBGPRINT(("RPC Exception %d\n",Result)); rc = FALSE; } RpcEndExcept return( rc ); } /******************************************************************************* * * WinStationEnumerateA (ANSI stub) * * Returns a list of window station objects. * * ENTRY: * * see WinStationEnumerateW * * EXIT: * * see WinStationEnumerateW, plus * * ERROR_NOT_ENOUGH_MEMORY - the LocalAlloc failed * ******************************************************************************/ BOOLEAN WINAPI WinStationEnumerateA( HANDLE hServer, PLOGONIDA *ppLogonId, PULONG pEntries ) { PLOGONIDW pLogonIdW, pLogonIdBaseW; PLOGONIDA pLogonIdA; BOOLEAN Status; ULONG Count; /* * Call UNICODE WinStationEnumerateW first. */ *pEntries = 0; *ppLogonId = NULL; Status = WinStationEnumerateW( hServer, &pLogonIdBaseW, &Count ); if ( !Status ) goto badenumerate; /* * Allocate buffer and perform conversion from UNICODE to ANSI. */ if ( !(pLogonIdA = (PLOGONIDA)LocalAlloc( 0, Count * sizeof(LOGONIDA) )) ) { SetLastError(ERROR_NOT_ENOUGH_MEMORY); Status = FALSE; goto nomemory; } *pEntries = Count; *ppLogonId = pLogonIdA; for ( pLogonIdW = pLogonIdBaseW; Count; Count-- ) { pLogonIdA->LogonId = pLogonIdW->LogonId; UnicodeToAnsi( pLogonIdA->WinStationName, sizeof(WINSTATIONNAMEA), pLogonIdW->WinStationName ); pLogonIdA->State = pLogonIdW->State; pLogonIdA++; pLogonIdW++; } nomemory: /* * Free the UNICODE enumerate buffer. */ WinStationFreeMemory( pLogonIdBaseW ); badenumerate: return(Status); } /******************************************************************************* * * WinStationEnumerateW (UNICODE) * * Returns a list of window station objects. * * ENTRY: * hServer (input) * Server handle * ppLogonId (output) * Points to a pointer to a buffer to receive the enumeration results, * which are returned as an array of LOGONID structures. The buffer is * allocated within this API and is disposed of using * WinStationFreeMemory. * pEntries (output) * Points to a variable specifying the number of entries read. * * EXIT: * * TRUE -- The enumerate operation succeeded. * * FALSE -- The operation failed. Extended error status is available * using GetLastError. * ******************************************************************************/ BOOLEAN WINAPI WinStationEnumerateW( HANDLE hServer, PLOGONIDW *ppLogonId, PULONG pEntries ) { DWORD Result; BOOLEAN rc; ULONG LogonIdCount = 50; PLOGONIDW pLogonId, pLogonIdTemp; ULONG Length; ULONG Index = 0; ULONG ByteCount = 0; HANDLE_CURRENT_BINDING( hServer ); *pEntries = 0; *ppLogonId = NULL; Length = LogonIdCount * sizeof(LOGONIDW); if ( !(pLogonId = (PLOGONIDW)LocalAlloc( 0, Length)) ) { Result = ERROR_NOT_ENOUGH_MEMORY; goto nomemexit; } /* * get list of all WinStations */ for (;;) { if ( Index ) { ByteCount = *pEntries * sizeof(LOGONIDW); *pEntries += LogonIdCount; if ( !(pLogonIdTemp = (PSESSIONIDW)LocalAlloc( 0, (*pEntries * sizeof(LOGONIDW)))) ) { Result = ERROR_NOT_ENOUGH_MEMORY; goto errexit; } if ( *ppLogonId ) { MoveMemory( pLogonIdTemp, *ppLogonId, ByteCount ); LocalFree(*ppLogonId); } MoveMemory( ((PBYTE)pLogonIdTemp + ByteCount), pLogonId, (LogonIdCount * sizeof(LOGONIDW)) ); *ppLogonId = pLogonIdTemp; } RpcTryExcept { rc = RpcWinStationEnumerate( hServer, &Result, &LogonIdCount, (PCHAR)pLogonId, &Length, &Index ); Result = RtlNtStatusToDosError( Result ); if ( Result == ERROR_NO_MORE_ITEMS) { Result = ERROR_SUCCESS; break; } } RpcExcept(I_RpcExceptionFilter(RpcExceptionCode())) { Result = RpcExceptionCode(); DBGPRINT(("RPC Exception %d\n",Result)); goto nomemexit; } RpcEndExcept } errexit: LocalFree( pLogonId ); nomemexit: if ( Result ) { if ( *ppLogonId ) { LocalFree( *ppLogonId ); *ppLogonId = NULL; } SetLastError(Result); return(FALSE); } else { return(TRUE); } } /******************************************************************************* * * WinStationEnumerate_IndexedA (ANSI stub) * * Returns a list of window station objects (multi-call indexed). * * NOTE: this API used to be WinStationEnumerateA in WinFrame 1.6 and * earlier. It is provided now for backward compatibility with * Citrix code built around the indexed enumeration procedure. * New code should use the WinStationEnumerateA call. * * ENTRY: * * see WinStationEnumerate_IndexedW * * EXIT: * * see WinStationEnumerate_IndexedW, plus * * ERROR_NOT_ENOUGH_MEMORY - the LocalAlloc failed * ******************************************************************************/ BOOLEAN WINAPI WinStationEnumerate_IndexedA( HANDLE hServer, PULONG pEntries, PLOGONIDA pLogonId, PULONG pByteCount, PULONG pIndex ) { PLOGONIDW pBuffer = NULL, pLogonIdW; BOOLEAN Status; ULONG Count, ByteCountW = (*pByteCount << 1); /* * If the caller supplied a buffer and the length is not 0, * allocate a corresponding (*2) buffer for UNICODE strings. */ if ( pLogonId && ByteCountW ) { if ( !(pBuffer = LocalAlloc(0, ByteCountW)) ) { SetLastError(ERROR_NOT_ENOUGH_MEMORY); return(FALSE); } } /* * Enumerate WinStations */ pLogonIdW = pBuffer; Status = WinStationEnumerate_IndexedW( hServer, pEntries, pLogonIdW, &ByteCountW, pIndex ); /* * Always /2 the resultant ByteCount (whether sucessful or not). */ *pByteCount = (ByteCountW >> 1); /* * If the function completed sucessfully and caller * (and stub) defined a buffer to copy into, perform conversion * from UNICODE to ANSI. */ if ( Status && pLogonIdW && pLogonId ) { for ( Count = *pEntries; Count; Count-- ) { pLogonId->LogonId = pLogonIdW->LogonId; UnicodeToAnsi( pLogonId->WinStationName, sizeof(WINSTATIONNAMEA), pLogonIdW->WinStationName ); pLogonId->State = pLogonIdW->State; (char*)pLogonId += sizeof(LOGONIDA); (char*)pLogonIdW += sizeof(LOGONIDW); } } /* * If we defined a buffer, free it now, then return the status of * the WinStationEnumerateW call. */ if ( pBuffer ) LocalFree(pBuffer); return(Status); } /******************************************************************************* * * WinStationEnumerate_IndexedW (UNICODE) * * Returns a list of window station objects (multi-call indexed). * * NOTE: this API used to be WinStationEnumerateW in WinFrame 1.6 and * earlier. It is provided now for backward compatibility with * Citrix code built around the indexed enumeration procedure. * New code should use the WinStationEnumerateW call. * * ENTRY: * * 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. * * pLogonId (output) * Points to the buffer to receive the enumeration results, which are * returned as an array of LOGONID structures. If the window * station is disconnected the name is null. * * pByteCount (input/output) * Points to a variable that specifies the size, in bytes, of the * pLogonId parameter. If the buffer is too small to receive even * one entry, this variable receives the required size of the buffer. * * pIndex (input/output) * Points to a ULONG that specifies where to start the enumeration. * The only user visible value is 0, for starting at the begining. * Each call will update this so that the next call will return the * next WinStation in the list, till end of list. * The user should not interpret, or use the internal values, other * than the special case 0. * * EXIT: * * TRUE - The enumeration succeeded, and the buffer contains the * requested data. The calling application can continue to call * the WinStationEnumerate function to complete the enumeration. * * FALSE - The operation failed. Extended error status is available using * GetLastError. Possible return values from GetLastError include * the following: * * ERROR_NO_MORE_ITEMS - There are no more entries. The buffer * contents are undefined. * ERROR_MORE_DATA - The buffer is too small for even one entry. * ******************************************************************************/ BOOLEAN WINAPI WinStationEnumerate_IndexedW( HANDLE hServer, PULONG pEntries, PLOGONIDW pLogonId, PULONG pByteCount, PULONG pIndex ) { DWORD Result; BOOLEAN rc; HANDLE_CURRENT_BINDING( hServer ); RpcTryExcept { rc = RpcWinStationEnumerate( hServer, &Result, pEntries, (PCHAR)pLogonId, pByteCount, pIndex ); Result = RtlNtStatusToDosError( Result ); if( !rc ) SetLastError(Result); } RpcExcept(I_RpcExceptionFilter(RpcExceptionCode())) { Result = RpcExceptionCode(); SetLastError( Result ); DBGPRINT(("RPC Exception %d\n",Result)); rc = FALSE; } RpcEndExcept return( rc ); } /******************************************************************************* * * WinStationGetAllProcesses (UNICODE) * * Returns a structure containing TS_SYS_PROCESS_INFORMATION structures * for each process on the specified server. * * ENTRY: * * EXIT: * TRUE - The enumeration succeeded, and the buffer contains the * requested data. * FALSE - The operation failed. Extended error status is available using * GetLastError. * ******************************************************************************/ BOOLEAN WINAPI WinStationGetAllProcesses( HANDLE hServer, ULONG Level, ULONG *pNumberOfProcesses, PVOID *ppProcessArray ) { BOOLEAN bGetAllProcessesOk = FALSE; DWORD dwResult; if (Level != GAP_LEVEL_BASIC) { dwResult = RtlNtStatusToDosError( STATUS_NOT_IMPLEMENTED ); SetLastError(dwResult); return FALSE; } HANDLE_CURRENT_BINDING( hServer ); // The Win2K server uses PTS_ALL_PROCESSES_INFO structure for the process information. // And the whistler server uses PTS_SYS_PROCESS_INFORMATION_NT6 structure for the same. // So, we have to try two different RPC APIs. Assume initially that the server is a // Whistler server and use RpcWinStationGetAllProcesses_NT6. If it is Win2K server, this // call will fail, because this API does not exist on Win2K server. In that case we will // use RpcWinStationGetAllProcesses. // Try out Whistler interface first. RpcTryExcept { bGetAllProcessesOk = RpcWinStationGetAllProcesses_NT6(hServer, (ULONG *)&dwResult, Level, pNumberOfProcesses, (PTS_ALL_PROCESSES_INFO_NT6 *)ppProcessArray); if( !bGetAllProcessesOk ) { dwResult = RtlNtStatusToDosError( dwResult ); SetLastError(dwResult); } } RpcExcept(I_RpcExceptionFilter(RpcExceptionCode())) { dwResult = RpcExceptionCode(); if (dwResult == RPC_S_PROCNUM_OUT_OF_RANGE) // Whistler interface failed. goto TryW2KInterface; SetLastError( dwResult ); DBGPRINT(("RPC Exception %d\n",dwResult)); bGetAllProcessesOk = FALSE; } RpcEndExcept return( bGetAllProcessesOk ); TryW2KInterface: // Try out Win2K interface now. RpcTryExcept { bGetAllProcessesOk = RpcWinStationGetAllProcesses(hServer, (ULONG *)&dwResult, Level, pNumberOfProcesses, (PTS_ALL_PROCESSES_INFO *)ppProcessArray); if( !bGetAllProcessesOk ) { dwResult = RtlNtStatusToDosError( dwResult ); SetLastError(dwResult); } } RpcExcept(I_RpcExceptionFilter(RpcExceptionCode())) { dwResult = RpcExceptionCode(); SetLastError( dwResult ); DBGPRINT(("RPC Exception %d\n",dwResult)); bGetAllProcessesOk = FALSE; } RpcEndExcept return( bGetAllProcessesOk ); } /******************************************************************************* * WinStationGetProcessSid() * username for the requested process * For identifying correct process processid and start * time are required * * hServer - input, Handle of the server to find info about, * if NULL use local. * ProcessId - input, ProcessID * ProcessStartTime- input, Process start time, (identifies unique process * together with ProcessID) * pProcessUserSid - output, process user sid * dwSidSize - input, memory allocated for pProcessUserSid * * returns TURE if succeeded, FALSE if failed. in case of failure * GetLastError() will gives more infromation about failure. * ******************************************************************************/ BOOLEAN WINAPI WinStationGetProcessSid( HANDLE hServer, DWORD ProcessId, FILETIME ProcessStartTime, PBYTE pProcessUserSid, DWORD *pdwSidSize ) { BOOLEAN rc; LARGE_INTEGER CreateTime; DWORD Result; NTSTATUS Status; HANDLE_CURRENT_BINDING( hServer ); CreateTime.LowPart = ProcessStartTime.dwLowDateTime; CreateTime.HighPart = ProcessStartTime.dwHighDateTime; RpcTryExcept { rc = RpcWinStationGetProcessSid( hServer, ProcessId, CreateTime, &Status, pProcessUserSid, *pdwSidSize, pdwSidSize ); if( !rc ) { Result = RtlNtStatusToDosError( Status ); SetLastError(Result); } } RpcExcept(I_RpcExceptionFilter(RpcExceptionCode())) { Result = RpcExceptionCode(); SetLastError(Result); DBGPRINT(("RPC Exception %d\n",Result)); rc = FALSE; } RpcEndExcept return( rc ); } /******************************************************************************* * * WinStationGetLanAdapterNameW (UNICODE) * * Returns a Network Adapter name * * 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 WINAPI WinStationGetLanAdapterNameW( HANDLE hServer, ULONG LanAdapter, ULONG pdNameLength, PWCHAR pPdName, ULONG *pLength, PWCHAR *ppLanAdapter ) { BOOLEAN bGetLanAdapter = FALSE; DWORD dwResult; HANDLE_CURRENT_BINDING( hServer ); RpcTryExcept { bGetLanAdapter = RpcWinStationGetLanAdapterName(hServer, &dwResult, pdNameLength, pPdName, LanAdapter, pLength, ppLanAdapter ); if( !bGetLanAdapter ) { dwResult = RtlNtStatusToDosError( dwResult ); SetLastError(dwResult); } } RpcExcept(I_RpcExceptionFilter(RpcExceptionCode())) { dwResult = RpcExceptionCode(); SetLastError( dwResult ); DBGPRINT(("RPC Exception %d\n",dwResult)); bGetLanAdapter = FALSE; } RpcEndExcept return( bGetLanAdapter ); } /******************************************************************************* * * WinStationGetLanAdapterNameA * * Returns a Network Adapter name - Ansi equivalent for WinStationGetLanAdapterNameW * * 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 WINAPI WinStationGetLanAdapterNameA( HANDLE hServer, ULONG LanAdapter, ULONG pdNameLength, PCHAR pPdName, ULONG *pLength, PCHAR *ppLanAdapter ) { BOOLEAN bGetLanAdapter = FALSE; PWCHAR pPdNameW = NULL; PWCHAR pLanAdapterW = NULL; ULONG Size = 0; *ppLanAdapter = NULL; *pLength = 0; pPdNameW = LocalAlloc(0,pdNameLength * sizeof(WCHAR)); if (NULL == pPdNameW) { SetLastError(ERROR_NOT_ENOUGH_MEMORY); return(FALSE); } AnsiToUnicode(pPdNameW, pdNameLength * sizeof(WCHAR), pPdName ); bGetLanAdapter = WinStationGetLanAdapterNameW(hServer,LanAdapter,pdNameLength * sizeof(WCHAR),pPdNameW,&Size,&pLanAdapterW); if(bGetLanAdapter ) { *ppLanAdapter = LocalAlloc(0,lstrlen(pLanAdapterW) + 1); if(NULL == *ppLanAdapter) { SetLastError(ERROR_NOT_ENOUGH_MEMORY); bGetLanAdapter = FALSE; } else { UnicodeToAnsi(*ppLanAdapter,lstrlen(pLanAdapterW) + 1,pLanAdapterW); *pLength = lstrlen(pLanAdapterW) + 1; } WinStationFreeMemory(pLanAdapterW); } LocalFree(pPdNameW); return( bGetLanAdapter ); } #if defined(_WIN64) void ConvertFromX86(PTS_SYS_PROCESS_INFORMATION pTSProcessInfo) { // // this function is called from WinStationEnumerateProcesses, which is used only for TS4 machines. // This structure has to be marshalled correctly to convert from x86 to ia64 when running on ia64. // typedef struct _WIRE_UNICODE_STRING { USHORT Length; USHORT MaximumLength; ULONG Buffer; } X86_UNICODE_STRING, *PX86_UNICODE_STRING; typedef struct TS_SYS_PROCESS_INFORMATION_X86 { ULONG NextEntryOffset; ULONG NumberOfThreads; LARGE_INTEGER SpareLi1; LARGE_INTEGER SpareLi2; LARGE_INTEGER SpareLi3; LARGE_INTEGER CreateTime; LARGE_INTEGER UserTime; LARGE_INTEGER KernelTime; X86_UNICODE_STRING ImageName; LONG BasePriority; // KPRIORITY in ntexapi.h DWORD UniqueProcessId; // HANDLE in ntexapi.h DWORD InheritedFromUniqueProcessId; // HANDLE in ntexapi.h ULONG HandleCount; ULONG SessionId; ULONG SpareUl3; ULONG PeakVirtualSize; ULONG VirtualSize; ULONG PageFaultCount; ULONG PeakWorkingSetSize; ULONG WorkingSetSize; ULONG QuotaPeakPagedPoolUsage; ULONG QuotaPagedPoolUsage; ULONG QuotaPeakNonPagedPoolUsage; ULONG QuotaNonPagedPoolUsage; ULONG PagefileUsage; ULONG PeakPagefileUsage; ULONG PrivatePageCount; } TS_SYS_PROCESS_INFORMATION_X86, *PTS_SYS_PROCESS_INFORMATION_X86; TS_SYS_PROCESS_INFORMATION_X86 TSProcInfoX86; TSProcInfoX86 = * (PTS_SYS_PROCESS_INFORMATION_X86) pTSProcessInfo; pTSProcessInfo->NextEntryOffset = TSProcInfoX86.NextEntryOffset ; pTSProcessInfo->NumberOfThreads = TSProcInfoX86.NumberOfThreads ; pTSProcessInfo->SpareLi1 = TSProcInfoX86.SpareLi1 ; pTSProcessInfo->SpareLi2 = TSProcInfoX86.SpareLi2 ; pTSProcessInfo->SpareLi3 = TSProcInfoX86.SpareLi3 ; pTSProcessInfo->CreateTime = TSProcInfoX86.CreateTime ; pTSProcessInfo->UserTime = TSProcInfoX86.UserTime ; pTSProcessInfo->KernelTime = TSProcInfoX86.KernelTime ; pTSProcessInfo->ImageName.Length = TSProcInfoX86.ImageName.Length; pTSProcessInfo->ImageName.MaximumLength = TSProcInfoX86.ImageName.MaximumLength; pTSProcessInfo->ImageName.Buffer = UlongToPtr(TSProcInfoX86.ImageName.Buffer); pTSProcessInfo->BasePriority = TSProcInfoX86.BasePriority ; pTSProcessInfo->UniqueProcessId = TSProcInfoX86.UniqueProcessId ; pTSProcessInfo->InheritedFromUniqueProcessId = TSProcInfoX86.InheritedFromUniqueProcessId ; pTSProcessInfo->HandleCount = TSProcInfoX86.HandleCount ; pTSProcessInfo->SessionId = TSProcInfoX86.SessionId ; // // Following members are not used so we dont need to copy their values. // if we did so, we overwrite data past the original structure, as the strucutre comes from x86 // its smaller than the WIN64 version. // //pTSProcessInfo->SpareUl3 = TSProcInfoX86.SpareUl3 ; //pTSProcessInfo->PeakVirtualSize = TSProcInfoX86.PeakVirtualSize ; //pTSProcessInfo->VirtualSize = TSProcInfoX86.VirtualSize ; //pTSProcessInfo->PageFaultCount = TSProcInfoX86.PageFaultCount ; //pTSProcessInfo->PeakWorkingSetSize = TSProcInfoX86.PeakWorkingSetSize ; //pTSProcessInfo->WorkingSetSize = TSProcInfoX86.WorkingSetSize ; //pTSProcessInfo->QuotaPeakPagedPoolUsage = TSProcInfoX86.QuotaPeakPagedPoolUsage ; //pTSProcessInfo->QuotaPagedPoolUsage = TSProcInfoX86.QuotaPagedPoolUsage ; //pTSProcessInfo->QuotaPeakNonPagedPoolUsage = TSProcInfoX86.QuotaPeakNonPagedPoolUsage ; //pTSProcessInfo->QuotaNonPagedPoolUsage = TSProcInfoX86.QuotaNonPagedPoolUsage ; //pTSProcessInfo->PagefileUsage = TSProcInfoX86.PagefileUsage ; //pTSProcessInfo->PeakPagefileUsage = TSProcInfoX86.PeakPagefileUsage ; //pTSProcessInfo->PrivatePageCount = TSProcInfoX86.PrivatePageCount ; } #endif /******************************************************************************* * * WinStationEnumerateProcesses (UNICODE) * * Returns a buffer containing SYSTEM_PROCESS_INFORMATION structures * for each process on the specified server. * * IMPORTANT: This API can ONLY be used to access TS 4.0 servers. * The process structure has changed in Windows 2000 ! * * ENTRY: * ppProcessBuffer (output) * Points to a variable that will be set to the beginning of the * process buffer on success. The buffer is allocated within this * API and is disposed of using WinStationFreeMemory. * * EXIT: * TRUE - The enumeration succeeded, and the buffer contains the * requested data. * FALSE - The operation failed. Extended error status is available using * GetLastError. * ******************************************************************************/ BOOLEAN WINAPI WinStationEnumerateProcesses( HANDLE hServer, PVOID *ppProcessBuffer ) { DWORD Result; BOOLEAN rc; PBYTE pBuffer; ULONG ByteCount; // From pstat.c #define BUFFER_SIZE 32*1024 HANDLE_CURRENT_BINDING( hServer ); RpcTryExcept { ByteCount = BUFFER_SIZE; *ppProcessBuffer = NULL; for(;;) { if ( (pBuffer = LocalAlloc( 0, ByteCount )) == NULL ) { Result = (DWORD)STATUS_NO_MEMORY; rc = FALSE; break; } //#ifdef notdef /* * get process info from server */ rc = RpcWinStationEnumerateProcesses( hServer, &Result, pBuffer, ByteCount ); //#else #ifdef notdef Result = NtQuerySystemInformation( SystemProcessInformation, (PVOID)pBuffer, ByteCount, NULL ); rc = (Result == STATUS_SUCCESS) ? TRUE : FALSE; #endif if ( rc || (Result != STATUS_INFO_LENGTH_MISMATCH) ) break; LocalFree( pBuffer ); ByteCount *= 2; } if( !rc ) { Result = RtlNtStatusToDosError( Result ); SetLastError(Result); LocalFree( pBuffer ); *ppProcessBuffer = NULL; } else { //#ifdef notdef PTS_SYS_PROCESS_INFORMATION ProcessInfo; PCITRIX_PROCESS_INFORMATION CitrixInfo; ULONG TotalOffset; /* * Walk the returned buffer (it's in PTS_SYS_PROCESS_INFORMATION * format) and fixup the addresses (now containing * offsets) to pointers in our address space within pBuffer. */ ProcessInfo = (PTS_SYS_PROCESS_INFORMATION)pBuffer; TotalOffset = 0; for(;;) { #if defined(_WIN64) ConvertFromX86(ProcessInfo); #endif /* * Fixup image name buffer address */ if ( ProcessInfo->ImageName.Buffer ) ProcessInfo->ImageName.Buffer = (PWSTR)&pBuffer[(ULONG_PTR)(ProcessInfo->ImageName.Buffer)]; /* * Fixup ProcessSid address */ // // Note: this is necessary because we may access to a Hydra 4 server // the MagicNumber should prevent us from doing wrong. // CitrixInfo = (PCITRIX_PROCESS_INFORMATION) (((PUCHAR)ProcessInfo) + SIZEOF_TS4_SYSTEM_PROCESS_INFORMATION + (SIZEOF_TS4_SYSTEM_THREAD_INFORMATION * (int)ProcessInfo->NumberOfThreads)); #if defined(_WIN64) //The pointer to SID came from x86 machine, so upper //32 bits contain garbage. Set them to 0. //This overrides original Pad value, bu it's okay, //because it is not used. (ULONG_PTR)CitrixInfo->ProcessSid &=0x00000000FFFFFFFF; #endif if( (CitrixInfo->MagicNumber == CITRIX_PROCESS_INFO_MAGIC) && (CitrixInfo->ProcessSid) ) { CitrixInfo->ProcessSid = (PVOID)&pBuffer[(ULONG_PTR)(CitrixInfo->ProcessSid)]; } if( ProcessInfo->NextEntryOffset == 0 ) break; else TotalOffset += ProcessInfo->NextEntryOffset; ProcessInfo = (PTS_SYS_PROCESS_INFORMATION)&pBuffer[TotalOffset]; } //#endif *ppProcessBuffer = (PVOID)pBuffer; } } RpcExcept(I_RpcExceptionFilter(RpcExceptionCode())) { Result = RpcExceptionCode(); SetLastError( Result ); DBGPRINT(("RPC Exception %d\n",Result)); rc = FALSE; } RpcEndExcept return( rc ); } /******************************************************************************* * * WinStationRenameA (ANSI stub) * * Renames a window station object in the session manager. * (see WinStationRenameW) * * ENTRY: * * see WinStationRenameW * * EXIT: * * see WinStationRenameW * ******************************************************************************/ BOOLEAN WinStationRenameA( HANDLE hServer, PWINSTATIONNAMEA pWinStationNameOld, PWINSTATIONNAMEA pWinStationNameNew ) { WINSTATIONNAMEW WinStationNameOldW; WINSTATIONNAMEW WinStationNameNewW; /* * Convert ANSI WinStationNames to UNICODE. */ AnsiToUnicode( WinStationNameOldW, sizeof(WINSTATIONNAMEW), pWinStationNameOld ); AnsiToUnicode( WinStationNameNewW, sizeof(WINSTATIONNAMEW), pWinStationNameNew ); /* * Call WinStationRenameW & return it's status. */ return ( WinStationRenameW( hServer, WinStationNameOldW, WinStationNameNewW ) ); } /******************************************************************************* * * WinStationRenameW (UNICODE) * * 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 WinStationRenameW( HANDLE hServer, PWINSTATIONNAMEW pWinStationNameOld, PWINSTATIONNAMEW pWinStationNameNew ) { DWORD Result; BOOLEAN rc; WCHAR* rpcBufferOld; WCHAR* rpcBufferNew; HANDLE_CURRENT_BINDING( hServer ); // Since, due to legacy clients, we cannot change the interface, // as a workarround to bug#265954, we double the size of RPC Buffers. #pragma prefast(suppress:260, legacy servers expect this behaviour (http://searchraid/ntbug/265954.asp)) rpcBufferOld = LocalAlloc(LPTR, sizeof(WINSTATIONNAMEW) * sizeof(WCHAR)); if (rpcBufferOld != NULL) { CopyMemory(rpcBufferOld, pWinStationNameOld, sizeof(WINSTATIONNAMEW)); } else { SetLastError(ERROR_OUTOFMEMORY); return(FALSE); } #pragma prefast(suppress:260, legacy clients expect this behaviour (http://searchraid/ntbug/229753.asp)) rpcBufferNew = LocalAlloc(LPTR, sizeof(WINSTATIONNAMEW) * sizeof(WCHAR)); if (rpcBufferNew != NULL) { CopyMemory(rpcBufferNew, pWinStationNameNew, sizeof(WINSTATIONNAMEW)); } else { LocalFree(rpcBufferOld); SetLastError(ERROR_OUTOFMEMORY); return(FALSE); } RpcTryExcept { rc = RpcWinStationRename( hServer, &Result, (PWCHAR)rpcBufferOld, sizeof(WINSTATIONNAMEW), (PWCHAR)rpcBufferNew, sizeof(WINSTATIONNAMEW) ); Result = RtlNtStatusToDosError( Result ); } RpcExcept(I_RpcExceptionFilter(RpcExceptionCode())) { Result = RpcExceptionCode(); DBGPRINT(("RPC Exception %d\n",Result)); rc = FALSE; } RpcEndExcept LocalFree(rpcBufferOld); LocalFree(rpcBufferNew); if( !rc ) SetLastError(Result); return( rc ); } /******************************************************************************* * * WinStationQueryInformationA (ANSI stub) * * Queries configuration information about a window station object. * * ENTRY: * * see WinStationQueryInformationW * * EXIT: * * see WinStationQueryInformationW * ******************************************************************************/ BOOLEAN WinStationQueryInformationA( HANDLE hServer, ULONG LogonId, WINSTATIONINFOCLASS WinStationInformationClass, PVOID pWinStationInformation, ULONG WinStationInformationLength, PULONG pReturnLength ) { PVOID pInfo; ULONG InfoLength, ValidInputLength; struct { union { WINSTATIONCREATEW CreateData; WINSTATIONCONFIGW Configuration; PDPARAMSW PdParams; WDCONFIGW Wd; PDCONFIGW Pd; WINSTATIONPRINTERW Printer; WINSTATIONINFORMATIONW Information; WINSTATIONCLIENTW Client; WINSTATIONPRODIDW DigProdId; }; } Info; /* * Validate the caller supplied buffer length and set up for * call to WinStationQueryInformationW. */ switch ( WinStationInformationClass ) { case WinStationCreateData: pInfo = &Info.CreateData; InfoLength = sizeof(Info.CreateData); ValidInputLength = sizeof(WINSTATIONCREATEA); break; case WinStationConfiguration: pInfo = &Info.Configuration; InfoLength = sizeof(Info.Configuration); ValidInputLength = sizeof(WINSTATIONCONFIGA); break; case WinStationPdParams: pInfo = &Info.PdParams; ((PPDPARAMSW)pInfo)->SdClass = ((PPDPARAMSA)pWinStationInformation)->SdClass; InfoLength = sizeof(Info.PdParams); ValidInputLength = sizeof(PDPARAMSA); break; case WinStationWd: pInfo = &Info.Wd; InfoLength = sizeof(Info.Wd); ValidInputLength = sizeof(WDCONFIGA); break; case WinStationPd: pInfo = &Info.Pd; InfoLength = sizeof(Info.Pd); ValidInputLength = sizeof(PDCONFIGA); break; case WinStationPrinter: pInfo = &Info.Printer; InfoLength = sizeof(Info.Printer); ValidInputLength = sizeof(WINSTATIONPRINTERA); break; case WinStationInformation: pInfo = &Info.Information; InfoLength = sizeof(Info.Information); ValidInputLength = sizeof(WINSTATIONINFORMATIONA); break; case WinStationClient: pInfo = &Info.Client; InfoLength = sizeof(Info.Client); ValidInputLength = sizeof(WINSTATIONCLIENTA); break; case WinStationDigProductId: pInfo = &Info.DigProdId; InfoLength = sizeof(Info.DigProdId); ValidInputLength = sizeof(WINSTATIONPRODIDA); break; /* * The other WINSTATIONINFOCLASSes don't need converting. */ default: pInfo = pWinStationInformation; ValidInputLength = InfoLength = WinStationInformationLength; break; } /* * If the caller-supplied buffer is not the proper size, set error * and return FALSE. */ if ( WinStationInformationLength != ValidInputLength ) { SetLastError(ERROR_INSUFFICIENT_BUFFER); return(FALSE); } /* * Call the WinStationQueryInformationW function, returning if * failure. */ if ( !WinStationQueryInformationW( hServer, LogonId, WinStationInformationClass, pInfo, InfoLength, pReturnLength ) ) return(FALSE); /* * Convert the returned UNICODE information to ANSI, if needed. */ switch ( WinStationInformationClass ) { case WinStationCreateData: WinStationCreateU2A( (PWINSTATIONCREATEA)pWinStationInformation, (PWINSTATIONCREATEW)pInfo ); *pReturnLength = ValidInputLength; break; case WinStationConfiguration: WinStationConfigU2A( (PWINSTATIONCONFIGA)pWinStationInformation, (PWINSTATIONCONFIGW)pInfo ); *pReturnLength = ValidInputLength; break; case WinStationPdParams: PdParamsU2A( (PPDPARAMSA)pWinStationInformation, (PPDPARAMSW)pInfo ); *pReturnLength = ValidInputLength; break; case WinStationWd: WdConfigU2A( (PWDCONFIGA)pWinStationInformation, (PWDCONFIGW)pInfo ); *pReturnLength = ValidInputLength; break; case WinStationPd: PdConfig2U2A( &((PPDCONFIGA)pWinStationInformation)->Create, &((PPDCONFIGW)pInfo)->Create ); PdParamsU2A( &((PPDCONFIGA)pWinStationInformation)->Params, &((PPDCONFIGW)pInfo)->Params ); *pReturnLength = ValidInputLength; break; case WinStationPrinter: WinStationPrinterU2A( (PWINSTATIONPRINTERA)pWinStationInformation, (PWINSTATIONPRINTERW)pInfo ); *pReturnLength = ValidInputLength; break; case WinStationInformation: WinStationInformationU2A( (PWINSTATIONINFORMATIONA)pWinStationInformation, (PWINSTATIONINFORMATIONW)pInfo ); *pReturnLength = ValidInputLength; break; case WinStationClient: WinStationClientU2A( (PWINSTATIONCLIENTA)pWinStationInformation, (PWINSTATIONCLIENTW)pInfo ); *pReturnLength = ValidInputLength; break; case WinStationDigProductId: WinStationProductIdU2A( (PWINSTATIONPRODIDA)pWinStationInformation, (PWINSTATIONPRODIDW)pInfo ); *pReturnLength = ValidInputLength; break; default: break; } return(TRUE); } /******************************************************************************* * * WinStationQueryInformationW (UNICODE) * * 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 WinStationQueryInformationW( HANDLE hServer, ULONG LogonId, WINSTATIONINFOCLASS WinStationInformationClass, PVOID pWinStationInformation, ULONG WinStationInformationLength, PULONG pReturnLength ) { DWORD Result; BOOLEAN rc; PCHAR RpcBuf; ULONG RpcBufLen; PVOID WireBuf; PVOID AllocatedBuff = NULL; ULONG WireBufLen; BOOLEAN WireBufAllocated; ULONG Status; static UINT AlreadyWaitedForTermsrv = 0; // a flag which helps to determine if we already waited for TermSrv to be up if ((Status = CheckUserBuffer(WinStationInformationClass, pWinStationInformation, WinStationInformationLength, &WireBuf, &WireBufLen, &WireBufAllocated)) != ERROR_SUCCESS) { SetLastError(Status); return(FALSE); } if (WireBufAllocated) { AllocatedBuff = WireBuf; RpcBuf = (PCHAR) WireBuf; RpcBufLen = WireBufLen; CopyInWireBuf(WinStationInformationClass, pWinStationInformation, WireBuf); } else { RpcBuf = (PCHAR) pWinStationInformation; RpcBufLen = WinStationInformationLength; } HANDLE_CURRENT_BINDING_BUFFER( hServer, AllocatedBuff ); // First wait for termsrv to get started if User Token is queried // This is for Session 0 only where termsrv is started after 60 seconds on Per and Pro // Need to do this only for the first time - AlreadyWaitedForTermsrv flag helps to determine this if ( (LogonId == 0) && (WinStationInformationClass == WinStationUserToken) && (AlreadyWaitedForTermsrv == 0) ) { HANDLE ReadyEventHandle ; ReadyEventHandle = OpenEvent(SYNCHRONIZE, FALSE, TEXT("Global\\TermSrvReadyEvent")); if (ReadyEventHandle != NULL) { DWORD dwTimeOut = 1000*60*3; // 3 minutes AlreadyWaitedForTermsrv++; // wait until termsrv is actually ready. WaitForSingleObject(ReadyEventHandle, dwTimeOut); CloseHandle(ReadyEventHandle); } } RpcTryExcept { rc = RpcWinStationQueryInformation( hServer, &Result, (LogonId == LOGONID_CURRENT) ? NtCurrentPeb()->SessionId : LogonId, (DWORD)WinStationInformationClass, RpcBuf, RpcBufLen, pReturnLength ); Result = RtlNtStatusToDosError( Result ); if( !rc ) SetLastError(Result); } RpcExcept(I_RpcExceptionFilter(RpcExceptionCode())) { Result = RpcExceptionCode(); SetLastError( Result ); DBGPRINT(("RPC Exception %d\n",Result)); rc = FALSE; } RpcEndExcept if (WireBufAllocated) { if (rc) { CopyOutWireBuf(WinStationInformationClass, pWinStationInformation, WireBuf); *pReturnLength = WinStationInformationLength; } LocalFree(WireBuf); } return( rc ); } /******************************************************************************* * * WinStationSetInformationA (ANSI stub) * * Sets configuration information for a window station object. * * ENTRY: * * see WinStationSetInformationW * * EXIT: * * see WinStationSetInformationW * ******************************************************************************/ BOOLEAN WinStationSetInformationA( HANDLE hServer, ULONG LogonId, WINSTATIONINFOCLASS WinStationInformationClass, PVOID pWinStationInformation, ULONG WinStationInformationLength ) { PVOID pInfo; ULONG InfoLength; struct { union { WINSTATIONCREATEW CreateData; WINSTATIONCONFIGW Configuration; PDPARAMSW PdParams; WDCONFIGW Wd; PDCONFIGW Pd; WINSTATIONPRINTERW Printer; WINSTATIONINFORMATIONW Information; }; } Info; /* * Validate the caller supplied buffer length and convert to the * appropriate UNICODE buffer for call to WinStationSetInformationW. */ switch ( WinStationInformationClass ) { case WinStationCreateData: pInfo = &Info.CreateData; InfoLength = sizeof(Info.CreateData); if ( WinStationInformationLength != sizeof(WINSTATIONCREATEA) ) goto BadBufferLength; WinStationCreateA2U( (PWINSTATIONCREATEW)pInfo, (PWINSTATIONCREATEA)pWinStationInformation ); break; case WinStationConfiguration: pInfo = &Info.Configuration; InfoLength = sizeof(Info.Configuration); if ( WinStationInformationLength != sizeof(WINSTATIONCONFIGA) ) goto BadBufferLength; WinStationConfigA2U( (PWINSTATIONCONFIGW)pInfo, (PWINSTATIONCONFIGA)pWinStationInformation ); break; case WinStationPdParams: pInfo = &Info.PdParams; InfoLength = sizeof(Info.PdParams); if ( WinStationInformationLength != sizeof(PDPARAMSA) ) goto BadBufferLength; PdParamsA2U( (PPDPARAMSW)pInfo, (PPDPARAMSA)pWinStationInformation ); break; case WinStationWd: pInfo = &Info.Wd; InfoLength = sizeof(Info.Wd); if ( WinStationInformationLength != sizeof(WDCONFIGA) ) goto BadBufferLength; WdConfigA2U( (PWDCONFIGW)pInfo, (PWDCONFIGA)pWinStationInformation ); break; case WinStationPd: pInfo = &Info.Pd; InfoLength = sizeof(Info.Pd); if ( WinStationInformationLength != sizeof(PDCONFIGA) ) goto BadBufferLength; PdConfig2A2U( &((PPDCONFIGW)pInfo)->Create, &((PPDCONFIGA)pWinStationInformation)->Create ); PdParamsA2U( &((PPDCONFIGW)pInfo)->Params, &((PPDCONFIGA)pWinStationInformation)->Params ); break; case WinStationPrinter: pInfo = &Info.Printer; InfoLength = sizeof(Info.Printer); if ( WinStationInformationLength != sizeof(WINSTATIONPRINTERA) ) goto BadBufferLength; WinStationPrinterA2U( (PWINSTATIONPRINTERW)pInfo, (PWINSTATIONPRINTERA)pWinStationInformation ); break; case WinStationInformation: pInfo = &Info.Information; InfoLength = sizeof(Info.Information); if ( WinStationInformationLength != sizeof(WINSTATIONINFORMATIONA) ) goto BadBufferLength; WinStationInformationA2U( (PWINSTATIONINFORMATIONW)pInfo, (PWINSTATIONINFORMATIONA)pWinStationInformation ); break; /* * The other WINSTATIONINFOCLASSes don't need converting. */ default: pInfo = pWinStationInformation; InfoLength = WinStationInformationLength; break; } /* * Call the WinStationSetInformationW function and return it's * status. */ return ( WinStationSetInformationW( hServer, LogonId, WinStationInformationClass, pInfo, InfoLength ) ); /*-------------------------------------- * Error clean-up and return... */ BadBufferLength: SetLastError(ERROR_INSUFFICIENT_BUFFER); return(FALSE); } /******************************************************************************* * * WinStationSetInformationW (UNICODE) * * 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 WinStationSetInformationW( HANDLE hServer, ULONG LogonId, WINSTATIONINFOCLASS WinStationInformationClass, PVOID pWinStationInformation, ULONG WinStationInformationLength ) { DWORD Result; BOOLEAN rc; PCHAR RpcBuf; ULONG RpcBufLen; PVOID WireBuf; PVOID AllocatedBuff = NULL; ULONG WireBufLen; BOOLEAN WireBufAllocated; ULONG Status; if ((Status = CheckUserBuffer(WinStationInformationClass, pWinStationInformation, WinStationInformationLength, &WireBuf, &WireBufLen, &WireBufAllocated)) != ERROR_SUCCESS) { SetLastError(Status); return(FALSE); } if (WireBufAllocated) { AllocatedBuff = WireBuf; RpcBuf = (PCHAR) WireBuf; RpcBufLen = WireBufLen; CopyInWireBuf(WinStationInformationClass, pWinStationInformation, WireBuf); } else { RpcBuf = (PCHAR) pWinStationInformation; RpcBufLen = WinStationInformationLength; } HANDLE_CURRENT_BINDING_BUFFER( hServer, AllocatedBuff ); RpcTryExcept { rc = RpcWinStationSetInformation( hServer, &Result, (LogonId == LOGONID_CURRENT) ? NtCurrentPeb()->SessionId : LogonId, (DWORD)WinStationInformationClass, RpcBuf, RpcBufLen ); Result = RtlNtStatusToDosError( Result ); if( !rc ) SetLastError(Result); } RpcExcept(I_RpcExceptionFilter(RpcExceptionCode())) { Result = RpcExceptionCode(); SetLastError( Result ); DBGPRINT(("RPC Exception %d\n",Result)); rc = FALSE; } RpcEndExcept if (WireBufAllocated) { LocalFree(WireBuf); } return( rc ); } /******************************************************************************* * * WinStationSendMessageA (ANSI stub) * * 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: * * see WinStationSendMessageW * * EXIT: * * see WinStationSendMessageW, plus * * ERROR_NOT_ENOUGH_MEMORY - the LocalAlloc failed * ******************************************************************************/ BOOLEAN WinStationSendMessageA( HANDLE hServer, ULONG LogonId, LPSTR pTitle, ULONG TitleLength, LPSTR pMessage, ULONG MessageLength, ULONG Style, ULONG Timeout, PULONG pResponse, BOOLEAN DoNotWait ) { BOOLEAN status; LPWSTR pTitleW, pMessageW; ULONG TitleLengthW, MessageLengthW; /* * Allocate a buffer for UNICODE version of Title and convert. */ if ( !(pTitleW = LocalAlloc( 0, TitleLengthW = (TitleLength*sizeof(WCHAR)) )) ) { SetLastError(ERROR_NOT_ENOUGH_MEMORY); return(FALSE); } AnsiToUnicode( pTitleW, TitleLengthW, pTitle ); /* * Allocate a buffer for UNICODE version of Message and convert. */ if ( !(pMessageW = LocalAlloc( 0, MessageLengthW = (MessageLength*sizeof(WCHAR)) )) ) { LocalFree(pTitleW); SetLastError(ERROR_NOT_ENOUGH_MEMORY); return(FALSE); } AnsiToUnicode( pMessageW, MessageLengthW, pMessage ); /* * Call WinStationSendMessageW */ status = WinStationSendMessageW( hServer, LogonId, pTitleW, TitleLengthW, pMessageW, MessageLengthW, Style, Timeout, pResponse, DoNotWait ); /* * Free allocated buffers and return status. */ LocalFree(pTitleW); LocalFree(pMessageW); return(status); } /******************************************************************************* * * WinStationSendMessageW (UNICODE) * * 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 WinStationSendMessageW( HANDLE hServer, ULONG LogonId, LPWSTR pTitle, ULONG TitleLength, LPWSTR pMessage, ULONG MessageLength, ULONG Style, ULONG Timeout, PULONG pResponse, BOOLEAN DoNotWait ) { DWORD Result; BOOLEAN rc; WCHAR* rpcBuffer1; WCHAR* rpcBuffer2; HANDLE_CURRENT_BINDING( hServer ); // Since, due to legacy clients, we cannot change the interface, // as a workarround to bug#265954, we double the size of RPC Buffers. rpcBuffer1 = LocalAlloc(LPTR, MessageLength * sizeof(WCHAR)); if (rpcBuffer1 != NULL) { CopyMemory(rpcBuffer1, pMessage, MessageLength); } else { SetLastError(ERROR_OUTOFMEMORY); return(FALSE); } rpcBuffer2 = LocalAlloc(LPTR, TitleLength * sizeof(WCHAR)); if (rpcBuffer2 != NULL) { CopyMemory(rpcBuffer2, pTitle, TitleLength); } else { LocalFree(rpcBuffer1); SetLastError(ERROR_OUTOFMEMORY); return(FALSE); } RpcTryExcept { rc = RpcWinStationSendMessage( hServer, &Result, (LogonId == LOGONID_CURRENT) ? NtCurrentPeb()->SessionId : LogonId, rpcBuffer2, TitleLength, rpcBuffer1, MessageLength, Style, Timeout, pResponse, DoNotWait ); Result = RtlNtStatusToDosError( Result ); } RpcExcept(I_RpcExceptionFilter(RpcExceptionCode())) { Result = RpcExceptionCode(); DBGPRINT(("RPC Exception %d\n",Result)); rc = FALSE; } RpcEndExcept LocalFree(rpcBuffer1); LocalFree(rpcBuffer2); if (!rc) { SetLastError( Result ); } return( rc ); } /******************************************************************************* * * LogonIdFromWinStationNameA (ANSI stub) * * Returns the LogonId for the specified window station name. * * ENTRY: * * see LogonIdFromWinStationNameW * * EXIT: * * see LogonIdFromWinStationNameW * ******************************************************************************/ BOOLEAN LogonIdFromWinStationNameA( HANDLE hServer, PWINSTATIONNAMEA pWinStationName, PULONG pLogonId ) { WINSTATIONNAMEW WinStationNameW; /* * Convert ANSI WinStationName to UNICODE. */ AnsiToUnicode( WinStationNameW, sizeof(WINSTATIONNAMEW), pWinStationName ); /* * Call LogonIdFromWinStationNameW & return it's status. */ return ( LogonIdFromWinStationNameW( hServer, WinStationNameW, pLogonId ) ); } /******************************************************************************* * * LogonIdFromWinStationNameW (UNICODE) * * 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 LogonIdFromWinStationNameW( HANDLE hServer, PWINSTATIONNAMEW pWinStationName, PULONG pLogonId ) { DWORD Result; BOOLEAN rc; WCHAR* rpcBuffer; HANDLE_CURRENT_BINDING( hServer ); /* * rpcBuffer is a workaround for bug 229753. The bug can't be fixed * completely without breaking TS4 clients. */ #pragma prefast(suppress:260, legacy clients expect this (http://searchraid/ntbug/229753.asp)) rpcBuffer = LocalAlloc(LPTR, sizeof(WINSTATIONNAMEW) * sizeof(WCHAR)); if (rpcBuffer != NULL) { CopyMemory(rpcBuffer, pWinStationName, sizeof(WINSTATIONNAMEW)); } else { SetLastError(ERROR_OUTOFMEMORY); return(FALSE); } RpcTryExcept { rc = RpcLogonIdFromWinStationName( hServer, &Result, rpcBuffer, sizeof(WINSTATIONNAMEW), pLogonId ); Result = RtlNtStatusToDosError( Result ); } RpcExcept(I_RpcExceptionFilter(RpcExceptionCode())) { Result = RpcExceptionCode(); DBGPRINT(("RPC Exception %d\n",Result)); rc = FALSE; } RpcEndExcept if (rpcBuffer != NULL) { LocalFree(rpcBuffer); } if( !rc ) SetLastError(Result); return( rc ); } /******************************************************************************* * * WinStationNameFromLogonIdA (ANSI stub) * * Returns the WinStation name for the specified LogonId. * * ENTRY: * * see WinStationNameFromLogonIdW * * EXIT: * * see WinStationNameFromLogonIdW * ******************************************************************************/ BOOLEAN WinStationNameFromLogonIdA( HANDLE hServer, ULONG LogonId, PWINSTATIONNAMEA pWinStationName ) { BOOLEAN Result; WINSTATIONNAMEW WinStationNameW; /* * Call WinStationNameFromLogonIdW */ Result = WinStationNameFromLogonIdW( hServer, LogonId, WinStationNameW ); /* * if successful, convert UNICODE WinStationName to ANSI. */ if ( Result ) { UnicodeToAnsi( pWinStationName, sizeof(WINSTATIONNAMEA), WinStationNameW ); } return( Result ); } /******************************************************************************* * * WinStationNameFromLogonIdW (UNICODE) * * Returns the WinStation name for the specified LogonId. * * ENTRY: * * LogonId (input) * LogonId to query * * pWinStationName (output) * 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 WinStationNameFromLogonIdW( HANDLE hServer, ULONG LogonId, PWINSTATIONNAMEW pWinStationName ) { DWORD Result; BOOLEAN rc; WCHAR* rpcBuffer; HANDLE_CURRENT_BINDING( hServer ); /* * rpcBuffer is a workaround for bug 229753. The bug can't be fixed * completely without breaking TS4 clients. */ #pragma prefast(suppress:260, legacy clients expect this (http://searchraid/ntbug/229753.asp)) rpcBuffer = LocalAlloc(LPTR, sizeof(WINSTATIONNAMEW) * sizeof(WCHAR)); if (rpcBuffer == NULL) { SetLastError(ERROR_OUTOFMEMORY); return(FALSE); } RpcTryExcept { rc = RpcWinStationNameFromLogonId( hServer, &Result, (LogonId == LOGONID_CURRENT) ? NtCurrentPeb()->SessionId : LogonId, rpcBuffer, sizeof(WINSTATIONNAMEW) ); Result = RtlNtStatusToDosError( Result ); if (rc) { CopyMemory(pWinStationName, rpcBuffer, sizeof(WINSTATIONNAMEW)); } } RpcExcept(I_RpcExceptionFilter(RpcExceptionCode())) { Result = RpcExceptionCode(); DBGPRINT(("RPC Exception %d\n",Result)); rc = FALSE; } RpcEndExcept if (rpcBuffer != NULL) { LocalFree(rpcBuffer); } if( !rc ) SetLastError(Result); return( rc ); } /******************************************************************************* * * WinStationConnectA (ANSI stub) * * Connects a window station object to the configured terminal and Pd. * * ENTRY: * * see WinStationConnectW * * EXIT: * * see WinStationConnectW * ******************************************************************************/ BOOLEAN WinStationConnectA( HANDLE hServer, ULONG LogonId, ULONG TargetLogonId, PCHAR pPassword, BOOLEAN bWait ) { WCHAR PasswordW[ PASSWORD_LENGTH + 1 ]; /* * Convert ANSI Password to UNICODE. */ AnsiToUnicode( PasswordW, sizeof(PasswordW), pPassword ); /* * Call WinStationConnectW & return it's status. */ return ( WinStationConnectW( hServer, LogonId, TargetLogonId, PasswordW, bWait ) ); } /******************************************************************************* * * WinStationConnectW (UNICODE) * * 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 WinStationConnectW( HANDLE hServer, ULONG LogonId, ULONG TargetLogonId, PWCHAR pPassword, BOOLEAN bWait ) { DWORD Result; BOOLEAN rc; DWORD PasswordLength; WCHAR* rpcBuffer; HANDLE_CURRENT_BINDING( hServer ); RpcTryExcept { if( pPassword ) { PasswordLength = (lstrlenW( pPassword ) + 1) * sizeof(WCHAR); /* * rpcBuffer is a workaround for bug 229753. The bug can't be * fixed completely without breaking TS4 clients. */ rpcBuffer = LocalAlloc(LPTR, PasswordLength * sizeof(WCHAR)); if (rpcBuffer != NULL) { CopyMemory(rpcBuffer, pPassword, PasswordLength); } else { SetLastError(ERROR_OUTOFMEMORY); return(FALSE); } } else { PasswordLength = 0; rpcBuffer = NULL; } rc = RpcWinStationConnect( hServer, &Result, NtCurrentPeb()->SessionId, (LogonId == LOGONID_CURRENT) ? NtCurrentPeb()->SessionId : LogonId, (TargetLogonId == LOGONID_CURRENT) ? NtCurrentPeb()->SessionId : TargetLogonId, rpcBuffer, PasswordLength, bWait ); Result = RtlNtStatusToDosError( Result ); } RpcExcept(I_RpcExceptionFilter(RpcExceptionCode())) { Result = RpcExceptionCode(); DBGPRINT(("RPC Exception %d\n",Result)); rc = FALSE; } RpcEndExcept if (rpcBuffer != NULL) { LocalFree(rpcBuffer); } if( !rc ) SetLastError(Result); return( rc ); } /***************************************************************************** * * WinStationVirtualOpen * * Open a virtual channel * * ENTRY: * Param1 (input/output) * Comments * * EXIT: * ERROR_SUCCESS - no error * ****************************************************************************/ HANDLE WINAPI WinStationVirtualOpen( HANDLE hServer, ULONG LogonId, PVIRTUALCHANNELNAME pVirtualName /* ascii name */ ) { BOOLEAN rc; DWORD Result; DWORD NameLength; ULONG_PTR VirtualHandle = (ULONG_PTR)0; HANDLE_CURRENT_BINDING( hServer ); RpcTryExcept { if( pVirtualName ) NameLength = strlen( pVirtualName ) + 1; else NameLength = 0; rc = RpcWinStationVirtualOpen( hServer, &Result, (LogonId == LOGONID_CURRENT) ? NtCurrentPeb()->SessionId : LogonId, GetCurrentProcessId(), (PCHAR)pVirtualName, NameLength, &VirtualHandle ); Result = RtlNtStatusToDosError( Result ); if( !rc ) { SetLastError(Result); VirtualHandle = (ULONG_PTR)0; } } RpcExcept(I_RpcExceptionFilter(RpcExceptionCode())) { Result = RpcExceptionCode(); SetLastError( Result ); DBGPRINT(("RPC Exception %d\n",Result)); rc = FALSE; } RpcEndExcept return( (HANDLE) ( VirtualHandle ) ); } /***************************************************************************** * * _WinStationBeepOpen * * Open a beep channel * * ENTRY: * Param1 (input/output) * Comments * * EXIT: * ERROR_SUCCESS - no error * ****************************************************************************/ HANDLE WINAPI _WinStationBeepOpen( ULONG LogonId ) { BOOLEAN rc; DWORD Result; ULONG_PTR VirtualHandle = (ULONG_PTR)0; HANDLE hServer = SERVERNAME_CURRENT; HANDLE_CURRENT_BINDING( hServer ); RpcTryExcept { rc = RpcWinStationBeepOpen( hServer, &Result, (LogonId == LOGONID_CURRENT) ? NtCurrentPeb()->SessionId : LogonId, GetCurrentProcessId(), &VirtualHandle ); Result = RtlNtStatusToDosError( Result ); if( !rc ) { SetLastError(Result); VirtualHandle = (ULONG_PTR)0; } } RpcExcept(I_RpcExceptionFilter(RpcExceptionCode())) { Result = RpcExceptionCode(); SetLastError( Result ); DBGPRINT(("RPC Exception %d\n",Result)); rc = FALSE; } RpcEndExcept return( (HANDLE) ( VirtualHandle ) ); } /******************************************************************************* * * WinStationDisconnect * * 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 WinStationDisconnect( HANDLE hServer, ULONG LogonId, BOOLEAN bWait ) { DWORD Result; BOOLEAN rc; HANDLE_CURRENT_BINDING( hServer ); RpcTryExcept { rc = RpcWinStationDisconnect( hServer, &Result, (LogonId == LOGONID_CURRENT) ? NtCurrentPeb()->SessionId : LogonId, bWait ); Result = RtlNtStatusToDosError( Result ); if( !rc ) SetLastError(Result); } RpcExcept(I_RpcExceptionFilter(RpcExceptionCode())) { Result = RpcExceptionCode(); SetLastError( Result ); DBGPRINT(("RPC Exception %d\n",Result)); rc = FALSE; } RpcEndExcept return( rc ); } /******************************************************************************* * * WinStationReset * * 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 WinStationReset( HANDLE hServer, ULONG LogonId, BOOLEAN bWait ) { DWORD Result; BOOLEAN rc; HANDLE_CURRENT_BINDING( hServer ); RpcTryExcept { rc = RpcWinStationReset( hServer, &Result, (LogonId == LOGONID_CURRENT) ? NtCurrentPeb()->SessionId : LogonId, bWait ); Result = RtlNtStatusToDosError( Result ); if( !rc ) SetLastError(Result); } RpcExcept(I_RpcExceptionFilter(RpcExceptionCode())) { Result = RpcExceptionCode(); SetLastError( Result ); DBGPRINT(("RPC Exception %d\n",Result)); rc = FALSE; } RpcEndExcept return( rc ); } /******************************************************************************* * * WinStationShadowStop * * Stop the shadow on the specified window station. * * ENTRY: * * LogonId (input) * Identifies the window station object to stop the shadow on. * bWait (input) * Specifies whether or not to wait for reset to complete * * EXIT: * * TRUE -- The operation succeeded. * * FALSE -- The operation failed. Extended error status is available * using GetLastError. * ******************************************************************************/ BOOLEAN WinStationShadowStop( HANDLE hServer, ULONG LogonId, BOOLEAN bWait ) { DWORD Result; BOOLEAN rc; HANDLE_CURRENT_BINDING( hServer ); RpcTryExcept { rc = RpcWinStationShadowStop( hServer, &Result, (LogonId == LOGONID_CURRENT) ? NtCurrentPeb()->SessionId : LogonId, bWait ); Result = RtlNtStatusToDosError( Result ); if( !rc ) SetLastError(Result); } RpcExcept(I_RpcExceptionFilter(RpcExceptionCode())) { Result = RpcExceptionCode(); SetLastError( Result ); DBGPRINT(("RPC Exception %d\n",Result)); rc = FALSE; } RpcEndExcept return( rc ); } /******************************************************************************* * * WinStationShutdownSystem * * 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 WinStationShutdownSystem( HANDLE hServer, ULONG ShutdownFlags ) { DWORD Result; BOOLEAN rc; HANDLE_CURRENT_BINDING( hServer ); RpcTryExcept { rc = RpcWinStationShutdownSystem( hServer, &Result, NtCurrentPeb()->SessionId, ShutdownFlags ); Result = RtlNtStatusToDosError( Result ); if( !rc ) SetLastError(Result); } RpcExcept(I_RpcExceptionFilter(RpcExceptionCode())) { Result = RpcExceptionCode(); SetLastError( Result ); DBGPRINT(("RPC Exception %d\n",Result)); rc = FALSE; } RpcEndExcept return( rc ); } /******************************************************************************* * * WinStationTerminateProcess * * Terminate the specified process * * ENTRY: * * hServer (input) * handle to winframe server * 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 WinStationTerminateProcess( HANDLE hServer, ULONG ProcessId, ULONG ExitCode ) { DWORD Result; BOOLEAN rc; HANDLE_CURRENT_BINDING( hServer ); RpcTryExcept { rc = RpcWinStationTerminateProcess( hServer, &Result, ProcessId, ExitCode ); Result = RtlNtStatusToDosError( Result ); if( !rc ) SetLastError(Result); } RpcExcept(I_RpcExceptionFilter(RpcExceptionCode())) { Result = RpcExceptionCode(); SetLastError( Result ); DBGPRINT(("RPC Exception %d\n",Result)); rc = FALSE; } RpcEndExcept return( rc ); } /******************************************************************************* * * WinStationWaitSystemEvent * * 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 WinStationWaitSystemEvent( HANDLE hServer, ULONG EventMask, PULONG pEventFlags ) { DWORD Result; BOOLEAN rc; HANDLE_CURRENT_BINDING( hServer ); RpcTryExcept { rc = RpcWinStationWaitSystemEvent( hServer, &Result, EventMask, pEventFlags ); Result = RtlNtStatusToDosError( Result ); if( !rc ) SetLastError(Result); } RpcExcept(I_RpcExceptionFilter(RpcExceptionCode())) { Result = RpcExceptionCode(); SetLastError( Result ); DBGPRINT(("RPC Exception %d\n",Result)); rc = FALSE; } RpcEndExcept return( rc ); } /***************************************************************************** * * WinStationShadow * * Start a Winstation shadow operation * * ENTRY: * hServer (input) * open RPC server handle * pTargetServerName (input) * name of target WinFrame server * 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) * * EXIT: * ERROR_SUCCESS - no error * ****************************************************************************/ BOOLEAN WINAPI WinStationShadow( HANDLE hServer, LPWSTR pTargetServerName, ULONG TargetLogonId, BYTE HotkeyVk, USHORT HotkeyModifiers ) { DWORD NameSize; DWORD Result; BOOLEAN rc; WCHAR* rpcBuffer; HANDLE_CURRENT_BINDING( hServer ); RpcTryExcept { if ( pTargetServerName && *pTargetServerName ) { NameSize = (lstrlenW( pTargetServerName ) + 1) * sizeof(WCHAR); /* * rpcBuffer is a workaround for bug 229753. The bug can't be * fixed completely without breaking TS4 clients. */ rpcBuffer = LocalAlloc(LPTR, NameSize * sizeof(WCHAR)); if (rpcBuffer != NULL) { CopyMemory(rpcBuffer, pTargetServerName, NameSize); } else { SetLastError(ERROR_OUTOFMEMORY); return(FALSE); } } else { NameSize = 0; rpcBuffer = NULL; } rc = RpcWinStationShadow( hServer, &Result, NtCurrentPeb()->SessionId, rpcBuffer, NameSize, TargetLogonId, HotkeyVk, HotkeyModifiers ); Result = RtlNtStatusToDosError( Result ); } RpcExcept(I_RpcExceptionFilter(RpcExceptionCode())) { Result = RpcExceptionCode(); DBGPRINT(("RPC Exception %d\n",Result)); rc = FALSE; } RpcEndExcept if (rpcBuffer != NULL) { LocalFree(rpcBuffer); } if( !rc ) SetLastError(Result); return( rc ); } /***************************************************************************** * * _WinStationShadowTargetSetup * * private api used to initialize the target size of a shadow * * ENTRY: * hServer (input) * target server * LogonId (input) * target logon id * pClientName (input) * pointer to client name string (domain/username) * ClientNameLength (input) * length of client name string * * EXIT: * ERROR_SUCCESS - no error * ****************************************************************************/ BOOLEAN WINAPI _WinStationShadowTargetSetup( HANDLE hServer, ULONG LogonId ) { DWORD Result; BOOLEAN rc; HANDLE_CURRENT_BINDING( hServer ); RpcTryExcept { rc = RpcWinStationShadowTargetSetup( hServer, &Result, (LogonId == LOGONID_CURRENT) ? NtCurrentPeb()->SessionId : LogonId ); //Result = RtlNtStatusToDosError( Result ); if( !rc ) SetLastError(RtlNtStatusToDosError(Result)); } RpcExcept(I_RpcExceptionFilter(RpcExceptionCode())) { Result = RpcExceptionCode(); SetLastError( Result ); DBGPRINT(("RPC Exception %d\n",Result)); rc = FALSE; } RpcEndExcept return( rc ); } /***************************************************************************** * * _WinStationShadowTarget * * private api used to initialize the target size of a shadow * * ENTRY: * hServer (input) * target server * LogonId (input) * target logon id * pConfig (input) * pointer to WinStation config data (to configure shadow stack) * pAddress (input) * address of shadow client * 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 * * EXIT: * ERROR_SUCCESS - no error * ****************************************************************************/ NTSTATUS WINAPI _WinStationShadowTarget( HANDLE hServer, ULONG LogonId, PWINSTATIONCONFIG2 pConfig, PICA_STACK_ADDRESS pAddress, PVOID pModuleData, ULONG ModuleDataLength, PVOID pThinwireData, ULONG ThinwireDataLength, PVOID pClientName, ULONG ClientNameLength ) { DWORD Result; BOOLEAN rc; HANDLE_CURRENT_BINDING( hServer ); RpcTryExcept { rc = RpcWinStationShadowTarget( hServer, &Result, (LogonId == LOGONID_CURRENT) ? NtCurrentPeb()->SessionId : LogonId, (PBYTE) pConfig, sizeof(*pConfig), (PBYTE) pAddress, sizeof(*pAddress), pModuleData, ModuleDataLength, pThinwireData, ThinwireDataLength, pClientName, ClientNameLength ); // Since a program has called us, we need to set the last error code such // that extended error information is available if (!rc) SetLastError(RtlNtStatusToDosError(Result)); } RpcExcept(I_RpcExceptionFilter(RpcExceptionCode())) { Result = RpcExceptionCode(); SetLastError( Result ); DBGPRINT(("RPC Exception %d\n",Result)); rc = FALSE; } RpcEndExcept return Result; } /******************************************************************************* * * WinStationFreeMemory * * Called to free memory which was allocated by a WinStation API. * * ENTRY: * pBuffer (input) * * EXIT: * TRUE -- The install operation succeeded. * ******************************************************************************/ BOOLEAN WINAPI WinStationFreeMemory( PVOID pBuffer ) { if ( pBuffer ) LocalFree( pBuffer ); return( TRUE ); } /******************************************************************************* * * WinStationFreeGAPMemory * * Called to free memory which was allocated by the WinStationGetAllProcesses API. * * ******************************************************************************/ BOOLEAN WINAPI WinStationFreeGAPMemory(ULONG Level, PVOID pProcArray, ULONG NumberOfProcesses) { ULONG i; PTS_ALL_PROCESSES_INFO pProcessArray = (PTS_ALL_PROCESSES_INFO)pProcArray; if (Level == GAP_LEVEL_BASIC) // only level supported right now { if ( pProcessArray != NULL) { for (i=0; i < NumberOfProcesses ; i++) { if (pProcessArray[i].pTsProcessInfo != NULL) { if (((pProcessArray[i].pTsProcessInfo)->ImageName).Buffer != NULL) { // // free the ImageName string // LocalFree(((pProcessArray[i].pTsProcessInfo)->ImageName).Buffer); } // // free the Process Info buffer // LocalFree(pProcessArray[i].pTsProcessInfo); } if (pProcessArray[i].pSid != NULL) { // // free the SID // LocalFree(pProcessArray[i].pSid); } } LocalFree(pProcessArray); } return TRUE; } else { return FALSE; } } /******************************************************************************* * * WinStationGenerateLicense * * 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 install operation succeeded. * * FALSE -- The operation failed. Extended error status is available * using GetLastError. * ******************************************************************************/ BOOLEAN WINAPI WinStationGenerateLicense( HANDLE hServer, PWCHAR pSerialNumberString, PVOID pLicense, DWORD LicenseSize ) { BOOLEAN rc; DWORD Result; DWORD Length; WCHAR* rpcBuffer; HANDLE_CURRENT_BINDING( hServer ); RpcTryExcept { if ( pSerialNumberString ) { Length = (lstrlenW( pSerialNumberString ) + 1) * sizeof(WCHAR); /* * rpcBuffer is a workaround for 229753. */ rpcBuffer = LocalAlloc(LPTR, Length * sizeof(WCHAR)); if (rpcBuffer != NULL) { CopyMemory(rpcBuffer, pSerialNumberString, Length); } else { SetLastError(ERROR_OUTOFMEMORY); return(FALSE); } } else { Length = 0; rpcBuffer = NULL; } rc = RpcWinStationGenerateLicense( hServer, &Result, rpcBuffer, Length, (PCHAR)pLicense, LicenseSize ); } RpcExcept(I_RpcExceptionFilter(RpcExceptionCode())) { Result = RpcExceptionCode(); DBGPRINT(("RPC Exception %d\n",Result)); rc = FALSE; } RpcEndExcept if (rpcBuffer != NULL) { LocalFree(rpcBuffer); } if( !rc ) SetLastError(Result); return( rc ); } /******************************************************************************* * * WinStationInstallLicense * * 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 * using GetLastError. * ******************************************************************************/ BOOLEAN WinStationInstallLicense( HANDLE hServer, PVOID pLicense, DWORD LicenseSize ) { BOOLEAN rc; DWORD Result; HANDLE_CURRENT_BINDING( hServer ); RpcTryExcept { rc = RpcWinStationInstallLicense( hServer, &Result, (PCHAR) pLicense, LicenseSize ); if( !rc ) SetLastError(Result); } RpcExcept(I_RpcExceptionFilter(RpcExceptionCode())) { Result = RpcExceptionCode(); SetLastError( Result ); DBGPRINT(("RPC Exception %d\n",Result)); rc = FALSE; } RpcEndExcept return( rc ); } /******************************************************************************* * * WinStationEnumerateLicenses * * Called to return the list of valid licenses. * * ENTRY: * hServer (input) * Server handle * ppLicense (output) * Points to a pointer to a buffer to receive the enumeration results, * which are returned as an array of LICENSE structures. The buffer is * allocated within this API and is disposed of using * WinStationFreeMemory. * pEntries (output) * Points to a variable specifying the number of entries read. * * EXIT: * * TRUE -- The enumerate operation succeeded. * * FALSE -- The operation failed. Extended error status is available * using GetLastError. * ******************************************************************************/ #define _LICENSE_REQUEST_SIZE 10 #define _LICENSE_SIZE 1024 // This is arbitrary BOOLEAN WinStationEnumerateLicenses( HANDLE hServer, PVOID *ppLicense, DWORD *pEntries ) { ULONG ByteCount; ULONG BumpSize; ULONG TotalSize; LONG Index; int i; BOOLEAN rc; DWORD Result; HANDLE_CURRENT_BINDING( hServer ); BumpSize = _LICENSE_SIZE * _LICENSE_REQUEST_SIZE; TotalSize = 0; *ppLicense = NULL; *pEntries = 0; Index = 0; for ( ;; ) { PVOID pNewLicense; LONG BumpEntries; /* * Allocate a enough memory for _LICENSE_REQUEST_SIZE more * entries. */ pNewLicense = LocalAlloc( 0, TotalSize + BumpSize ); if ( !pNewLicense ) { if ( *ppLicense ) WinStationFreeMemory( *ppLicense ); SetLastError( ERROR_OUTOFMEMORY ); return( FALSE ); } /* * If this is not the first pass through, then copy * the previous buffer's contents to the new buffer. */ if ( TotalSize ) { RtlCopyMemory( pNewLicense, *ppLicense, TotalSize ); WinStationFreeMemory( *ppLicense ); } *ppLicense = pNewLicense; /* * Get up to _LICENSE_REQUEST_SIZE Licenses */ ByteCount = BumpSize; BumpEntries = _LICENSE_REQUEST_SIZE; RpcTryExcept { rc = RpcWinStationEnumerateLicenses( hServer, &Result, &Index, &BumpEntries, (PCHAR) (((PCHAR) *ppLicense) + TotalSize), ByteCount, &ByteCount ); Result = rc ? ERROR_SUCCESS : Result; } RpcExcept(I_RpcExceptionFilter(RpcExceptionCode())) { Result = RpcExceptionCode(); DBGPRINT(("RPC Exception %d\n",Result)); } RpcEndExcept if ( Result != ERROR_SUCCESS && Result != ERROR_NO_MORE_ITEMS ) { SetLastError( Result ); return( FALSE ); } else { /* * Bump the Total Size of the License buffer by the size of * the request */ TotalSize += BumpSize; /* * Include the new Licenses in the entry count */ *pEntries += BumpEntries; if ( Result == ERROR_NO_MORE_ITEMS ) { return( TRUE ); } } } // for ( ;; ) } /******************************************************************************* * * WinStationActivateLicense * * Called to Activate a license for a given License * * ENTRY: * hServer (input) * Server handle * pLicense (input/output) * Pointer to a License structure that will be activated * 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 install operation succeeded. * * FALSE -- The operation failed. Extended error status is available * using GetLastError. * ******************************************************************************/ BOOLEAN WinStationActivateLicense( HANDLE hServer, PVOID pLicense, DWORD LicenseSize, PWCHAR pActivationCode ) { BOOLEAN rc; DWORD Result; DWORD Length; WCHAR* rpcBuffer; HANDLE_CURRENT_BINDING( hServer ); RpcTryExcept { if ( pActivationCode ) { Length = (lstrlenW( pActivationCode ) + 1) * sizeof(WCHAR); /* * rpcBuffer is a workaround for 229753. */ rpcBuffer = LocalAlloc(LPTR, Length * sizeof(WCHAR)); if (rpcBuffer != NULL) { CopyMemory(rpcBuffer, pActivationCode, Length); } else { SetLastError(ERROR_OUTOFMEMORY); return(FALSE); } } else { Length = 0; rpcBuffer = NULL; } rc = RpcWinStationActivateLicense( hServer, &Result, (PCHAR)pLicense, LicenseSize, rpcBuffer, Length ); } RpcExcept(I_RpcExceptionFilter(RpcExceptionCode())) { Result = RpcExceptionCode(); DBGPRINT(("RPC Exception %d\n",Result)); rc = FALSE; } RpcEndExcept if (rpcBuffer != NULL) { LocalFree(rpcBuffer); } if( !rc ) SetLastError(Result); return( rc ); } /***************************************************************************** * * WinStationQueryLicense * * 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: * ERROR_SUCCESS - no error * ****************************************************************************/ BOOLEAN WINAPI WinStationQueryLicense( HANDLE hServer, PVOID pLicenseCounts, ULONG ByteCount ) { DWORD Result; BOOLEAN rc; HANDLE_CURRENT_BINDING( hServer ); RpcTryExcept { memset( pLicenseCounts, 0, ByteCount ); rc = RpcWinStationQueryLicense( hServer, &Result, (PCHAR) pLicenseCounts, ByteCount ); Result = RtlNtStatusToDosError( Result ); if( !rc ) SetLastError(Result); } RpcExcept(I_RpcExceptionFilter(RpcExceptionCode())) { Result = RpcExceptionCode(); SetLastError( Result ); DBGPRINT(("RPC Exception %d\n",Result)); rc = FALSE; } RpcEndExcept return( rc ); } /***************************************************************************** * * WinStationQueryUpdateRequired * * Query the license(s) on the WinFrame server and determine if an * update is required. (worker) * * ENTRY: * hServer (input) * Server handle * pUpdateFlag (output) * Update flag, set if an update is required * * EXIT: * ERROR_SUCCESS - no error * ****************************************************************************/ BOOLEAN WINAPI WinStationQueryUpdateRequired( HANDLE hServer, PULONG pUpdateFlag ) { DWORD Result; BOOLEAN rc; HANDLE_CURRENT_BINDING( hServer ); RpcTryExcept { rc = RpcWinStationQueryUpdateRequired( hServer, &Result, pUpdateFlag ); Result = RtlNtStatusToDosError( Result ); if( !rc ) SetLastError(Result); } RpcExcept(I_RpcExceptionFilter(RpcExceptionCode())) { Result = RpcExceptionCode(); SetLastError( Result ); DBGPRINT(("RPC Exception %d\n",Result)); rc = FALSE; } RpcEndExcept return( rc ); } /******************************************************************************* * * WinStationRemoveLicense * * 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 install operation succeeded. * * FALSE -- The operation failed. Extended error status is available * using GetLastError. * ******************************************************************************/ BOOLEAN WinStationRemoveLicense( HANDLE hServer, PVOID pLicense, DWORD LicenseSize ) { BOOLEAN rc; DWORD Result; HANDLE_CURRENT_BINDING( hServer ); RpcTryExcept { rc = RpcWinStationRemoveLicense( hServer, &Result, (PCHAR) pLicense, LicenseSize ); if( !rc ) SetLastError(Result); } RpcExcept(I_RpcExceptionFilter(RpcExceptionCode())) { Result = RpcExceptionCode(); SetLastError( Result ); DBGPRINT(("RPC Exception %d\n",Result)); rc = FALSE; } RpcEndExcept return( rc ); } /******************************************************************************* * * WinStationSetPoolCount * * Called to change the PoolCount for a given License * * ENTRY: * hServer (input) * Server handle * pLicense (input/output) * Pointer to a License structure that will be changed * 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 * using GetLastError. * ******************************************************************************/ BOOLEAN WinStationSetPoolCount( HANDLE hServer, PVOID pLicense, DWORD LicenseSize ) { BOOLEAN rc; DWORD Result; HANDLE_CURRENT_BINDING( hServer ); RpcTryExcept { rc = RpcWinStationSetPoolCount( hServer, &Result, (PCHAR) pLicense, LicenseSize ); if( !rc ) SetLastError(Result); } RpcExcept(I_RpcExceptionFilter(RpcExceptionCode())) { Result = RpcExceptionCode(); SetLastError( Result ); DBGPRINT(("RPC Exception %d\n",Result)); rc = FALSE; } RpcEndExcept return( rc ); } /***************************************************************************** * * _WinStationAnnoyancePopup * * Comment * * ENTRY: * Param1 (input/output) * Comments * * EXIT: * ERROR_SUCCESS - no error * ****************************************************************************/ BOOLEAN WINAPI _WinStationAnnoyancePopup( HANDLE hServer, ULONG LogonId ) { BOOLEAN rc; DWORD Result; HANDLE_CURRENT_BINDING( hServer ); RpcTryExcept { rc = RpcWinStationAnnoyancePopup( hServer, &Result, (LogonId == LOGONID_CURRENT) ? NtCurrentPeb()->SessionId : LogonId ); Result = RtlNtStatusToDosError( Result ); if( !rc ) SetLastError(Result); } RpcExcept(I_RpcExceptionFilter(RpcExceptionCode())) { Result = RpcExceptionCode(); SetLastError( Result ); DBGPRINT(("RPC Exception %d\n",Result)); rc = FALSE; } RpcEndExcept return( rc ); } /***************************************************************************** * * _WinStationCallback * * Comment * * ENTRY: * Param1 (input/output) * Comments * * EXIT: * ERROR_SUCCESS - no error * ****************************************************************************/ BOOLEAN WINAPI _WinStationCallback( HANDLE hServer, ULONG LogonId, LPWSTR pPhoneNumber ) { BOOLEAN rc; DWORD Result; DWORD Length; WCHAR* rpcBuffer; HANDLE_CURRENT_BINDING_NO_SERVER( hServer ); if( hServer == RPC_HANDLE_NO_SERVER ) { return TRUE; } RpcTryExcept { if( pPhoneNumber ) { Length = (lstrlenW( pPhoneNumber ) + 1) * sizeof(WCHAR); /* * rpcBuffer is a workaround for 229753. */ rpcBuffer = LocalAlloc(LPTR, Length * sizeof(WCHAR)); if (rpcBuffer != NULL) { CopyMemory(rpcBuffer, pPhoneNumber, Length); } else { SetLastError(ERROR_OUTOFMEMORY); return(FALSE); } } else { Length = 0; rpcBuffer = NULL; } rc = RpcWinStationCallback( hServer, &Result, (LogonId == LOGONID_CURRENT) ? NtCurrentPeb()->SessionId : LogonId, rpcBuffer, Length ); Result = RtlNtStatusToDosError( Result ); } RpcExcept(I_RpcExceptionFilter(RpcExceptionCode())) { Result = RpcExceptionCode(); DBGPRINT(("RPC Exception %d\n",Result)); rc = FALSE; } RpcEndExcept if (rpcBuffer != NULL) { LocalFree(rpcBuffer); } if( !rc ) SetLastError(Result); return( rc ); } /***************************************************************************** * * _WinStationBreakPoint * * Comment * * ENTRY: * Param1 (input/output) * Comments * * EXIT: * ERROR_SUCCESS - no error * ****************************************************************************/ BOOLEAN WINAPI _WinStationBreakPoint( HANDLE hServer, ULONG LogonId, BOOLEAN KernelFlag ) { DWORD Result; BOOLEAN rc; HANDLE_CURRENT_BINDING( hServer ); RpcTryExcept { rc = RpcWinStationBreakPoint( hServer, &Result, (LogonId == LOGONID_CURRENT) ? NtCurrentPeb()->SessionId : LogonId, KernelFlag ); Result = RtlNtStatusToDosError( Result ); if( !rc ) SetLastError(Result); } RpcExcept(I_RpcExceptionFilter(RpcExceptionCode())) { Result = RpcExceptionCode(); SetLastError( Result ); DBGPRINT(("RPC Exception %d\n",Result)); rc = FALSE; } RpcEndExcept return( rc ); } /***************************************************************************** * * _WinStationReadRegistry * * Comment * * ENTRY: * Param1 (input/output) * Comments * * EXIT: * ERROR_SUCCESS - no error * ****************************************************************************/ BOOLEAN WINAPI _WinStationReadRegistry( HANDLE hServer ) { DWORD Result; BOOLEAN rc; HANDLE_CURRENT_BINDING_NO_SERVER( hServer ); if( hServer == RPC_HANDLE_NO_SERVER ) { return TRUE; } RpcTryExcept { rc = RpcWinStationReadRegistry( hServer, &Result ); Result = RtlNtStatusToDosError( Result ); if( !rc ) SetLastError(Result); } RpcExcept(I_RpcExceptionFilter(RpcExceptionCode())) { Result = RpcExceptionCode(); SetLastError( Result ); DBGPRINT(("RPC Exception %d\n",Result)); rc = FALSE; } RpcEndExcept return( rc ); } /***************************************************************************** * * _WinStationUpdateSettings * * Comment * * ENTRY: * Param1 (input/output) * Comments * * EXIT: * ERROR_SUCCESS - no error * ****************************************************************************/ BOOLEAN WINAPI _WinStationUpdateSettings( HANDLE hServer, WINSTATIONUPDATECFGCLASS SettingsClass, DWORD SettingsParameters ) { DWORD Result; BOOLEAN rc; HANDLE_CURRENT_BINDING_NO_SERVER( hServer ); if( hServer == RPC_HANDLE_NO_SERVER ) { return TRUE; } RpcTryExcept { rc = RpcWinStationUpdateSettings( hServer, &Result, (DWORD)SettingsClass, SettingsParameters ); Result = RtlNtStatusToDosError( Result ); if( !rc ) SetLastError(Result); } RpcExcept(I_RpcExceptionFilter(RpcExceptionCode())) { Result = RpcExceptionCode(); SetLastError( Result ); DBGPRINT(("RPC Exception %d\n",Result)); rc = FALSE; } RpcEndExcept return( rc ); } /***************************************************************************** * * _WinStationReInitializeSecurity * * Comment * * ENTRY: * Param1 (input/output) * Comments * * EXIT: * ERROR_SUCCESS - no error * ****************************************************************************/ BOOLEAN WINAPI _WinStationReInitializeSecurity( HANDLE hServer ) { DWORD Result; BOOLEAN rc; HANDLE_CURRENT_BINDING_NO_SERVER( hServer ); if( hServer == RPC_HANDLE_NO_SERVER ) { return TRUE; } RpcTryExcept { rc = RpcWinStationReInitializeSecurity( hServer, &Result ); Result = RtlNtStatusToDosError( Result ); if( !rc ) SetLastError(Result); } RpcExcept(I_RpcExceptionFilter(RpcExceptionCode())) { Result = RpcExceptionCode(); SetLastError( Result ); DBGPRINT(("RPC Exception %d\n",Result)); rc = FALSE; } RpcEndExcept return( rc ); } /***************************************************************************** * * _WinStationWaitForConnect * * Comment * * ENTRY: * Param1 (input/output) * Comments * * EXIT: * ERROR_SUCCESS - no error * ****************************************************************************/ BOOLEAN WINAPI _WinStationWaitForConnect( VOID ) { DWORD Result; BOOLEAN rc; HANDLE hServer = SERVERNAME_CURRENT; HANDLE_CURRENT_BINDING_NO_SERVER( hServer ); if( hServer == RPC_HANDLE_NO_SERVER ) { if (NtCurrentPeb()->SessionId != 0) { DbgPrint("hServer == RPC_HANDLE_NO_SERVER for SessionId %d\n",NtCurrentPeb()->SessionId); ASSERT(FALSE); return FALSE; } else { return TRUE; } } RpcTryExcept { rc = RpcWinStationWaitForConnect( hServer, &Result, NtCurrentPeb()->SessionId, GetCurrentProcessId() ); Result = RtlNtStatusToDosError( Result ); if( !rc ) SetLastError(Result); } RpcExcept(I_RpcExceptionFilter(RpcExceptionCode())) { Result = RpcExceptionCode(); SetLastError( Result ); DBGPRINT(("RPC Exception %d\n",Result)); rc = FALSE; } RpcEndExcept return( rc ); } /***************************************************************************** * * _WinStationNotifyLogon * * Comment * * ENTRY: * Param1 (input/output) * Comments * * EXIT: * ERROR_SUCCESS - no error * ****************************************************************************/ BOOLEAN WINAPI _WinStationNotifyLogon( BOOLEAN fUserIsAdmin, HANDLE UserToken, PWCHAR pDomain, PWCHAR pUserName, PWCHAR pPassword, UCHAR Seed, PUSERCONFIGW pUserConfig, BOOLEAN *pfIsRedirected ) { BOOLEAN rc; DWORD Result; DWORD DomainLength; DWORD UserNameLength; DWORD PasswordLength; HANDLE hServer = SERVERNAME_CURRENT; HANDLE ReadyEventHandle; DWORD TermSrvWaitTime = 180 * 1000; // 3 Minutes WCHAR* rpcBuffer1 = NULL; WCHAR* rpcBuffer2 = NULL; WCHAR* rpcBuffer3 = NULL; HANDLE_CURRENT_BINDING_NO_SERVER( hServer ); if( hServer == RPC_HANDLE_NO_SERVER ) { return TRUE; } // // Wait for the TermSrvReadyEvent to be set by TERMSRV.EXE. This // event indicates that TermSrv is initialized to the point that // the data used by _WinStationNotifyLogon() is available. // ReadyEventHandle = OpenEvent(SYNCHRONIZE, FALSE, TEXT("Global\\TermSrvReadyEvent")); if (ReadyEventHandle != NULL) { if (WaitForSingleObject(ReadyEventHandle, TermSrvWaitTime) != 0) { DBGPRINT(("WinLogon: Wait for ReadyEventHandle failed\n")); } CloseHandle(ReadyEventHandle); } else { DBGPRINT(("WinLogon: Create failed for ReadyEventHandle\n")); } RpcTryExcept { if( pDomain ) { DomainLength = (lstrlenW( pDomain ) + 1) * sizeof(WCHAR); /* * rpcBuffer[1,2,3] is a workaround for 229753. */ rpcBuffer1 = LocalAlloc(LPTR, DomainLength * sizeof(WCHAR)); if (rpcBuffer1 != NULL) { CopyMemory(rpcBuffer1, pDomain, DomainLength); } else { Result = ERROR_OUTOFMEMORY; rc = FALSE; goto Error; } } else { DomainLength = 0; rpcBuffer1 = NULL; } if( pUserName ) { UserNameLength = (lstrlenW( pUserName ) + 1) * sizeof(WCHAR); rpcBuffer2 = LocalAlloc(LPTR, UserNameLength * sizeof(WCHAR)); if (rpcBuffer2 != NULL) { CopyMemory(rpcBuffer2, pUserName, UserNameLength); } else { Result = ERROR_OUTOFMEMORY; rc = FALSE; goto Error; } } else { UserNameLength = 0; rpcBuffer2 = NULL; } if( pPassword ) { PasswordLength = (lstrlenW( pPassword ) + 1) * sizeof(WCHAR); rpcBuffer3 = LocalAlloc(LPTR, PasswordLength * sizeof(WCHAR)); if (rpcBuffer3 != NULL) { CopyMemory(rpcBuffer3, pPassword, PasswordLength); } else { Result = ERROR_OUTOFMEMORY; rc = FALSE; goto Error; } } else { PasswordLength = 0; rpcBuffer3 = NULL; } rc = RpcWinStationNotifyLogon( hServer, &Result, NtCurrentPeb()->SessionId, GetCurrentProcessId(), fUserIsAdmin, (DWORD)(INT_PTR)UserToken, rpcBuffer1, DomainLength, rpcBuffer2, UserNameLength, rpcBuffer3, PasswordLength, Seed, (PCHAR)pUserConfig, sizeof(*pUserConfig), pfIsRedirected ); if( !rc ) { Result = RtlNtStatusToDosError( Result ); } } RpcExcept(I_RpcExceptionFilter(RpcExceptionCode())) { Result = RpcExceptionCode(); DBGPRINT(("RPC Exception %d\n",Result)); rc = FALSE; } RpcEndExcept Error: if (rpcBuffer1 != NULL) { LocalFree(rpcBuffer1); } if (rpcBuffer2 != NULL) { LocalFree(rpcBuffer2); } if (rpcBuffer3 != NULL) { LocalFree(rpcBuffer3); } if( !rc ) SetLastError(Result); return( rc ); } /***************************************************************************** * * _WinStationNotifyLogoff * * Comment * * ENTRY: * Param1 (input/output) * Comments * * EXIT: * ERROR_SUCCESS - no error * ****************************************************************************/ BOOLEAN WINAPI _WinStationNotifyLogoff( VOID ) { DWORD Result; BOOLEAN rc; HANDLE hServer = SERVERNAME_CURRENT; HANDLE_CURRENT_BINDING_NO_SERVER( hServer ); if( hServer == RPC_HANDLE_NO_SERVER ) { return TRUE; } RpcTryExcept { rc = RpcWinStationNotifyLogoff( hServer, NtCurrentPeb()->SessionId, GetCurrentProcessId(), &Result ); Result = RtlNtStatusToDosError( Result ); if( !rc ) SetLastError(Result); } RpcExcept(I_RpcExceptionFilter(RpcExceptionCode())) { Result = RpcExceptionCode(); SetLastError( Result ); DBGPRINT(("RPC Exception %d\n",Result)); rc = FALSE; } RpcEndExcept return( rc ); } /***************************************************************************** * * _WinStationNotifyNewSession * * Comment * * ENTRY: * Param1 (input/output) * Comments * * EXIT: * ERROR_SUCCESS - no error * ****************************************************************************/ BOOLEAN WINAPI _WinStationNotifyNewSession( HANDLE hServer, ULONG LogonId ) { DWORD Result; BOOLEAN rc; // // If the local machine has no TSRPC interface running, this is most // likely the console winlogon attempting to logon before termsrv.exe // is running. // HANDLE_CURRENT_BINDING_NO_SERVER( hServer ); if( hServer == RPC_HANDLE_NO_SERVER ) { return(TRUE); } RpcTryExcept { rc = RpcWinStationNotifyNewSession( hServer, &Result, (LogonId == LOGONID_CURRENT) ? NtCurrentPeb()->SessionId : LogonId ); Result = RtlNtStatusToDosError( Result ); if( !rc ) SetLastError(Result); } RpcExcept(I_RpcExceptionFilter(RpcExceptionCode())) { Result = RpcExceptionCode(); SetLastError( Result ); DBGPRINT(("RPC Exception %d\n",Result)); rc = FALSE; } RpcEndExcept return( rc ); } /***************************************************************************** * * _RpcServerNWLogonSetAdmin * * Comment * * ENTRY: * Param1 (input/output) * Comments * * EXIT: * ERROR_SUCCESS - no error * ****************************************************************************/ BOOLEAN WINAPI _NWLogonSetAdmin( HANDLE hServer, PWCHAR pServerName, PNWLOGONADMIN pNWLogon ) { BOOLEAN rc; DWORD Result; DWORD ServerNameLength; WCHAR* rpcBuffer; HANDLE_CURRENT_BINDING( hServer ); if (pServerName) { ServerNameLength = (lstrlenW(pServerName) + 1) * sizeof(WCHAR); /* * rpcBuffer is a workaround for bug 229753. The bug can't be fixed * completely without breaking TS4 clients. */ rpcBuffer = LocalAlloc(LPTR, ServerNameLength * sizeof(WCHAR)); if (rpcBuffer != NULL) { CopyMemory(rpcBuffer, pServerName, ServerNameLength); } else { SetLastError(ERROR_OUTOFMEMORY); return(FALSE); } } else { ServerNameLength = 0; rpcBuffer = NULL; } RpcTryExcept { rc = RpcServerNWLogonSetAdmin( hServer, &Result, rpcBuffer, ServerNameLength, (PCHAR)pNWLogon, sizeof(NWLOGONADMIN) ); Result = RtlNtStatusToDosError( Result ); } RpcExcept(I_RpcExceptionFilter(RpcExceptionCode())) { Result = RpcExceptionCode(); DBGPRINT(("RPC Exception %d\n",Result)); rc = FALSE; } RpcEndExcept if (rpcBuffer != NULL) { LocalFree(rpcBuffer); } if( !rc ) SetLastError(Result); return( rc ); } /***************************************************************************** * * _RpcServerNWLogonQueryAdmin * * Comment * * ENTRY: * Param1 (input/output) * Comments * * EXIT: * ERROR_SUCCESS - no error * ****************************************************************************/ BOOLEAN WINAPI _NWLogonQueryAdmin( HANDLE hServer, PWCHAR pServerName, PNWLOGONADMIN pNWLogon ) { BOOLEAN rc; DWORD Result; DWORD ServerNameLength; WCHAR* rpcBuffer; HANDLE_CURRENT_BINDING( hServer ); if (pServerName) { ServerNameLength = (lstrlenW(pServerName) + 1) * sizeof(WCHAR); /* * rpcBuffer is a workaround for bug 229753. The bug can't be fixed * completely without breaking TS4 clients. */ rpcBuffer = LocalAlloc(LPTR, ServerNameLength * sizeof(WCHAR)); if (rpcBuffer != NULL) { CopyMemory(rpcBuffer, pServerName, ServerNameLength); } else { SetLastError(ERROR_OUTOFMEMORY); return(FALSE); } } else { ServerNameLength = 0; rpcBuffer = NULL; } RpcTryExcept { rc = RpcServerNWLogonQueryAdmin( hServer, &Result, rpcBuffer, ServerNameLength, (PCHAR)pNWLogon, sizeof(NWLOGONADMIN) ); Result = RtlNtStatusToDosError( Result ); } RpcExcept(I_RpcExceptionFilter(RpcExceptionCode())) { Result = RpcExceptionCode(); DBGPRINT(("RPC Exception %d\n",Result)); rc = FALSE; } RpcEndExcept if (rpcBuffer != NULL) { LocalFree(rpcBuffer); } if( !rc ) SetLastError(Result); return( rc ); } /******************************************************************************* * * _WinStationCheckForApplicationName * * Handles 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 _WinStationCheckForApplicationName( HANDLE hServer, ULONG LogonId, PWCHAR pUserName, DWORD UserNameSize, PWCHAR pDomain, DWORD DomainSize, PWCHAR pPassword, DWORD *pPasswordSize, DWORD MaxPasswordSize, PCHAR pSeed, PBOOLEAN pfPublished, PBOOLEAN pfAnonymous ) { DWORD Result; BOOLEAN rc; WCHAR* rpcBufferName; WCHAR* rpcBufferDomain; WCHAR* rpcBufferPassword; HANDLE_CURRENT_BINDING( hServer ); // Since, due to legacy clients, we cannot change the interface, // as a workarround to bug#265954, we double the size of RPC Buffers. rpcBufferName = LocalAlloc(LPTR, UserNameSize * sizeof(WCHAR)); if (rpcBufferName != NULL) { CopyMemory(rpcBufferName, pUserName, UserNameSize); } else { SetLastError(ERROR_OUTOFMEMORY); return(FALSE); } rpcBufferDomain = LocalAlloc(LPTR, DomainSize * sizeof(WCHAR)); if (rpcBufferDomain != NULL) { CopyMemory(rpcBufferDomain, pDomain, DomainSize); } else { LocalFree(rpcBufferName); SetLastError(ERROR_OUTOFMEMORY); return(FALSE); } rpcBufferPassword = LocalAlloc(LPTR,MaxPasswordSize * sizeof(WCHAR)); if (rpcBufferPassword != NULL) { CopyMemory(rpcBufferPassword, pPassword, MaxPasswordSize); } else { LocalFree(rpcBufferName); LocalFree(rpcBufferDomain); SetLastError(ERROR_OUTOFMEMORY); return(FALSE); } RpcTryExcept { rc = RpcWinStationCheckForApplicationName( hServer, &Result, LogonId, rpcBufferName, UserNameSize, rpcBufferDomain, DomainSize, rpcBufferPassword, pPasswordSize, MaxPasswordSize, pSeed, pfPublished, pfAnonymous ); Result = RtlNtStatusToDosError( Result ); } RpcExcept(I_RpcExceptionFilter(RpcExceptionCode())) { Result = RpcExceptionCode(); DBGPRINT(("RPC Exception %d\n",Result)); rc = FALSE; } RpcEndExcept LocalFree(rpcBufferName); LocalFree(rpcBufferDomain); LocalFree(rpcBufferPassword); if( !rc ) SetLastError(Result); return( rc ); } /******************************************************************************* * * _WinStationGetApplicationInfo * * 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 _WinStationGetApplicationInfo( HANDLE hServer, ULONG LogonId, PBOOLEAN pfPublished, PBOOLEAN pfAnonymous ) { DWORD Result; BOOLEAN rc; HANDLE_CURRENT_BINDING( hServer ); RpcTryExcept { rc = RpcWinStationGetApplicationInfo( hServer, &Result, LogonId, pfPublished, pfAnonymous ); Result = RtlNtStatusToDosError( Result ); if( !rc ) SetLastError(Result); } RpcExcept(I_RpcExceptionFilter(RpcExceptionCode())) { Result = RpcExceptionCode(); SetLastError( Result ); DBGPRINT(("RPC Exception %d\n",Result)); rc = FALSE; } RpcEndExcept return( rc ); } /******************************************************************************* * * WinStationNtsdDebug * * Set up a debug connection for ntsd * * ENTRY: * * EXIT: * * TRUE -- The function succeeds * * FALSE -- The operation failed. Extended error status is available * using GetLastError. * ******************************************************************************/ BOOLEAN WinStationNtsdDebug( ULONG LogonId, LONG ProcessId, ULONG DbgProcessId, ULONG DbgThreadId, PVOID AttachCompletionRoutine ) { DWORD Result; BOOLEAN rc; HANDLE hServer = SERVERNAME_CURRENT; NTSDDBGPRINT(("In WinStationNtsdDebug command\n")); HANDLE_CURRENT_BINDING( hServer ); RpcTryExcept { rc = RpcWinStationNtsdDebug( hServer, &Result, LogonId, ProcessId, DbgProcessId, DbgThreadId, (DWORD_PTR) AttachCompletionRoutine ); DbgPrint("RpcWinStationNtsdDebug: returned 0x%x\n", rc); Result = RtlNtStatusToDosError( Result ); if( !rc ) SetLastError(Result); } RpcExcept(I_RpcExceptionFilter(RpcExceptionCode())) { Result = RpcExceptionCode(); SetLastError( Result ); DBGPRINT(("RPC Exception %d\n",Result)); rc = FALSE; } RpcEndExcept NTSDDBGPRINT(("WinStationNtsdDebug returning %d\n", rc)); return( rc ); } /******************************************************************************* * * WinStationGetTermSrvCountersValue * * 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 WinStationGetTermSrvCountersValue( HANDLE hServer, ULONG dwEntries, PVOID pCounter ) { DWORD Result; BOOLEAN rc; HANDLE_CURRENT_BINDING( hServer ); RpcTryExcept { rc = RpcWinStationGetTermSrvCountersValue( hServer, &Result, dwEntries, (PTS_COUNTER)pCounter ); Result = RtlNtStatusToDosError( Result ); if( !rc ) SetLastError(Result); } RpcExcept(I_RpcExceptionFilter(RpcExceptionCode())) { Result = RpcExceptionCode(); SetLastError( Result ); DBGPRINT(("RPC Exception %d\n",Result)); rc = FALSE; } RpcEndExcept return( rc ); } /***************************************************************************** * * WinStationBroadcastSystemMessageWorker * * Perform the the equivalent to Window's standard API BroadcastSystemMessage to * all Hydra sessions. This is an exported function, at least used by the PNP manager to * send a device change message to all sessions. * * LIMITATIONS: * some messages, such as WM_COPYDATA send an address pointer to some user data as lParam. * In this API. the only such case that is currently supported is for WM_DEVICECHANGE * No error code will be returned if you try to use such an unsupported message, simply the * lParam will be ignored. * * ENTRY: * hServer * this is a handle which identifies a Hydra server. For the local server, hServer * should be set to SERVERNAME_CURRENT * * sendToAllWinstations * This should be set to TRUE if you want to broadcast message to all winstations * * sessionID, * if sendToAllWinstations = FALSE, then message is only sent to only the * winstation with the specified sessionID * * 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 * see MSDN on BroadcastSystemMessage(). Be aware that POST is not allowed on any * where the wparam is a pointer to some user mode data structure. * For more info, see ntos\...\client\ntstubs.c * * lpdwRecipients * Pointer to a variable that contains and receives information about the recipients of the message. * see MSDN for more info * * uiMessage * the window's message to send, limited to WM_DEVICECHANGE and WM_SETTINGSCHANGE * at this time. * * wParam * first message param * * lParam * second message parameter * * pResponse * this is the response to the message sent, see MSDN * * idOfSessionBeingIgnored * if -1, then no sessions are ignored. Else, the id of the session passed in is ignored * * EXIT: * TRUE if all went well or * FALSE if something went wrong. * * 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. * * Also, 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 WinStationBroadcastSystemMessageWorker( HANDLE hServer, BOOL sendToAllWinstations, ULONG sessionID, ULONG timeOut, DWORD dwFlags, DWORD *lpdwRecipients, ULONG uiMessage, WPARAM wParam, LPARAM lParam, LONG *pResponse, // this is the response to the message sent DWORD idOfSessionBeingIgnored ) { DWORD Result = ERROR_SUCCESS; LONG rc; LONG status; ULONG i; LONG response=0; PLOGONID pWd; ULONG ByteCount, Index; UINT WdCount; // these are used for PNP messages PBYTE rpcBuffer=NULL; ULONG bufferSize=0; ULONG maxSize; BOOLEAN fBufferHasValidData = FALSE; // Since the PNP message uses the lparam to pass the address of a user memory location, we // need to handle this by creating our own copy of that data, and then pass it to // termServ // we may want to make this general for the future... hence use switch switch( uiMessage ) { // if this is a PNP message case WM_DEVICECHANGE: if ( lParam ) // see if the PNP message has a pointer to some user data { bufferSize = ( (DEV_BROADCAST_HDR *)(lParam))->dbch_size; rpcBuffer = LocalAlloc( LPTR, bufferSize ); if ( rpcBuffer ) { // copy from user-space into our local rpc buffer CopyMemory(rpcBuffer, (PBYTE)lParam, bufferSize ); fBufferHasValidData = TRUE; } else { SetLastError( ERROR_OUTOFMEMORY ); return ( FALSE ); } } break; // if this is a settings change message the system-CPL sends out // when an Admin changes the system env vars... case WM_SETTINGCHANGE: if ( lParam ) // see if message has a string data { // put some artificial limit on how large a buffer we are willing to use // in order to protect against malicious use of this api maxSize = 4096; bufferSize = lstrlenW( (PWCHAR) lParam ) * sizeof( WCHAR ); if ( bufferSize < maxSize ) { rpcBuffer = LocalAlloc( LPTR, bufferSize ); if ( rpcBuffer ) { // copy from user-space into our local rpc buffer CopyMemory(rpcBuffer, (PBYTE) lParam, bufferSize ); fBufferHasValidData = TRUE; } else { SetLastError( ERROR_OUTOFMEMORY ); return ( FALSE ); } } else { // we have too many // vars in the user's profile. KdPrint(("lParam length too big = %d \n", bufferSize)); break; SetLastError( ERROR_MESSAGE_EXCEEDS_MAX_SIZE ); return ( FALSE ); } } break; } // // if the rpcBuffer is still empty (meaning, this was not a PNP message), we must fill it up // with some bogus data, otherwise, we will get an RPC error of RPC_X_NULL_REF_POINTER // (error code of 1780). It looks like Rpc does not check the // bufferSize value, and it just throws an exception if the buffer is NULL. // if ( !rpcBuffer ) { rpcBuffer = LocalAlloc( LPTR, sizeof(UINT) ); if (!rpcBuffer) { SetLastError( ERROR_OUTOFMEMORY ); return ( FALSE ); } bufferSize = sizeof(UINT); fBufferHasValidData = FALSE; // note that this is set to FALSE, which means, the recepient will // not use the buffer. We do free the alloc below in either case. } HANDLE_CURRENT_BINDING_BUFFER( hServer, rpcBuffer ); WdCount = 1000; pWd = NULL; // it will be allocated by Winstation Enumerate() rc = WinStationEnumerate( hServer, &pWd, &WdCount ); /* * Do not use this flag, since no process on the session side can respond back to a console process * thru the post message mechanism, since there is no session ID abstraction in that call. */ dwFlags &= ~BSF_POSTMESSAGE; if ( rc != TRUE ) { status = GetLastError(); DBGPRINT(( "WinstationEnumerate = %d, failed at %s %d\n", status,__FILE__,__LINE__)); if ( pWd ) { WinStationFreeMemory(pWd); } ASSERT(rpcBuffer); LocalFree( rpcBuffer ); return(FALSE); } // // the loop for sending data to each winstation // for ( i=0; i < WdCount; i++ ) { // id of the session being ignored if ( pWd[i].SessionId == idOfSessionBeingIgnored) continue; // either send to all winstations, or to a specific winstation if ( sendToAllWinstations || pWd[i].SessionId == sessionID ) { // don't send message to any winstation unless it is either Active or in the disconnect state if ( pWd[i].State == State_Active || pWd[i].State == State_Disconnected) { RpcTryExcept { rc = RpcWinStationBroadcastSystemMessage( hServer, pWd[i].SessionId, timeOut, dwFlags, lpdwRecipients, uiMessage, wParam, lParam, rpcBuffer, bufferSize, fBufferHasValidData, &response ); DBGPRINT(("done with call RpcWinStationBroadcastSystemMessage() for sessionID= %d\n", pWd[i].SessionId )); *pResponse |= response; // keep an OR of all return values // @@@ // if response is -1 from any winstation, maybe we should give up and return ? } RpcExcept(I_RpcExceptionFilter(RpcExceptionCode())) { Result = RpcExceptionCode(); DBGPRINT(("RPC Exception %d in RpcWinStationBroadcastSystemMessage() for sessionID = %d \n",Result, sessionID)); rc = FALSE; // change rc to FALSE break; // get out of the for-loop, we have a problem with at least one of the winstations } RpcEndExcept } // end if winstation state check } // if ( sendToAllWinstations || pWd[i].SessionId == sessionID ) } // end of the for loop WinStationFreeMemory(pWd); LocalFree( rpcBuffer ); SetLastError( Result ); return( rc ); } /************************************************************************* * * * This struct is used to pack data passed into a workder thread which is * * altimetly passed to WinStationBroadcastSystemMessageWorker() * * * *************************************************************************/ typedef struct { HANDLE hServer; BOOL sendToAllWinstations; ULONG sessionID; ULONG timeOut; DWORD dwFlags; DWORD *lpdwRecipients; ULONG uiMessage; WPARAM wParam; LPARAM lParam; LONG *pResponse; DWORD idOfSessionBeingIgnored ; } BSM_DATA_PACKAGE; /*********************************************************************************************** * * * This is a workder thread used to make a call into WinStationBroadcastSystemMessageWorker() * * The reason for this is in certain cases, we don't want to block the caller of this func from * * processing window messages * * DWORD WINAPI WinStationBSMWorkerThread( LPVOID p ) * * ***********************************************************************************************/ DWORD WINAPI WinStationBSMWorkerThread( LPVOID p ) { DWORD rc; BSM_DATA_PACKAGE *pd = (BSM_DATA_PACKAGE *)p; rc = WinStationBroadcastSystemMessageWorker( pd->hServer , pd->sendToAllWinstations , pd->sessionID , pd->timeOut , pd->dwFlags , pd->lpdwRecipients , pd->uiMessage , pd->wParam , pd->lParam , pd->pResponse , pd->idOfSessionBeingIgnored); return rc; } /************************************************************************************************** * * * This func is used to wait on a thread, and still allow the user of this thread (aka the creator * * of this thread) to process window messages * * * **************************************************************************************************/ DWORD MsgWaitForMultipleObjectsLoop(HANDLE hEvent, DWORD dwTimeout) { while (1) { MSG msg; DWORD dwObject = MsgWaitForMultipleObjects(1, &hEvent, FALSE, dwTimeout, QS_ALLEVENTS); // Are we done waiting? switch (dwObject) { case WAIT_OBJECT_0: case WAIT_FAILED: return dwObject; case WAIT_TIMEOUT: return WAIT_TIMEOUT; case WAIT_OBJECT_0 + 1: // This PeekMessage has the side effect of processing any broadcast messages. // It doesn't matter what message we actually peek for but if we don't peek // then other threads that have sent broadcast sendmessages will hang until // hEvent is signaled. Since the process we're waiting on could be the one // that sent the broadcast message that could cause a deadlock otherwise. PeekMessage(&msg, NULL, WM_NULL, WM_USER, PM_NOREMOVE); break; } } // never gets here // return dwObject; } /***************************************************************************** * * WinStationBroadcastSystemMessage * * Perform the the equivalent to Window's standard API BroadcastSystemMessage to * all Hydra sessions. This is an exported function, at least used by the PNP manager to * send a device change message to all sessions. * * LIMITATIONS: * some messages, such as WM_COPYDATA send an address pointer to some user data as lParam. * In this API. the only such case that is currently supported is for WM_DEVICECHANGE * No error code will be returned if you try to use such an unsupported message, simply the * lParam will be ignored. * * This func will only allow WM_DEVICECHNAGE and WM_SETTINGSCHANGE to go thru. * * ENTRY: * hServer * this is a handle which identifies a Hydra server. For the local server, hServer * should be set to SERVERNAME_CURRENT * * sendToAllWinstations * This should be set to TRUE if you want to broadcast message to all winstations * * sessionID, * if sendToAllWinstations = FALSE, then message is only sent to only the * winstation with the specified sessionID * * timeOut [ IN SECONDS ] * 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 * see MSDN on BroadcastSystemMessage(). Be aware that POST is not allowed on any * where the wparam is a pointer to some user mode data structure. * For more info, see ntos\...\client\ntstubs.c * * lpdwRecipients * Pointer to a variable that contains and receives information about the recipients of the message. * see MSDN for more info * * uiMessage * the window's message to send, limited to WM_DEVICECHANGE and WM_SETTINGSCHANGE * at this time. * * wParam * first message param * * lParam * second message parameter * * pResponse * this is the response to the message sent, see MSDN * * EXIT: * TRUE if all went well or * FALSE if something went wrong. * * 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. * * Also, 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). * * For WM_SETTINGGSCHNAGE, a second therad is used to allow the caller to still process windows * messages. * For WM_DEVICECHANGE, no such thread is used. * * Comments: * For more info, please see MSDN for BroadcastSystemMessage() * ****************************************************************************/ LONG WinStationBroadcastSystemMessage( HANDLE hServer, BOOL sendToAllWinstations, ULONG sessionID, ULONG timeOut, DWORD dwFlags, DWORD *lpdwRecipients, ULONG uiMessage, WPARAM wParam, LPARAM lParam, LONG *pResponse // this is the response to the message sent ) { LONG rc; DWORD dwRecipients=0; // caller may be passing null, so use a local var 1st, and then set // value passed in by caller if an only if the caller's address is not null. BOOLEAN fBufferHasValidData = FALSE; BOOL bIsTerminalServer = !!(USER_SHARED_DATA->SuiteMask & (1 << TerminalServer)); if (!bIsTerminalServer) { return TRUE; // all is well, but we are not on a Hydra server } if (lpdwRecipients) // if caller passed in a non-NULL pointer for lpdwRec, use it's value { dwRecipients = *lpdwRecipients ; } // we may want to make this general for the future, but for now... // we only let WM_DEVICECHANGE or WM_SETTINGCHANGE messages to go thru switch ( uiMessage) { case WM_DEVICECHANGE: rc = WinStationBroadcastSystemMessageWorker( hServer, sendToAllWinstations, sessionID, timeOut, dwFlags, &dwRecipients, uiMessage, wParam, lParam, pResponse, NtCurrentPeb()->SessionId // ID of the session to be ignored. ); if (lpdwRecipients) // if caller passed in a non-NULL pointer for lpdwRec, then set value { *lpdwRecipients = dwRecipients; } break; case WM_SETTINGCHANGE: { BSM_DATA_PACKAGE d; ULONG threadID; HANDLE hThread; //pack the data passed to the thread proc d.hServer = hServer ; d.sendToAllWinstations = sendToAllWinstations; d.sessionID = sessionID; d.timeOut = timeOut; d.dwFlags = dwFlags; d.lpdwRecipients = &dwRecipients; d.uiMessage = uiMessage; d.wParam = wParam; d.lParam = lParam; d.pResponse = pResponse; d.idOfSessionBeingIgnored = NtCurrentPeb()->SessionId ; // a remote admin may change env-settings // and expect all sessions includin the // console session to be updated // A -1 means no sessions are ignored // Call from shell\cpls\system\envvar.c already sent the message to the current session hThread = CreateThread( NULL, 0, WinStationBSMWorkerThread, (void *) &d, 0 , &threadID ); if ( hThread ) { MsgWaitForMultipleObjectsLoop( hThread, INFINITE ); if (lpdwRecipients) // if caller passed in a non-NULL pointer for lpdwRec, then set value { *lpdwRecipients = *d.lpdwRecipients ; } GetExitCodeThread( hThread, &rc ); CloseHandle( hThread ); } else { rc = FALSE; } } break; default: DBGPRINT(("Request is rejected \n")); rc = FALSE; break; } return rc; } /***************************************************************************** * * WinStationSendWindowMessage * * 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: * some messages, such as WM_COPYDATA send an address pointer to some user data as lParam. * In this API, the only such case that is currently supported is for WM_DEVICECHANGE * No error code will be returned if you try to use such an unsupported message, simply the * lParam will be ignored. * * 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 [ IN SECONDS ] * 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 WinStationSendWindowMessage( HANDLE hServer, ULONG sessionID, ULONG timeOut, ULONG hWnd, // handle of destination window ULONG Msg, // message to send WPARAM wParam, // first message parameter LPARAM lParam, // second message parameter LONG *pResponse ) { DWORD Result = ERROR_SUCCESS; LONG rc = TRUE ; // these are used for PNP messages PBYTE rpcBuffer=NULL; ULONG bufferSize=0; PWCHAR lpStr; ULONG maxSize; BOOLEAN fBufferHasValidData=FALSE; BOOL bIsTerminalServer = !!(USER_SHARED_DATA->SuiteMask & (1 << TerminalServer)); if (!bIsTerminalServer) { return TRUE; // all is well, but we are not on a Hydra server } // we may want to make this general for the future, but for now... // since we only alloc/copy the lparam in case of an WM_DEVICECHANGE msg, then, only // let message with either lparam=0 to go thru, or any WM_DEVICECHANGE msg. if (lParam) { switch ( Msg) { case WM_DEVICECHANGE: case WM_SETTINGCHANGE: case WM_APPCOMMAND: case WM_KEYDOWN: case WM_KEYUP: // these are ok break; default: DBGPRINT(("Request is rejected \n")); return FALSE; break; } } HANDLE_CURRENT_BINDING( hServer ); // Since the PNP message uses the lparam to pass the address of a user memory location, we // need to handle this by creating our own copy of that data, and then pass it to // termServ switch( Msg ) { // if this is a PNP message case WM_DEVICECHANGE: if ( lParam ) // see if the PNP message has a pointer to some user data { bufferSize = ( (DEV_BROADCAST_HDR *)(lParam))->dbch_size; rpcBuffer = LocalAlloc( LPTR, bufferSize ); if ( rpcBuffer ) { // copy from user-space into our local rpc buffer CopyMemory(rpcBuffer, (PBYTE) lParam, bufferSize ); fBufferHasValidData = TRUE; } else { SetLastError( ERROR_OUTOFMEMORY ); return ( FALSE ); } } break; // if this is a settings change message the system-CPL sends out // when an Admin changes the system env vars... case WM_SETTINGCHANGE: if ( lParam ) // see if message has a string data { // put some artificial limit on how large a buffer we are willing to use // in order to protect against malicious use of this api maxSize = 4096; bufferSize = lstrlenW( (PWCHAR) lParam ) * sizeof( WCHAR ); if ( bufferSize < maxSize ) { rpcBuffer = LocalAlloc( LPTR, bufferSize ); if ( rpcBuffer ) { // copy from user-space into our local rpc buffer CopyMemory(rpcBuffer, (PBYTE) lParam, bufferSize ); fBufferHasValidData = TRUE; } else { SetLastError( ERROR_OUTOFMEMORY ); return ( FALSE ); } } else { // we have too many // vars in the user's profile. KdPrint(("lParam length too big = %d \n", bufferSize)); break; SetLastError( ERROR_MESSAGE_EXCEEDS_MAX_SIZE ); return ( FALSE ); } } break; } // if the rpcBuffer is still empty, we must fill it up with some bogus data, otherwise, we will get // an RPC error of RPC_X_NULL_REF_POINTER (error code of 1780). It looks like Rpc does not check the // bufferSize value, and it just throws an exception if the buffer is NULL. if ( !rpcBuffer ) { rpcBuffer = LocalAlloc( LPTR, sizeof(UINT) ); if ( !rpcBuffer ) { SetLastError( ERROR_OUTOFMEMORY ); return ( FALSE ); } bufferSize = sizeof(UINT); fBufferHasValidData = FALSE; // note that this is set to FALSE, which means, the recepient will // not use the buffer. We do free the alloc below in either case. } RpcTryExcept { // rc is set to TRUE for a successful call, else, FALSE rc = RpcWinStationSendWindowMessage( hServer, sessionID , timeOut, hWnd, Msg, wParam, lParam , rpcBuffer , bufferSize, fBufferHasValidData, pResponse ); //DBGPRINT(("done with call RpcWinStationSendWindowMessage() for sessionID= %d\n", sessionID )); } RpcExcept(I_RpcExceptionFilter(RpcExceptionCode())) { Result = RpcExceptionCode(); DBGPRINT(("RPC Exception %d in RpcWinStationSendWindowMessage() for sessionID = %d \n",Result, sessionID )); rc = FALSE; } RpcEndExcept LocalFree( rpcBuffer ); SetLastError( Result ); return( rc ); } /**************************************************************************** * * _WinStationUpdateUserConfig() * Used by notify when shell is about to start * This will cause an update to the userconfig of the session by loading the user profile * and reading policy data from their HKCU * * Params: * [in] UserToken, * [in] pDomain, * [in] pUserName * * Return: * TRUE if no errors, FALSE in case of error, use GetLastError() for more info * ****************************************************************************/ BOOLEAN WINAPI _WinStationUpdateUserConfig( HANDLE UserToken ) { DWORD Result; BOOLEAN rc = TRUE; HANDLE hServer = SERVERNAME_CURRENT; DWORD result; HANDLE_CURRENT_BINDING_NO_SERVER( hServer ); if( hServer == RPC_HANDLE_NO_SERVER ) { return FALSE; } RpcTryExcept { rc = RpcWinStationUpdateUserConfig( hServer, NtCurrentPeb()->SessionId, GetCurrentProcessId(), (DWORD)(INT_PTR) UserToken, &result ); } RpcExcept(I_RpcExceptionFilter(RpcExceptionCode())) { Result = RpcExceptionCode(); SetLastError( Result ); DBGPRINT(("RPC Exception %d\n",Result)); rc = FALSE; } RpcEndExcept return( rc ); } /* * WinStationQueryLogonCredentialsW * * Used by Winlogon to get auto-logon credentials from termsrv. This replaces * the dual calls to WinStationQueryInformation and * ServerQueryInetConnectorInformation. */ BOOLEAN WINAPI WinStationQueryLogonCredentialsW( PWLX_CLIENT_CREDENTIALS_INFO_V2_0 pCredentials ) { BOOLEAN fRet; HANDLE hServer; NTSTATUS Status; PCHAR pWire; ULONG cbWire; if (pCredentials == NULL) { return(FALSE); } if (pCredentials->dwType != WLX_CREDENTIAL_TYPE_V2_0) { return(FALSE); } hServer = SERVERNAME_CURRENT; HANDLE_CURRENT_BINDING(hServer); pWire = NULL; cbWire = 0; __try { fRet = RpcWinStationQueryLogonCredentials( hServer, NtCurrentPeb()->SessionId, &pWire, &cbWire ); } __except(EXCEPTION_EXECUTE_HANDLER) { fRet = FALSE; } if (fRet) { fRet = CopyCredFromWire((PWLXCLIENTCREDWIREW)pWire, pCredentials); } if (pWire != NULL) { MIDL_user_free(pWire); } return(fRet); } BOOL WINAPI WinStationUnRegisterNotificationEvent ( ULONG_PTR NotificationId ) { HANDLE hServer = SERVERNAME_CURRENT; NTSTATUS Status = STATUS_UNSUCCESSFUL; BOOL bResult = FALSE; HANDLE_CURRENT_BINDING(hServer); RpcTryExcept { bResult = RpcWinStationUnRegisterNotificationEvent( hServer, &Status, NotificationId, NtCurrentPeb()->SessionId ); if (!bResult) { // // Convert NTSTATUS to winerror, and set last error here. // SetLastError(RtlNtStatusToDosError(Status)); } } RpcExcept(I_RpcExceptionFilter(RpcExceptionCode())) { SetLastError(RpcExceptionCode()); } RpcEndExcept return (bResult); } BOOL WINAPI WinStationRegisterNotificationEvent ( HANDLE hEventHandle, ULONG_PTR *pNotificationId, DWORD dwFlags, DWORD dwMask ) { HANDLE hServer = SERVERNAME_CURRENT; NTSTATUS Status = STATUS_UNSUCCESSFUL; BOOL bResult = FALSE; if (!pNotificationId) { return FALSE; } HANDLE_CURRENT_BINDING(hServer); RpcTryExcept { ULONG_PTR Notificationid; bResult = RpcWinStationRegisterNotificationEvent( hServer, &Status, pNotificationId, (ULONG_PTR)hEventHandle, dwFlags, dwMask, NtCurrentPeb()->SessionId, GetCurrentProcessId() ); if (!bResult) { // // Convert NTSTATUS to winerror, and set last error here. // SetLastError(RtlNtStatusToDosError(Status)); } } RpcExcept(I_RpcExceptionFilter(RpcExceptionCode())) { SetLastError(RpcExceptionCode()); } RpcEndExcept return (bResult); } BOOL WINAPI WinStationRegisterConsoleNotification ( HANDLE hServer, HWND hWnd, DWORD dwFlags ) { return WinStationRegisterConsoleNotificationEx(hServer, hWnd, dwFlags, WTS_ALL_NOTIFICATION_MASK); } BOOL WINAPI WinStationRegisterConsoleNotificationEx ( HANDLE hServer, HWND hWnd, DWORD dwFlags, DWORD dwMask ) { NTSTATUS Status = STATUS_UNSUCCESSFUL; BOOL bResult = FALSE; HANDLE_CURRENT_BINDING( hServer ); RpcTryExcept { bResult = RpcWinStationRegisterConsoleNotification ( hServer, &Status, NtCurrentPeb()->SessionId, HandleToUlong(hWnd), dwFlags, dwMask ); if (!bResult) { // // Convert NTSTATUS to winerror, and set last error here. // SetLastError(RtlNtStatusToDosError(Status)); } } RpcExcept(I_RpcExceptionFilter(RpcExceptionCode())) { SetLastError(RpcExceptionCode()); } RpcEndExcept return (bResult); } BOOL WINAPI WinStationUnRegisterConsoleNotification ( HANDLE hServer, HWND hWnd ) { NTSTATUS Status = STATUS_UNSUCCESSFUL; BOOL bResult = FALSE; HANDLE_CURRENT_BINDING( hServer ); RpcTryExcept { bResult = RpcWinStationUnRegisterConsoleNotification ( hServer, &Status, NtCurrentPeb()->SessionId, HandleToUlong(hWnd) ); if (!bResult) { SetLastError(RtlNtStatusToDosError(Status)); } } RpcExcept(I_RpcExceptionFilter(RpcExceptionCode())) { SetLastError(RpcExceptionCode()); } RpcEndExcept return (bResult); } BOOLEAN CloseContextHandle(HANDLE *pHandle, DWORD *pdwResult) { BOOLEAN bSuccess; ASSERT(pHandle); ASSERT(pdwResult); RpcTryExcept { bSuccess = RpcWinStationCloseServerEx( pHandle, pdwResult ); if( !bSuccess ) *pdwResult = RtlNtStatusToDosError( *pdwResult ); } RpcExcept(I_RpcExceptionFilter(RpcExceptionCode())) { *pdwResult = RpcExceptionCode(); bSuccess = FALSE; } RpcEndExcept if (!bSuccess && (*pdwResult == RPC_S_PROCNUM_OUT_OF_RANGE)) { // // most probabaly we are calling an older server which does not have // RpcWinStationCloseServerEx, so lets give a try to RpcWinStationCloseServer // RpcTryExcept { bSuccess = RpcWinStationCloseServer( *pHandle, pdwResult ); if( !bSuccess ) *pdwResult = RtlNtStatusToDosError( *pdwResult ); } RpcExcept(I_RpcExceptionFilter(RpcExceptionCode())) { *pdwResult = RpcExceptionCode(); bSuccess = FALSE; DBGPRINT(("RPC Exception %d\n", *pdwResult)); } RpcEndExcept // // RpcWinStationCloseServer does not take care of destroying the context handle. // we we have to do it here at client end. // RpcTryExcept { RpcSsDestroyClientContext(pHandle); } RpcExcept(I_RpcExceptionFilter(RpcExceptionCode())) { ASSERT(FALSE); } RpcEndExcept } return (bSuccess); } BOOLEAN WINAPI RemoteAssistancePrepareSystemRestore( HANDLE hServer ) /*++ --*/ { DWORD Result; BOOLEAN rc; HANDLE_CURRENT_BINDING( hServer ); RpcTryExcept { rc = RpcRemoteAssistancePrepareSystemRestore( hServer, &Result ); // TermSrv RpcRemoteAssistancePrepareSystemRestore() return // win32 ERROR code or actual HRESULT code. SetLastError(Result); } RpcExcept(I_RpcExceptionFilter(RpcExceptionCode())) { Result = RpcExceptionCode(); SetLastError( Result ); DBGPRINT(("RPC Exception %d\n",Result)); rc = FALSE; } RpcEndExcept return rc; } BOOLEAN WinStationIsHelpAssistantSession( SERVER_HANDLE hServer, ULONG LogonId ) /*++ --*/ { DWORD Result; BOOLEAN rc; HANDLE_CURRENT_BINDING( hServer ); RpcTryExcept { rc = RpcWinStationIsHelpAssistantSession( hServer, &Result, (LogonId == LOGONID_CURRENT) ? NtCurrentPeb()->SessionId : LogonId ); // Since a program has called us, we need to set the last error code such // that extended error information is available // // Ticket might be invalid so we set last error but we still // return TRUE // SetLastError(RtlNtStatusToDosError(Result)); } RpcExcept(I_RpcExceptionFilter(RpcExceptionCode())) { Result = RpcExceptionCode(); SetLastError( Result ); DBGPRINT(("RPC Exception %d\n",Result)); rc = FALSE; } RpcEndExcept return rc; } /* * * WinStationGetMachinePolicy * Pass it a pointer to the callers ALREADY allocated policy struct, and this func * will fill it up from the current machine policy known to TermSrv * * Params: * hServer * this is a handle which identifies a Hydra server. For the local server, hServer * should be set to SERVERNAME_CURRENT * * pPolicy * pointer to POLICY_TS_MACHINE already allocated by the caller. * */ BOOLEAN WinStationGetMachinePolicy ( HANDLE hServer, POLICY_TS_MACHINE *pPolicy ) { NTSTATUS Status = STATUS_UNSUCCESSFUL; BOOLEAN bResult = FALSE; HANDLE_CURRENT_BINDING( hServer ); RpcTryExcept { bResult = RpcWinStationGetMachinePolicy ( hServer, (PBYTE)pPolicy, sizeof( POLICY_TS_MACHINE ) ); if (!bResult) { SetLastError(RtlNtStatusToDosError(Status)); } } RpcExcept(I_RpcExceptionFilter(RpcExceptionCode())) { SetLastError(RpcExceptionCode()); } RpcEndExcept return (bResult); } /***************************************************************************************************************** * * _WinStationUpdateClientCachedCreadentials * * Comment * Msgina calls this routine to notify TermSrv about the exact credentials specified by the User during logon * Gina also notifies us if a SmartCard was used for logging into this particular TS session * TermSrv uses this information to send back notification information to the client * This call was introduced because the notification used before did not support UPN Names * * ENTRY: * [in] pDomain * [in] pUserName * [in] fSmartCard * * EXIT: * ERROR_SUCCESS - no error * ******************************************************************************************************************/ BOOLEAN WINAPI _WinStationUpdateClientCachedCredentials( PWCHAR pDomain, PWCHAR pUserName, BOOLEAN fSmartCard ) { BOOLEAN rc; DWORD Result; DWORD DomainLength; DWORD UserNameLength; HANDLE hServer = SERVERNAME_CURRENT; HANDLE ReadyEventHandle; DWORD TermSrvWaitTime = 0; HANDLE_CURRENT_BINDING_NO_SERVER( hServer ); if( hServer == RPC_HANDLE_NO_SERVER ) { return TRUE; } // // Wait for the TermSrvReadyEvent to be set by TERMSRV.EXE. This // event indicates that TermSrv is initialized to the point that // the data used by _WinStationUpdateClientCachedCredentials() is available. // ReadyEventHandle = OpenEvent(SYNCHRONIZE, FALSE, TEXT("Global\\TermSrvReadyEvent")); if (ReadyEventHandle != NULL) { if (WaitForSingleObject(ReadyEventHandle, TermSrvWaitTime) != 0) { DBGPRINT(("WinLogon: Wait for ReadyEventHandle failed\n")); return TRUE; } CloseHandle(ReadyEventHandle); } else { DBGPRINT(("WinLogon: Create failed for ReadyEventHandle\n")); return TRUE; } RpcTryExcept { if( pDomain ) { DomainLength = lstrlenW(pDomain) + 1; } else { DomainLength = 0; } if( pUserName ) { UserNameLength = lstrlenW(pUserName) + 1; } else { UserNameLength = 0; } rc = RpcWinStationUpdateClientCachedCredentials( hServer, &Result, NtCurrentPeb()->SessionId, GetCurrentProcessId(), pDomain, DomainLength, pUserName, UserNameLength, fSmartCard ); if( !rc ) { Result = RtlNtStatusToDosError( Result ); } } RpcExcept(I_RpcExceptionFilter(RpcExceptionCode())) { Result = RpcExceptionCode(); DBGPRINT(("RPC Exception %d\n",Result)); rc = FALSE; } RpcEndExcept if( !rc ) SetLastError(Result); return( rc ); } /***************************************************************************************************************** * * _WinStationFUSCanRemoteUserDisconnect * * Comment * FUS specific call when a remote user wants to connect and hence disconnect the present User * Winlogon calls this routine so that we can ask the present user if it is ok to disconnect him * The Target LogonId, Username and Domain of the remote user are passed on from Winlogon (useful to display the MessageBox) * * ENTRY: * [in] LogonId - Session Id of the new session * [in] pDomain - Domain name of the remote user trying to connect * [in] pUserName - Username of the remote user trying to connect * * EXIT: * TRUE when local user allows the remote user to connect. FALSE otherwise. * ******************************************************************************************************************/ BOOLEAN WINAPI _WinStationFUSCanRemoteUserDisconnect( ULONG LogonId, PWCHAR pDomain, PWCHAR pUserName ) { BOOLEAN rc; DWORD Result; DWORD DomainLength; DWORD UserNameLength; HANDLE hServer = SERVERNAME_CURRENT; HANDLE ReadyEventHandle; DWORD TermSrvWaitTime = 0; HANDLE_CURRENT_BINDING_NO_SERVER( hServer ); if( hServer == RPC_HANDLE_NO_SERVER ) { return TRUE; } RpcTryExcept { if( pDomain ) { DomainLength = lstrlenW(pDomain) + 1; } else { DomainLength = 0; } if( pUserName ) { UserNameLength = lstrlenW(pUserName) + 1; } else { UserNameLength = 0; } rc = RpcWinStationFUSCanRemoteUserDisconnect( hServer, &Result, LogonId, NtCurrentPeb()->SessionId, GetCurrentProcessId(), pDomain, DomainLength, pUserName, UserNameLength ); if( !rc ) { Result = RtlNtStatusToDosError( Result ); } } RpcExcept(I_RpcExceptionFilter(RpcExceptionCode())) { Result = RpcExceptionCode(); DBGPRINT(("RPC Exception %d\n",Result)); rc = FALSE; } RpcEndExcept if( !rc ) SetLastError(Result); return( rc ); } /***************************************************************************** * * WinStationCheckLoopBack * * Check if there is a loopback when a client tries to connect * * ENTRY: * IN hServer : open RPC server handle * IN ClientSessionId : ID of the Session from which the Client was started * IN TargetLogonId : Session ID to which the client is trying to connect to * IN pTargetServerName : name of target server * * EXIT: * TRUE if there is a Loopback. FALSE otherwise. * ****************************************************************************/ BOOLEAN WINAPI WinStationCheckLoopBack( HANDLE hServer, ULONG ClientSessionId, ULONG TargetLogonId, LPWSTR pTargetServerName ) { DWORD NameSize; DWORD Result; BOOLEAN rc; HANDLE_CURRENT_BINDING_NO_SERVER( hServer ); if( hServer == RPC_HANDLE_NO_SERVER ) { return TRUE; } RpcTryExcept { if (pTargetServerName) { NameSize = lstrlenW(pTargetServerName) + 1; } else { NameSize = 0; } rc = RpcWinStationCheckLoopBack( hServer, &Result, ClientSessionId, TargetLogonId, pTargetServerName, NameSize ); Result = RtlNtStatusToDosError( Result ); } RpcExcept(I_RpcExceptionFilter(RpcExceptionCode())) { Result = RpcExceptionCode(); DBGPRINT(("RPC Exception %d\n",Result)); rc = FALSE; } RpcEndExcept if( !rc ) SetLastError(Result); return( rc ); } // // generic routine that can support all kind of protocol but this will // require including tdi.h // BOOLEAN WinStationConnectCallback( HANDLE hServer, DWORD Timeout, ULONG AddressType, PBYTE pAddress, ULONG AddressSize ) { BOOLEAN rc; DWORD Result; HANDLE_CURRENT_BINDING( hServer ); RpcTryExcept { rc = RpcConnectCallback( hServer, &Result, Timeout, AddressType, pAddress, AddressSize ); if( !rc ) SetLastError( RtlNtStatusToDosError(Result) ); } RpcExcept(I_RpcExceptionFilter(RpcExceptionCode())) { Result = RpcExceptionCode(); SetLastError( Result ); DBGPRINT(("RPC Exception %d\n",Result)); rc = FALSE; } RpcEndExcept return( rc ); } /***************************************************************************************************************** * * _WinStationNotifyDisconnectPipe * * Comment * This routine is called by the temperory winlogon created during console reconnect, when it wants to inform * the session 0 winlogon to disconnect the autologon Named Pipe. This can happen in some error handling paths * during console reconnect. * * ENTRY: None * * EXIT: * TRUE when notification succeeded. FALSE otherwise. * ******************************************************************************************************************/ BOOLEAN WINAPI _WinStationNotifyDisconnectPipe( VOID ) { BOOLEAN rc; DWORD Result; HANDLE hServer = SERVERNAME_CURRENT; HANDLE_CURRENT_BINDING_NO_SERVER( hServer ); if( hServer == RPC_HANDLE_NO_SERVER ) { return TRUE; } RpcTryExcept { rc = RpcWinStationNotifyDisconnectPipe( hServer, &Result, NtCurrentPeb()->SessionId, GetCurrentProcessId() ); if( !rc ) { Result = RtlNtStatusToDosError( Result ); } } RpcExcept(I_RpcExceptionFilter(RpcExceptionCode())) { Result = RpcExceptionCode(); DBGPRINT(("RPC Exception %d\n",Result)); rc = FALSE; } RpcEndExcept if( !rc ) SetLastError(Result); return( rc ); } /***************************************************************************************************************** * * _WinStationSessionInitialized * * Comment * This routine is called by the winlogon of a newly created session after it is done creating the * windowstation and desktops for the new session * * ENTRY: None * * EXIT: * TRUE when everything goes fine. FALSE otherwise. * ******************************************************************************************************************/ BOOLEAN WINAPI _WinStationSessionInitialized( VOID ) { BOOLEAN rc; DWORD Result; HANDLE hServer = SERVERNAME_CURRENT; HANDLE_CURRENT_BINDING_NO_SERVER( hServer ); if( hServer == RPC_HANDLE_NO_SERVER ) { return TRUE; } RpcTryExcept { rc = RpcWinStationSessionInitialized( hServer, &Result, NtCurrentPeb()->SessionId, GetCurrentProcessId() ); if( !rc ) { Result = RtlNtStatusToDosError( Result ); } } RpcExcept(I_RpcExceptionFilter(RpcExceptionCode())) { Result = RpcExceptionCode(); DBGPRINT(("RPC Exception %d\n",Result)); rc = FALSE; } RpcEndExcept if( !rc ) SetLastError(Result); return( rc ); } /******************************************************************************* * * WinStationAutoReconnect * * Atomically: * 1) Queries a winstation to see if it should be autoreconnected * and which session ID to autoreconnect to * 2) Performs security checks to ensure session is authorized to ARC * 3) Auto reconnect is done * * ENTRY: * * flags (input) * Extra settings, currently unused * * EXIT: * The return value is an NTSTATUS code which could have the infromational * class set to specify the call succeeded but autoreconnect did not happen * ******************************************************************************/ ULONG WINAPI WinStationAutoReconnect( ULONG flags ) { DWORD Result; BOOLEAN rc; HANDLE hServer = SERVERNAME_CURRENT; HANDLE_CURRENT_BINDING( hServer ); RpcTryExcept { rc = RpcWinStationAutoReconnect( hServer, &Result, NtCurrentPeb()->SessionId, flags ); } RpcExcept(I_RpcExceptionFilter(RpcExceptionCode())) { Result = RpcExceptionCode(); SetLastError( Result ); DBGPRINT(("RPC Exception %d\n",Result)); rc = FALSE; } RpcEndExcept return( Result ); } /***************************************************************************************************************** * * WinStationCheckAccess * * Comment * Check if the User has the desired access to a WinStation * * ENTRY: * [in] UserToken - token of the user against whom Access Check is made * [in] TargetLogonId - Target Session for which access needs to be determined * [in] AccessMask - The desired access (eg WINSTATION_LOGON ) * * EXIT: * TRUE when the User has required Access ; FALSE when not. * ******************************************************************************************************************/ BOOLEAN WINAPI WinStationCheckAccess( HANDLE UserToken, ULONG TargetLogonId, ULONG AccessMask ) { BOOLEAN rc; DWORD Result; HANDLE hServer = SERVERNAME_CURRENT; HANDLE_CURRENT_BINDING_NO_SERVER( hServer ); if( hServer == RPC_HANDLE_NO_SERVER ) { return FALSE; } RpcTryExcept { rc = RpcWinStationCheckAccess( hServer, &Result, NtCurrentPeb()->SessionId, (DWORD)(INT_PTR)UserToken, TargetLogonId, AccessMask ); if( !rc ) { Result = RtlNtStatusToDosError( Result ); } } RpcExcept(I_RpcExceptionFilter(RpcExceptionCode())) { Result = RpcExceptionCode(); DBGPRINT(("RPC Exception %d\n",Result)); rc = FALSE; } RpcEndExcept if( !rc ) SetLastError(Result); return( rc ); } /***************************************************************************** * * _WinStationOpenSessionDirectory * * Call to Session Directory server to see if if it's accessible * * ENTRY: * hServer: SERVERNAME_CURRENT * pszServerName: Session Directory server name * EXIT: * ERROR_SUCCESS if Session Directory server is accessible * otherwise NT error code is returned * ****************************************************************************/ BOOLEAN WINAPI _WinStationOpenSessionDirectory( HANDLE hServer, LPWSTR pszServerName ) { DWORD Result; BOOLEAN rc; HANDLE_CURRENT_BINDING_NO_SERVER( hServer ); if( hServer == RPC_HANDLE_NO_SERVER ) { SetLastError( ERROR_INVALID_PARAMETER ); return FALSE; } RpcTryExcept { rc = RpcWinStationOpenSessionDirectory( hServer, &Result, pszServerName ); if( !rc ) { Result = RtlNtStatusToDosError( Result ); } } RpcExcept(I_RpcExceptionFilter(RpcExceptionCode())) { Result = RpcExceptionCode(); DBGPRINT(("RPC Exception %d\n",Result)); rc = FALSE; } RpcEndExcept if( !rc ) SetLastError(Result); return( rc ); }