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.
 
 
 
 
 
 

1865 lines
80 KiB

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