|
|
/****************************************************************************/ // nwdwint.c
//
// RDP WD code.
//
// Copyright (C) 1996-2000 Microsoft Corporation
/****************************************************************************/
#include <precomp.h>
#pragma hdrstop
#define pTRCWd pTSWd
#define TRC_FILE "nwdwint"
#include <adcg.h>
#include <randlib.h>
#include <pchannel.h>
#include <anmapi.h>
#include <asmint.h>
#include <nwdwapi.h>
#include <nwdwioct.h>
#include <nwdwint.h>
#include <nwdwdata.c>
#include <asmint.h>
#include <anmint.h>
#include <tsperf.h>
//
// RNG api doesn't refcount it's shutdown so do it for them
//
LONG g_RngUsers = 0;
/****************************************************************************/ /* Name: WDWLoad */ /* */ /* Purpose: Load WinStation driver */ /* */ /* Params: INOUT pContext - pointer to the SD context structure */ /****************************************************************************/ NTSTATUS WDWLoad(PSDCONTEXT pContext) { NTSTATUS Status; PTSHARE_WD pTSWd; unsigned smnmBytes; unsigned wdBytes;
DC_BEGIN_FN("WDWLoad");
/************************************************************************/ /* WARNING: Don't trace in this function, it will cause ICADD to crash, */ /* as the stack context hasn't been set up properly until we return */ /* from this function. */ /* Use KdPrint instead. */ /************************************************************************/
// Initialize Calldown and callup procedures.
pContext->pProcedures = (PSDPROCEDURE)G_pWdProcedures; pContext->pCallup = (SDCALLUP *)G_pWdCallups;
// Do a quick sanity check on the SHM size to alert of bad paging
// characteristics.
wdBytes = sizeof(SHM_SHARED_MEMORY); #ifdef DC_DEBUG
wdBytes -= sizeof(TRC_SHARED_DATA); #endif
if ((wdBytes % PAGE_SIZE) < (PAGE_SIZE / 8)) KdPrintEx((DPFLTR_TERMSRV_ID, DPFLTR_INFO_LEVEL, "RDPWD: **** Note SHM_SHARED_MEMORY fre size is wasting " "at least 7/8 of a page - page size=%u, SHM=%u, wasting %u\n", PAGE_SIZE, wdBytes, PAGE_SIZE - (wdBytes % PAGE_SIZE)));
// Allocate WD data structure - first find out how many bytes are
// needed by the SM/NM code.
smnmBytes = SM_GetDataSize(); wdBytes = DC_ROUND_UP_4(sizeof(TSHARE_WD));
KdPrintEx((DPFLTR_TERMSRV_ID, DPFLTR_INFO_LEVEL, "RDPWD: WDWLoad: Alloc TSWD=%d + NM/SM=%d (= %d) bytes for TSWd\n", wdBytes, smnmBytes, wdBytes + smnmBytes)); if ((wdBytes + smnmBytes) >= PAGE_SIZE) KdPrintEx((DPFLTR_TERMSRV_ID, DPFLTR_INFO_LEVEL, "RDPWD: **** Note TSWd allocation is above page size %u, " "wasting %u\n", PAGE_SIZE, PAGE_SIZE - ((wdBytes + smnmBytes) % PAGE_SIZE)));
#ifdef DC_DEBUG
// Preinit pTSWd for debug COM_Malloc.
pTSWd = NULL; #endif
pTSWd = COM_Malloc(wdBytes + smnmBytes); if (pTSWd != NULL) { // Zero the allocated mem.
memset(pTSWd, 0, wdBytes + smnmBytes); } else { KdPrintEx((DPFLTR_TERMSRV_ID, DPFLTR_ERROR_LEVEL, "RDPWD: WDWLoad: Failed alloc TSWD\n")); Status = STATUS_NO_MEMORY; DC_QUIT; }
//
// Init the performance flags to non-perf-aware client setting.
// We need this to differentiate between
// non-experience-aware clients and xp clients.
// We cannot use protocol versions as they are
// are not being setup properly now.
//
pTSWd->performanceFlags = TS_PERF_DEFAULT_NONPERFCLIENT_SETTING; // Set up pointers both ways between PSDCONTEXT and PTSHARE_WD.
// Note that we still can't yet trace.
pTSWd->pSmInfo = ((BYTE *)pTSWd) + wdBytes; pTSWd->pNMInfo = (BYTE *)pTSWd->pSmInfo + sizeof(SM_HANDLE_DATA);
KdPrintEx((DPFLTR_TERMSRV_ID, DPFLTR_INFO_LEVEL, "RDPWD: pTSWd=%p, pSM=%p, pNM=%p, sizeof(TSWd)=%u, sizeof(SM)=%u\n", pTSWd, pTSWd->pSmInfo, pTSWd->pNMInfo, sizeof(TSHARE_WD), sizeof(SM_HANDLE_DATA), sizeof(NM_HANDLE_DATA)));
pTSWd->pContext = pContext; pContext->pContext = pTSWd;
// Now allocate the RNS_INFO_PACKET as a separate allocation. InfoPkt
// is large (~3K) and if included in TSHARE_WD pushes the TSWd
// allocation over the Intel 4K page size, causing most of a second
// page to be wasted. Since we can't get rid of the data (it's
// referenced at various points in the normal and shadow connection
// sequences), we alloc it separately to let the system sub-page
// allocator use memory more effectively.
KdPrintEx((DPFLTR_TERMSRV_ID, DPFLTR_INFO_LEVEL, "RDPWD: WDWLoad: Alloc %u bytes for InfoPkt\n", sizeof(RNS_INFO_PACKET))); if ((sizeof(RNS_INFO_PACKET)) >= PAGE_SIZE) KdPrintEx((DPFLTR_TERMSRV_ID, DPFLTR_INFO_LEVEL, "RDPWD: **** Note INFO_PACKET allocation is above " "page size %u, wasting %u\n", PAGE_SIZE, PAGE_SIZE - (sizeof(RNS_INFO_PACKET) % PAGE_SIZE))); pTSWd->pInfoPkt = COM_Malloc(sizeof(RNS_INFO_PACKET)); if (pTSWd->pInfoPkt != NULL) { memset(pTSWd->pInfoPkt, 0, sizeof(RNS_INFO_PACKET)); } else { KdPrintEx((DPFLTR_TERMSRV_ID, DPFLTR_ERROR_LEVEL, "RDPWD: WDWLoad: Failed alloc InfoPkt\n")); COM_Free(pTSWd); Status = STATUS_NO_MEMORY; DC_QUIT; }
// Allocate and initialize the connEvent, createEvent, secEvent,
// SessKeyEvent, and ClientDisconnectEvent. Use ExAllocatePool directly,
// as COM_Malloc allocates paged memory, and these events must be in
// non-paged memory.
pTSWd->pConnEvent = ExAllocatePoolWithTag(NonPagedPool, sizeof(KEVENT) * 5, WD_ALLOC_TAG); if (pTSWd->pConnEvent != NULL) { pTSWd->pCreateEvent = pTSWd->pConnEvent + 1; pTSWd->pSecEvent = pTSWd->pCreateEvent + 1; pTSWd->pSessKeyEvent = pTSWd->pSecEvent + 1; pTSWd->pClientDisconnectEvent = pTSWd->pSessKeyEvent + 1;
KeInitializeEvent(pTSWd->pConnEvent, NotificationEvent, FALSE); KeInitializeEvent(pTSWd->pCreateEvent, NotificationEvent, FALSE); KeInitializeEvent(pTSWd->pSecEvent, NotificationEvent, FALSE); KeInitializeEvent(pTSWd->pSessKeyEvent, NotificationEvent, FALSE); KeInitializeEvent(pTSWd->pClientDisconnectEvent, NotificationEvent, FALSE); } else { KdPrintEx((DPFLTR_TERMSRV_ID, DPFLTR_ERROR_LEVEL, "RDPWD: Failed to allocate memory for WD events\n")); COM_Free(pTSWd->pInfoPkt); COM_Free(pTSWd); Status = STATUS_NO_MEMORY; DC_QUIT; }
//
// Init the random number generator
//
if (InitializeRNG(NULL)) { InterlockedIncrement(&g_RngUsers); }
Status = STATUS_SUCCESS;
KdPrintEx((DPFLTR_TERMSRV_ID, DPFLTR_INFO_LEVEL, "RDPWD: WDWLoad done\n"));
DC_EXIT_POINT: DC_END_FN(); return Status; }
/****************************************************************************/ /* Name: WDWUnload */ /* */ /* Purpose: Unload WinStation driver */ /* */ /* Params: INOUT pContext - pointer to the SD context structure */ /****************************************************************************/ NTSTATUS WDWUnload( PSDCONTEXT pContext ) { PTSHARE_WD pTSWd;
/************************************************************************/ /* Get pointers to WD data structures */ /************************************************************************/ pTSWd = (PTSHARE_WD)pContext->pContext; if (pTSWd != NULL) { // Free connEvent & createEvent.
if (NULL != pTSWd->pConnEvent) ExFreePool(pTSWd->pConnEvent);
// Free the InfoPkt.
if (pTSWd->pInfoPkt != NULL) COM_Free(pTSWd->pInfoPkt);
// Free TSWd itself.
COM_Free(pTSWd); }
/************************************************************************/ /* Clear context structure */ /************************************************************************/ pContext->pContext = NULL; pContext->pProcedures = NULL; pContext->pCallup = NULL;
//
// Shutdown the random number generator
//
if (0L == InterlockedDecrement(&g_RngUsers)) { ShutdownRNG(NULL); }
return STATUS_SUCCESS; }
/****************************************************************************/ /* Name: WDWConnect */ /* */ /* Purpose: Processes a conference connect request from the WD. It is */ /* used by connects from TShareSrv as well as shadow connects. */ /* */ /* Params: IN pTSWd - pointer to WD struct */ /* IN PRNS_UD_CS_CORE - Client Core Data */ /* IN PRNS_UD_CS_SEC - Client Security Data */ /* IN PRNS_UD_CS_NET - Client Net Data */ /* INOUT PSD_IOCTL - pointer to received IOCtl this will be one */ /* of IOCTL_TSHARE_CONF_CONNECT or */ /* IOCTL_ICA_SET_CONNECTED(shadow) */ /* */ /* Operation: Parse the user data for core, security, and network bits. */ /* Pull the values we want out of the core piece */ /* Initialize the Security Manager */ /* Create a share core, passing in the key values from user data */ /* Tell the user manager to proceed with connecting */ /****************************************************************************/ NTSTATUS WDWConnect( PTSHARE_WD pTSWd, PRNS_UD_CS_CORE pClientCoreData, PRNS_UD_CS_SEC pClientSecurityData, PRNS_UD_CS_NET pClientNetData, PTS_UD_CS_CLUSTER pClientClusterData, PSD_IOCTL pSdIoctl, BOOLEAN bOldShadow) { NTSTATUS status = STATUS_SUCCESS; BOOL smInit = FALSE;
DC_BEGIN_FN("WDWConnect");
// Get the required values from the core user data.
pTSWd->version = pClientCoreData->version; pTSWd->desktopWidth = pClientCoreData->desktopWidth; pTSWd->desktopHeight = pClientCoreData->desktopHeight;
// We only support 4096 x 2048
if (pTSWd->desktopHeight > 2048) pTSWd->desktopHeight = 2048; if (pTSWd->desktopWidth > 4096) pTSWd->desktopWidth = 4096;
// Checks the client software for compatibility and rejects if not
// equivalent to server software.
TRC_NRM((TB, "Client version is %#lx", pClientCoreData->version)); if (_RNS_MAJOR_VERSION(pClientCoreData->version) != RNS_UD_MAJOR_VERSION) { TRC_ERR((TB, "Unmatching software version, expected %#lx got %#lx", RNS_UD_VERSION, pClientCoreData->version)); status = RPC_NT_INVALID_VERS_OPTION; DC_QUIT; }
if(pClientCoreData->header.length >= (FIELDOFFSET(RNS_UD_CS_CORE, earlyCapabilityFlags) + FIELDSIZE(RNS_UD_CS_CORE, earlyCapabilityFlags))) { //
// Does client support extended error reporting PDU
//
pTSWd->bSupportErrorInfoPDU = (pClientCoreData->earlyCapabilityFlags & RNS_UD_CS_SUPPORT_ERRINFO_PDU) ? TRUE : FALSE; } else { pTSWd->bSupportErrorInfoPDU = FALSE; } TRC_NRM((TB, "ErrorInfoPDU supported = %d", pTSWd->bSupportErrorInfoPDU)); #ifdef DC_HICOLOR
// Work out high color support.
if (pClientCoreData->header.length >= (FIELDOFFSET(RNS_UD_CS_CORE, supportedColorDepths) + FIELDSIZE(RNS_UD_CS_CORE, supportedColorDepths))) { long maxServerBpp; long limitedBpp;
// Store off the supported color depths.
pTSWd->supportedBpps = pClientCoreData->supportedColorDepths;
// Client may want other than 4 or 8bpp, so lets see what we can
// do. First up is to see what limits are imposed at this end.
maxServerBpp = pTSWd->maxServerBpp;
TRC_NRM((TB, "Client requests color depth %u, server limit %d", pClientCoreData->highColorDepth, maxServerBpp));
// Now see if we can allow the requested value.
if (pClientCoreData->highColorDepth > maxServerBpp) { TRC_NRM((TB, "Limiting requested color depth...")); switch (maxServerBpp) { case 16: if (pClientCoreData->supportedColorDepths & RNS_UD_16BPP_SUPPORT) { limitedBpp = 16; break; } // deliberate fall through!
case 15: if (pClientCoreData->supportedColorDepths & RNS_UD_15BPP_SUPPORT) { limitedBpp = 15; break; } // deliberate fall through!
default: limitedBpp = 8; break; }
TRC_ALT((TB, "Restricted requested color depth %d to %d", pClientCoreData->highColorDepth, maxServerBpp)); pClientCoreData->highColorDepth = (UINT16)limitedBpp; }
// Now set up the proper color depth from the (possibly
// restricted) high color value.
if (pClientCoreData->highColorDepth == 24) pClientCoreData->colorDepth = RNS_UD_COLOR_24BPP; else if (pClientCoreData->highColorDepth == 16) pClientCoreData->colorDepth = RNS_UD_COLOR_16BPP_565; else if (pClientCoreData->highColorDepth == 15) pClientCoreData->colorDepth = RNS_UD_COLOR_16BPP_555; else if (pClientCoreData->highColorDepth == 4) pClientCoreData->colorDepth = RNS_UD_COLOR_4BPP; else pClientCoreData->colorDepth = RNS_UD_COLOR_8BPP; } else {
// No hicolor support.
pTSWd->supportedBpps = 0; #endif
// A beta2 Server rejects Clients with a color depth of 4bpp.
// Therefore a new field, postBeta2ColorDepth, is added, which can be
// 4bpp. If this field exists, use it instead of colorDepth.
if (pClientCoreData->header.length >= (FIELDOFFSET(RNS_UD_CS_CORE, postBeta2ColorDepth) + FIELDSIZE(RNS_UD_CS_CORE, postBeta2ColorDepth))) { TRC_NRM((TB, "Post-beta2 color depth id %#x", pClientCoreData->postBeta2ColorDepth)); pClientCoreData->colorDepth = pClientCoreData->postBeta2ColorDepth; } #ifdef DC_HICOLOR
} #endif
if (pClientCoreData->colorDepth == RNS_UD_COLOR_8BPP) { TRC_NRM((TB, "8 BPP")); pTSWd->desktopBpp = 8; } else if (pClientCoreData->colorDepth == RNS_UD_COLOR_4BPP) { TRC_NRM((TB, "4 BPP")); pTSWd->desktopBpp = 4; } else if (pClientCoreData->colorDepth == RNS_UD_COLOR_16BPP_555) { #ifdef DC_HICOLOR
TRC_NRM((TB, "15 BPP (16 BPP, 555)")); pTSWd->desktopBpp = 15; #else
// May want to save whether it's 555 or 565.
TRC_NRM((TB, "16 BPP 555")); pTSWd->desktopBpp = 16; #endif
} else if (pClientCoreData->colorDepth == RNS_UD_COLOR_16BPP_565) { #ifdef DC_HICOLOR
TRC_NRM((TB, "16 BPP (565)")); #else
TRC_NRM((TB, "16 BPP 565")); #endif
pTSWd->desktopBpp = 16; } else if (pClientCoreData->colorDepth == RNS_UD_COLOR_24BPP) { TRC_NRM((TB, "24 BPP")); pTSWd->desktopBpp = 24; } else { TRC_ERR((TB, "Unknown BPP %x returned by client", pClientCoreData->colorDepth)); status = STATUS_UNSUCCESSFUL; DC_QUIT; }
pTSWd->sas = pClientCoreData->SASSequence; pTSWd->kbdLayout = pClientCoreData->keyboardLayout; pTSWd->clientBuild = pClientCoreData->clientBuild; // here we don't copy the last character in the buffer because
// we force zero termination later by writting a 0 at the end;
memcpy(pTSWd->clientName, pClientCoreData->clientName, sizeof(pTSWd->clientName)-sizeof(pTSWd->clientName[0])); pTSWd->clientName[sizeof(pTSWd->clientName) / sizeof(pTSWd->clientName[0]) - 1] = 0;
pTSWd->keyboardType = pClientCoreData->keyboardType; pTSWd->keyboardSubType = pClientCoreData->keyboardSubType; pTSWd->keyboardFunctionKey = pClientCoreData->keyboardFunctionKey; // here we don't copy the last character in the buffer because
// we force zero termination later by writting a 0 at the end;
memcpy(pTSWd->imeFileName, pClientCoreData->imeFileName, sizeof(pTSWd->imeFileName)-sizeof(pTSWd->imeFileName[0])); pTSWd->imeFileName[sizeof(pTSWd->imeFileName) / sizeof(pTSWd->imeFileName[0]) - 1] = 0;
pTSWd->clientDigProductId[0] = 0;
// Win2000 Post Beta 3 fields added
if (pClientCoreData->header.length >= (FIELDOFFSET(RNS_UD_CS_CORE, serialNumber) + FIELDSIZE(RNS_UD_CS_CORE, serialNumber))) { pTSWd->clientProductId = pClientCoreData->clientProductId; pTSWd->serialNumber = pClientCoreData->serialNumber; //shadow loop fix
if (pClientCoreData->header.length >= (FIELDOFFSET(RNS_UD_CS_CORE, clientDigProductId) + FIELDSIZE(RNS_UD_CS_CORE, clientDigProductId))) { // here we don't copy the last character in the buffer because
// we force zero termination later by writting a 0 at the end;
memcpy( pTSWd->clientDigProductId, pClientCoreData->clientDigProductId, sizeof(pTSWd->clientDigProductId) -sizeof(pTSWd->clientDigProductId[0])); pTSWd->clientDigProductId[sizeof(pTSWd->clientDigProductId) / sizeof(pTSWd->clientDigProductId[0]) -1] = 0; } }
// Parse and store the client's cluster support info, if provided.
// If not present, the memset here will implicitly set FALSE the flags
// for the client cluster capabilities. Note we do not have the
// username and domain available yet (it comes in the info packet
// later) so we cannot fill out the username and domain yet.
if (pClientClusterData != NULL) { if (pClientClusterData->Flags & TS_CLUSTER_REDIRECTION_SUPPORTED) { TRC_NRM((TB,"Client supports load balance redirection")); pTSWd->bClientSupportsRedirection = TRUE; } if (pClientClusterData->Flags & TS_CLUSTER_REDIRECTED_SESSIONID_FIELD_VALID) { TRC_NRM((TB,"Client has been load-balanced to this server, " "sessid=%u", pClientClusterData->RedirectedSessionID)); pTSWd->bRequestedSessionIDFieldValid = TRUE; pTSWd->RequestedSessionID = pClientClusterData->RedirectedSessionID; if (pClientClusterData->Flags & TS_CLUSTER_REDIRECTED_SMARTCARD) { pTSWd->bUseSmartcardLogon = TRUE; } }
// The 2..5 bits (start from 0) are the PDU version
pTSWd->ClientRedirectionVersion = ((pClientClusterData->Flags & 0x3C) >> 2); }
// Create a new share object.
status = WDWNewShareClass(pTSWd); if (!NT_SUCCESS(status)) { TRC_ERR((TB, "Failed to get a new Share Object - quit")); DC_QUIT; }
// Bring up SM.
status = SM_Init(pTSWd->pSmInfo, pTSWd, bOldShadow); if (NT_SUCCESS(status)) { smInit = TRUE; } else { TRC_ERR((TB, "Failed to init SM, rc %lu", status)); DC_QUIT; }
// Hook the IOCtl off the WD structure to allow the SM callback to
// process it.
// Also NULL the output buffer - this is solely to allow us to assert
// (later) that the callback has in fact been taken.
TRC_ASSERT((pTSWd->pSdIoctl == NULL), (TB,"Already an IOCTL linked from pTSWd")); pTSWd->pSdIoctl = pSdIoctl; if ((pSdIoctl->IoControlCode == IOCTL_TSHARE_CONF_CONNECT) && pSdIoctl->OutputBuffer) { ((PUSERDATAINFO)pSdIoctl->OutputBuffer)->ulUserDataMembers = 0; }
// Tell SM we're done here.
status = SM_Connect(pTSWd->pSmInfo, pClientSecurityData, pClientNetData, bOldShadow); if (status != STATUS_SUCCESS) { TRC_ERR((TB, "SM_Connect failed: rc=%lx", status)); DC_QUIT; }
/************************************************************************/ /* The scheduling is such that we are guaranteed that the connecting */ /* status will have been received from SM before the previous call */ /* returns. Just for safety we assert that this is so! */ /************************************************************************/ if ((pSdIoctl->IoControlCode == IOCTL_TSHARE_CONF_CONNECT) && pSdIoctl->OutputBuffer) { TRC_ASSERT((((PUSERDATAINFO)pSdIoctl->OutputBuffer)->ulUserDataMembers != 0), (TB,"We didn't get callback from SM - BAD NEWS")); }
DC_EXIT_POINT: // Clean up anything we created if we failed.
if (status == STATUS_SUCCESS) { pTSWd->pSdIoctl = NULL; } else { TRC_NRM((TB, "Cleaning up...")); if (pTSWd->dcShare != NULL) { TRC_NRM((TB, "Deleting Share object")); WDWDeleteShareClass(pTSWd); } if (smInit) { TRC_NRM((TB, "Terminating SM")); SM_Term(pTSWd->pSmInfo); } }
DC_END_FN(); return status; } /* WDWConnect */
/****************************************************************************/ /* Name: WDWConfConnect */ /* */ /* Purpose: Processes a TSHARE_CONF_CONNECT IOCtl from TShareSRV */ /* */ /* Params: IN pTSWd - pointer to WD struct */ /* INOUT PSD_IOCTL - pointer to received IOCtl */ /* */ /* Operation: Parse the user data for core bits and SM bits. */ /* pull the values we want out of the core piece */ /* initialize the Security Manager */ /* create a share core, passing in the key values from user data */ /* tell the user manager to proceed with connecting */ /****************************************************************************/ NTSTATUS WDWConfConnect(PTSHARE_WD pTSWd, PSD_IOCTL pSdIoctl) { NTSTATUS status = STATUS_SUCCESS; unsigned DataLen; PRNS_UD_CS_CORE pClientCoreData; PRNS_UD_CS_SEC pClientSecurityData; PRNS_UD_CS_NET pClientNetData; PTS_UD_CS_CLUSTER pClientClusterData;
DC_BEGIN_FN("WDWConfConnect");
// First make sure we've received enough data for the initial headers
// and that the sizes presented in the data block are valid. An attacker
// might try sending malformed data here to fault the server.
DataLen = pSdIoctl->InputBufferLength; if (sizeof(USERDATAINFO)>DataLen) { TRC_ERR((TB,"Apparent attack via user data, size %u too small for UD hdr", DataLen)); WDW_LogAndDisconnect(pTSWd, TRUE, Log_RDP_BadUserData, pSdIoctl->InputBuffer, DataLen); status = STATUS_UNSUCCESSFUL; DC_QUIT; }
if (((PUSERDATAINFO)pSdIoctl->InputBuffer)->cbSize > DataLen) { TRC_ERR((TB,"Apparent attack via user data, the cbSize is set to a length bigger then the total buffer %u", ((PUSERDATAINFO)pSdIoctl->InputBuffer)->cbSize > DataLen)); WDW_LogAndDisconnect(pTSWd, TRUE, Log_RDP_BadUserData, pSdIoctl->InputBuffer, DataLen); status = STATUS_UNSUCCESSFUL; DC_QUIT; } // Validate that the output buffer is big enough.
if ((pSdIoctl->OutputBuffer == NULL) || (pSdIoctl->OutputBufferLength < MIN_USERDATAINFO_SIZE)) { TRC_ERR((TB, "No Out Buffer on TSHARE_CONF_CONNECT.")); status = STATUS_BUFFER_TOO_SMALL; DC_QUIT; }
if (((PUSERDATAINFO)pSdIoctl->OutputBuffer)->cbSize < MIN_USERDATAINFO_SIZE) { // Buffer has been supplied but is too small, - so tell
// TShareSRV how big a buffer we actually need.
((PUSERDATAINFO)pSdIoctl->OutputBuffer)->cbSize = MIN_USERDATAINFO_SIZE;
TRC_ERR((TB, "Telling TShareSRV to have another go with %d", MIN_USERDATAINFO_SIZE)); status = STATUS_BUFFER_TOO_SMALL; DC_QUIT; }
// Parse the input data.
if (WDWParseUserData(pTSWd, (PUSERDATAINFO)pSdIoctl->InputBuffer, DataLen, NULL, 0, &pClientCoreData, &pClientSecurityData, &pClientNetData, &pClientClusterData)) { status = WDWConnect(pTSWd, pClientCoreData, pClientSecurityData, pClientNetData, pClientClusterData, pSdIoctl, FALSE); } else { status = STATUS_UNSUCCESSFUL; TRC_ERR((TB, "Could not parse the user data successfully")); }
DC_EXIT_POINT: DC_END_FN(); return status; } /* WDWConfConnect */
/****************************************************************************/ /* Name: WDWConsoleConnect */ /* */ /* Purpose: Processes a TSHARE_CONSOLE_CONNECT IOCtl from TShareSRV */ /* */ /* Params: IN pTSWd - pointer to WD struct */ /* INOUT PSD_IOCTL - pointer to received IOCtl */ /* */ /* Operation: Parse the user data for core bits and SM bits. */ /* pull the values we want out of the core piece */ /* initialize the Security Manager */ /* create a share core, passing in the key values from user data */ /* tell the user manager to proceed with connecting */ /****************************************************************************/ NTSTATUS WDWConsoleConnect(PTSHARE_WD pTSWd, PSD_IOCTL pSdIoctl) { NTSTATUS status = STATUS_SUCCESS; PUSERDATAINFO pUserInfo; PRNS_UD_CS_CORE pClientCoreData;
BOOL smInit = FALSE;
DC_BEGIN_FN("WDWConsoleConnect");
/************************************************************************/ /* get the Client data from the IOCTL */ /************************************************************************/ pUserInfo = (PUSERDATAINFO)(pSdIoctl->InputBuffer); pClientCoreData = (PRNS_UD_CS_CORE)(pUserInfo->rgUserData);
/************************************************************************/ /* version info */ /************************************************************************/ pTSWd->version = pClientCoreData->version;
/************************************************************************/ /* Set up the desktop size */ /************************************************************************/ pTSWd->desktopWidth = pClientCoreData->desktopWidth; pTSWd->desktopHeight = pClientCoreData->desktopHeight;
#ifdef DC_HICOLOR
/************************************************************************/ /* And the color depth */ /************************************************************************/ if (pClientCoreData->colorDepth == RNS_UD_COLOR_8BPP) { pTSWd->desktopBpp = 8; } else if (pClientCoreData->colorDepth == RNS_UD_COLOR_4BPP) { pTSWd->desktopBpp = 4; } else if (pClientCoreData->colorDepth == RNS_UD_COLOR_16BPP_555) { pTSWd->desktopBpp = 15; } else if (pClientCoreData->colorDepth == RNS_UD_COLOR_16BPP_565) { pTSWd->desktopBpp = 16; } else if (pClientCoreData->colorDepth == RNS_UD_COLOR_24BPP) { pTSWd->desktopBpp = 24; } else { TRC_ERR((TB, "Unknown BPP %x returned by client", pClientCoreData->colorDepth)); pTSWd->desktopBpp = 8; }
pTSWd->supportedBpps = pClientCoreData->supportedColorDepths;
TRC_ALT((TB, "Console at %d bpp", pTSWd->desktopBpp)); #else
/************************************************************************/ /* always 8bpp */ /************************************************************************/ pTSWd->desktopBpp = 8; #endif
/************************************************************************/ /* @@@ Need to set these up in RDPWSX first */ /************************************************************************/ // pTSWd->sas = pClientCoreData->SASSequence;
// pTSWd->kbdLayout = pClientCoreData->keyboardLayout;
// pTSWd->clientBuild = pClientCoreData->clientBuild;
// wcscpy(pTSWd->clientName, pClientCoreData->clientName);
//
// pTSWd->keyboardType = pClientCoreData->keyboardType;
// pTSWd->keyboardSubType = pClientCoreData->keyboardSubType;
// pTSWd->keyboardFunctionKey = pClientCoreData->keyboardFunctionKey;
// wcscpy(pTSWd->imeFileName, pClientCoreData->imeFileName);
/************************************************************************/ /* ... now a new share object... */ /************************************************************************/ status = WDWNewShareClass(pTSWd); if (!NT_SUCCESS(status)) { TRC_ERR((TB, "Failed to get a new Share Object - quit")); DC_QUIT; }
/************************************************************************/ /* ...then bring up SM... */ /************************************************************************/ status = SM_Init(pTSWd->pSmInfo, pTSWd, FALSE); if (NT_SUCCESS(status)) { smInit = TRUE; } else { TRC_ERR((TB, "Failed to init SM, rc %lu", status)); DC_QUIT; }
//
// Always compress at the highest level.
//
pTSWd->pInfoPkt->flags |= RNS_INFO_COMPRESSION | (PACKET_COMPR_TYPE_64K << RNS_INFO_COMPR_TYPE_SHIFT);
/************************************************************************/ /* Now we bypass the rest of SM setup altogether! */ /************************************************************************/ WDW_OnSMConnected(pTSWd, NM_CB_CONN_OK);
DC_EXIT_POINT: /************************************************************************/ /* Clean up anything we created if we failed. */ /************************************************************************/ if (status == STATUS_SUCCESS) { pTSWd->pSdIoctl = NULL; } else { TRC_NRM((TB, "Cleaning up..."));
if (pTSWd->dcShare != NULL) { TRC_NRM((TB, "Deleting Share object")); WDWDeleteShareClass(pTSWd); }
if (smInit) { TRC_NRM((TB, "Terminating SM")); SM_Term(pTSWd->pSmInfo); } }
DC_END_FN(); return status; } /* WDWConsoleConnect */
/****************************************************************************/ /* Name: WDWShadowConnect */ /* */ /* Purpose: Processes an IOCTL_ICA_STACK_SET_CONNECTED ioctl from TermSrv.*
/* The contents of this message are gathered from the shadow */ /* client. */ /* */ /* Params: IN pTSWd - pointer to WD struct */ /* INOUT PSD_IOCTL - pointer to received IOCtl */ /* */ /* Operation: Parse the user data for core bits and SM bits. */ /* pull the values we want out of the core piece */ /* initialize the Security Manager */ /* create a share core, passing in the key values from user data */ /* tell the user manager to proceed with connecting */ /****************************************************************************/ NTSTATUS WDWShadowConnect(PTSHARE_WD pTSWd, PSD_IOCTL pSdIoctl) { NTSTATUS status = STATUS_SUCCESS; MCSError MCSErr; UserHandle hUser; ChannelHandle hChannel; UINT32 maxPDUSize; BOOLEAN bCompleted; BOOL bSuccess = FALSE; BOOLEAN bOldShadow = FALSE; RNS_UD_CS_CORE clientCoreData, *pClientCoreData; RNS_UD_CS_SEC clientSecurityData, *pClientSecurityData; RNS_UD_CS_NET clientNetData, *pClientNetData; PTS_UD_CS_CLUSTER pClientClusterData;
PTSHARE_MODULE_DATA pModuleData = (PTSHARE_MODULE_DATA) pSdIoctl->InputBuffer; PTSHARE_MODULE_DATA_B3 pModuleDataB3 = (PTSHARE_MODULE_DATA_B3) pSdIoctl->InputBuffer;
DC_BEGIN_FN("WDWShadowConnect");
TRC_ERR((TB, "%s stack: WDWShadowConnect (data=%p), (size=%ld)", pTSWd->StackClass == Stack_Shadow ? "Shadow" : (pTSWd->StackClass == Stack_Primary ? "Primary" : "Passthru"), pModuleData, pSdIoctl->InputBufferLength)); /************************************************************************/ /* Validate that the output buffer is big enough */ /************************************************************************/ if ((pSdIoctl->OutputBuffer == NULL) || (pSdIoctl->OutputBufferLength < MIN_USERDATAINFO_SIZE) || (((PUSERDATAINFO)pSdIoctl->OutputBuffer)->cbSize < MIN_USERDATAINFO_SIZE)) { if (pSdIoctl->OutputBuffer != NULL) { /****************************************************************/ /* Buffer has been supplied but is too small, - so tell */ /* TShareSRV how big a buffer we actually need. */ /****************************************************************/ ((PUSERDATAINFO)pSdIoctl->OutputBuffer)->cbSize = MIN_USERDATAINFO_SIZE; TRC_ERR((TB, "Telling rdpwsx to have another go with %d", MIN_USERDATAINFO_SIZE)); } else { TRC_ERR((TB, "No Out Buffer on TSHARE_SHADOW_CONNECT.")); }
status = STATUS_BUFFER_TOO_SMALL; DC_QUIT; } switch (pTSWd->StackClass) { // Use the parameters we collected from the shadow client
case Stack_Shadow:
// B3 and B3_oops! servers used a fixed length user data structure
if (pSdIoctl->InputBufferLength == sizeof(TSHARE_MODULE_DATA_B3)) { TRC_ERR((TB, "B3 shadow request!: %ld", pSdIoctl->InputBufferLength)); bSuccess = WDWParseUserData( pTSWd, NULL, 0, (PRNS_UD_HEADER) &pModuleDataB3->clientCoreData, sizeof(RNS_UD_CS_CORE_V0) + sizeof(RNS_UD_CS_SEC_V0), &pClientCoreData, &pClientSecurityData, &pClientNetData, &pClientClusterData); bOldShadow = TRUE; } else if (pSdIoctl->InputBufferLength == sizeof(TSHARE_MODULE_DATA_B3_OOPS)) { TRC_ERR((TB, "B3 Oops! shadow request!: %ld", pSdIoctl->InputBufferLength)); bSuccess = WDWParseUserData( pTSWd, NULL, 0, (PRNS_UD_HEADER)&pModuleDataB3->clientCoreData, sizeof(RNS_UD_CS_CORE_V1) + sizeof(RNS_UD_CS_SEC_V1), &pClientCoreData, &pClientSecurityData, &pClientNetData, &pClientClusterData); bOldShadow = TRUE; }
// else, parse variable length data
else if (pSdIoctl->InputBufferLength == (sizeof(TSHARE_MODULE_DATA) + pModuleData->userDataLen - sizeof(RNS_UD_HEADER))) { TRC_ERR((TB, "RC1 shadow request!: %ld", pSdIoctl->InputBufferLength)); bSuccess = WDWParseUserData( pTSWd, NULL, 0, (PRNS_UD_HEADER) &pModuleData->userData, pModuleData->userDataLen, &pClientCoreData, &pClientSecurityData, &pClientNetData, &pClientClusterData); } else { TRC_ERR((TB, "Invalid module data size: %ld", pSdIoctl->InputBufferLength)); bSuccess = FALSE; status = STATUS_INVALID_PARAMETER; DC_QUIT; }
if (bSuccess) { TRC_ALT((TB, "Parsed shadow user data: %ld", pSdIoctl->InputBufferLength)); } else { status = STATUS_INVALID_PARAMETER; DC_QUIT; } break;
// passthru stacks are initialized to defaults on open, but we can't
// return any user data until rdpwsx asks for it. If the user data is
// hanging around then return it, otherwise generate it.
case Stack_Passthru: if (pTSWd->pUserData != NULL) { memcpy(pSdIoctl->OutputBuffer, pTSWd->pUserData, pTSWd->pUserData->cbSize); pSdIoctl->OutputBufferLength = pTSWd->pUserData->cbSize; pSdIoctl->BytesReturned = pTSWd->pUserData->cbSize; status = STATUS_SUCCESS; COM_Free(pTSWd->pUserData); pTSWd->pUserData = NULL; DC_QUIT; } pClientCoreData = &clientCoreData; pClientSecurityData = &clientSecurityData; pClientNetData = &clientNetData; WDWGetDefaultCoreParams(pClientCoreData); SM_GetDefaultSecuritySettings(pClientSecurityData); TRC_ALT((TB, "WDWShadowConnect: Defaulting passthru stack params")) break;
default: TRC_ERR((TB, "WDWShadowConnect: Unexpected stack type: %ld", pTSWd->StackClass)); status = STATUS_INVALID_PARAMETER; DC_QUIT; break; }
status = WDWConnect(pTSWd, pClientCoreData, pClientSecurityData, NULL, NULL, pSdIoctl, bOldShadow);
// If we successfully connected the server to the share, then connect the
// remote client as well.
if (NT_SUCCESS(status)) { MCSErr = MCSAttachUserRequest(pTSWd->hDomainKernel, NULL, // request callback (remote)
NULL, // data callback (remote)
NULL, // context (remote)
&hUser, &maxPDUSize, &bCompleted); if (MCSErr != MCS_NO_ERROR) { TRC_ERR((TB, "Shadow MCSAttachUserRequest failed %d", MCSErr)); status = STATUS_INSUFFICIENT_RESOURCES; DC_QUIT; }
// Join the remote user to the broadcast channel
MCSErr = MCSChannelJoinRequest(hUser, pTSWd->broadcastChannel, &hChannel, &bCompleted); if (MCSErr != MCS_NO_ERROR) { TRC_ERR((TB, "Remote broadcast channel join failed returned %d", MCSErr)); status = STATUS_INSUFFICIENT_RESOURCES; DC_QUIT; }
// Join the remote user to their own private channel
MCSErr = MCSChannelJoinRequest(hUser, MCSGetUserIDFromHandle(hUser), &hChannel, &bCompleted); if (MCSErr != MCS_NO_ERROR) { TRC_ERR((TB, "Remote user channel join failed %d", MCSErr)); status = STATUS_INSUFFICIENT_RESOURCES; DC_QUIT; }
// Tell MCS which channel(s) we want shadowed.
if (pTSWd->StackClass == Stack_Shadow) { MCSErr = MCSSetShadowChannel(pTSWd->hDomainKernel, pTSWd->broadcastChannel); if (MCSErr != MCS_NO_ERROR) { TRC_ERR((TB, "Remote user channel join failed %d", MCSErr)); status = STATUS_INSUFFICIENT_RESOURCES; DC_QUIT; } } }
DC_EXIT_POINT:
if (NT_SUCCESS(status)) { TRC_ALT((TB, "WDWShadowConnect [%ld]: success!", pTSWd->StackClass)); } else { TRC_ERR((TB, "WDWShadowConnect [%ld]: failed! rc=%lx", pTSWd->StackClass, status)); }
DC_END_FN(); return status; } /* WDWShadowConnect */
/****************************************************************************/ /* Name: WDWGetClientData */ /* */ /* Purpose: Process an IOCTL_ICA_STACK_QUERY_CLIENT */ /* */ /* Returns: STATUS_SUCCESS so long as buffer is big enough. */ /* */ /* Params: IN pTSWd - WD ptr. */ /* IN pSdIoctl - IOCtl struct. */ /* */ /* Operation: Wait for the connected indication (this is to prevent the */ /* rest of the system going running off before we're ready). */ /* */ /* Fill in the required data and then return the IOCtl. */ /****************************************************************************/ NTSTATUS WDWGetClientData(PTSHARE_WD pTSWd, PSD_IOCTL pSdIoctl) { NTSTATUS status = STATUS_SUCCESS; PWINSTATIONCLIENTW pClientData = (PWINSTATIONCLIENTW)pSdIoctl->OutputBuffer;
DC_BEGIN_FN("WDWGetClientData"); /************************************************************************/ /* Validate that the output buffer is big enough */ /************************************************************************/ if (pClientData != NULL && pSdIoctl->OutputBufferLength >= sizeof(WINSTATIONCLIENTW)) { memset(pClientData, 0, sizeof(WINSTATIONCLIENTW)); } else { status = STATUS_BUFFER_TOO_SMALL; TRC_ERR((TB, "Stack_Query_Client OutBuf too small - expected/got %d/%d", sizeof(WINSTATIONCLIENTW), pSdIoctl->OutputBufferLength)); DC_QUIT; }
/************************************************************************/ /* ...and now fill out the reply buffer. */ /************************************************************************/ pClientData->fTextOnly = 0;
/************************************************************************/ /* Set the client StartSessionInfo values as specified by the client. */ /************************************************************************/ pClientData->fMouse = (pTSWd->pInfoPkt->flags & RNS_INFO_MOUSE) != 0;
pClientData->fDisableCtrlAltDel = (pTSWd->pInfoPkt->flags & RNS_INFO_DISABLECTRLALTDEL) != 0;
pClientData->fEnableWindowsKey = (pTSWd->pInfoPkt->flags & RNS_INFO_ENABLEWINDOWSKEY) != 0;
pClientData->fDoubleClickDetect = (pTSWd->pInfoPkt->flags & RNS_INFO_DOUBLECLICKDETECT) != 0;
pClientData->fMaximizeShell = (pTSWd->pInfoPkt->flags & RNS_INFO_MAXIMIZESHELL) != 0;
pClientData->fRemoteConsoleAudio = (pTSWd->pInfoPkt->flags & RNS_INFO_REMOTECONSOLEAUDIO) != 0;
wcsncpy(pClientData->Domain, (LPWSTR)pTSWd->pInfoPkt->Domain, ((sizeof(pClientData->Domain) / sizeof(WCHAR)) - 1)); pClientData->Domain[sizeof(pClientData->Domain) / sizeof(WCHAR) - 1] = L'\0';
wcsncpy(pClientData->UserName, (LPWSTR)pTSWd->pInfoPkt->UserName, ((sizeof(pClientData->UserName) / sizeof(WCHAR)) - 1)); pClientData->UserName[sizeof(pClientData->UserName) / sizeof(WCHAR) - 1] = L'\0';
wcsncpy(pClientData->Password, (LPWSTR)pTSWd->pInfoPkt->Password, ((sizeof(pClientData->Password) / sizeof(WCHAR)) - 1)); pClientData->Password[sizeof(pClientData->Password) / sizeof(WCHAR) - 1] = L'\0';
pClientData->fPromptForPassword = !(pTSWd->pInfoPkt->flags & RNS_INFO_AUTOLOGON);
/************************************************************************/ /* The next fields are only used for the case (now supported by us) */ /* where the function is to have a specific app loaded as part of the */ /* WinStation creation. */ /************************************************************************/ memcpy(pClientData->WorkDirectory, pTSWd->pInfoPkt->WorkingDir, sizeof(pClientData->WorkDirectory)); memcpy(pClientData->InitialProgram, pTSWd->pInfoPkt->AlternateShell, sizeof(pClientData->InitialProgram));
// These fields are set by post Win2000 Beta 3 clients
pClientData->SerialNumber = pTSWd->serialNumber; pClientData->ClientAddressFamily = pTSWd->clientAddressFamily; wcscpy(pClientData->ClientAddress, pTSWd->clientAddress); wcscpy(pClientData->ClientDirectory, pTSWd->clientDir);
// Client time zone information
pClientData->ClientTimeZone.Bias = pTSWd->clientTimeZone.Bias; pClientData->ClientTimeZone.StandardBias = pTSWd->clientTimeZone.StandardBias; pClientData->ClientTimeZone.DaylightBias = pTSWd->clientTimeZone.DaylightBias; memcpy(&pClientData->ClientTimeZone.StandardName,&pTSWd->clientTimeZone.StandardName, sizeof(pClientData->ClientTimeZone.StandardName)); memcpy(&pClientData->ClientTimeZone.DaylightName,&pTSWd->clientTimeZone.DaylightName, sizeof(pClientData->ClientTimeZone.DaylightName));
pClientData->ClientTimeZone.StandardDate.wYear = pTSWd->clientTimeZone.StandardDate.wYear ; pClientData->ClientTimeZone.StandardDate.wMonth = pTSWd->clientTimeZone.StandardDate.wMonth ; pClientData->ClientTimeZone.StandardDate.wDayOfWeek = pTSWd->clientTimeZone.StandardDate.wDayOfWeek ; pClientData->ClientTimeZone.StandardDate.wDay = pTSWd->clientTimeZone.StandardDate.wDay ; pClientData->ClientTimeZone.StandardDate.wHour = pTSWd->clientTimeZone.StandardDate.wHour ; pClientData->ClientTimeZone.StandardDate.wMinute = pTSWd->clientTimeZone.StandardDate.wMinute ; pClientData->ClientTimeZone.StandardDate.wSecond = pTSWd->clientTimeZone.StandardDate.wSecond ; pClientData->ClientTimeZone.StandardDate.wMilliseconds = pTSWd->clientTimeZone.StandardDate.wMilliseconds;
pClientData->ClientTimeZone.DaylightDate.wYear = pTSWd->clientTimeZone.DaylightDate.wYear ; pClientData->ClientTimeZone.DaylightDate.wMonth = pTSWd->clientTimeZone.DaylightDate.wMonth ; pClientData->ClientTimeZone.DaylightDate.wDayOfWeek = pTSWd->clientTimeZone.DaylightDate.wDayOfWeek ; pClientData->ClientTimeZone.DaylightDate.wDay = pTSWd->clientTimeZone.DaylightDate.wDay ; pClientData->ClientTimeZone.DaylightDate.wHour = pTSWd->clientTimeZone.DaylightDate.wHour ; pClientData->ClientTimeZone.DaylightDate.wMinute = pTSWd->clientTimeZone.DaylightDate.wMinute ; pClientData->ClientTimeZone.DaylightDate.wSecond = pTSWd->clientTimeZone.DaylightDate.wSecond ; pClientData->ClientTimeZone.DaylightDate.wMilliseconds = pTSWd->clientTimeZone.DaylightDate.wMilliseconds;
// Client session id
pClientData->ClientSessionId = pTSWd->clientSessionId;
// Client performance flags (currently just disabled feature list)
pClientData->PerformanceFlags = pTSWd->performanceFlags;
// Client active input locale
pClientData->ActiveInputLocale = pTSWd->activeInputLocale;
// Set the client encryption level.
pClientData->EncryptionLevel = (BYTE) ((PSM_HANDLE_DATA)(pTSWd->pSmInfo))->encryptionLevel;
/************************************************************************/ /* Unused. */ /************************************************************************/ pClientData->ClientLicense[0] = '\0'; pClientData->ClientModem[0] = '\0'; pClientData->ClientHardwareId = 0;
/************************************************************************/ /* Finally some real values. */ /************************************************************************/ wcscpy(pClientData->ClientName, pTSWd->clientName); pClientData->ClientBuildNumber = pTSWd->clientBuild; pClientData->ClientProductId = pTSWd->clientProductId; pClientData->OutBufCountClient = TSHARE_WD_BUFFER_COUNT; pClientData->OutBufCountHost = TSHARE_WD_BUFFER_COUNT; pClientData->OutBufLength = 1460; /* LARGE_OUTBUF_SIZE in TermDD. */ pClientData->HRes = (UINT16)pTSWd->desktopWidth; pClientData->VRes = (UINT16)pTSWd->desktopHeight; pClientData->ProtocolType = PROTOCOL_RDP; pClientData->KeyboardLayout = pTSWd->kbdLayout; //shadow loop fix
wcscpy( pClientData->clientDigProductId, pTSWd->clientDigProductId );
/************************************************************************/ /* WinAdmin uses special numbers for ColorDepth. */ /************************************************************************/ #ifdef DC_HICOLOR
pClientData->ColorDepth = (pTSWd->desktopBpp == 4 ? 1 : pTSWd->desktopBpp == 8 ? 2 : pTSWd->desktopBpp == 16 ? 4 : pTSWd->desktopBpp == 24 ? 8 : pTSWd->desktopBpp == 15 ? 16: 2); #else
pClientData->ColorDepth = (pTSWd->desktopBpp == 4 ? 1 : pTSWd->desktopBpp == 8 ? 2 : pTSWd->desktopBpp == 16 ? 4 : pTSWd->desktopBpp == 24 ? 8 : 2); #endif
/************************************************************************/ /* FE data */ /************************************************************************/ pClientData->KeyboardType = pTSWd->keyboardType; pClientData->KeyboardSubType = pTSWd->keyboardSubType; pClientData->KeyboardFunctionKey = pTSWd->keyboardFunctionKey; wcscpy(pClientData->imeFileName, pTSWd->imeFileName);
pSdIoctl->BytesReturned = sizeof(WINSTATIONCLIENTW);
DC_EXIT_POINT: DC_END_FN(); return status; } /* WDWGetClientData */
/****************************************************************************/ /* Name: WDWGetExtendedClientData */ /* */ /* Purpose: Process an IOCTL_ICA_STACK_QUERY_CLIENT_EXTENSION */ /* Was introduced for Long UserName, Password support */ /* */ /* Returns: STATUS_SUCCESS so long as buffer is big enough. */ /* */ /* Params: IN RnsInfoPacket - ptr to protocol packet from client. */ /* IN pSdIoctl - IOCtl struct. */ /* */ /* Operation: Fill in the required data and then return the IOCtl. */ /* The data filled in are the long UserName, Password and Domain */ /****************************************************************************/ NTSTATUS WDWGetExtendedClientData(RNS_INFO_PACKET *RnsInfoPacket, PSD_IOCTL pSdIoctl) { NTSTATUS status = STATUS_SUCCESS; pExtendedClientCredentials pExtendedClientData = (pExtendedClientCredentials)pSdIoctl->OutputBuffer;
/************************************************************************/ /* Validate that the output buffer is big enough */ /************************************************************************/ if (pExtendedClientData != NULL && pSdIoctl->OutputBufferLength >= sizeof(ExtendedClientCredentials)) { memset(pExtendedClientData, 0, sizeof(ExtendedClientCredentials)); } else { status = STATUS_BUFFER_TOO_SMALL; return status; }
//copy the long UserName, Password and Domain from protocol packet to the IOCTL buffer
wcsncpy(pExtendedClientData->Domain, (LPWSTR)RnsInfoPacket->Domain, ((sizeof(pExtendedClientData->Domain) / sizeof(WCHAR)) - 1)); pExtendedClientData->Domain[sizeof(pExtendedClientData->Domain) / sizeof(WCHAR) - 1] = L'\0';
wcsncpy(pExtendedClientData->UserName, (LPWSTR)RnsInfoPacket->UserName, ((sizeof(pExtendedClientData->UserName) / sizeof(WCHAR)) - 1)); pExtendedClientData->UserName[sizeof(pExtendedClientData->UserName) / sizeof(WCHAR) - 1] = L'\0';
wcsncpy(pExtendedClientData->Password, (LPWSTR)RnsInfoPacket->Password, ((sizeof(pExtendedClientData->Password) / sizeof(WCHAR)) - 1)); pExtendedClientData->Password[sizeof(pExtendedClientData->Password) / sizeof(WCHAR) - 1] = L'\0';
return status ; }
//
// WDWGetAutoReconnectInfo
// Process an IOCTL_ICA_STACK_QUERY_AUTORECONNECT to retreive
// autoreconnect info.
//
// Returns: STATUS_SUCCESS so long as buffer is big enough.
//
// Params: IN RnsInfoPacket - ptr to protocol packet from client.
//
NTSTATUS WDWGetAutoReconnectInfo(PTSHARE_WD pTSWd, RNS_INFO_PACKET* pRnsInfoPacket, PSD_IOCTL pSdIoctl) { NTSTATUS status = STATUS_SUCCESS; PTS_AUTORECONNECTINFO pAutoReconnectInfo; BYTE fGetServerToClientInfo; ULONG cb = 0; DC_BEGIN_FN("WDWGetAutoReconnectInfo");
//
// Expect a byte as input
//
TRC_ASSERT((pSdIoctl->InputBufferLength == sizeof(BYTE)), (TB,"Already an IOCTL linked from pTSWd"));
pAutoReconnectInfo = (PTS_AUTORECONNECTINFO)pSdIoctl->OutputBuffer; memcpy(&fGetServerToClientInfo, pSdIoctl->InputBuffer, sizeof(fGetServerToClientInfo));
//
// Validate that the output buffer is big enough
//
if (pAutoReconnectInfo != NULL && pSdIoctl->OutputBufferLength >= sizeof(TS_AUTORECONNECTINFO)) { memset(pAutoReconnectInfo, 0, sizeof(TS_AUTORECONNECTINFO)); } else { status = STATUS_BUFFER_TOO_SMALL; TRC_ERR((TB, "Stack_Query_Client OutBuf too small - expected/got %d/%d", sizeof(TS_AUTORECONNECTINFO), pSdIoctl->OutputBufferLength)); DC_QUIT; }
if (fGetServerToClientInfo) {
//
// Get the server to client ARC cookie contents (if present)
//
if (pTSWd->arcTokenValid) { pAutoReconnectInfo->cbAutoReconnectInfo = sizeof(pTSWd->arcCookie); memcpy(pAutoReconnectInfo->AutoReconnectInfo, pTSWd->arcCookie, sizeof(pTSWd->arcCookie)); pSdIoctl->BytesReturned = sizeof(pTSWd->arcCookie); } else { status = STATUS_NOT_FOUND; } } else {
//
// Get info sent from the client to the server
//
if (pRnsInfoPacket->ExtraInfo.cbAutoReconnectLen <= sizeof(pAutoReconnectInfo->AutoReconnectInfo)) {
pAutoReconnectInfo->cbAutoReconnectInfo = pRnsInfoPacket->ExtraInfo.cbAutoReconnectLen; memcpy(pAutoReconnectInfo->AutoReconnectInfo, pRnsInfoPacket->ExtraInfo.autoReconnectCookie, pRnsInfoPacket->ExtraInfo.cbAutoReconnectLen);
pSdIoctl->BytesReturned = pRnsInfoPacket->ExtraInfo.cbAutoReconnectLen; } else { status = STATUS_BUFFER_TOO_SMALL; TRC_ERR((TB, "Buffer from client too large got: %d limit: %d", pRnsInfoPacket->ExtraInfo.cbAutoReconnectLen, sizeof(pAutoReconnectInfo->AutoReconnectInfo))); DC_QUIT; } }
DC_EXIT_POINT: DC_END_FN(); return status; }
/****************************************************************************/ /* Name: WDWParseUserData */ /* */ /* Purpose: Separate out the parts of the GCC User Data */ /* */ /* Returns: TRUE if all data found OK; else FALSE. */ /* */ /* Params: IN pTSWd - WD Handle */ /* IN pUserData - user data from TShareSRV (from indication) */ /* IN pHeader - optional post parse data (shadow) */ /* IN cbParsedData - optional post data length (shadow) */ /* OUT ppClientCoreData - ptr to Core data */ /* OUT ppClientSecurityData - ptr to SM data */ /* OUT ppNetSecurityData - ptr to Net data */ /* */ /* Operation: Locate our user data, then the two items required from within */ /* it. */ /****************************************************************************/ BOOL WDWParseUserData( PTSHARE_WD pTSWd, PUSERDATAINFO pUserData, unsigned UserDataLen, PRNS_UD_HEADER pHeader, ULONG cbParsedData, PPRNS_UD_CS_CORE ppClientCoreData, PPRNS_UD_CS_SEC ppClientSecurityData, PPRNS_UD_CS_NET ppClientNetData, PTS_UD_CS_CLUSTER *ppClientClusterData) { BOOL success = FALSE; GCCUserData *pClientUserData; char clientH221Key[] = CLIENT_H221_KEY; PRNS_UD_HEADER pEnd; GCCOctetString UNALIGNED *pOctet; unsigned char *pStr; UINT32 dataLen; UINT32 keyLen;
DC_BEGIN_FN("WDWParseUserData");
*ppClientNetData = NULL; *ppClientSecurityData = NULL; *ppClientCoreData = NULL; *ppClientClusterData = NULL;
// Actual GCC user data so parse it to make sure it's good.
if (pHeader == NULL) { // We assume the data length was checked by the caller for at least
// the length of the USERDATAINFO header. We have to validate the rest.
// We are expecting exactly 1 piece of user data.
if (pUserData->ulUserDataMembers == 1) { // Check that it has a non-standard key.
pClientUserData = &(pUserData->rgUserData[0]);
if (pClientUserData->key.key_type == GCC_H221_NONSTANDARD_KEY) { // Check it has our non-standard key.
keyLen = pClientUserData->key.u.h221_non_standard_id. octet_string_length; pStr = (unsigned char *)((BYTE *)pUserData + (UINT_PTR)pClientUserData->key.u. h221_non_standard_id.octet_string); TRC_DATA_DBG("GCC_H221_NONSTANDARD_KEY", pStr, keyLen); // We check here if this is exactly our key.
// pStr was obtained by adding to the pUserData an untrusted
// length so we have to check for overflow (pStr should not
// be smaller then pUserData). Then we check if adding keyLen
// will overrun our buffer.
if ((keyLen != sizeof(clientH221Key) - 1) || ((PBYTE)pStr < (PBYTE)pUserData) || ((PBYTE)pStr+keyLen > (PBYTE)(pUserData) + UserDataLen)) { TRC_ERR((TB, "Invalid key buffer %d %p", keyLen, pStr)); WDW_LogAndDisconnect(pTSWd, TRUE, Log_RDP_BadUserData, (PBYTE)pUserData, UserDataLen); DC_QUIT; }
if (strncmp(pStr, clientH221Key, sizeof(clientH221Key) - 1)) { TRC_ERR((TB, "Wrong key %*s", pStr)); WDW_LogAndDisconnect(pTSWd, TRUE, Log_RDP_BadUserData, (PBYTE)pUserData, UserDataLen); DC_QUIT; } } else { TRC_ERR((TB, "Wrong key %d on user data", pClientUserData->key.key_type)); WDW_LogAndDisconnect(pTSWd, TRUE, Log_RDP_BadUserData, (PBYTE)pUserData, UserDataLen); DC_QUIT; } } else { TRC_ERR((TB, "<%p> %d pieces of user data on Conf Create Indication: reject it", pUserData->hDomain, pUserData->ulUserDataMembers)); WDW_LogAndDisconnect(pTSWd, TRUE, Log_RDP_BadUserData, (PBYTE)pUserData, UserDataLen);
DC_QUIT; } // This is our client data.
// Save the domain handle for later.
pTSWd->hDomain = pUserData->hDomain;
// Parse the user data. Make sure the octet string is well-formed.
// pClientUserData->octet_string is an offset from the start of the
// user data.
// Validate data length
if ((UINT_PTR)pClientUserData->octet_string < sizeof(USERDATAINFO)) { TRC_ERR((TB,"UserData octet_string offset %p too short", pClientUserData->octet_string)); WDW_LogAndDisconnect(pTSWd, TRUE, Log_RDP_BadUserData, (PBYTE)pUserData, UserDataLen); DC_QUIT; } if ((UINT_PTR)pClientUserData->octet_string >= UserDataLen) { TRC_ERR((TB,"UserData octet_string offset %p too long for data len %u", pClientUserData->octet_string, UserDataLen)); WDW_LogAndDisconnect(pTSWd, TRUE, Log_RDP_BadUserData, (PBYTE)pUserData, UserDataLen); DC_QUIT; }
pOctet = (GCCOctetString UNALIGNED *)((PBYTE)pUserData + (UINT_PTR)pClientUserData->octet_string);
// Here we have to ckeck if we can actually dereference.
// We obtained pOcted by adding a size to the pUserData. And we already
// checked that what we added is less then the UserData length.
if (((LPBYTE)pOctet+sizeof(GCCOctetString) > (LPBYTE)pUserData+UserDataLen) || ((LPBYTE)pOctet+sizeof(GCCOctetString) < (LPBYTE)pOctet)) { TRC_ERR((TB,"Not enough buffer for an sizeof(GCCOctetString)=%d at %p ", sizeof(GCCOctetString), pOctet->octet_string)); WDW_LogAndDisconnect(pTSWd, TRUE, Log_RDP_BadUserData, (PBYTE)pUserData, UserDataLen); DC_QUIT; } if ((UINT_PTR)pOctet->octet_string >= UserDataLen) { TRC_ERR((TB,"UserData octet_string offset %p too long for data len %u", pOctet->octet_string, UserDataLen)); WDW_LogAndDisconnect(pTSWd, TRUE, Log_RDP_BadUserData, (PBYTE)pUserData, UserDataLen); DC_QUIT; }
pHeader = (PRNS_UD_HEADER)((PBYTE)pUserData + (UINT_PTR)pOctet->octet_string); dataLen = pOctet->octet_string_length;
// Validate the datalength
if (dataLen < sizeof(RNS_UD_HEADER)) { TRC_ERR((TB, "Error: User data too short!")); WDW_LogAndDisconnect(pTSWd, TRUE, Log_RDP_BadUserData, (PBYTE)pUserData, UserDataLen); DC_QUIT; }
// At this point we know that pHeader points within the buffer.
// We checked that the pOctet->octet_string is less then the UserDataLen
// We just have to check that we have enough buffer left
// after that fordataLen.
// Note taht dataLen at this point is at least the size of RNS_UD_HEADER.
if (((LPBYTE)pHeader +dataLen > (PBYTE)pUserData + UserDataLen ) || ((LPBYTE)pHeader +dataLen < (PBYTE)pHeader ) || (pHeader->length >dataLen)) { TRC_ERR((TB,"Not enough buffer left to store RNS_UD_HEADER %p, %p, %u ", pUserData, pHeader, UserDataLen )); WDW_LogAndDisconnect(pTSWd, TRUE, Log_RDP_BadUserData, (PBYTE)pUserData, UserDataLen); DC_QUIT; } } // Else, this is pre-parsed user data via a shadow connection
else { dataLen = cbParsedData; }
// We assume that the pre-parsed data is trusted.
pEnd = (PRNS_UD_HEADER)((PBYTE)pHeader + dataLen);
TRC_DATA_DBG("Our client's User Data", pHeader, dataLen); // Loop through user data, extracting each piece.
do { switch (pHeader->type) { case RNS_UD_CS_CORE_ID: // Beta2 Client core user data did not include the new
// field postBeta2ColorDepth, so check that the length of
// the incoming user data is at least this long.
// The WDWConnect parses this data and it checks the length we
// supply before it derefs parameters that are declared after
// postBeta2ColorDepth in the struct.
if (pHeader->length >= (FIELDOFFSET(RNS_UD_CS_CORE, postBeta2ColorDepth) + FIELDSIZE(RNS_UD_CS_CORE, postBeta2ColorDepth))) { *ppClientCoreData = (PRNS_UD_CS_CORE)pHeader; TRC_DATA_DBG("Core data", pHeader, pHeader->length); } else { TRC_ERR((TB, "Core data not long enough -- old client?")); WDW_LogAndDisconnect(pTSWd, TRUE, Log_RDP_BadUserData, (PBYTE)pUserData, UserDataLen); DC_QUIT; } break;
case RNS_UD_CS_SEC_ID: // Old clients don't have the extEncryptionMethods.
// The extEncryptionMethods field is used only for french locale.
// We have to allow buffers that don't have space for extEncryptionMethods
// because the buffer will be processed in SM_Connect and there we take care of shorter fields.
// Nothing else processes this buffer after SM_Connect at this point.
if (pHeader->length >= FIELDOFFSET(RNS_UD_CS_SEC,encryptionMethods) + FIELDSIZE(RNS_UD_CS_SEC,encryptionMethods)) { *ppClientSecurityData = (PRNS_UD_CS_SEC)pHeader; TRC_DATA_DBG("Security data", pHeader, pHeader->length); } else { TRC_ERR((TB, "Security data not long enough")); WDW_LogAndDisconnect(pTSWd, TRUE, Log_RDP_BadUserData, (PBYTE)pUserData, UserDataLen); DC_QUIT; } break; case RNS_UD_CS_NET_ID: if (pHeader->length >= sizeof(RNS_UD_CS_NET)) { *ppClientNetData = (PRNS_UD_CS_NET)pHeader; TRC_DATA_DBG("Net data", pHeader, pHeader->length); } else { TRC_ERR((TB, "Net data not long enough")); WDW_LogAndDisconnect(pTSWd, TRUE, Log_RDP_BadUserData, (PBYTE)pUserData, UserDataLen); DC_QUIT; } break;
case TS_UD_CS_CLUSTER_ID: if (pHeader->length >=sizeof(TS_UD_CS_CLUSTER)) { *ppClientClusterData = (TS_UD_CS_CLUSTER *)pHeader; TRC_DATA_DBG("Cluster data", pHeader, pHeader->length); } else { TRC_ERR((TB, "Cluster data not long enough")); WDW_LogAndDisconnect(pTSWd, TRUE, Log_RDP_BadUserData, (PBYTE)pUserData, UserDataLen); DC_QUIT; } break;
default: TRC_ERR((TB, "Unknown user data type %d", pHeader->type)); TRC_DATA_ERR("Unknown user data", pHeader, pHeader->length); WDW_LogAndDisconnect(pTSWd, TRUE, Log_RDP_BadUserData, (PBYTE)pUserData, UserDataLen);
break; } if ((PBYTE)pHeader + pHeader->length < (PBYTE)pHeader) { // we detected a length that causes overflow
TRC_ERR((TB, "Header length too big! Overflow detected !")); WDW_LogAndDisconnect(pTSWd, TRUE, Log_RDP_BadUserData, (PBYTE)pUserData, UserDataLen);
DC_QUIT; }
// We check the zero length now after we update the pHeader value.
// Otherwize we will exit with an error when we actually check the sizes.
// don't get stuck here for ever...
if (pHeader->length == 0) { TRC_ERR((TB, "header length was zero!")); break; }
// Move on to the next user data string.
pHeader = (PRNS_UD_HEADER)((PBYTE)pHeader + pHeader->length); } while ((pHeader +1) <= pEnd);
if ((PBYTE)pHeader > (PBYTE)pEnd) { TRC_ERR((TB, "Error: User data too short!")); WDW_LogAndDisconnect(pTSWd, TRUE, Log_RDP_BadUserData, (PBYTE)pUserData, UserDataLen); DC_QUIT; }
// Make sure we found all our client data. Note that Net and
// Cluster data blocks are optional - RDP4 client doesn't send Net data,
// RDP4 and 5 don't send Cluster data.
if ((*ppClientSecurityData == NULL) || (*ppClientCoreData == NULL)) { TRC_ERR((TB,"<%p> Security [%p] or Core [%p] data missing", pUserData ? pUserData->hDomain : 0, *ppClientSecurityData, *ppClientCoreData)); DC_QUIT; }
success = TRUE;
DC_EXIT_POINT: DC_END_FN(); return success;
} /* WDWParseUserData */
/****************************************************************************/ /* Name: WDWVCMessage */ /* */ /* Purpose: Send a control message to the Client's VC subsystem */ /* */ /* Params: flags - VC header flags to send */ /****************************************************************************/ void WDWVCMessage(PTSHARE_WD pTSWd, UINT32 flags) { PVOID pBuffer; CHANNEL_PDU_HEADER UNALIGNED *pHdr; PNM_CHANNEL_DATA pChannelData; UINT16 MCSChannelID;
DC_BEGIN_FN("WDWVCMessage");
/************************************************************************/ /* Pick a random channel - any channel will reach the VC subsystem */ /************************************************************************/ MCSChannelID = NM_VirtualChannelToMCS(pTSWd->pNMInfo, 0, &pChannelData);
/************************************************************************/ /* If channel 0 doesn't exist, there are no channels - drop out now. */ /************************************************************************/ if (MCSChannelID != (UINT16) -1) { /********************************************************************/ /* Get a buffer */ /********************************************************************/ if ( STATUS_SUCCESS == SM_AllocBuffer(pTSWd->pSmInfo, &pBuffer, sizeof(CHANNEL_PDU_HEADER), TRUE, FALSE) ) { pHdr = (CHANNEL_PDU_HEADER UNALIGNED *)pBuffer; pHdr->flags = flags; pHdr->length = sizeof(CHANNEL_PDU_HEADER);
/****************************************************************/ /* Send the info */ /****************************************************************/ SM_SendData(pTSWd->pSmInfo, pBuffer, sizeof(CHANNEL_PDU_HEADER), TS_LOWPRIORITY, MCSChannelID, FALSE, RNS_SEC_ENCRYPT, FALSE);
TRC_NRM((TB, "Sent VC flags %#x", flags)); } else { TRC_ERR((TB, "Failed to alloc %d byte buffer", sizeof(CHANNEL_PDU_HEADER))); } } else { TRC_ALT((TB, "Dropping VC message for channel 0!")); }
DC_END_FN(); } /* WDWVCMessage */
//
// WDWCompressToOutbuf
// Compressed the buffer directly into the outbuf.
// Caller MUST decide if input buf is in size range for compression
// and should handle copying over the buffer directly in that case.
//
// Note this function does not update the SC compression estimates.
// It is intended for compressing VC data. The SC compression estimates
// are used to predict how much space to allocate for the graphics outbuf's
// anyway so it is actually more appropriate for that estimate to be computed
// separately. VC compression does not need an estimate, we allocate up to
// our max channel chunk length.
//
// Params:
// pSrcData - input buffer
// cbSrcLen - length of input buffer
// pOutBuf - output buffer
// pcbOutLen- compressed output size
// Returns:
// Compression result (see compress() fn)
//
UCHAR WDWCompressToOutbuf(PTSHARE_WD pTSWd, UCHAR* pSrcData, ULONG cbSrcLen, UCHAR* pOutBuf, ULONG* pcbOutLen) { UCHAR compressResult = 0; ULONG CompressedSize = cbSrcLen;
DC_BEGIN_FN("WDWCompressToOutbuf");
TRC_ASSERT((pTSWd != NULL), (TB,"NULL pTSWd")); TRC_ASSERT(((cbSrcLen > WD_MIN_COMPRESS_INPUT_BUF) && (cbSrcLen < MAX_COMPRESS_INPUT_BUF)), (TB,"Compression src len out of range: %d", cbSrcLen));
//Attempt to compress directly into the outbuf
compressResult = compress(pSrcData, pOutBuf, &CompressedSize, pTSWd->pMPPCContext); if(compressResult & PACKET_COMPRESSED) { //Successful compression.
TRC_ASSERT((CompressedSize >= CompressedSize), (TB,"Compression created larger size than uncompr")); compressResult |= pTSWd->bFlushed; pTSWd->bFlushed = 0; } else if(compressResult & PACKET_FLUSHED) { //Overran compression history, copy over the
//uncompressed buffer.
pTSWd->bFlushed = PACKET_FLUSHED; memcpy(pOutBuf, pSrcData, cbSrcLen); pTSWd->pProtocolStatus->Output.CompressFlushes++; } else { TRC_ALT((TB, "Compression FAILURE")); }
DC_END_FN(); *pcbOutLen = CompressedSize; return compressResult; }
|