Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

1389 lines
58 KiB

/****************************************************************************/
// 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 */