|
|
/****************************************************************************/ // nwdwapi.c
//
// RDPWD general header
//
// Copyright (C) 1997-2000 Microsoft Corporation
/****************************************************************************/
#include <precomp.h>
#pragma hdrstop
#define pTRCWd pTSWd
#define TRC_FILE "nwdwapi"
#include <adcg.h>
#include <nwdwapi.h>
#include <nwdwioct.h>
#include <nwdwint.h>
#include <aschapi.h>
#include <anmapi.h>
#include <asmapi.h>
#include <asmint.h>
#include <mcsioctl.h>
#include <tsrvexp.h>
#include "domain.h"
/****************************************************************************/ /* Name: DriverEntry */ /* */ /* Purpose: Default driver entry point */ /* */ /* Returns: NTSTATUS value. */ /* */ /* Params: INOUT pContext - Pointer to the SD context structure */ /* IN fLoad - TRUE: load driver */ /* FALSE: unload driver */ /****************************************************************************/ #ifdef _HYDRA_
const PWCHAR ModuleName = L"rdpwd"; NTSTATUS ModuleEntry(PSDCONTEXT pContext, BOOLEAN fLoad) #else
NTSTATUS DriverEntry(PSDCONTEXT pContext, BOOLEAN fLoad) #endif
{ NTSTATUS rc;
if (fLoad) { rc = WDWLoad(pContext); } else { rc = WDWUnload(pContext); }
return(rc); }
/****************************************************************************/ /* Name: WD_Open */ /* */ /* Purpose: Open and initialize winstation driver */ /* */ /* Returns: NTSTATUS value. */ /* */ /* Params: IN pTSWd - Points to wd data structure */ /* INOUT pSdOpen - Points to the parameter structure SD_OPEN */ /* */ /* Operation: Sanity check the details of the Open packet. */ /* Save the address of the protocol counters struct. */ /* */ /****************************************************************************/ NTSTATUS WD_Open(PTSHARE_WD pTSWd, PSD_OPEN pSdOpen) { NTSTATUS rc = STATUS_SUCCESS;
MCSError MCSErr;
DC_BEGIN_FN("WD_Open");
/************************************************************************/ /* None of the info in the SD_OPEN is of great relevance to us, but we */ /* do some sanity checks that we aren't being confused with an ICA WD. */ /************************************************************************/ TRC_ASSERT((pSdOpen->WdConfig.WdFlag & WDF_TSHARE), (TB,"Not a TShare WD, %x", pSdOpen->WdConfig.WdFlag));
pTSWd->StackClass = pSdOpen->StackClass; TRC_ALT((TB,"Stack class (%ld)", pSdOpen->StackClass));
pTSWd->pProtocolStatus = pSdOpen->pStatus; TRC_DBG((TB, "Protocol counters are at %p", pTSWd->pProtocolStatus));
/************************************************************************/ /* Save our name for later use */ /************************************************************************/ memcpy(pTSWd->DLLName, pSdOpen->WdConfig.WdDLL, sizeof(DLLNAME)); TRC_NRM((TB, "Our name is >%S<", pTSWd->DLLName));
/************************************************************************/ /* Save off the connection name. This is the registry key to look */ /* under for config settings. */ /************************************************************************/ memcpy(pTSWd->WinStationRegName, pSdOpen->WinStationRegName, sizeof(pTSWd->WinStationRegName));
/************************************************************************/ /* Save the scheduling timings. These get copied later into */ /* schNormalPeriod and schTurboPeriod respectively. */ /************************************************************************/ pTSWd->outBufDelay = pSdOpen->PdConfig.Create.OutBufDelay; pTSWd->interactiveDelay = pSdOpen->PdConfig.Create.InteractiveDelay;
/************************************************************************/ /* Indicate that we do not want ICADD managing outbuf headers/trailers */ /************************************************************************/ pSdOpen->SdOutBufHeader = 0; pSdOpen->SdOutBufTrailer = 0;
/************************************************************************/ /* Initalize MCS */ /************************************************************************/ MCSErr = MCSInitialize(pTSWd->pContext, pSdOpen, &pTSWd->hDomainKernel, pTSWd->pSmInfo); if (MCSErr != MCS_NO_ERROR) { TRC_ERR((TB, "MCSInitialize returned %d", MCSErr)); return STATUS_INSUFFICIENT_RESOURCES; }
/************************************************************************/ /* We have not yet initialized virtual channels */ /************************************************************************/ pTSWd->bVirtualChannelBound = FALSE;
pTSWd->_pRecvDecomprContext2 = NULL; pTSWd->_pVcDecomprReassemblyBuf = NULL;
#ifdef DC_DEBUG
/************************************************************************/ /* There's no trace config yet */ /************************************************************************/ pTSWd->trcShmNeedsUpdate = FALSE;
// // TODO: Need to fix termsrv so that it enabled tracing on shadow &
// // primary stacks. For now, set it to standard alert level tracing.
// if ((pTSWd->StackClass == Stack_Primary) ||
// (pTSWd->StackClass == Stack_Console))
// {
// SD_IOCTL SdIoctl;
// ICA_TRACE traceSettings;
//
// traceSettings.fDebugger = TRUE;
// traceSettings.fTimestamp = TRUE;
// traceSettings.TraceClass = 0x10000008;
// traceSettings.TraceEnable = 0x000000cc;
// memset(traceSettings.TraceOption, 0, sizeof(traceSettings.TraceOption));
//
// SdIoctl.IoControlCode = IOCTL_ICA_SET_TRACE; // IN
// SdIoctl.InputBuffer = &traceSettings; // IN OPTIONAL
// SdIoctl.InputBufferLength = sizeof(traceSettings); // IN
// SdIoctl.OutputBuffer = NULL; // OUT OPTIONAL
// SdIoctl.OutputBufferLength = 0; // OUT
// SdIoctl.BytesReturned = 0; // OUT
//
// TRC_UpdateConfig(pTSWd, &SdIoctl);
// }
#endif /* DC_DEBUG */
/************************************************************************/ /* Read registry settings.
/************************************************************************/ if (COM_OpenRegistry(pTSWd, L"")) { /************************************************************************/ /* Read the flow control sleep interval.
/************************************************************************/ COM_ReadProfInt32(pTSWd, WD_FLOWCONTROL_SLEEPINTERVAL, WD_FLOWCONTROL_SLEEPINTERVAL_DFLT, &(pTSWd->flowControlSleepInterval));
TRC_NRM((TB, "Flow control sleep interval %ld", pTSWd->flowControlSleepInterval));
#ifdef DC_DEBUG
#ifndef NO_MEMORY_CHECK
/************************************************************************/ /* Decide whether to break on memory leaks
/************************************************************************/ COM_ReadProfInt32(pTSWd, WD_BREAK_ON_MEMORY_LEAK, WD_BREAK_ON_MEMORY_LEAK_DFLT, &(pTSWd->breakOnLeak)); TRC_NRM((TB, "Break on memory leak ? %s", pTSWd->breakOnLeak ? "yes" : "no")); #endif
#endif /* DC_DEBUG */
} COM_CloseRegistry(pTSWd);
// Establish full connectivity (sans encryption) for passthru stacks.
// The encrypted context will come up later if supported by the requesting
// server. Hang the output user data off of our context so it can be
// retrieved later by rpdwsx.
if (pTSWd->StackClass == Stack_Passthru) {
PUSERDATAINFO pUserData; ULONG OutputLength;
// Because WDW_OnSMConnecting doesn't check the length of the
// ioctl output buffer and doesn't return any error status, we
// have to allocate enough space at the first try.
// Use 128 as it is the amount used in rdpwsx/TSrvInitWDConnectInfo.
OutputLength = 128;
pUserData = (PUSERDATAINFO) COM_Malloc(OutputLength /*MIN_USERDATAINFO_SIZE*/) ; if (pUserData != NULL) { SD_IOCTL SdIoctl;
memset(&SdIoctl, 0, sizeof(SdIoctl)); memset(pUserData, 0, OutputLength /*MIN_USERDATAINFO_SIZE*/); pUserData->cbSize = OutputLength /*MIN_USERDATAINFO_SIZE*/; SdIoctl.IoControlCode = IOCTL_TSHARE_SHADOW_CONNECT; SdIoctl.OutputBuffer = pUserData; SdIoctl.OutputBufferLength = OutputLength /*MIN_USERDATAINFO_SIZE*/;
rc = WDWShadowConnect(pTSWd, &SdIoctl) ; TRC_ALT((TB, "Passthru stack connected: rc=%lx", rc));
if (NT_SUCCESS(rc)) { pTSWd->pUserData = pUserData; } } else { TRC_ERR((TB, "Passthru stack unable to allocate output user data")); rc = STATUS_NO_MEMORY; } }
DC_END_FN(); return rc; } /* WD_Open */
/****************************************************************************/ /* Name: WD_Close */ /* */ /* Purpose: Close winstation driver */ /* */ /* Params: IN pTSWd - Points to wd data structure */ /* INOUT pSdClose - Points to the parameter structure SD_CLOSE */ /****************************************************************************/ NTSTATUS WD_Close(PTSHARE_WD pTSWd, PSD_CLOSE pSdClose) { DC_BEGIN_FN("WD_Close");
TRC_NRM((TB, "WD_Close on WD %p", pTSWd));
// Make sure that Domain.StatusDead is consistent with TSWd.dead
pTSWd->dead = TRUE; ((PDomain)(pTSWd->hDomainKernel))->StatusDead = TRUE;
pSdClose->SdOutBufHeader = 0; // OUT: returned by sd
pSdClose->SdOutBufTrailer = 0; // OUT: returned by sd
// Clean up MCS.
if (pTSWd->hDomainKernel != NULL) MCSCleanup(&pTSWd->hDomainKernel);
// Opportunity to clean up if anything has been left lying around.
if (pTSWd->dcShare != NULL) { if (pTSWd->shareClassInit) { // Terminate the Share Class.
TRC_NRM((TB, "Terminate Share Class")); WDWTermShareClass(pTSWd); }
// It's OK to free the Share object - this is allocated out of
// system memory and is therefore accessible to WD_Close.
TRC_NRM((TB, "Delete Share object")); WDWDeleteShareClass(pTSWd); }
// Terminate SM.
if (pTSWd->pSmInfo != NULL) { TRC_NRM((TB, "Terminate SM")); SM_Term(pTSWd->pSmInfo); }
// Clean up protocol stats pointer. NOTE: It's a pointer to TermDD's
// memory - we should not attempt to free it!
pTSWd->pProtocolStatus = NULL;
// Clean up the MPPC compression context and buffer, if allocated.
// Note that both buffers are concatenated into one allocation
// starting at pMPPCContext.
if (pTSWd->pMPPCContext != NULL) { COM_Free(pTSWd->pMPPCContext); pTSWd->pMPPCContext = NULL; pTSWd->pCompressBuffer = NULL; }
// Clean up the decompression context buffer if allocated
if( pTSWd->_pRecvDecomprContext2) { COM_Free( pTSWd->_pRecvDecomprContext2); pTSWd->_pRecvDecomprContext2 = NULL; }
// Clean up the decompression reassembly buffer if allocated
if(pTSWd->_pVcDecomprReassemblyBuf) { COM_Free(pTSWd->_pVcDecomprReassemblyBuf); pTSWd->_pVcDecomprReassemblyBuf = NULL; }
// Set compression state to default.
pTSWd->bCompress = FALSE;
// Clean up shadow buffers if allocated.
if (pTSWd->pShadowInfo != NULL) { COM_Free(pTSWd->pShadowInfo); pTSWd->pShadowInfo = NULL; }
if (pTSWd->pShadowCert != NULL) { TRC_NRM((TB, "Free pShadowCert")); COM_Free(pTSWd->pShadowCert); pTSWd->pShadowCert = NULL; }
if (pTSWd->pShadowRandom != NULL) { TRC_NRM((TB, "Free pShadowRandom")); COM_Free(pTSWd->pShadowRandom); pTSWd->pShadowRandom = NULL; } if (pTSWd->pUserData != NULL) { TRC_NRM((TB, "Free pUserData")); COM_Free(pTSWd->pUserData); pTSWd->pUserData = NULL; } // Free any shadow hotkey processing structures. Note that we don't free
// pWd->pKbdTbl because we didn't allocate it!
if (pTSWd->pgafPhysKeyState != NULL) { COM_Free(pTSWd->pgafPhysKeyState); pTSWd->pgafPhysKeyState = NULL; }
if (pTSWd->pKbdLayout != NULL) { COM_Free(pTSWd->pKbdLayout); pTSWd->pKbdLayout = NULL; }
if (pTSWd->gpScancodeMap != NULL) { COM_Free(pTSWd->gpScancodeMap); pTSWd->gpScancodeMap = NULL; }
// Free the InfoPkt.
if (pTSWd->pInfoPkt != NULL) { COM_Free(pTSWd->pInfoPkt); pTSWd->pInfoPkt = NULL; }
#ifdef DC_DEBUG
#ifndef NO_MEMORY_CHECK
// Check for un-freed memory.
if (pTSWd->memoryHeader.pNext != NULL) { PMALLOC_HEADER pNext; TRC_ERR((TB, "Unfreed memory")); pNext = pTSWd->memoryHeader.pNext; while (pNext != NULL) { TRC_ERR((TB, "At %#p, len %d, caller %#p", pNext, pNext->length, pNext->pCaller)); pNext = pNext->pNext; }
if (pTSWd->breakOnLeak) DbgBreakPoint(); } #endif /* NO_MEMORY_CHECK */
#endif /* DC_DEBUG */
DC_END_FN(); return STATUS_SUCCESS; } /* WD_Close */
/****************************************************************************/ /* Name: WD_ChannelWrite */ /* */ /* Purpose: Handle channel writing (virtual channel) */ /****************************************************************************/ NTSTATUS WD_ChannelWrite(PTSHARE_WD pTSWd, PSD_CHANNELWRITE pSdChannelWrite) { NTSTATUS status = STATUS_SUCCESS; PVOID pBuffer; UINT16 MCSChannelID; BOOL bRc; CHANNEL_PDU_HEADER UNALIGNED *pHdr; PBYTE pSrc; unsigned dataLeft; unsigned thisLength; unsigned lengthToSend; UINT16 flags; PNM_CHANNEL_DATA pChannelData; UCHAR compressResult = 0; ULONG CompressedSize = 0; BOOL fCompressVC; DWORD ret;
DC_BEGIN_FN("WD_ChannelWrite");
/************************************************************************/ /* Check parameters */ /************************************************************************/ TRC_ASSERT((pTSWd != NULL), (TB,"NULL pTSWd")); TRC_ASSERT((pSdChannelWrite != NULL), (TB,"NULL pTSdChannelWrite")); TRC_ASSERT((pSdChannelWrite->ChannelClass == Channel_Virtual), (TB,"non Virtual Channel, class=%lu", pSdChannelWrite->ChannelClass));
TRC_DBG((TB, "Received channel write. class %lu, channel %lu, numbytes %lu", (ULONG)pSdChannelWrite->ChannelClass, (ULONG)pSdChannelWrite->VirtualClass, pSdChannelWrite->ByteCount));
/************************************************************************/ /* Don't do this if we're dead */ /************************************************************************/ if (pTSWd->dead) { TRC_ALT((TB, "Dead - don't do anything")); status = STATUS_UNSUCCESSFUL; DC_QUIT; }
/************************************************************************/ /* Convert virtual channel ID to MCS channel ID */ /************************************************************************/ MCSChannelID = NM_VirtualChannelToMCS(pTSWd->pNMInfo, pSdChannelWrite->VirtualClass, &pChannelData); if (MCSChannelID == (UINT16) -1) { TRC_ERR((TB, "Unsupported virtual channel %d", pSdChannelWrite->VirtualClass)); status = STATUS_UNSUCCESSFUL; DC_QUIT; } TRC_NRM((TB, "Virtual channel %d = MCS Channel %hx", pSdChannelWrite->VirtualClass, MCSChannelID));
//
// Check if VC compression is enabled for this channel
//
fCompressVC = ((pChannelData->flags & CHANNEL_OPTION_COMPRESS_RDP) && (pTSWd->bCompress) && (pTSWd->shadowState == SHADOW_NONE) && (pTSWd->bClientSupportsVCCompression)); TRC_NRM((TB,"Virtual Channel %d will be compressed.", pSdChannelWrite->VirtualClass));
/************************************************************************/ /* Initialize loop variables */ /************************************************************************/ flags = CHANNEL_FLAG_FIRST; pSrc = pSdChannelWrite->pBuffer; dataLeft = pSdChannelWrite->ByteCount;
/************************************************************************/ /* Loop through the data */ /************************************************************************/ while (dataLeft > 0) { /********************************************************************/ /* Decide how much to send in this chunk */ /********************************************************************/ if (dataLeft > CHANNEL_CHUNK_LENGTH) { thisLength = CHANNEL_CHUNK_LENGTH; } else { thisLength = dataLeft; flags |= CHANNEL_FLAG_LAST; }
/********************************************************************/ // If the buffer is not a low prio write, then block right behind the
// rest of the pending buffer allocs until we get a free buffer.
/********************************************************************/ if (!(pSdChannelWrite->fFlags & SD_CHANNELWRITE_LOWPRIO)) { status = SM_AllocBuffer(pTSWd->pSmInfo, &pBuffer, thisLength + sizeof(CHANNEL_PDU_HEADER), TRUE, FALSE); } /********************************************************************/ // Else if the buffer is a low prio write, then sleep and alloc until
// we get a buffer without blocking. This allows default priority allocs
// that block in SM_AllocBuffer to take precedence.
/********************************************************************/ else { status = SM_AllocBuffer(pTSWd->pSmInfo, &pBuffer, thisLength + sizeof(CHANNEL_PDU_HEADER), FALSE, FALSE); while (status == STATUS_IO_TIMEOUT) {
TRC_NRM((TB, "SM_AllocBuffer would block"));
// Bail out on any failure in this function to prevent an
// infinite loop. STATUS_CTX_CLOSE_PENDING will be returned
// if the connection is being shut down.
ret = IcaFlowControlSleep(pTSWd->pContext, pTSWd->flowControlSleepInterval); if (ret == STATUS_SUCCESS) { status = SM_AllocBuffer(pTSWd->pSmInfo, &pBuffer, thisLength + sizeof(CHANNEL_PDU_HEADER), FALSE, FALSE); } else { TRC_ALT((TB, "IcaFlowControlSleep failed.")); status = ret; DC_QUIT; } } }
if (status != STATUS_SUCCESS) { TRC_ALT((TB, "Failed to get a %d-byte buffer", thisLength));
// prevent regression, keep original code path
status = STATUS_NO_MEMORY; DC_QUIT; }
TRC_NRM((TB, "Buffer (%d bytes) allocated OK", thisLength));
/************************************************************************/ /* Fill in the buffer header */ /************************************************************************/ pHdr = (CHANNEL_PDU_HEADER UNALIGNED *)pBuffer; pHdr->length = pSdChannelWrite->ByteCount; pHdr->flags = flags;
/************************************************************************/ /* Copy the data */ /* If compression is enabled, try to compress directly into the outbuf */ /************************************************************************/ CompressedSize=0; lengthToSend=0; __try { if((fCompressVC) && (thisLength > WD_MIN_COMPRESS_INPUT_BUF) && (thisLength < MAX_COMPRESS_INPUT_BUF)) { compressResult = WDWCompressToOutbuf(pTSWd,(UCHAR*)pSrc,thisLength, (UCHAR*)(pHdr+1),&CompressedSize); if(0 != compressResult) { lengthToSend = CompressedSize; //Update the VC packet header flags with the compression info
pHdr->flags |= ((compressResult & VC_FLAG_COMPRESS_MASK) << VC_FLAG_COMPRESS_SHIFT); } else { TRC_ERR((TB, "SC_CompressToOutbuf failed")); SM_FreeBuffer(pTSWd->pSmInfo, pBuffer, FALSE); DC_QUIT; } } else { //copy directly
memcpy(pHdr + 1, pSrc, thisLength); lengthToSend = thisLength; } } __except (EXCEPTION_EXECUTE_HANDLER) { status = GetExceptionCode(); TRC_ERR((TB, "Exception (0x%08lx) copying evil user buffer", status)); SM_FreeBuffer(pTSWd->pSmInfo, pBuffer, FALSE); DC_QUIT; }
TRC_NRM((TB, "Copied user buffer"));
/************************************************************************/ /* Send it */ /************************************************************************/ bRc = SM_SendData(pTSWd->pSmInfo, pBuffer, lengthToSend + sizeof(CHANNEL_PDU_HEADER), TS_LOWPRIORITY, MCSChannelID, FALSE, RNS_SEC_ENCRYPT, FALSE); if (!bRc) { TRC_ERR((TB, "Failed to send data")); status = STATUS_UNSUCCESSFUL; DC_QUIT; } TRC_NRM((TB, "Sent a %d-byte chunk, flags %#x", lengthToSend, flags));
/********************************************************************/ /* Set up for next loop */ /********************************************************************/ pSrc += thisLength; dataLeft -= thisLength; flags = 0; }
/************************************************************************/ /* Well, it's gone somwehere. */ /************************************************************************/ TRC_NRM((TB, "Data sent OK"));
DC_EXIT_POINT: DC_END_FN(); return(status); } /* WD_ChannelWrite */
/****************************************************************************/ // WDW_OnSMConnecting
//
// Handles a 'connecting' state change callback from SM. This state occurs
// when the network connection is up but security negotiation has not yet
// begun. It is assumed that at this point GCCConferenceCreateResponse will
// be issued. Here we use the user data received from the client to build
// return GCC data in the TShareSRV IOCTL buffer.
/****************************************************************************/ void RDPCALL WDW_OnSMConnecting( PVOID hWD, PRNS_UD_SC_SEC pSecData, PRNS_UD_SC_NET pNetData) { PTSHARE_WD pTSWd = (PTSHARE_WD)hWD; PUSERDATAINFO pOutData; RNS_UD_SC_CORE coreData; BOOL error = FALSE; BYTE *pRgData; GCCOctetString UNALIGNED *pOctet;
DC_BEGIN_FN("WDW_OnSMConnecting");
// Save the broadcast channel ID for use in shadowing
pTSWd->broadcastChannel = pNetData->MCSChannelID;
/************************************************************************/ /* Locate the output buffer. NB the size of this has already been */ /* checked when we rx'd the IOCtl. */ /************************************************************************/ if (pTSWd->pSdIoctl == NULL) { TRC_ERR((TB, "No IOCtl to fill in")); error = TRUE; DC_QUIT; }
// Build the return GCC data required for the IOCTL_TSHARE_CONF_CONNECT
pOutData = pTSWd->pSdIoctl->OutputBuffer; TRC_DBG((TB, "pOutData at %p", pOutData));
/************************************************************************/ /* Build core user data */ /************************************************************************/ memset(&coreData, 0, sizeof(coreData)); coreData.header.type = RNS_UD_SC_CORE_ID; coreData.header.length = sizeof(coreData); coreData.version = RNS_UD_VERSION;
/************************************************************************/ /* Build the user data header and key */ /************************************************************************/ /************************************************************************/ /* Only one piece of user data to fill in, and the following code is */ /* specific to that. */ /************************************************************************/ pOutData->ulUserDataMembers = 1; pOutData->hDomain = pTSWd->hDomain; pOutData->version = pTSWd->version; pOutData->rgUserData[0].key.key_type = GCC_H221_NONSTANDARD_KEY;
/************************************************************************/ /* Put the key octet immediately after the rgUserData. */ /************************************************************************/ pRgData = (BYTE *)(pOutData + 1); pOctet = &(pOutData->rgUserData[0].key.u.h221_non_standard_id);
pOctet->octet_string = pRgData - (UINT_PTR)pOutData; pOctet->octet_string_length = sizeof(SERVER_H221_KEY) - 1; strncpy(pRgData, (const char*)SERVER_H221_KEY, sizeof(SERVER_H221_KEY) - 1); TRC_DBG((TB, "Key octet at %p (offs %p)", pRgData, pRgData - (UINT_PTR)pOutData));
/************************************************************************/ /* Put the data octet immediately after the key octet. */ /************************************************************************/ pRgData += sizeof(SERVER_H221_KEY) - 1; pOctet = (GCCOctetString UNALIGNED *)pRgData; TRC_DBG((TB, "Data octet pointer at %p (offs %p)", pRgData, pRgData - (UINT_PTR)pOutData));
pOutData->rgUserData[0].octet_string = (GCCOctetString *)(pRgData - (UINT_PTR)pOutData); pOctet->octet_string_length = pSecData->header.length + coreData.header.length + pNetData->header.length; pRgData += sizeof(GCCOctetString); pOctet->octet_string = pRgData - (UINT_PTR)pOutData;
/************************************************************************/ /* Now add the data itself */ /************************************************************************/ TRC_DBG((TB, "Core data at %p (offs %p)", pRgData, pRgData - (UINT_PTR)pOutData)); memcpy(pRgData, &coreData, coreData.header.length); pRgData += coreData.header.length;
TRC_DBG((TB, "Net data at %p (offs %p)", pRgData, pRgData - (UINT_PTR)pOutData)); memcpy(pRgData, pNetData, pNetData->header.length); pRgData += pNetData->header.length;
/************************************************************************/ /* the security data is moved to the end of user data, fix the client */ /* code accordingly. */ /************************************************************************/ TRC_DBG((TB, "Sec data at %p (offs %p)", pRgData, pRgData - (UINT_PTR)pOutData)); memcpy(pRgData, pSecData, pSecData->header.length); pRgData += pSecData->header.length;
/************************************************************************/ /* Finally, set up the number of valid bytes. */ /************************************************************************/ pOutData->cbSize = (ULONG)(UINT_PTR)(pRgData - (UINT_PTR)pOutData); pTSWd->pSdIoctl->BytesReturned = pOutData->cbSize;
TRC_DBG((TB, "Build %d bytes of returned user data", pOutData->cbSize)); TRC_DATA_NRM("Returned user data", pOutData, pOutData->cbSize);
DC_EXIT_POINT: if (error) { TRC_ERR((TB, "Something went wrong - bring down the WinStation")); WDW_LogAndDisconnect(pTSWd, TRUE, Log_RDP_CreateUserDataFailed, NULL, 0); }
DC_END_FN(); } /* WDW_OnSMConnecting */
/****************************************************************************/ // WDW_OnSMConnected
//
// Receives connection-completed state change callback from SM.
/****************************************************************************/ void RDPCALL WDW_OnSMConnected(PVOID hWD, unsigned Result) { PTSHARE_WD pTSWd = (PTSHARE_WD)hWD;
DC_BEGIN_FN("WDW_OnSMConnected");
TRC_NRM((TB, "Got Connected Notification, rc %lu", Result));
// Unblock the query IOCtl.
pTSWd->connected = TRUE; KeSetEvent (pTSWd->pConnEvent, EVENT_INCREMENT, FALSE);
// If we failed, get the whole thing winding down straight away.
if (Result != NM_CB_CONN_ERR) { // If compression is enabled in the net flags, indicate we need to
// do the compression, get the compression level, and allocate
// the context buffers.
if (pTSWd->pInfoPkt->flags & RNS_INFO_COMPRESSION) { unsigned MPPCCompressionLevel;
pTSWd->pMPPCContext = COM_Malloc(sizeof(SendContext) + MAX_COMPRESSED_BUFFER); if (pTSWd->pMPPCContext != NULL) { pTSWd->pCompressBuffer = (BYTE *)pTSWd->pMPPCContext + sizeof(SendContext);
// Negotiate down to our highest level of compression support
// if we receive a larger number.
MPPCCompressionLevel = (pTSWd->pInfoPkt->flags & RNS_INFO_COMPR_TYPE_MASK) >> RNS_INFO_COMPR_TYPE_SHIFT; if (MPPCCompressionLevel > PACKET_COMPR_TYPE_MAX) MPPCCompressionLevel = PACKET_COMPR_TYPE_MAX;
initsendcontext(pTSWd->pMPPCContext, MPPCCompressionLevel);
pTSWd->bCompress = TRUE; } else { TRC_ERR((TB,"Failed allocation of MPPC compression buffers")); } } } else { TRC_ERR((TB, "Connection error: winding down now")); WDW_LogAndDisconnect(pTSWd, TRUE, Log_RDP_ConnectFailed, NULL, 0); }
if(pTSWd->bCompress) { //If we're compressing then allocate a decompression context
//for virtual channels
pTSWd->_pRecvDecomprContext2 = (RecvContext2_8K*)COM_Malloc(sizeof(RecvContext2_8K)); if(pTSWd->_pRecvDecomprContext2) { pTSWd->_pRecvDecomprContext2->cbSize = sizeof(RecvContext2_8K); initrecvcontext(&pTSWd->_DecomprContext1, (RecvContext2_Generic*)pTSWd->_pRecvDecomprContext2, PACKET_COMPR_TYPE_8K); } }
DC_END_FN(); }
/****************************************************************************/ // WDW_OnSMDisconnected
//
// Handles disconnection state change callback from SM.
/****************************************************************************/ void WDW_OnSMDisconnected(PVOID hWD) { PTSHARE_WD pTSWd = (PTSHARE_WD)hWD;
DC_BEGIN_FN("WDW_OnSMDisconnected");
TRC_ALT((TB, "Got Disconnected notification"));
// Unblock the query IOCtl.
pTSWd->connected = FALSE; KeSetEvent(pTSWd->pConnEvent, EVENT_INCREMENT, FALSE);
DC_END_FN(); }
/****************************************************************************/ // WDW_OnClientDisconnected
//
// Direct-disconnect path called from MCS to set the create event so that
// CSRSS threads waiting for DD completion of DrvConnect or DrvDisconnect
// will be freed when the client goes down. This prevents a timing window for
// a denial-of-service attack where the client connects then closes its socket
// immediately, leaving the DD waiting and the rest of rdpwsx unable
// to complete closing the TermDD handle, until the 60-second create event
// wait completes.
//
// We cannot use WDW_OnSMDisconnected because its being called is dependent
// on the NM and SM state machines and whether they believe the disconnect
// should be called.
/****************************************************************************/ void RDPCALL WDW_OnClientDisconnected(void *pWD) { PTSHARE_WD pTSWd = (PTSHARE_WD)pWD;
DC_BEGIN_FN("WDW_OnClientDisconnected");
// Set the disconnect event to cause any waiting connect-time events
// to get a STATUS_TIMEOUT.
KeSetEvent(pTSWd->pClientDisconnectEvent, EVENT_INCREMENT, FALSE);
DC_END_FN(); }
/****************************************************************************/ // WDW_WaitForConnectionEvent
//
// Encapsulates a wait for a connection-time event (e.g. pTSWd->pCreateEvent
// for waiting for the font PDU to release the DD to draw). Adds
// functionality to also wait on a single "client disconnected" event,
// which allows the client disconnection code a single point of signaling
// to shut down the various waits.
/****************************************************************************/ NTSTATUS RDPCALL WDW_WaitForConnectionEvent( PTSHARE_WD pTSWd, PKEVENT pEvent, LONG Timeout) { NTSTATUS Status; PKEVENT Events[2];
DC_BEGIN_FN("WDW_WaitForConnectionEvent");
Events[0] = pEvent; Events[1] = pTSWd->pClientDisconnectEvent;
Status = IcaWaitForMultipleObjects(pTSWd->pContext, 2, Events, WaitAny, Timeout); if (Status == 0) { // First object (real wait) hit. We just return the status value.
TRC_DBG((TB,"Primary event hit")); } else if (Status == 1) { // Second object (clietn disconnect) hit. Translate to a TIMEOUT
// for the caller, so they clean up properly.
Status = STATUS_TIMEOUT; TRC_ALT((TB,"Client disconnect event hit")); } else { // Other return (e.g. timeout or close error). Just return it normally.
TRC_DBG((TB,"Other status 0x%X", Status)); }
DC_END_FN(); return Status; }
/****************************************************************************/ /* Name: WDW_OnDataReceived */ /* */ /* Purpose: Callback when virtual channel data received from Client */ /* */ /* Returns: none */ /* */ /* Params: pTSWd - ptr to WD */ /* pData - ptr to data received */ /* dataLength - length of data received */ /* chnnelID - MCS channel on which data was received */ /* */ /* NOTE: Can be called when dead, in which case our only job should be to */ /* decompress the data to make sure the context remains in sync */ /****************************************************************************/ void WDW_OnDataReceived(PTSHARE_WD pTSWd, PVOID pData, unsigned dataLength, UINT16 channelID) { VIRTUALCHANNELCLASS virtualClass; NTSTATUS status; PNM_CHANNEL_DATA pChannelData; CHANNEL_PDU_HEADER UNALIGNED *pHdr; ULONG thisLength; unsigned totalLength; PUCHAR pDataOut; UCHAR vcCompressFlags; UCHAR *pDecompOutBuf; int cbDecompLen;
DC_BEGIN_FN("WDW_OnDataReceived");
/************************************************************************/ /* Translate MCS channel ID to virtual channel ID */ /************************************************************************/ virtualClass = NM_MCSChannelToVirtual(pTSWd->pNMInfo, channelID, &pChannelData); if ((-1 == virtualClass) || (NULL == pChannelData)) { TRC_ERR((TB,"Invalid MCS Channel ID: %u", channelID)); WDW_LogAndDisconnect(pTSWd, TRUE, Log_RDP_InvalidChannelID, (BYTE *)pData, dataLength); DC_QUIT; } TRC_ASSERT((virtualClass < 32), (TB, "Invalid virtual channel %d for MCS channel %hx", virtualClass, channelID));
TRC_NRM((TB, "Data received on MCS channel %hx, virtual channel %d", channelID, virtualClass)); TRC_DATA_NRM("Channel data received", pData, dataLength);
if (dataLength >= sizeof(CHANNEL_PDU_HEADER)) { pHdr = (CHANNEL_PDU_HEADER UNALIGNED *)pData; totalLength = pHdr->length; } else { TRC_ERR((TB,"Channel data len %u not enough for channel header", dataLength)); WDW_LogAndDisconnect(pTSWd, TRUE, Log_RDP_VChannelDataTooShort, (BYTE *)pData, dataLength); DC_QUIT; }
//
// Decompress the buffer
//
vcCompressFlags = (pHdr->flags >> VC_FLAG_COMPRESS_SHIFT) & VC_FLAG_COMPRESS_MASK;
//
// Server only supports 8K decompression context
//
if((pChannelData->flags & CHANNEL_OPTION_COMPRESS_RDP) && (vcCompressFlags & PACKET_COMPRESSED)) { if(!pTSWd->_pRecvDecomprContext2) { TRC_ERR((TB,"No decompression context!!!")); DC_QUIT; }
if(PACKET_COMPR_TYPE_8K == (vcCompressFlags & PACKET_COMPR_TYPE_MASK)) { //Decompress channel data
if(vcCompressFlags & PACKET_FLUSHED) { initrecvcontext (&pTSWd->_DecomprContext1, (RecvContext2_Generic*)pTSWd->_pRecvDecomprContext2, PACKET_COMPR_TYPE_8K); } if (decompress((PUCHAR)(pHdr+1), dataLength - sizeof(CHANNEL_PDU_HEADER), (vcCompressFlags & PACKET_AT_FRONT), &pDecompOutBuf, &cbDecompLen, &pTSWd->_DecomprContext1, (RecvContext2_Generic*)pTSWd->_pRecvDecomprContext2, vcCompressFlags & PACKET_COMPR_TYPE_MASK)) { //
// Successful decompression
// If we're in the dead state then bail out now as the context
// has been updated
//
if (!pTSWd->dead && (pHdr->flags & CHANNEL_FLAG_SHOW_PROTOCOL)) { TRC_DBG((TB, "Include VC protocol header (decompressed)")); //Here is where things get nasty, we need to prepend
//the header to the decompression buffer which lives
//within the decompression context buffer.
//There is no (un-hackerific) way to do this without a
//memcpy, so go ahead and copy using a cached reassembly
//buffer.
if(!pTSWd->_pVcDecomprReassemblyBuf) { pTSWd->_pVcDecomprReassemblyBuf=(PUCHAR) COM_Malloc(WD_VC_DECOMPR_REASSEMBLY_BUF); } //Data received cannot decompress to something bigger
//than the chunk length.
TRC_ASSERT((cbDecompLen + sizeof(CHANNEL_PDU_HEADER)) < WD_VC_DECOMPR_REASSEMBLY_BUF, (TB,"Reassembly buffer too small")); if(pTSWd->_pVcDecomprReassemblyBuf && ((cbDecompLen + sizeof(CHANNEL_PDU_HEADER)) < WD_VC_DECOMPR_REASSEMBLY_BUF)) { memcpy(pTSWd->_pVcDecomprReassemblyBuf, pHdr, sizeof(CHANNEL_PDU_HEADER)); memcpy(pTSWd->_pVcDecomprReassemblyBuf + sizeof(CHANNEL_PDU_HEADER), pDecompOutBuf, cbDecompLen); //Hide the internal protocol from the user
pDataOut = pTSWd->_pVcDecomprReassemblyBuf; thisLength = cbDecompLen + sizeof(CHANNEL_PDU_HEADER); //Hide the internal protocol fields from the user
((CHANNEL_PDU_HEADER UNALIGNED *)pDataOut)->flags &= ~VC_FLAG_PRIVATE_PROTOCOL_MASK; } else { //Either the allocation failed or the channel
//decompressed to something bigger than a chunk
TRC_ERR((TB,"Can't use reassembly buffer")); DC_QUIT; } } else if (pTSWd->dead) { TRC_NRM((TB,"Decompressed when dead, bailing out")); DC_QUIT; } else { TRC_DBG((TB, "Exclude VC protocol header (decompressed)")); pDataOut = (PUCHAR)pDecompOutBuf; thisLength = cbDecompLen; } } else { TRC_ABORT((TB, "Decompression FAILURE!!!")); WDW_LogAndDisconnect(pTSWd, TRUE, Log_RDP_VirtualChannelDecompressionErr, NULL, 0); DC_QUIT; } } else { //
//This server only supports 8K VC compression from client
//(Specified by capabilities) it should not have
//been sent this invalid compression type
TRC_ABORT((TB,"Received packet with invalid compression type %d", (vcCompressFlags & PACKET_COMPR_TYPE_MASK))); WDW_LogAndDisconnect(pTSWd, TRUE, Log_RDP_InvalidVCCompressionType, NULL, 0); DC_QUIT; } } else { //Channel data is not compressd
if (pHdr->flags & CHANNEL_FLAG_SHOW_PROTOCOL) { TRC_DBG((TB, "Include VC protocol header")); pDataOut = (PUCHAR)pHdr; thisLength = dataLength; //Hide the internal protocol fields from the user
((CHANNEL_PDU_HEADER UNALIGNED *)pDataOut)->flags &= ~VC_FLAG_PRIVATE_PROTOCOL_MASK; } else { TRC_DBG((TB, "Exclude VC protocol header")); pDataOut = (PUCHAR)(pHdr + 1); thisLength = dataLength - sizeof(*pHdr); } }
if (!pTSWd->dead) { TRC_NRM((TB, "Input %d bytes (of %d) at %p (Hdr %p, flags %#x) on channel %d", thisLength, totalLength, pDataOut, pHdr, pHdr->flags, virtualClass)); status = IcaChannelInput(pTSWd->pContext, Channel_Virtual, virtualClass, NULL, pDataOut, thisLength); } else { TRC_NRM((TB,"Skipping input (%d bytes) because dead", thisLength)); }
DC_EXIT_POINT: DC_END_FN(); } /* WDW_OnDataReceived */
/****************************************************************************/ /* Name: WDW_InvalidateRect */ /* */ /* Purpose: Tell ICADD to redraw a given rectangle. */ /* */ /* Returns: VOID. */ /* */ /* Params: IN pTSWd - ptr to WD */ /* IN personID - the originator of this PDU */ /* IN rect - the area to redraw */ /* */ /* Operation: Build command and pass it to ICADD. */ /****************************************************************************/ void WDW_InvalidateRect( PTSHARE_WD pTSWd, PTS_REFRESH_RECT_PDU pRRPDU, unsigned DataLength) { ICA_CHANNEL_COMMAND Cmd; NTSTATUS status; unsigned i;
DC_BEGIN_FN("WDW_InvalidateRect");
// Make sure we have enough data before accessing.
if (DataLength >= (sizeof(TS_REFRESH_RECT_PDU) - sizeof(TS_RECTANGLE16))) { TRC_NRM((TB, "Got request to refresh %hu area%s", (UINT16)pRRPDU->numberOfAreas, pRRPDU->numberOfAreas == 1 ? " " : "s")); if ((unsigned)(TS_UNCOMP_LEN(pRRPDU) - FIELDOFFSET(TS_REFRESH_RECT_PDU, areaToRefresh[0])) >= (pRRPDU->numberOfAreas * sizeof(TS_RECTANGLE16)) && (unsigned)(DataLength - FIELDOFFSET(TS_REFRESH_RECT_PDU, areaToRefresh[0])) >= (pRRPDU->numberOfAreas * sizeof(TS_RECTANGLE16))) { for (i = 0; i < pRRPDU->numberOfAreas; i++) { // Rects arrive inclusive, convert to exclusive for the system.
Cmd.Header.Command = ICA_COMMAND_REDRAW_RECTANGLE; Cmd.RedrawRectangle.Rect.Left = pRRPDU->areaToRefresh[i].left; Cmd.RedrawRectangle.Rect.Top = pRRPDU->areaToRefresh[i].top; Cmd.RedrawRectangle.Rect.Right = pRRPDU->areaToRefresh[i]. right + 1; Cmd.RedrawRectangle.Rect.Bottom = pRRPDU->areaToRefresh[i]. bottom + 1;
/************************************************************/ // Pass the filled in structure to ICADD.
/************************************************************/ status = IcaChannelInput(pTSWd->pContext, Channel_Command, 0, NULL, (unsigned char *) &Cmd, sizeof(ICA_CHANNEL_COMMAND));
TRC_DBG((TB,"Issued Refresh Rect for %u,%u,%u,%u (exclusive); " "status %lu", pRRPDU->areaToRefresh[i].left, pRRPDU->areaToRefresh[i].top, pRRPDU->areaToRefresh[i].right + 1, pRRPDU->areaToRefresh[i].bottom + 1, status)); } } else { /****************************************************************/ // There can't be enough space in this PDU to store the number of
// rectangles that it apparently contains. Don't process it.
/****************************************************************/ TCHAR detailData[(sizeof(UINT16) * 4) + 2]; TRC_ERR((TB, "Invalid RefreshRectPDU: %hu rects; %hu bytes long", (UINT16)pRRPDU->numberOfAreas, pRRPDU->shareDataHeader.uncompressedLength));
/****************************************************************/ // Log an error and disconnect the Client
/****************************************************************/ swprintf(detailData, L"%hx %hx", (UINT16)pRRPDU->numberOfAreas, pRRPDU->shareDataHeader.uncompressedLength, sizeof(detailData)); WDW_LogAndDisconnect(pTSWd, TRUE, Log_RDP_InvalidRefreshRectPDU, NULL, 0); } } else { TRC_ERR((TB,"Data len %u not enough for refresh rect PDU", DataLength)); WDW_LogAndDisconnect(pTSWd, TRUE, Log_RDP_InvalidRefreshRectPDU, (BYTE *)pRRPDU, DataLength); }
DC_END_FN(); } /* WDW_InvalidateRect */
#ifdef DC_DEBUG
/****************************************************************************/ /* Name: WDW_Malloc */ /* */ /* Purpose: Allocate memory (checked builds only) */ /* */ /* Returns: ptr to memory allocated */ /* */ /* Params: pTSWd */ /* length - size of memory required */ /****************************************************************************/ PVOID RDPCALL WDW_Malloc(PTSHARE_WD pTSWd, ULONG length) { PVOID pMemory;
#ifndef NO_MEMORY_CHECK
/************************************************************************/ /* If we're checking memory, allow space for the header */ /************************************************************************/ length += sizeof(MALLOC_HEADER); #endif
/************************************************************************/ /* Allocate the memory */ /************************************************************************/ pMemory = ExAllocatePoolWithTag(PagedPool, length, WD_ALLOC_TAG);
if (pMemory == NULL) { KdPrint(("WDTShare: COM_Malloc failed to alloc %u bytes\n", length)); DC_QUIT; }
#ifndef NO_MEMORY_CHECK
/************************************************************************/ /* If we haven't been passed a TSWd, we can't save the memory details - */ /* clear the header. */ /************************************************************************/ if (pTSWd == NULL) { memset(pMemory, 0, sizeof(MALLOC_HEADER)); } else { /********************************************************************/ /* we've been passed a TSWd - save memory details */ /********************************************************************/ PVOID pReturnAddress = NULL; PMALLOC_HEADER pHeader;
#ifdef _X86_
/********************************************************************/ /* Find caller's address (X86 only) */ /********************************************************************/ _asm mov eax,[ebp+4] _asm mov pReturnAddress,eax #endif /* _X86_ */
/********************************************************************/ /* Save memory allocation details */ /********************************************************************/ pHeader = (PMALLOC_HEADER)pMemory; pHeader->pCaller = pReturnAddress; pHeader->length = length; pHeader->pPrev = &(pTSWd->memoryHeader); if (pTSWd->memoryHeader.pNext != NULL) { (pTSWd->memoryHeader.pNext)->pPrev = pHeader; } pHeader->pNext = pTSWd->memoryHeader.pNext; pTSWd->memoryHeader.pNext = pHeader;
}
/************************************************************************/ /* Bump pointer past header */ /************************************************************************/ pMemory = (PVOID)((BYTE *)pMemory + sizeof(MALLOC_HEADER)); #endif /* NO_MEMORY_CHECK */
DC_EXIT_POINT: return(pMemory); }
/****************************************************************************/ /* Name: WDW_Free */ /* */ /* Purpose: Free memory (checked builds only) */ /* */ /* Params: pMemory - pointer to memory to free */ /****************************************************************************/ void RDPCALL WDW_Free(PVOID pMemory) { #ifndef NO_MEMORY_CHECK
/************************************************************************/ /* Remove this block from memory allocation chain */ /************************************************************************/ PMALLOC_HEADER pHeader;
pHeader = (PMALLOC_HEADER)pMemory - 1; if (pHeader->pNext != NULL) { pHeader->pNext->pPrev = pHeader->pPrev; } if (pHeader->pPrev != NULL) { pHeader->pPrev->pNext = pHeader->pNext; }
pMemory = (PVOID)pHeader; #endif /* NO_MEMORY_CHECK */
/************************************************************************/ /* Free the memory */ /************************************************************************/ ExFreePool(pMemory); } #endif /* DC_DEBUG */
|