/****************************************************************************/ // nwdwcpp.cpp // // WDW internal functions. // // Copyright (C) 1997-2000 Microsoft Corporation /****************************************************************************/ #include #pragma hdrstop #define pTRCWd pTSWd #define TRC_FILE "nwdwcpp" #include extern "C" { #include #include #include #include } #include "slicense.h" #include #include #include "domain.h" //Client side error reporting #include "tserrs.h" #ifdef DC_DEBUG extern "C" { VOID IcaBreakOnDebugger( ); } #endif extern "C" { /****************************************************************************/ /* Data returned on IOCTL_VIDEO_QUERY_CURRENT_MODE */ /* */ /* This code unashamedly filched from Remotedd code developed for */ /* NetMeeting. */ /****************************************************************************/ const VIDEO_MODE_INFORMATION wdSimModes[] = { sizeof(VIDEO_MODE_INFORMATION), /* length */ 0, /* Mode index */ /************************************************************************/ /* VisScreenWidth and VisScreenHeight can be in two forms: */ /* - 0xaaaabbbb - range of values supported (aaaa = max, bbbb = min) */ /* - 0x0000aaaa - single value supported */ /* For example: */ /* - 0x07d0012c = 2000-300 */ /* - 0x0640012c = 1600-300 */ /* - 0x04b000c8 = 1200-200 */ /* */ /* @@@MF For now, support 800x600 only */ /************************************************************************/ 0x00000320, /* VisScreenWidth */ 0x00000258, /* VisScrenHeight */ 0x00000320, /* ScreenStride (0xffff0000 = any) */ 0x00000001, /* NumberOfPlanes */ 0x00000008, /* BitsPerPlane */ 0, /* Frequency */ 0, /* XMillimeter */ 0, /* YMillimeter */ 0, /* NumberRedBits */ 0, /* NumberGreenBits */ 0, /* NumberBlueBits */ 0x00000000, /* RedMask */ 0x00000000, /* GreenMask */ 0x00000000, /* BlueMask */ VIDEO_MODE_COLOR | VIDEO_MODE_GRAPHICS, /* AttributeFlags */ 0x00000320, /* VideoMemoryBitmapWidth */ 0x00000258, /* VideoMemoryBitmapHeight */ 0 /* DriverSpecificAttributeFlags */ }; #ifdef DC_DEBUG /****************************************************************************/ /* IOCtl descriptions (debug build only) */ /****************************************************************************/ const char *wdIoctlA[] = { "IOCTL_ICA_SET_TRACE", "IOCTL_ICA_TRACE", "IOCTL_ICA_SET_SYSTEM_TRACE", "IOCTL_ICA_SYSTEM_TRACE", "IOCTL_ICA_UNBIND_VIRTUAL_CHANNEL", "Unknown", "Unknown", "Unknown", "Unknown", "Unknown", "IOCTL_ICA_STACK_PUSH", "IOCTL_ICA_STACK_POP", "IOCTL_ICA_STACK_CREATE_ENDPOINT", "IOCTL_ICA_STACK_CD_CREATE_ENDPOINT", "IOCTL_ICA_STACK_OPEN_ENDPOINT", "IOCTL_ICA_STACK_CLOSE_ENDPOINT", "IOCTL_ICA_STACK_ENABLE_DRIVER", "IOCTL_ICA_STACK_CONNECTION_WAIT", "IOCTL_ICA_STACK_WAIT_FOR_ICA", "IOCTL_ICA_STACK_CONNECTION_QUERY", "IOCTL_ICA_STACK_CONNECTION_SEND", "IOCTL_ICA_STACK_CONNECTION_REQUEST", "IOCTL_ICA_STACK_QUERY_PARAMS", "IOCTL_ICA_STACK_SET_PARAMS", "IOCTL_ICA_STACK_ENCRYPTION_OFF", "IOCTL_ICA_STACK_ENCRYPTION_PERM", "IOCTL_ICA_STACK_CALLBACK_INITIATE", "IOCTL_ICA_STACK_QUERY_LAST_ERROR", "IOCTL_ICA_STACK_WAIT_FOR_STATUS", "IOCTL_ICA_STACK_QUERY_STATUS", "IOCTL_ICA_STACK_REGISTER_HOTKEY", "IOCTL_ICA_STACK_CANCEL_IO", "IOCTL_ICA_STACK_QUERY_STATE", "IOCTL_ICA_STACK_SET_STATE", "IOCTL_ICA_STACK_QUERY_LAST_INPUT_TIME", "IOCTL_ICA_STACK_TRACE", "IOCTL_ICA_STACK_CALLBACK_COMPLETE", "IOCTL_ICA_STACK_CD_CANCEL_IO", "IOCTL_ICA_STACK_QUERY_CLIENT", "IOCTL_ICA_STACK_QUERY_MODULE_DATA", "IOCTL_ICA_STACK_REGISTER_BROKEN", "IOCTL_ICA_STACK_ENABLE_IO", "IOCTL_ICA_STACK_DISABLE_IO", "IOCTL_ICA_STACK_SET_CONNECTED", "IOCTL_ICA_STACK_SET_CLIENT_DATA", "IOCTL_ICA_STACK_QUERY_BUFFER", "IOCTL_ICA_STACK_DISCONNECT", "IOCTL_ICA_STACK_RECONNECT", "IOCTL_ICA_STACK_CONSOLE_CONNECT", "IOCTL_ICA_STACK_SET_CONFIG" }; const char *wdIoctlB[] = { "IOCTL_ICA_CHANNEL_TRACE", "IOCTL_ICA_CHANNEL_ENABLE_SHADOW", "Unknown", "Unknown", "Unknown", "Unknown", "Unknown", "Unknown", "Unknown", "Unknown", "IOCTL_ICA_VIRTUAL_LOAD_FILTER", "IOCTL_ICA_VIRTUAL_UNLOAD_FILTER", "IOCTL_ICA_VIRTUAL_ENABLE_FILTER", "IOCTL_ICA_VIRTUAL_DISABLE_FILTER", "IOCTL_ICA_VIRTUAL_BOUND", "IOCTL_ICA_VIRTUAL_CANCEL_INPUT", "IOCTL_ICA_VIRTUAL_CANCEL_OUTPUT", "IOCTL_ICA_VIRTUAL_QUERY_MODULE_DATA", "IOCTL_ICA_VIRTUAL_QUERY_BINDINGS", "IOCTL_ICA_STACK_QUERY_LICENSE_CAPABILITIES", "IOCTL_ICA_STACK_REQUEST_CLIENT_LICENSE", "IOCTL_ICA_STACK_SEND_CLIENT_LICENSE", "IOCTL_ICA_STACK_LICENSE_PROTOCOL_COMPLETE", "IOCTL_ICA_STACK_GET_LICENSE_DATA", "IOCTL_ICA_STACK_SEND_KEEPALIVE_PDU", "IOCTL_TS_STACK_QUERY_LOAD_BALANCE_INFO", "IOCTL_TS_STACK_SEND_CLIENT_REDIRECTION", "IOCTL_ICA_STACK_QUERY_CLIENT_EXTENDED", "IOCTL_ICA_STACK_QUERY_AUTORECONNECT" }; const char *wdIoctlC[] = { "IOCTL_VIDEO_QUERY_AVAIL_MODES", "IOCTL_VIDEO_QUERY_NUM_AVAIL_MODES", "IOCTL_VIDEO_QUERY_CURRENT_MODE", "IOCTL_VIDEO_SET_CURRENT_MODE", "IOCTL_VIDEO_RESET_DEVICE", "IOCTL_VIDEO_LOAD_AND_SET_FONT", "IOCTL_VIDEO_SET_PALETTE_REGISTERS", "IOCTL_VIDEO_SET_COLOR_REGISTERS", "IOCTL_VIDEO_ENABLE_CURSOR", "IOCTL_VIDEO_DISABLE_CURSOR", "IOCTL_VIDEO_SET_CURSOR_ATTR", "IOCTL_VIDEO_QUERY_CURSOR_ATTR", "IOCTL_VIDEO_SET_CURSOR_POSITION", "IOCTL_VIDEO_QUERY_CURSOR_POSITION", "IOCTL_VIDEO_ENABLE_POINTER", "IOCTL_VIDEO_DISABLE_POINTER", "IOCTL_VIDEO_SET_POINTER_ATTR", "IOCTL_VIDEO_QUERY_POINTER_ATTR", "IOCTL_VIDEO_SET_POINTER_POSITION", "IOCTL_VIDEO_QUERY_POINTER_POSITION", "IOCTL_VIDEO_QUERY_POINTER_CAPABILITIES", "IOCTL_VIDEO_GET_BANK_SELECT_CODE", "IOCTL_VIDEO_MAP_VIDEO_MEMORY", "IOCTL_VIDEO_UNMAP_VIDEO_MEMORY", "IOCTL_VIDEO_QUERY_PUBLIC_ACCESS_RANGES", "IOCTL_VIDEO_FREE_PUBLIC_ACCESS_RANGES", "IOCTL_VIDEO_QUERY_COLOR_CAPABILITIES", "IOCTL_VIDEO_SET_POWER_MANAGEMENT", "IOCTL_VIDEO_GET_POWER_MANAGEMENT", "IOCTL_VIDEO_SHARE_VIDEO_MEMORY", "IOCTL_VIDEO_UNSHARE_VIDEO_MEMORY", }; const char *wdIoctlD[] = { "IOCTL_KEYBOARD_ICA_INPUT", "IOCTL_KEYBOARD_ICA_LAYOUT", "IOCTL_KEYBOARD_ICA_SCANMAP", "IOCTL_KEYBOARD_ICA_TYPE" }; const char *wdIoctlE[] = { "IOCTL_VIDEO_ICA_QUERY_FONT_PAIRS", "IOCTL_VIDEO_ICA_ENABLE_GRAPHICS", "IOCTL_VIDEO_ICA_DISABLE_GRAPHICS", "IOCTL_VIDEO_ICA_SET_CP", "IOCTL_VIDEO_ICA_STOP_OK", "IOCTL_VIDEO_ICA_REVERSE_MOUSE_POINTER", "IOCTL_VIDEO_ICA_COPY_FRAME_BUFFER", "IOCTL_VIDEO_ICA_WRITE_TO_FRAME_BUFFER", "IOCTL_VIDEO_ICA_INVALIDATE_MODES", "IOCTL_VIDEO_ICA_SCROLL", "Unknown", "Unknown", "Unknown", "Unknown", "Unknown", "Unknown", "IOCTL_ICA_STACK_ENCRYPTION_ENTER", "IOCTL_ICA_STACK_ENCRYPTION_EXIT", }; const char *wdIoctlTsh[] = { "IOCTL_WDTS_DD_CONNECT", "IOCTL_WDTS_DD_DISCONNECT", "IOCTL_WDTS_DD_RECONNECT", "IOCTL_WDTS_DD_OUTPUT_AVAILABLE", "IOCTL_WDTS_DD_TIMER_INFO", "IOCTL_WDTS_DD_CLIP", "IOCTL_WDTS_DD_SHADOW_CONNECT", "IOCTL_WDTS_DD_SHADOW_DISCONNECT", "IOCTL_WDTS_DD_SHADOW_SYNCHRONIZE", "IOCTL_WDTS_DD_REDRAW_SCREEN", "IOCTL_WDTS_DD_QUERY_SHADOW_CAPS", "IOCTL_WDTS_DD_GET_BITMAP_KEYDATABASE", }; #endif /* DC_DEBUG */ /****************************************************************************/ // WD_Ioctl // // Query/Set configuration information for the WD. /****************************************************************************/ NTSTATUS WD_Ioctl(PTSHARE_WD pTSWd, PSD_IOCTL pSdIoctl) { NTSTATUS status = STATUS_SUCCESS; UINT32 bufferLen; unsigned fn; PVIDEO_MODE_INFORMATION pVidInfo; DC_BEGIN_FN("WD_Ioctl"); // Special-case output-available DD ioctl for speed - separating the // most commonly-run case into a separate if that will tend to fall // through greatly speeds up Pentium Pro branch prediction and cache // line hit probability. if (pSdIoctl->IoControlCode == IOCTL_WDTS_DD_OUTPUT_AVAILABLE) { PTSHARE_DD_OUTPUT_IN pOutputIn; PTSHARE_DD_OUTPUT_OUT pOutputOut; ShareClass *dcShare; // Local variables to make the code more readable. pOutputIn = (PTSHARE_DD_OUTPUT_IN)pSdIoctl->InputBuffer; pOutputOut = (PTSHARE_DD_OUTPUT_OUT)pSdIoctl->OutputBuffer; dcShare = (ShareClass *)(pTSWd->dcShare); dcShare->m_pShm = (PSHM_SHARED_MEMORY)pOutputIn->pShm; WDW_CHECK_SHM((pOutputIn->pShm)); if ((pTSWd->StackClass == Stack_Primary) || (pTSWd->StackClass == Stack_Console)) { INT32 milliSecs; TRC_DBG((TB, "IOCTL_WDTS_DD_OUTPUT_AVAILABLE")); if (!pTSWd->dead) { TRC_DBG((TB, "OK to process the IOCtl")); TRC_ASSERT((dcShare != NULL), (TB, "NULL Share Class")); // NB There is no code here to check the size of the buffers // on the IOCtl. This is a performance critical path which // can do without it. TRC_DBG((TB, "OutputAvailable IOCtl: force send=%d", pOutputIn->forceSend)); // Check if the framebuffer is valid if (pOutputIn->pFrameBuf != NULL && pOutputIn->frameBufHeight != 0 && pOutputIn->frameBufWidth != 0) { // For normal output IOCTLs, call DCS_TTDS. if (!pOutputIn->schedOnly) { TRC_DBG((TB, "Normal output")); // Stop the timer (in the main we don't use it, so // avoid excess context switches). WDWStopRITTimer(pTSWd); // Call the Share Core to do the work. // need to return status code so caller can bail out // in case of error status = dcShare->DCS_TimeToDoStuff(pOutputIn, &(pOutputOut->schCurrentMode), &milliSecs); // Restart the timer if requested by the core. if (milliSecs != -1L) { TRC_DBG((TB, "Run the RIT timer for %ld ms", milliSecs)); WDW_StartRITTimer(pTSWd, milliSecs); } else { TRC_DBG((TB, "Skipped starting the timer!")); } } else { // It's just a wake-up call to the scheduler. TRC_NRM((TB, "Just wake up the scheduler")); dcShare->SCH_ContinueScheduling(SCH_MODE_NORMAL); // Be sure to set the current scheduler mode. pOutputOut->schCurrentMode = dcShare->SCH_GetCurrentMode(); } pOutputOut->schInputKickMode = dcShare->SCH_GetInputKickMode(); } else { TRC_ERR((TB, "Bad FrameBuffer input parameter")); status = STATUS_INVALID_PARAMETER; } } else { dcShare->DCS_DiscardAllOutput(); TRC_ERR((TB, "Dead - ignoring IOCTL_WDTS_DD_OUTPUT_AVAILABLE")); status = STATUS_DEVICE_NOT_READY; } dcShare->m_pShm = NULL; WDW_CHECK_SHM((pOutputIn->pShm)); DC_QUIT; } // Shadow stack: Duplicate the data send. Note that the target's primary // stack will have already placed the data in the shadow buffer so we // don't need to re-encode it. There may be multiple shadow stacks // consuming data from the primary stack so don't touch it! else { PSHADOW_INFO pShadowInfo = dcShare->m_pShm->pShadowInfo; if (pShadowInfo && pShadowInfo->messageSize) { PBYTE pShadowBuffer; // //find out if the stack doesn't want a low water mark //if so, allocate out of the ring (8192) // UINT32 sizeToAlloc = IcaGetSizeForNoLowWaterMark(pTSWd->pContext); // fWait is TRUE means that we will always wait for a buffer to be avail status = SM_AllocBuffer(dcShare->m_pSmInfo, (PPVOID) &pShadowBuffer, sizeToAlloc > pShadowInfo->messageSize? sizeToAlloc : pShadowInfo->messageSize, TRUE, FALSE); if ( STATUS_SUCCESS == status ) { memcpy(pShadowBuffer, pShadowInfo->data, pShadowInfo->messageSize); if (SM_SendData(dcShare->m_pSmInfo, pShadowBuffer, pShadowInfo->messageSize, PROT_PRIO_MISC, 0, FALSE, RNS_SEC_ENCRYPT, FALSE)) { status = STATUS_SUCCESS; TRC_NRM((TB, "Shadow stack send: %ld", pShadowInfo->messageSize)); } else { status = STATUS_UNEXPECTED_IO_ERROR; TRC_ALT((TB, "Shadow stack send failed: %ld", pShadowInfo->messageSize)); } #ifdef DC_HICOLOR // Is there any overflow data too send? if (pShadowInfo->messageSizeEx) { status = SM_AllocBuffer(dcShare->m_pSmInfo,(PPVOID)&pShadowBuffer, (sizeToAlloc > pShadowInfo->messageSizeEx )? sizeToAlloc : pShadowInfo->messageSizeEx, TRUE, FALSE); if ( STATUS_SUCCESS == status ) { memcpy( pShadowBuffer, &pShadowInfo->data[WD_MAX_SHADOW_BUFFER], pShadowInfo->messageSizeEx); if (SM_SendData(dcShare->m_pSmInfo, pShadowBuffer, pShadowInfo->messageSizeEx, PROT_PRIO_MISC, 0, FALSE, RNS_SEC_ENCRYPT, FALSE)) { status = STATUS_SUCCESS; TRC_NRM((TB, "Shadow stack send: %ld", pShadowInfo->messageSizeEx)); } else { status = STATUS_UNEXPECTED_IO_ERROR; TRC_ALT((TB, "Shadow stack send failed: %ld", pShadowInfo->messageSizeEx)); } } else { // Prevent regression, keep original return code status = STATUS_UNEXPECTED_IO_ERROR; TRC_ERR((TB, "Failed to allocate shadow stack send buffer")); } } #endif } else { // Prevent regression, keep original return code status = STATUS_UNEXPECTED_IO_ERROR; TRC_ERR((TB, "Failed to allocate shadow stack send buffer")); } } dcShare->m_pShm = NULL; WDW_CHECK_SHM((pOutputIn->pShm)); DC_QUIT; } } else { // Non-perf path IOCTLs. fn = WDW_IOCTL_FUNCTION(pSdIoctl->IoControlCode); TRC_NRM((TB, "%s (%d)", fn == 6 ? "IOCTL_VIDEO_ENUM_MONITOR_PDO" : fn < 49 ? wdIoctlA[fn] : fn < 50 ? "Unknown Ioctl" : fn < 77 ? wdIoctlB[fn - 50] : fn < 0x100 ? "Unknown Ioctl" : fn < 0x11f ? wdIoctlC[fn - 0x100] : fn < 0x200 ? "Unknown Ioctl" : fn < 0x204 ? wdIoctlD[fn - 0x200] : fn == 0x300 ? "IOCTL_MOUSE_ICA_INPUT" : fn < 0x400 ? "Unknown Ioctl" : fn < 0x412 ? wdIoctlE[fn - 0x400] : fn == 0x500 ? "IOCTL_T120_REQUEST" : fn < 0x510 ? "Unknown Ioctl" : fn < 0x520 ? wdIoctlTsh[fn - 0x510] : fn == 0x900 ? "IOCTL_TSHARE_CONF_CONNECT" : fn == 0x901 ? "IOCTL_TSHARE_CONF_DISCONNECT" : fn == 0x903 ? "IOCTL_TSHARE_USER_LOGON" : fn == 0x904 ? "IOCTL_TSHARE_GET_SEC_DATA" : fn == 0x905 ? "IOCTL_TSHARE_SET_SEC_DATA" : fn == 0x906 ? "IOCTL_TSHARE_SET_NO_ENCRYPT" : fn == 0x907 ? "IOCTL_TSHARE_QUERY_CHANNELS" : fn == 0x908 ? "IOCTL_TSHARE_CONSOLE_CONNECT" : fn == 0x909 ? "IOCTL_TSHARE_SEND_CERT_DATA" : fn == 0x90A ? "IOCTL_TSHARE_GET_CERT_DATA" : fn == 0x90B ? "IOCTL_TSHARE_SEND_CLIENT_RANDOM" : fn == 0x90C ? "IOCTL_TSHARE_GET_CLIENT_RANDOM" : fn == 0x90D ? "IOCTL_TSHARE_SHADOW_CONNECT" : fn == 0x90E ? "IOCTL_TSHARE_SET_ERROR_INFO" : "Unknown Ioctl", fn)); } /************************************************************************/ /* Firstly, zero the no. of returned bytes. */ /************************************************************************/ pSdIoctl->BytesReturned = 0; switch ( pSdIoctl->IoControlCode ) { /********************************************************************/ // We expect IOCTL_ICA_TRACE before tracing anything. Check for NULL // Inbuf - we've seen this happen. // // *** DO NOT TRACE IN THIS BRANCH *** /********************************************************************/ case IOCTL_ICA_TRACE: { PICA_TRACE_BUFFER pTrc = (PICA_TRACE_BUFFER)pSdIoctl->InputBuffer; if (pTrc != NULL) { IcaStackTrace(pTSWd->pContext, TC_DISPLAY, // @@@MF Should be pTrc->TraceClass // but it's overwritten with '7' pTrc->TraceEnable, (char *)pTrc->Data); } break; } /****************************************************************************/ /****************************************************************************/ /* Firstly, for debug purposes, group together those IOCtls that we do not */ /* expect to get (ICA uses them for text mode support, which we do not */ /* implement). */ /****************************************************************************/ /****************************************************************************/ case IOCTL_VIDEO_QUERY_CURSOR_ATTR : case IOCTL_VIDEO_SET_CURSOR_ATTR : case IOCTL_VIDEO_QUERY_CURSOR_POSITION : case IOCTL_VIDEO_SET_CURSOR_POSITION : case IOCTL_VIDEO_ENABLE_CURSOR : case IOCTL_VIDEO_DISABLE_CURSOR : case IOCTL_VIDEO_QUERY_POINTER_ATTR : case IOCTL_VIDEO_SET_POINTER_ATTR : case IOCTL_VIDEO_QUERY_POINTER_POSITION : case IOCTL_VIDEO_ENABLE_POINTER : case IOCTL_VIDEO_DISABLE_POINTER : case IOCTL_VIDEO_QUERY_POINTER_CAPABILITIES : case IOCTL_VIDEO_SET_PALETTE_REGISTERS : case IOCTL_VIDEO_LOAD_AND_SET_FONT : case IOCTL_VIDEO_MAP_VIDEO_MEMORY : case IOCTL_VIDEO_UNMAP_VIDEO_MEMORY : case IOCTL_VIDEO_ICA_QUERY_FONT_PAIRS : case IOCTL_VIDEO_ICA_COPY_FRAME_BUFFER : case IOCTL_VIDEO_ICA_WRITE_TO_FRAME_BUFFER : case IOCTL_VIDEO_ICA_REVERSE_MOUSE_POINTER : case IOCTL_VIDEO_ICA_SET_CP : case IOCTL_VIDEO_ICA_SCROLL : { TRC_ALT((TB, "Unexpected IOCtl %x (function %d)", pSdIoctl->IoControlCode, WDW_IOCTL_FUNCTION(pSdIoctl->IoControlCode))); } break; /****************************************************************************/ /****************************************************************************/ /* Now the IOCtls that we do nothing with but just return OK. */ /****************************************************************************/ /****************************************************************************/ /********************************************************************/ /* Both of the following are expected (they occur whenever we */ /* enable or disable graphics - typically when the client is */ /* minimized and restored). However we don't need to do anything */ /* with them. */ /********************************************************************/ case IOCTL_VIDEO_ICA_ENABLE_GRAPHICS : case IOCTL_VIDEO_ICA_DISABLE_GRAPHICS : /********************************************************************/ /* Miscellaneous IOCTLs we don't process. */ /********************************************************************/ case IOCTL_ICA_STACK_DISCONNECT: case IOCTL_VIDEO_SET_POINTER_POSITION : case IOCTL_VIDEO_ICA_STOP_OK : case IOCTL_ICA_STACK_SET_CLIENT_DATA: case IOCTL_ICA_STACK_ENCRYPTION_OFF: case IOCTL_ICA_STACK_ENCRYPTION_PERM: case IOCTL_VIDEO_ICA_INVALIDATE_MODES : { TRC_NRM((TB, "Nothing to do")); } break; /****************************************************************************/ /****************************************************************************/ /* Here are a block of IOCtls that we process exactly as per Citrix. The */ /* calls are to unmodified Citrix routines. */ /****************************************************************************/ /****************************************************************************/ case IOCTL_KEYBOARD_QUERY_ATTRIBUTES : { status = KeyboardQueryAttributes( pTSWd, pSdIoctl ); } break; case IOCTL_KEYBOARD_QUERY_TYPEMATIC : { status = KeyboardQueryTypematic( pTSWd, pSdIoctl ); } break; case IOCTL_KEYBOARD_SET_TYPEMATIC : { status = KeyboardSetTypematic( pTSWd, pSdIoctl ); } break; case IOCTL_KEYBOARD_QUERY_INDICATORS : { status = KeyboardQueryIndicators( pTSWd, pSdIoctl ); } break; case IOCTL_KEYBOARD_SET_INDICATORS : { status = KeyboardSetIndicators( pTSWd, pSdIoctl ); } break; case IOCTL_KEYBOARD_QUERY_INDICATOR_TRANSLATION : { status = KeyboardQueryIndicatorTranslation( pTSWd, pSdIoctl ); } break; case IOCTL_KEYBOARD_SET_IME_STATUS : { status = KeyboardSetImeStatus( pTSWd, pSdIoctl ); } break; case IOCTL_MOUSE_QUERY_ATTRIBUTES : { status = MouseQueryAttributes( pTSWd, pSdIoctl ); } break; case IOCTL_KEYBOARD_ICA_LAYOUT : status = KeyboardSetLayout( pTSWd, pSdIoctl ); break; case IOCTL_KEYBOARD_ICA_SCANMAP : status = KeyboardSetScanMap( pTSWd, pSdIoctl ); break; case IOCTL_KEYBOARD_ICA_TYPE : status = KeyboardSetType( pTSWd, pSdIoctl ); break; /****************************************************************************/ /****************************************************************************/ /* The next set of cases are the IOCtls with which we do significant real */ /* work. */ /****************************************************************************/ /****************************************************************************/ // stash our new session ID case IOCTL_ICA_STACK_RECONNECT: { TRC_NRM((TB, "Got reconnect IOCTL")); TRC_ASSERT((pSdIoctl->InputBufferLength == sizeof(ICA_STACK_RECONNECT)), (TB, "Bad Reconnect Info")); pTSWd->sessionId = ((PICA_STACK_RECONNECT)(pSdIoctl->InputBuffer))->sessionId; } break; case IOCTL_ICA_SET_TRACE: { #ifdef DC_DEBUG TRC_UpdateConfig(pTSWd, pSdIoctl); TRC_NRM((TB, "Got Set Trace IOCtl")); #endif } break; case IOCTL_BEEP_SET: { TRC_NRM((TB, "Got Beep Set IOCtl")); WDWSendBeep(pTSWd, pSdIoctl); } break; case IOCTL_TSHARE_USER_LOGON: { TRC_NRM((TB, "Got user logon IOCtl")); WDWUserLoggedOn(pTSWd, pSdIoctl); pSdIoctl->BytesReturned = 0; } break; case IOCTL_TSHARE_GET_SEC_DATA: { TRC_NRM((TB, "Got GetSecurityData IOCtl")); status = SM_GetSecurityData(pTSWd->pSmInfo, pSdIoctl); } break; case IOCTL_TSHARE_SET_SEC_DATA: { TRC_NRM((TB, "Got SetSecurityData IOCtl")); if ((pSdIoctl->InputBuffer != NULL) && (pSdIoctl->InputBufferLength >= sizeof(SECINFO))) { status = pTSWd->SessKeyCreationStatus = SM_SetSecurityData(pTSWd->pSmInfo, (PSECINFO) pSdIoctl->InputBuffer); } else { // NULL data is sent when the client random or shadow // stack random could not be generated in user mode, // likely because of decryption failure on the random value. // We need to succeedd the IOCTL, but fail the key creation // return to the pSessKeyEvent waiter. status = STATUS_SUCCESS; pTSWd->SessKeyCreationStatus = STATUS_UNSUCCESSFUL; } // We always set the session key event to prevent a deadlock // if we are being attacked with bad client security data. This // set used to be in SM_SetSecurityData(), but there it might // not have been set if any encryption errors occurred. KeSetEvent(pTSWd->pSessKeyEvent, 0, FALSE); } break; // The shadow server sends it's certificate and shadow random to the // shadow client, which then sends an encrypted client random. This // is identical to the standard connection sequence. case IOCTL_TSHARE_SEND_CERT_DATA: { ShareClass *dcShare; dcShare = (ShareClass *)(pTSWd->dcShare); status = dcShare->SC_SendServerCert( (PSHADOWCERT) pSdIoctl->InputBuffer, pSdIoctl->InputBufferLength); } break; case IOCTL_TSHARE_SEND_CLIENT_RANDOM: { ShareClass *dcShare; dcShare = (ShareClass *)(pTSWd->dcShare); status = dcShare->SC_SendClientRandom((PBYTE) pSdIoctl->InputBuffer, pSdIoctl->InputBufferLength); } break; case IOCTL_TSHARE_GET_CERT_DATA: case IOCTL_TSHARE_GET_CLIENT_RANDOM: { ShareClass *dcShare; dcShare = (ShareClass *)(pTSWd->dcShare); status = dcShare->SC_GetSecurityData(pSdIoctl); } break; case IOCTL_ICA_STACK_SET_CONFIG: { PICA_STACK_CONFIG_DATA pConfigData; pConfigData = (PICA_STACK_CONFIG_DATA) pSdIoctl->InputBuffer; TRC_NRM((TB, "Got stack config data")); WDWSetConfigData(pTSWd, pConfigData); } break; case IOCTL_ICA_STACK_WAIT_FOR_ICA : { /****************************************************************/ /* Return the "default query stack," meaning reuse these */ /* drivers. */ /****************************************************************/ TRC_NRM((TB, "Stack wait for ICA")); } break; case IOCTL_ICA_STACK_CONSOLE_CONNECT : { /****************************************************************/ /* Return the "default query stack," meaning reuse these */ /* drivers. */ /****************************************************************/ TRC_NRM((TB, "Stack Console Connect")); } break; case IOCTL_ICA_STACK_QUERY_BUFFER : { ICA_STACK_QUERY_BUFFER *pBuffers; pBuffers = (ICA_STACK_QUERY_BUFFER *) pSdIoctl->OutputBuffer; pBuffers->WdBufferCount = TSHARE_WD_BUFFER_COUNT; pBuffers->TdBufferSize = TSHARE_TD_BUFFER_SIZE; pSdIoctl->BytesReturned = sizeof(ICA_STACK_QUERY_BUFFER); TRC_NRM((TB, "Stack query buffer, num %d, size %d", pBuffers->WdBufferCount, pBuffers->TdBufferSize)); } break; case IOCTL_TSHARE_CONF_CONNECT: { TRC_NRM((TB, "Got TSHARE_CONF_CONNECT IOCtl")); status = WDWConfConnect(pTSWd, pSdIoctl); } break; case IOCTL_TSHARE_CONSOLE_CONNECT: { TRC_NRM((TB, "Got TSHARE_CONSOLE_CONNECT IOCtl")); status = WDWConsoleConnect(pTSWd, pSdIoctl); } break; case IOCTL_TSHARE_SHADOW_CONNECT: status = WDWShadowConnect(pTSWd, pSdIoctl) ; break; case IOCTL_TSHARE_SET_ERROR_INFO: { TRC_NRM((TB, "Got SetErrorInfo IOCtl")); status = WDWSetErrorInfo(pTSWd, pSdIoctl); pSdIoctl->BytesReturned = 0; } break; case IOCTL_TSHARE_SEND_ARC_STATUS: { TRC_NRM((TB, "Got SetArcStatus IOCtl")); status = WDWSendArcStatus(pTSWd, pSdIoctl); pSdIoctl->BytesReturned = 0; } break; case IOCTL_ICA_STACK_SET_CONNECTED: status = STATUS_SUCCESS; break; case IOCTL_ICA_STACK_CONNECTION_QUERY : { PICA_STACK_CONFIG pIcaStackConfig; pIcaStackConfig = (PICA_STACK_CONFIG) pSdIoctl->OutputBuffer; memcpy(pIcaStackConfig->WdDLL, pTSWd->DLLName, sizeof(pIcaStackConfig->WdDLL)); pIcaStackConfig->SdClass[0] = SdNone; pSdIoctl->BytesReturned = pSdIoctl->OutputBufferLength; TRC_NRM((TB, "Stack Connection Query")); } break; case IOCTL_TSHARE_QUERY_CHANNELS: { TRC_NRM((TB, "Query Virtual Channel data")); status = NM_QueryChannels(pTSWd->pNMInfo, pSdIoctl->OutputBuffer, pSdIoctl->OutputBufferLength, &(pSdIoctl->BytesReturned)); } break; case IOCTL_WDTS_DD_CONNECT: { if (pSdIoctl->InputBuffer && (((PTSHARE_DD_CONNECT_IN)pSdIoctl->InputBuffer)->pShm)) { WDW_CHECK_SHM( (((PTSHARE_DD_CONNECT_IN)pSdIoctl->InputBuffer)->pShm)); TRC_DBG((TB, "Got TSHARE_DD_CONNECT IOCtl")); status = WDWDDConnect(pTSWd, pSdIoctl, FALSE); WDW_CHECK_SHM( (((PTSHARE_DD_CONNECT_IN)pSdIoctl->InputBuffer)->pShm)); } else { status = STATUS_INVALID_PARAMETER; } } break; case IOCTL_WDTS_DD_DISCONNECT: { if (pSdIoctl->InputBuffer && (((PTSHARE_DD_DISCONNECT_IN)pSdIoctl->InputBuffer)->pShm)) { WDW_CHECK_SHM( (((PTSHARE_DD_DISCONNECT_IN)pSdIoctl->InputBuffer)->pShm)); TRC_ALT((TB, "Got TSHARE_DD_DISCONNECT IOCtl: Stack (%ld)", pTSWd->StackClass)); if ((pTSWd->StackClass == Stack_Primary) || (pTSWd->StackClass == Stack_Console)) { status = WDWDDDisconnect(pTSWd, pSdIoctl, FALSE); } WDW_CHECK_SHM( (((PTSHARE_DD_DISCONNECT_IN)pSdIoctl->InputBuffer)->pShm)); } else { status = STATUS_INVALID_PARAMETER; } } break; case IOCTL_WDTS_DD_RECONNECT: { if (pSdIoctl->InputBuffer && (((PTSHARE_DD_CONNECT_IN)pSdIoctl->InputBuffer)->pShm)) { WDW_CHECK_SHM( (((PTSHARE_DD_CONNECT_IN)pSdIoctl->InputBuffer)->pShm)); TRC_DBG((TB, "Got TSHARE_DD_RECONNECT IOCtl")); if (pTSWd->shadowState == SHADOW_CLIENT) { TRC_ALT((TB, "Shadow termination on reconnect, in share(%ld)", pTSWd->bInShadowShare)); pTSWd->shadowState = SHADOW_NONE; // If we were formerly in an active shadow, then we need to // deactivate the client before reconnecting in a new share if (pTSWd->bInShadowShare) { ShareClass *pSC = (ShareClass *)pTSWd->dcShare; pSC->SC_EndShare(TRUE); pTSWd->bInShadowShare = FALSE; } // Make sure that Domain.StatusDead is consistent with TSWd.dead pTSWd->dead = TRUE; ((PDomain)(pTSWd->hDomainKernel))->StatusDead = TRUE; SM_Dead(pTSWd->pSmInfo, TRUE); if (pTSWd->bCompress == TRUE) { // the compression history will be flushed pTSWd->bFlushed = PACKET_FLUSHED; // the compression will restart over initsendcontext(pTSWd->pMPPCContext, pTSWd->pMPPCContext->ClientComprType); } } status = WDWDDConnect(pTSWd, pSdIoctl, TRUE); WDW_CHECK_SHM( (((PTSHARE_DD_CONNECT_IN)pSdIoctl->InputBuffer)->pShm)); } else { status = STATUS_INVALID_PARAMETER; } } break; case IOCTL_WDTS_DD_TIMER_INFO: { if (pSdIoctl->InputBufferLength < sizeof(TSHARE_DD_TIMER_INFO)) { TRC_ERR((TB, "Timer info IOCtl too small at %lu", pSdIoctl->InputBufferLength)); status = STATUS_BUFFER_TOO_SMALL; } else { /************************************************************/ /* Store the timer handle */ /************************************************************/ pTSWd->ritTimer = ((PTSHARE_DD_TIMER_INFO)(pSdIoctl->InputBuffer))-> pKickTimer; TRC_DBG((TB, "Got TSHARE_DD_TIMER_INFO IOCtl, handle %p", pTSWd->ritTimer)); /************************************************************/ /* Start a timer to get things moving */ /************************************************************/ WDW_StartRITTimer(pTSWd, pTSWd->outBufDelay); } } break; case IOCTL_WDTS_DD_REDRAW_SCREEN : { ShareClass *dcShare; dcShare = (ShareClass *)(pTSWd->dcShare); TRC_NRM((TB, "RDPDD requests to redraw screen\n")); if (dcShare != NULL) { // We have a valid share class, do screen redraw dcShare->SC_RedrawScreen(); } } break; case IOCTL_ICA_STACK_CONNECTION_SEND : { // Wait for the connected indication from SM. TRC_DBG((TB, "About to wait for connected indication")); status = WDW_WaitForConnectionEvent(pTSWd, pTSWd->pConnEvent, 60000); TRC_DBG((TB, "Back from wait for connected indication")); if (status != STATUS_SUCCESS) { TRC_ERR((TB, "Connected indication timed out (%x)", status)); status = STATUS_IO_TIMEOUT; DC_QUIT; } // Pass the IOCtl on to the next driver. status = IcaCallNextDriver(pTSWd->pContext, SD$IOCTL, pSdIoctl); } break; case IOCTL_ICA_STACK_QUERY_CLIENT : { status = WDWGetClientData( pTSWd, pSdIoctl ); TRC_NRM((TB, "Return client data")); } break; // This IOCTL was introduced to support long UserName, Password and Domain names case IOCTL_ICA_STACK_QUERY_CLIENT_EXTENDED : { status = WDWGetExtendedClientData(pTSWd->pInfoPkt, pSdIoctl); TRC_NRM((TB, "Return Extended client data")); } break; case IOCTL_ICA_STACK_QUERY_AUTORECONNECT: { TRC_NRM((TB, "Query autoreconnect information")); status = WDWGetAutoReconnectInfo(pTSWd, pTSWd->pInfoPkt, pSdIoctl); } break; /****************************************************************************/ /****************************************************************************/ /* Here are some IOCtls that we have to deal with in our guise of miniport */ /* driver. */ /****************************************************************************/ /****************************************************************************/ case IOCTL_VIDEO_QUERY_CURRENT_MODE: { TRC_NRM((TB, "QueryCurrentModes")); if (pSdIoctl->OutputBufferLength < sizeof(VIDEO_MODE_INFORMATION)) { TRC_ERR((TB, "QueryCurrentMode buffer too small: got/expected %d/%d", pSdIoctl->OutputBufferLength, sizeof(VIDEO_MODE_INFORMATION) )); status = STATUS_BUFFER_TOO_SMALL; } else { TRC_NRM((TB, "Return current mode")); /************************************************************/ /* Copy the default mode information, and then update it */ /* with our current screen dimensions. */ /************************************************************/ pVidInfo = (PVIDEO_MODE_INFORMATION)pSdIoctl->OutputBuffer; memcpy(pVidInfo, wdSimModes, sizeof(wdSimModes)); pVidInfo->Length = sizeof(VIDEO_MODE_INFORMATION); pVidInfo->VisScreenWidth = pTSWd->desktopWidth; pVidInfo->VisScreenHeight = pTSWd->desktopHeight; pVidInfo->BitsPerPlane = pTSWd->desktopBpp; pVidInfo->VideoMemoryBitmapWidth = pTSWd->desktopWidth; pVidInfo->VideoMemoryBitmapHeight = pTSWd->desktopHeight; #ifdef DC_HICOLOR switch (pTSWd->desktopBpp) { case 24: { pVidInfo->RedMask = TS_RED_MASK_24BPP; pVidInfo->GreenMask = TS_GREEN_MASK_24BPP; pVidInfo->BlueMask = TS_BLUE_MASK_24BPP; } break; case 16: { pVidInfo->RedMask = TS_RED_MASK_16BPP; pVidInfo->GreenMask = TS_GREEN_MASK_16BPP; pVidInfo->BlueMask = TS_BLUE_MASK_16BPP; } break; case 15: { pVidInfo->RedMask = TS_RED_MASK_15BPP; pVidInfo->GreenMask = TS_GREEN_MASK_15BPP; pVidInfo->BlueMask = TS_BLUE_MASK_15BPP; } break; default: { pVidInfo->RedMask = 0; pVidInfo->GreenMask = 0; pVidInfo->BlueMask = 0; } break; } #endif pSdIoctl->BytesReturned = sizeof(wdSimModes); } } break; case IOCTL_VIDEO_QUERY_AVAIL_MODES: { TRC_NRM((TB, "QueryAvailableModes")); if (pSdIoctl->OutputBufferLength < sizeof(VIDEO_MODE_INFORMATION)) { TRC_ERR((TB, "QueryCurrentMode buffer too small: got/expected %d/%d", pSdIoctl->OutputBufferLength, sizeof(VIDEO_MODE_INFORMATION) )); status = STATUS_BUFFER_TOO_SMALL; } else { TRC_NRM((TB, "Return just one mode")); // Copy the default mode information, and then update it // with our current screen dimensions. pVidInfo = (PVIDEO_MODE_INFORMATION)pSdIoctl->OutputBuffer; memcpy(pVidInfo, wdSimModes, sizeof(wdSimModes)); pVidInfo->Length = sizeof(VIDEO_MODE_INFORMATION); pVidInfo->VisScreenWidth = pTSWd->desktopWidth; pVidInfo->VisScreenHeight = pTSWd->desktopHeight; pVidInfo->BitsPerPlane = pTSWd->desktopBpp; pVidInfo->VideoMemoryBitmapWidth = pTSWd->desktopWidth; pVidInfo->VideoMemoryBitmapHeight = pTSWd->desktopHeight; pVidInfo->Frequency = 42; // required by the display cpl #ifdef DC_HICOLOR switch (pTSWd->desktopBpp) { case 24: { pVidInfo->RedMask = TS_RED_MASK_24BPP; pVidInfo->GreenMask = TS_GREEN_MASK_24BPP; pVidInfo->BlueMask = TS_BLUE_MASK_24BPP; } break; case 16: { pVidInfo->RedMask = TS_RED_MASK_16BPP; pVidInfo->GreenMask = TS_GREEN_MASK_16BPP; pVidInfo->BlueMask = TS_BLUE_MASK_16BPP; } break; case 15: { pVidInfo->RedMask = TS_RED_MASK_15BPP; pVidInfo->GreenMask = TS_GREEN_MASK_15BPP; pVidInfo->BlueMask = TS_BLUE_MASK_15BPP; } break; default: { pVidInfo->RedMask = 0; pVidInfo->GreenMask = 0; pVidInfo->BlueMask = 0; } break; } #endif pSdIoctl->BytesReturned = sizeof(wdSimModes); } } break; case IOCTL_VIDEO_QUERY_NUM_AVAIL_MODES: { TRC_NRM((TB, "QueryNumAvailableModes")); if (pSdIoctl->OutputBufferLength < sizeof(VIDEO_NUM_MODES)) { TRC_ERR((TB, "QueryNumAvailableModes buffer too small: got/expected %d/%d", pSdIoctl->OutputBufferLength, sizeof(VIDEO_NUM_MODES))); status = STATUS_BUFFER_TOO_SMALL; } else { PVIDEO_NUM_MODES pNumModes = (PVIDEO_NUM_MODES)(pSdIoctl->OutputBuffer); TRC_NRM((TB, "Return 1 mode available")); pNumModes->NumModes = 1; pNumModes->ModeInformationLength = sizeof(VIDEO_MODE_INFORMATION); pSdIoctl->BytesReturned = sizeof(VIDEO_NUM_MODES); } } break; case IOCTL_VIDEO_SET_CURRENT_MODE: { /****************************************************************/ /* Not clear why we might get this, hence we trace at high */ /* level for now. In any case, the IOCtl is sent to set a */ /* particular VGA mode: we have told Win32 what we support: */ /* either it is setting that, or we have a problem waiting to */ /* happen. */ /****************************************************************/ TRC_ALT((TB, "SetCurrentMode")); if (pSdIoctl->InputBufferLength < sizeof(VIDEO_MODE)) { TRC_ERR((TB, "SetCurrentMode buffer too small: got/expected %d/%d", pSdIoctl->InputBufferLength, sizeof(VIDEO_MODE) )); status = STATUS_BUFFER_TOO_SMALL; } else { TRC_ALT((TB, "Set current mode to %d", ((PVIDEO_MODE)(pSdIoctl->InputBuffer))->RequestedMode)); } } break; case IOCTL_VIDEO_SET_COLOR_REGISTERS: { TRC_NRM((TB, "SetColorRegisters")); } break; case IOCTL_VIDEO_RESET_DEVICE: { TRC_NRM((TB, "ResetDevice")); } break; /****************************************************************************/ /****************************************************************************/ /* IOCtls that require translation for MCS */ /****************************************************************************/ /****************************************************************************/ /********************************************************************/ /* Process Query Bindings for local and MCS virtual channels */ /********************************************************************/ case IOCTL_ICA_VIRTUAL_QUERY_BINDINGS : { PSD_VCBIND pVBind; /****************************************************************/ /* This IOCtl is issued twice */ /****************************************************************/ if (!pTSWd->bVirtualChannelBound) { /************************************************************/ /* First time, return internal channels */ /************************************************************/ pVBind = (PSD_VCBIND) pSdIoctl->OutputBuffer; /************************************************************/ /* Let MCS define channel(s) */ /************************************************************/ MCSIcaVirtualQueryBindings(pTSWd->hDomainKernel, &pVBind, (unsigned int *)&pSdIoctl-> BytesReturned); // Add RDPDD->RDPWD channel. RtlCopyMemory(pVBind->VirtualName, VIRTUAL_THINWIRE, sizeof(VIRTUAL_THINWIRE)); pVBind->VirtualClass = WD_THINWIRE_CHANNEL; pSdIoctl->BytesReturned += sizeof(SD_VCBIND); pTSWd->bVirtualChannelBound = TRUE; TRC_NRM((TB, "%d Virtual Channels (first time)", pSdIoctl->BytesReturned/sizeof(SD_VCBIND))); } else { /************************************************************/ /* Second time, return virtual channels */ /************************************************************/ pVBind = (PSD_VCBIND)pSdIoctl->OutputBuffer; status = NM_VirtualQueryBindings(pTSWd->pNMInfo, pVBind, pSdIoctl->OutputBufferLength, &(pSdIoctl->BytesReturned)); TRC_NRM((TB, "%d Virtual Channels (second time)", pSdIoctl->BytesReturned/sizeof(SD_VCBIND))); } } break; /********************************************************************/ /* T.120 request from user mode - pass it on */ /********************************************************************/ case IOCTL_T120_REQUEST: { status = MCSIcaT120Request(pTSWd->hDomainKernel, pSdIoctl); } break; #ifdef USE_LICENSE /****************************************************************************/ /****************************************************************************/ /* Licensing IOCtls */ /****************************************************************************/ /****************************************************************************/ /********************************************************************/ /* Query the client licensing capabilities /********************************************************************/ case IOCTL_ICA_STACK_QUERY_LICENSE_CAPABILITIES: { PLICENSE_CAPABILITIES pLicenseCap; if( pSdIoctl->OutputBufferLength < sizeof( LICENSE_CAPABILITIES ) ) { TRC_ERR( ( TB, "QueryLicenseCapabilities buffer too small: got/expected %d/%d", pSdIoctl->OutputBufferLength, sizeof( LICENSE_CAPABILITIES ) ) ); status = STATUS_BUFFER_TOO_SMALL; } else { // // set the client licensing capability. Here we temporarily hard-code // the client to use the RSA key exchange algorithm and the licensing // protocol version. // pLicenseCap = ( PLICENSE_CAPABILITIES )( pSdIoctl->OutputBuffer ); pLicenseCap->KeyExchangeAlg = KEY_EXCHANGE_ALG_RSA; if( RNS_TERMSRV_40_UD_VERSION >= pTSWd->version ) { // // this is a hydra 4.0 client, use the corresponding licensing // protocol. // pLicenseCap->ProtocolVer = LICENSE_HYDRA_40_PROTOCOL_VERSION; pLicenseCap->fAuthenticateServer = TRUE; } else { // // Use the latest licensing protocol for later clients // pLicenseCap->ProtocolVer = LICENSE_HIGHEST_PROTOCOL_VERSION; // // if encryption is enabled, then the server has already been // authenticated in the earlier key exchange protocol and the // licensing protocol does not have to authenticate the server // again. // pLicenseCap->fAuthenticateServer = ( SM_IsSecurityExchangeCompleted( pTSWd->pSmInfo, &pLicenseCap->CertType ) ? FALSE : TRUE ); } TRC_NRM( ( TB, "Key Exchange Alg = %d", pLicenseCap->KeyExchangeAlg ) ); TRC_NRM( ( TB, "License Protocol Version = %x", pLicenseCap->ProtocolVer ) ); // // copy the client name // if( pLicenseCap->pbClientName ) { memcpy( pLicenseCap->pbClientName, pTSWd->clientName, ( ( pLicenseCap->cbClientName < sizeof( pTSWd->clientName ) ) ? pLicenseCap->cbClientName : sizeof( pTSWd->clientName ) ) ); } pSdIoctl->BytesReturned = sizeof( LICENSE_CAPABILITIES ); } } break; /********************************************************************/ /* Send and receive licensing protocol data to and from client. /********************************************************************/ case IOCTL_ICA_STACK_REQUEST_CLIENT_LICENSE: { PLicense_Handle pLicenseHandle; BOOL rc = FALSE; BOOL encrypingLicToCli; NTSTATUS waitStatus; PBYTE pOutBuffer; PSM_HANDLE_DATA pRealSMHandle = (PSM_HANDLE_DATA)(pTSWd->pSmInfo); PRNS_SECURITY_HEADER pLicenseHeader; pLicenseHandle = ( PLicense_Handle )pTSWd->pSLicenseHandle; // // validate input parameters // ASSERT( NULL != pLicenseHandle ); ASSERT( NULL != pSdIoctl->InputBuffer ); ASSERT( 0 < pSdIoctl->InputBufferLength ); if( ( NULL == pLicenseHandle ) || ( NULL == pSdIoctl->InputBuffer ) || ( 0 >= pSdIoctl->InputBufferLength ) ) { TRC_ERR( ( TB, "invalid Licensing IOCTL parameters" ) ); status = STATUS_INVALID_PARAMETER; break; } if( ( pSdIoctl->OutputBuffer ) && ( pSdIoctl->OutputBufferLength > 0 ) ) { // // set the output buffer pointer so that we can receive data // when the client response // pLicenseHandle->pDataBuf = ( PBYTE )pSdIoctl->OutputBuffer; pLicenseHandle->cbDataBuf = pSdIoctl->OutputBufferLength; } else { pLicenseHandle->pDataBuf = NULL; pLicenseHandle->cbDataBuf = 0; } // // We will encrypt the S->C licensing packet if encryption is // on AND if the client told us they can decrypt this particular // packet. // If encryptDisplayData is not set (low encryption), we don't // encrypt the S->C licensing packet // encrypingLicToCli = (pRealSMHandle->encrypting && pRealSMHandle->encryptingLicToClient && pRealSMHandle->encryptDisplayData); if (!encrypingLicToCli) { // // Allocate an NM buffer for sending the data. we are allocating an extra // DWORD to hack around the encryption problem. // fWait is TRUE means that we will always wait for a buffer to be avail status = NM_AllocBuffer( pTSWd->pNMInfo, ( PPVOID )&pOutBuffer, pSdIoctl->InputBufferLength + sizeof( RNS_SECURITY_HEADER ), TRUE ); if( STATUS_SUCCESS != status || pTSWd->hDomainKernel == NULL) { TRC_ERR( ( TB, "Failed to allocate NM buffer" ) ); if (STATUS_SUCCESS == status) { NM_FreeBuffer(pTSWd->pNMInfo, pOutBuffer); status = STATUS_NET_WRITE_FAULT; } else { // Follow old code path. status = STATUS_NO_MEMORY; } break; } // // initialize the license data header // pLicenseHeader = ( PRNS_SECURITY_HEADER )pOutBuffer; // // Indicate this is a licensing packet and then cheat and sneak // in the flag that indicates the client should encrypt all // licensing data sent to the server (early capabilities) // pLicenseHeader->flags = RNS_SEC_LICENSE_PKT | RDP_SEC_LICENSE_ENCRYPT_CS; pLicenseHeader->flagsHi = ( WORD )pSdIoctl->InputBufferLength; // // copy the data over // ASSERT( NULL != pOutBuffer ); memcpy( pOutBuffer + sizeof( RNS_SECURITY_HEADER ), pSdIoctl->InputBuffer, pSdIoctl->InputBufferLength ); } else { if (STATUS_SUCCESS == SM_AllocBuffer(pTSWd->pSmInfo, (PPVOID) &pOutBuffer, pSdIoctl->InputBufferLength, TRUE, FALSE)) { memcpy(pOutBuffer, (PBYTE)pSdIoctl->InputBuffer, pSdIoctl->InputBufferLength); } else { TRC_ERR((TB, "FAILED to alloc license data buffer")); status = STATUS_NO_MEMORY; break; } } // // clear the incoming data event // KeClearEvent( pLicenseHandle->pDataEvent ); // // send the data in the input buffer // if (encrypingLicToCli) { rc = SM_SendData(pTSWd->pSmInfo, pOutBuffer, pSdIoctl->InputBufferLength, TS_HIGHPRIORITY, 0, FALSE, RNS_SEC_LICENSE_PKT | RDP_SEC_LICENSE_ENCRYPT_CS | RNS_SEC_ENCRYPT, FALSE); } else { rc = NM_SendData(pTSWd->pNMInfo, (BYTE *)pOutBuffer, pSdIoctl->InputBufferLength + sizeof(RNS_SECURITY_HEADER), TS_HIGHPRIORITY, 0, FALSE); } if (!rc) { TRC_ERR((TB, "Failed to send licensing data")); status = STATUS_NET_WRITE_FAULT; break; } if (pLicenseHandle->pDataBuf) { // // caller supplied a return buffer, wait for the client response // waitStatus = WDW_WaitForConnectionEvent(pTSWd, pLicenseHandle->pDataEvent, 60000L); if (STATUS_TIMEOUT == waitStatus) { TRC_ERR( ( TB, "Timeout waiting for client licensing response" ) ); pSdIoctl->BytesReturned = 0; status = STATUS_IO_TIMEOUT; } else { // // got the client response, check that the data is received // correctly // if( !NT_SUCCESS( pLicenseHandle->Status ) ) { status = pLicenseHandle->Status; // // The data was not copied correctly. If the buffer provided is // too small, let the caller know the right size of the // buffer to provide. // if( STATUS_BUFFER_TOO_SMALL == status ) { TRC_ERR( ( TB, "IOCTL_ICA_STACK_REQUEST_CLIENT_LICENSE buffer too small: got/expected %d/%d", pSdIoctl->InputBufferLength, pLicenseHandle->cbCacheBuf ) ); pSdIoctl->BytesReturned = pLicenseHandle->cbCacheBuf; } else { pSdIoctl->BytesReturned = 0; } } else { pSdIoctl->BytesReturned = pLicenseHandle->cbDataBuf; } } if (status != STATUS_SUCCESS) { // Make sure we don't try to write to pointer when // client data comes in pLicenseHandle->pDataBuf = NULL; pLicenseHandle->cbDataBuf = 0; } } else { // // caller did not supply a return buffer, simply return // pSdIoctl->BytesReturned = 0; } } break; /********************************************************************/ /* Send licensing protocol data to client without waiting for reply. /********************************************************************/ case IOCTL_ICA_STACK_SEND_CLIENT_LICENSE: { PLicense_Handle pLicenseHandle; BOOL rc = FALSE; BOOL encrypingLicToCli; PBYTE pOutBuffer; PSM_HANDLE_DATA pRealSMHandle = (PSM_HANDLE_DATA)(pTSWd->pSmInfo); PRNS_SECURITY_HEADER pLicenseHeader; pLicenseHandle = ( PLicense_Handle )pTSWd->pSLicenseHandle; // // validate input parameters // ASSERT( NULL != pLicenseHandle ); ASSERT( NULL != pSdIoctl->InputBuffer ); ASSERT( 0 < pSdIoctl->InputBufferLength ); if( ( NULL == pLicenseHandle ) || ( NULL == pSdIoctl->InputBuffer ) || ( 0 >= pSdIoctl->InputBufferLength ) ) { TRC_ERR( ( TB, "invalid Licensing IOCTL parameters" ) ); status = STATUS_INVALID_PARAMETER; break; } // // We will encrypt the S->C licensing packet if encryption is // on AND if the client told us they can decrypt this particular // packet. // If encryptDisplayData is not set (low encryption), we don't // encrypt the S->C licensing packet // encrypingLicToCli = (pRealSMHandle->encrypting && pRealSMHandle->encryptingLicToClient && pRealSMHandle->encryptDisplayData); if (!encrypingLicToCli) { // // allocate NM buffer for sending // fWait is TRUE means that we will always wait for a buffer to be avail status = NM_AllocBuffer( pTSWd->pNMInfo, ( PPVOID )&pOutBuffer, pSdIoctl->InputBufferLength + sizeof( RNS_SECURITY_HEADER ), TRUE ); if( STATUS_SUCCESS != status || pTSWd->hDomainKernel == NULL) { TRC_ERR( ( TB, "Failed to allocate SM buffer" ) ); if (STATUS_SUCCESS == status) { NM_FreeBuffer(pTSWd->pNMInfo, pOutBuffer); status = STATUS_NET_WRITE_FAULT; } else { // Follow old code path. status = STATUS_NO_MEMORY; } break; } // // initialize the license data header // pLicenseHeader = ( PRNS_SECURITY_HEADER )pOutBuffer; // // Indicate this is a licensing packet and then cheat and sneak // in the flag that indicates the client should encrypt all // licensing data sent to the server (early capabilities) // pLicenseHeader->flags = RNS_SEC_LICENSE_PKT | RDP_SEC_LICENSE_ENCRYPT_CS; pLicenseHeader->flagsHi = ( WORD )pSdIoctl->InputBufferLength; // // copy the data over // ASSERT( NULL != pOutBuffer ); memcpy( pOutBuffer + sizeof( RNS_SECURITY_HEADER ), pSdIoctl->InputBuffer, pSdIoctl->InputBufferLength ); } else { if (STATUS_SUCCESS == SM_AllocBuffer(pTSWd->pSmInfo, (PPVOID) &pOutBuffer, pSdIoctl->InputBufferLength, TRUE, FALSE)) { memcpy(pOutBuffer, (PBYTE)pSdIoctl->InputBuffer, pSdIoctl->InputBufferLength); } else { TRC_ERR((TB, "FAILED to alloc license data buffer")); status = STATUS_NO_MEMORY; break; } } // // clear the incoming data event // KeClearEvent(pLicenseHandle->pDataEvent); // // send the data in the input buffer // if (encrypingLicToCli) { rc = SM_SendData(pTSWd->pSmInfo, pOutBuffer, pSdIoctl->InputBufferLength, TS_HIGHPRIORITY, 0, FALSE, RNS_SEC_LICENSE_PKT | RDP_SEC_LICENSE_ENCRYPT_CS | RNS_SEC_ENCRYPT, FALSE); } else { rc = NM_SendData(pTSWd->pNMInfo, (BYTE *)pOutBuffer, pSdIoctl->InputBufferLength + sizeof(RNS_SECURITY_HEADER), TS_HIGHPRIORITY, 0, FALSE); } if (!rc) { TRC_ERR( ( TB, "Failed to send licensing data" ) ); status = STATUS_NET_WRITE_FAULT; } } break; /********************************************************************/ /* Indicate that the licensing protocol has completed. /********************************************************************/ case IOCTL_ICA_STACK_LICENSE_PROTOCOL_COMPLETE: { PULONG pResult; // // validate input parameters // ASSERT( NULL != pSdIoctl->InputBuffer ); ASSERT( 0 < pSdIoctl->InputBufferLength ); if( ( NULL == pSdIoctl->InputBuffer ) || ( 0 >= pSdIoctl->InputBufferLength ) ) { TRC_ERR( ( TB, "invalid Licensing IOCTL parameters" ) ); status = STATUS_INVALID_PARAMETER; break; } // // Tell SM if the client license has been validated successfully // pResult = ( PULONG )( pSdIoctl->InputBuffer ); if( LICENSE_PROTOCOL_SUCCESS == ( *pResult ) ) { SM_LicenseOK(pTSWd->pSmInfo); } } break; /********************************************************************/ /* Indicate to retrieve the licensing data that was previously /* cached. /********************************************************************/ case IOCTL_ICA_STACK_GET_LICENSE_DATA: { PLicense_Handle pLicenseHandle = ( PLicense_Handle )pTSWd->pSLicenseHandle; // // validate input parameters // if ((NULL == pSdIoctl->OutputBuffer) || (NULL == pLicenseHandle)) { pSdIoctl->BytesReturned = 0; status = STATUS_INVALID_PARAMETER; break; } // // check that there's actually cached data // if( NULL == pLicenseHandle->pCacheBuf ) { pSdIoctl->BytesReturned = 0; status = STATUS_NO_DATA_DETECTED; break; } if( pSdIoctl->OutputBufferLength < pLicenseHandle->cbCacheBuf ) { pSdIoctl->BytesReturned = 0; status = STATUS_BUFFER_TOO_SMALL; break; } // // copy the cached data and free the cached data buffer // memcpy(pSdIoctl->OutputBuffer, pLicenseHandle->pCacheBuf, pLicenseHandle->cbCacheBuf ); pSdIoctl->BytesReturned = pLicenseHandle->cbCacheBuf; ExFreePool( pLicenseHandle->pCacheBuf ); pLicenseHandle->pCacheBuf = NULL; } break; #endif // #ifdef USE_LICENSE /********************************************************************/ /* shadow only IOCTLS */ /********************************************************************/ // Pass all relevent stack data from the client's primary stack to the // target's shadow stack. case IOCTL_ICA_STACK_QUERY_MODULE_DATA: TRC_ALT((TB, "IOCTL_ICA_STACK_QUERY_MODULE_DATA(%p) - stack class %d", pTSWd, pTSWd->StackClass)); if ((pTSWd->StackClass == Stack_Primary) || (pTSWd->StackClass == Stack_Console)) { status = WDWGetModuleData(pTSWd, pSdIoctl); } break; // Pass all relevant capabilities data from the client to the shadow // target display driver. case IOCTL_ICA_VIRTUAL_QUERY_MODULE_DATA: PTSHARE_VIRTUAL_MODULE_DATA pVirtModuleData; PTS_COMBINED_CAPABILITIES pCaps; PTS_GENERAL_CAPABILITYSET pGenCapSet; unsigned capsLength; ShareClass * dcShare; dcShare = (ShareClass *)(pTSWd->dcShare); dcShare->SC_GetCombinedCapabilities(SC_REMOTE_PERSON_ID, &capsLength, &pCaps); if (pCaps != NULL) { pGenCapSet = (PTS_GENERAL_CAPABILITYSET) WDW_GetCapSet( pTSWd, TS_CAPSETTYPE_GENERAL, pCaps, capsLength); if (pGenCapSet != NULL) { // update the compression capability if (pTSWd->bCompress) { pGenCapSet->extraFlags |= TS_SHADOW_COMPRESSION_LEVEL; pGenCapSet->generalCompressionLevel = (TSUINT16)pTSWd->pMPPCContext->ClientComprType; } } if (pSdIoctl->OutputBufferLength >= (capsLength + sizeof(TSHARE_VIRTUAL_MODULE_DATA) - 1)) { pVirtModuleData = (PTSHARE_VIRTUAL_MODULE_DATA) pSdIoctl->OutputBuffer; pVirtModuleData->capsLength = capsLength; memcpy(&pVirtModuleData->combinedCapabilities, pCaps, capsLength); } else { status = STATUS_BUFFER_OVERFLOW; } } else { status = STATUS_NO_MEMORY; } pSdIoctl->BytesReturned = capsLength + sizeof(TSHARE_VIRTUAL_MODULE_DATA) - 1; TRC_ALT((TB, "IOCTL_ICA_VIRTUAL_QUERY_MODULE_DATA: rc=%lx, in=%ld, out=%ld", status, pSdIoctl->OutputBufferLength, pSdIoctl->BytesReturned)); break; case IOCTL_WDTS_DD_SHADOW_CONNECT: { if (pSdIoctl->InputBuffer && (((PTSHARE_DD_CONNECT_IN)pSdIoctl->InputBuffer)->pShm)) { WDW_CHECK_SHM( (((PTSHARE_DD_CONNECT_IN)pSdIoctl->InputBuffer)->pShm)); TRC_ALT((TB, "++TSHARE_DD_SHADOW_CONNECT(%p) - stack class %d", pTSWd, pTSWd->StackClass)); status = WDWDDShadowConnect(pTSWd, pSdIoctl); TRC_ALT((TB, "--TSHARE_DD_SHADOW_CONNECT(%p) - stack class %d", pTSWd, pTSWd->StackClass)); WDW_CHECK_SHM( (((PTSHARE_DD_CONNECT_IN)pSdIoctl->InputBuffer)->pShm)); } else { status = STATUS_INVALID_PARAMETER; } } break; #ifdef DC_HICOLOR // Maybe get the caps of the shadower case IOCTL_WDTS_DD_QUERY_SHADOW_CAPS: { // only respond to this if we're a shadow stack if (pTSWd->StackClass == Stack_Shadow) { PTS_COMBINED_CAPABILITIES pCaps; PTSHARE_VIRTUAL_MODULE_DATA pVMData = NULL; unsigned capsLength; ShareClass * dcShare; dcShare = (ShareClass *)(pTSWd->dcShare); if (pSdIoctl->OutputBufferLength >= sizeof(unsigned)) { pVMData = (PTSHARE_VIRTUAL_MODULE_DATA)pSdIoctl->OutputBuffer; } dcShare->SC_GetCombinedCapabilities(SC_REMOTE_PERSON_ID, &capsLength, &pCaps); if (pCaps != NULL) { if (pSdIoctl->OutputBufferLength >= (capsLength + sizeof(unsigned))) { memcpy(&pVMData->combinedCapabilities, pCaps, capsLength); } else { status = STATUS_BUFFER_OVERFLOW; } } else { status = STATUS_NO_MEMORY; } pSdIoctl->BytesReturned = capsLength + sizeof(unsigned); if (pVMData) { pVMData->capsLength = capsLength; } TRC_ALT((TB, "IOCTL_WDTS_DD_QUERY_SHADOW_CAPS:" \ " rc=%lx, in=%ld, out=%ld", status, pSdIoctl->OutputBufferLength, pSdIoctl->BytesReturned)); } else { TRC_ALT((TB, "IOCTL_WDTS_DD_QUERY_SHADOW_CAPS: " \ "not shadow stack so ignoring")); TRC_ALT((TB, " rc=%lx, in=%ld, out=%ld", status, pSdIoctl->OutputBufferLength, pSdIoctl->BytesReturned)); } } break; case IOCTL_WDTS_DD_SHADOW_SYNCHRONIZE: { ShareClass * dcShare; PTS_COMBINED_CAPABILITIES pCaps; unsigned capsLen; if (pSdIoctl->InputBuffer && (((PTSHARE_DD_SHADOWSYNC_IN)pSdIoctl->InputBuffer)->pShm)) { WDW_CHECK_SHM( (((PTSHARE_DD_SHADOWSYNC_IN)pSdIoctl->InputBuffer)->pShm)); // synchronize this stack, this is required so that OE2 will match up // for both shadow client and target. dcShare = (ShareClass *)(pTSWd->dcShare); pCaps = ((PTSHARE_DD_SHADOWSYNC_IN)pSdIoctl->InputBuffer)->pShadowCaps; capsLen = ((PTSHARE_DD_SHADOWSYNC_IN)pSdIoctl->InputBuffer)->capsLen; dcShare->SC_ShadowSyncShares(pCaps, capsLen); TRC_ALT((TB, "Synchronized share for stack [%ld]", pTSWd->StackClass)); WDW_CHECK_SHM( (((PTSHARE_DD_SHADOWSYNC_IN)pSdIoctl->InputBuffer)->pShm)); } else { status = STATUS_INVALID_PARAMETER; } } break; #else case IOCTL_WDTS_DD_SHADOW_SYNCHRONIZE: { ShareClass * dcShare; if (pSdIoctl->InputBuffer && (((PTSHARE_DD_SHADOWSYNC_IN)pSdIoctl->InputBuffer)->pShm)) { WDW_CHECK_SHM( (((PTSHARE_DD_SHADOWSYNC_IN)pSdIoctl->InputBuffer)->pShm)); // synchronize this stack, this is required so that OE2 will match up // for both shadow client and target. dcShare = (ShareClass *)(pTSWd->dcShare); dcShare->SC_ShadowSyncShares(); TRC_ALT((TB, "Synchronized share for stack [%ld]", pTSWd->StackClass)); WDW_CHECK_SHM( (((PTSHARE_DD_SHADOWSYNC_IN)pSdIoctl->InputBuffer)->pShm)); } else { status = STATUS_INVALID_PARAMETER; } } break; #endif case IOCTL_WDTS_DD_SHADOW_DISCONNECT: { if (pSdIoctl->InputBuffer && (((PTSHARE_DD_DISCONNECT_IN)pSdIoctl->InputBuffer)->pShm)) { WDW_CHECK_SHM( (((PTSHARE_DD_DISCONNECT_IN)pSdIoctl->InputBuffer)->pShm)); status = WDWDDShadowDisconnect(pTSWd, pSdIoctl); WDW_CHECK_SHM( (((PTSHARE_DD_DISCONNECT_IN)pSdIoctl->InputBuffer)->pShm)); } else { status = STATUS_INVALID_PARAMETER; } } break; case IOCTL_ICA_STACK_REGISTER_HOTKEY : { PICA_STACK_HOTKEY pHotkey = (PICA_STACK_HOTKEY) pSdIoctl->InputBuffer; if (pHotkey->HotkeyVk) { pTSWd->shadowState = SHADOW_CLIENT; pTSWd->HotkeyVk = pHotkey->HotkeyVk; pTSWd->HotkeyModifiers = pHotkey->HotkeyModifiers; TRC_ALT((TB, "IOCTL_ICA_STACK_REGISTER_HOTKEY - Enable Vk(%ld, %lx)", pHotkey->HotkeyVk, pHotkey->HotkeyModifiers)); // Allocate and initialize a physical key state array status = KeyboardSetKeyState(pTSWd, &pTSWd->pgafPhysKeyState); if (NT_SUCCESS(status)) { TRC_ALT((TB, "Allocated phys key state")); } else { TRC_ALT((TB, "Failed to alloc phys key states: %lx", status)); } // VCs don't work when shadowing. Tell VC subsystem to // suspend now. WDWVCMessage(pTSWd, CHANNEL_FLAG_SUSPEND); } else { pTSWd->shadowState = SHADOW_NONE; pTSWd->HotkeyVk = 0; pTSWd->HotkeyModifiers = 0; TRC_ALT((TB, "IOCTL_ICA_STACK_REGISTER_HOTKEY - Disable")); if (pTSWd->pShadowInfo != NULL) { TRC_ALT((TB, "Primary client stack freeing reassembly info [%p]", pTSWd->pShadowInfo)); COM_Free(pTSWd->pShadowInfo); pTSWd->pShadowInfo = NULL; } // VCs don't work when shadowing. Tell VC subsystem to // resume now. WDWVCMessage(pTSWd, CHANNEL_FLAG_RESUME); } } break; case IOCTL_WDTS_DD_GET_BITMAP_KEYDATABASE: { ShareClass *dcShare; PTSHARE_DD_BITMAP_KEYDATABASE_OUT pKDBOut = (PTSHARE_DD_BITMAP_KEYDATABASE_OUT) pSdIoctl->OutputBuffer; dcShare = (ShareClass *)(pTSWd->dcShare); TRC_NRM((TB, "DD tries to get keydatabase\n")); if (dcShare != NULL) { // We have a valid share class, get the key database dcShare->SBC_GetBitmapKeyDatabase(&pKDBOut->bitmapKeyDatabaseSize, &pKDBOut->bitmapKeyDatabase); } } break; #ifdef DC_DEBUG case IOCTL_WDTS_DD_ICABREAKONDEBUGGER: { IcaBreakOnDebugger(); } break; #endif /********************************************************************/ // Send KeepAlive PDU IOCTL /********************************************************************/ case IOCTL_ICA_STACK_SEND_KEEPALIVE_PDU: { ShareClass *dcShare; dcShare = (ShareClass *)(pTSWd->dcShare); TRC_NRM((TB, "TermDD requests to send a keepalive pkt to client\n")); if (dcShare != NULL) { // We have a valid share class, send a keepalive pdu to client dcShare->SC_KeepAlive(); } } break; /********************************************************************/ // Load balancing IOCTLs. /********************************************************************/ case IOCTL_TS_STACK_QUERY_LOAD_BALANCE_INFO: { TS_LOAD_BALANCE_INFO *pLBInfo = (TS_LOAD_BALANCE_INFO *)pSdIoctl->OutputBuffer; TRC_ASSERT((pSdIoctl->OutputBufferLength >= sizeof(TS_LOAD_BALANCE_INFO)), (TB,"Invalid output buf size %u for STACK_QUERY_LBINFO", pSdIoctl->OutputBufferLength)); // We need to fill in the IOCTL info from the gathered client // info packet and the initial capabilities. pLBInfo->bClientSupportsRedirection = pTSWd->bClientSupportsRedirection; pLBInfo->bRequestedSessionIDFieldValid = pTSWd->bRequestedSessionIDFieldValid; pLBInfo->RequestedSessionID = pTSWd->RequestedSessionID; pLBInfo->bUseSmartcardLogon = pTSWd->bUseSmartcardLogon; pLBInfo->ProtocolType = PROTOCOL_RDP; pLBInfo->bClientRequireServerAddr = pTSWd->ClientRedirectionVersion > TS_CLUSTER_REDIRECTION_VERSION1 ? 0 : 1; pLBInfo->ClientRedirectionVersion = pTSWd->ClientRedirectionVersion; wcsncpy(pLBInfo->UserName, (WCHAR *)pTSWd->pInfoPkt->UserName, sizeof(pLBInfo->UserName) / sizeof(WCHAR) - 1); pLBInfo->UserName[sizeof(pLBInfo->UserName) / sizeof(WCHAR) - 1] = L'\0'; wcsncpy(pLBInfo->Domain, (WCHAR *)pTSWd->pInfoPkt->Domain, sizeof(pLBInfo->Domain) / sizeof(WCHAR) - 1); pLBInfo->Domain[sizeof(pLBInfo->Domain) / sizeof(WCHAR) - 1] = L'\0'; wcsncpy(pLBInfo->InitialProgram, (WCHAR *)pTSWd->pInfoPkt->AlternateShell, sizeof(pLBInfo->InitialProgram) / sizeof(WCHAR) - 1); pLBInfo->InitialProgram[sizeof(pLBInfo->InitialProgram) / sizeof(WCHAR) - 1] = L'\0'; break; } case IOCTL_TS_STACK_SEND_CLIENT_REDIRECTION: { BOOL rc; TS_CLIENT_REDIRECTION_INFO *pRedirInfo = (TS_CLIENT_REDIRECTION_INFO *)pSdIoctl->InputBuffer; TRC_ASSERT((pSdIoctl->InputBufferLength >= sizeof(TS_CLIENT_REDIRECTION_INFO)), (TB,"Invalid input buf size %u for STACK_CLIENT_REDIR", pSdIoctl->InputBufferLength)); if (pTSWd->ClientRedirectionVersion == TS_CLUSTER_REDIRECTION_VERSION1) { RDP_SERVER_REDIRECTION_PACKET *pPkt; PBYTE ServerName; unsigned PktSize; unsigned ServerNameLen; // Get the server name length in bytes, including null. ServerNameLen = *((ULONG UNALIGNED*)(pRedirInfo + 1)); ServerName = (PBYTE)(pRedirInfo + 1) + sizeof(ULONG); // Calculate the PDU size. PktSize = sizeof(RDP_SERVER_REDIRECTION_PACKET) + ServerNameLen - sizeof(WCHAR); // The client username/domain info resulted in an off-machine // session to be redirected-to. We receive this IOCTL before // the licensing protocll occurs, hence we need to send a // non-data packet. If the client indicated support for // redirection, it must know how to parse this type of packet. status = NM_AllocBuffer(pTSWd->pNMInfo, (PPVOID)&pPkt, PktSize, TRUE); if ( STATUS_SUCCESS == status && pTSWd->hDomainKernel != NULL) { // Fill in the packet fields. pPkt->Flags = RDP_SEC_REDIRECTION_PKT; pPkt->Length = (UINT16)PktSize; pPkt->SessionID = pRedirInfo->SessionID; memcpy(pPkt->ServerAddress, ServerName, ServerNameLen); TRC_DBG((TB, "Client Redirection PDU V1, ServerName: %S, ServerNameLen: %d", ServerName, ServerNameLen)); rc = NM_SendData(pTSWd->pNMInfo, (BYTE *)pPkt, PktSize, TS_HIGHPRIORITY, 0, FALSE); if (rc) { TRC_ALT((TB, "Sent TS_SERVER_REDIRECT_PDU: %u", PktSize)); status = STATUS_SUCCESS; } else { TRC_ERR((TB,"Failed to send redir PDU")); status = STATUS_UNSUCCESSFUL; } } else { TRC_ERR((TB, "Failed to alloc %d bytes for redir PDU", PktSize)); if (STATUS_SUCCESS == status) { NM_FreeBuffer(pTSWd->pNMInfo, pPkt); } // prevent regression, keep original return code. status = STATUS_UNSUCCESSFUL; } } else if (pTSWd->ClientRedirectionVersion == TS_CLUSTER_REDIRECTION_VERSION2) { RDP_SERVER_REDIRECTION_PACKET_V2 *pPkt; unsigned PktSize, DataLen; // Calculate the PDU size DataLen = pSdIoctl->InputBufferLength - sizeof(TS_CLIENT_REDIRECTION_INFO); PktSize = sizeof(RDP_SERVER_REDIRECTION_PACKET_V2) + DataLen; // The client username/domain info resulted in an off-machine // session to be redirected-to. We receive this IOCTL before // the licensing protocll occurs, hence we need to send a // non-data packet. If the client indicated support for // redirection, it must know how to parse this type of packet. status = NM_AllocBuffer(pTSWd->pNMInfo, (PPVOID)&pPkt, PktSize, TRUE); if ( STATUS_SUCCESS == status && pTSWd->hDomainKernel != NULL) { // Fill in the packet fields. pPkt->Flags = RDP_SEC_REDIRECTION_PKT2; pPkt->Length = (UINT16)PktSize; pPkt->SessionID = pRedirInfo->SessionID; pPkt->RedirFlags = pRedirInfo->Flags; memcpy(pPkt + 1, pRedirInfo + 1, DataLen); TRC_DBG((TB, "Client Redirection PDU V2")); rc = NM_SendData(pTSWd->pNMInfo, (BYTE *)pPkt, PktSize, TS_HIGHPRIORITY, 0, FALSE); if (rc) { TRC_ALT((TB, "Sent TS_SERVER_REDIRECT_PDU: %u", PktSize)); status = STATUS_SUCCESS; } else { TRC_ERR((TB,"Failed to send redir PDU")); status = STATUS_UNSUCCESSFUL; } } else { TRC_ERR((TB, "Failed to alloc %d bytes for redir PDU", PktSize)); if (STATUS_SUCCESS == status) { NM_FreeBuffer(pTSWd->pNMInfo, pPkt); } // prevent regression, keep original return code. status = STATUS_UNSUCCESSFUL; } } else { RDP_SERVER_REDIRECTION_PACKET_V3 *pPkt; unsigned PktSize, DataLen; // Calculate the PDU size DataLen = pSdIoctl->InputBufferLength - sizeof(TS_CLIENT_REDIRECTION_INFO); PktSize = sizeof(RDP_SERVER_REDIRECTION_PACKET_V3) + DataLen; status = SM_AllocBuffer(pTSWd->pSmInfo, (PPVOID)&pPkt, PktSize, TRUE, TRUE); if ( STATUS_SUCCESS == status ) { // Fill in the packet fields. pPkt->Flags = RDP_SEC_REDIRECTION_PKT3; pPkt->Length = (UINT16)PktSize; pPkt->SessionID = pRedirInfo->SessionID; pPkt->RedirFlags = pRedirInfo->Flags; if (pTSWd->fDontDisplayLastUserName) { pPkt->RedirFlags |= LB_DONTSTOREUSERNAME; } memcpy(pPkt + 1, pRedirInfo + 1, DataLen); TRC_DBG((TB, "Client Redirection PDU V3")); rc = SM_SendData(pTSWd->pSmInfo, (BYTE *)pPkt, PktSize, TS_HIGHPRIORITY, 0, FALSE, RDP_SEC_REDIRECTION_PKT3, TRUE); if (rc) { TRC_NRM((TB, "Sent TS_SERVER_REDIRECT_PDU: %u", PktSize)); status = STATUS_SUCCESS; } else { TRC_ERR((TB,"Failed to send redir PDU")); status = STATUS_UNSUCCESSFUL; } } else { TRC_ERR((TB, "Failed to alloc %d bytes for redir PDU", PktSize)); // prevent regression, keep original return code. status = STATUS_UNSUCCESSFUL; } } break; } /****************************************************************************/ /****************************************************************************/ /* Finally the IOCtls that we pass on to the rest of the stack without */ /* getting in the way. */ /****************************************************************************/ /****************************************************************************/ /********************************************************************/ /* This IOCtl indicates the connection is down. Tell MCS before */ /* forwarding the IOCtl */ /********************************************************************/ case IOCTL_ICA_STACK_CANCEL_IO : { MCSIcaStackCancelIo(pTSWd->hDomainKernel); TRC_NRM((TB, "CancelIO - set WD dead")); // Make sure that Domain.StatusDead is consistent with TSWd.dead pTSWd->dead = TRUE; ((PDomain)(pTSWd->hDomainKernel))->StatusDead = TRUE; } /********************************************************************/ /* NB NOTE NO BREAK here - we drop through deliberately. */ /********************************************************************/ /********************************************************************/ /* modem callback and some others we're not interested in but lower */ /* layers might be */ /********************************************************************/ case IOCTL_ICA_STACK_CALLBACK_INITIATE : case IOCTL_ICA_STACK_CALLBACK_COMPLETE : case IOCTL_ICA_STACK_CREATE_ENDPOINT : case IOCTL_ICA_STACK_OPEN_ENDPOINT : case IOCTL_ICA_STACK_CLOSE_ENDPOINT : case IOCTL_ICA_STACK_CONNECTION_WAIT : case IOCTL_ICA_STACK_CONNECTION_REQUEST : // required for shadowing case IOCTL_ICA_STACK_QUERY_LOCALADDRESS : { status = IcaCallNextDriver( pTSWd->pContext, SD$IOCTL, pSdIoctl ); TRC_DBG((TB, "Chaining on IOCtl %#x (function %d): status %#x", pSdIoctl->IoControlCode, WDW_IOCTL_FUNCTION(pSdIoctl->IoControlCode), status)); } break; // Returning bad status for this makes GRE ignore it. case IOCTL_VIDEO_ENUM_MONITOR_PDO: status = STATUS_DEVICE_NOT_READY; break; default: { TRC_ALT((TB, "UNKNOWN WdIoctl %#x (function %d): status %#x", pSdIoctl->IoControlCode, WDW_IOCTL_FUNCTION(pSdIoctl->IoControlCode), status)); status = IcaCallNextDriver( pTSWd->pContext, SD$IOCTL, pSdIoctl ); break; } } DC_END_FN(); DC_EXIT_POINT: return status; } /****************************************************************************/ /* Name: WD_RawWrite */ /* */ /* Purpose: Handle I/O writes to and from the client of a shadow operation*/ /* */ /* Params: IN pTSWd - Points to wd data structure */ /* INOUT pSdRawWrite - Points to a SD_RAWWRITE structure */ /* */ /* Operation: Forward the data to the client of this stack. */ /****************************************************************************/ NTSTATUS WD_RawWrite(PTSHARE_WD pTSWd, PSD_RAWWRITE pSdRawWrite) { PUCHAR pInBuffer; PBYTE pOutBuffer; ULONG newBytes; BOOL bSuccess = TRUE; NTSTATUS status; DC_BEGIN_FN("WD_RawWrite"); pInBuffer = pSdRawWrite->pBuffer; newBytes = pSdRawWrite->ByteCount; status = SM_AllocBuffer(pTSWd->pSmInfo, (PPVOID) &pOutBuffer, newBytes, TRUE, FALSE); if ( STATUS_SUCCESS == status ) { memcpy(pOutBuffer, pInBuffer, newBytes); bSuccess = SM_SendData(pTSWd->pSmInfo, pOutBuffer, newBytes, PROT_PRIO_MISC, 0, FALSE, RNS_SEC_ENCRYPT, FALSE); if (bSuccess) { TRC_NRM((TB, "Sent shadow data to %s: %ld", (pTSWd->StackClass == Stack_Primary) ? "client" : "target", newBytes)); status=STATUS_SUCCESS; } else { TRC_ERR((TB, "FAILED to Send shadow data to %s: %ld", (pTSWd->StackClass == Stack_Primary) ? "client" : "target", newBytes)); status = STATUS_UNEXPECTED_IO_ERROR; } } else { TRC_ERR((TB, "FAILED to alloc shadow buffer for %s: %ld", (pTSWd->StackClass == Stack_Primary) ? "client" : "target", newBytes)); // prevent regression, keep original return code. status = STATUS_NO_MEMORY; } DC_END_FN(); return status; } /****************************************************************************/ /* Name: WDWNewShareClass */ /* */ /* Purpose: Create a new ShareClass object */ /****************************************************************************/ NTSTATUS WDWNewShareClass(PTSHARE_WD pTSWd) { NTSTATUS status = STATUS_SUCCESS; ShareClass *pSC; DC_BEGIN_FN("WDWNewShareClass"); #ifdef DC_HICOLOR pSC = new ShareClass(pTSWd, pTSWd->desktopHeight, pTSWd->desktopWidth, pTSWd->desktopBpp, pTSWd->pSmInfo); #else pSC = new ShareClass(pTSWd, pTSWd->desktopHeight, pTSWd->desktopWidth, 8, pTSWd->pSmInfo); #endif if (pSC != NULL) { TRC_NRM((TB, "Created Share Class")); pTSWd->dcShare = (PVOID)pSC; } else { TRC_ERR((TB, "Failed to create Share Class")); status = STATUS_NO_MEMORY; } DC_END_FN(); return status; } /* WDWNewShareClass */ /****************************************************************************/ /* Name: WDWDeleteShareClass */ /* */ /* Purpose: Delete a Share Class object */ /****************************************************************************/ void WDWDeleteShareClass(PTSHARE_WD pTSWd) { ShareClass *pSC = (ShareClass *)pTSWd->dcShare; DC_BEGIN_FN("WDWDeleteShareClass"); TRC_ASSERT((pSC != NULL), (TB, "NULL Share Class")); TRC_NRM((TB, "Delete Share Class")); delete pSC; pTSWd->dcShare = NULL; DC_END_FN(); } /* WDWDeleteShareClass */ /****************************************************************************/ /* Name: WDWTermShareClass */ /* */ /* Purpose: Terminate the Share Class */ /****************************************************************************/ void WDWTermShareClass(PTSHARE_WD pTSWd) { ShareClass *pSC = (ShareClass *)pTSWd->dcShare; DC_BEGIN_FN("WDWTermShareClass"); TRC_ASSERT((pSC != NULL), (TB, "NULL Share Class")); if (pTSWd->shareClassInit) { pSC->DCS_Term(); TRC_NRM((TB, "Share Class terminated")); pTSWd->shareClassInit = FALSE; } else { TRC_ALT((TB, "Can't terminate uninitialized Share Core")); } DC_END_FN(); } /* WDWTermShareClass */ /****************************************************************************/ /* Name: WDWDDConnect */ /* */ /* Purpose: Process an IOCTL_WDTS_DD_CONNECT or */ /* IOCTL_WDTS_DD_SHADOW_CONNECT from the client. */ /* */ /* Params: IN pTSWd - pointer to WD struct */ /* INOUT PSD_IOCTL - pointer to received IOCtl */ /* IN reconnect - TRUE - this is a reconnect */ /* FALSE - this is a connect */ /* */ /* Operation: Save the frame buffer pointer */ /* Initialize the share core (will start to bring the share up) */ /* Return the required pointers to the DD. */ /****************************************************************************/ NTSTATUS WDWDDConnect(PTSHARE_WD pTSWd, PSD_IOCTL pSdIoctl, BOOL reconnect) { NTSTATUS status = STATUS_UNSUCCESSFUL; NTSTATUS waitStatus; BOOL rc; PTS_BITMAP_CAPABILITYSET pBitmapCaps; ShareClass *pSC = (ShareClass *)pTSWd->dcShare; PTSHARE_DD_CONNECT_IN pIn = (PTSHARE_DD_CONNECT_IN)pSdIoctl->InputBuffer; PTSHARE_DD_CONNECT_OUT pOut = (PTSHARE_DD_CONNECT_OUT)pSdIoctl->OutputBuffer; DC_BEGIN_FN("WDWDDConnect"); TRC_ASSERT((pSC != NULL), (TB, "NULL Share Class")); // Check we're connected OK. if (!pTSWd->connected) { TRC_ERR((TB, "Not connected")); status = STATUS_CONNECTION_DISCONNECTED; DC_QUIT; } // Check we've been given a sensible IOCtl. if ((pIn == NULL) || (pOut == NULL) || (pSdIoctl->InputBufferLength < sizeof(TSHARE_DD_CONNECT_IN)) || (pSdIoctl->OutputBufferLength < sizeof(TSHARE_DD_CONNECT_OUT))) { status = STATUS_BUFFER_TOO_SMALL; TRC_ERR((TB, "A buffer not present or big enough, %p, %lu; %p, %lu", pIn, pSdIoctl->InputBufferLength, pOut, pSdIoctl->OutputBufferLength)); DC_QUIT; } // Check that the DD sizeof(SHM_SHARED_MEMORY) matches our expectations. // If not, we have mismatched binaries. if (pIn->DDShmSize != sizeof(SHM_SHARED_MEMORY)) { DbgPrint("****** RDPWD: Mismatched DD/WD - DD Shm size=%u, WD=%u\n", pIn->DDShmSize, sizeof(SHM_SHARED_MEMORY)); return STATUS_INVALID_PARAMETER; } // Set returned buffer length. pSdIoctl->BytesReturned = sizeof(TSHARE_DD_CONNECT_OUT); // Increment the loadcount pTSWd->shareId = InterlockedIncrement(&WD_ShareId); ((PSHM_SHARED_MEMORY) (pIn->pShm))->shareId = pTSWd->shareId; // Now get the share core initialized or reconnected. if (reconnect) { // Restore the timer info. TRC_NRM((TB, "Reconnect Share Core")); pTSWd->ritTimer = pIn->pKickTimer; WDW_StartRITTimer(pTSWd, pTSWd->interactiveDelay); } else { // Check for re-initialization. // This is now legal with console disconnect. // if (pTSWd->shareClassInit) // { // // if (pTSWd->StackClass != Stack_Console) // { // TRC_ERR((TB, "Re-initialization - fail it")); // status = STATUS_UNSUCCESSFUL; // DC_QUIT; // } // else // { // TRC_ALT((TB, "Re-initialize console stack")); // } // } //Make sure the sbcKeyDatabase is freed before initialization if (pTSWd->shareClassInit) { pSC->SBC_FreeBitmapKeyDatabase(); } // Initialize the Share Core. TRC_NRM((TB, "Initialize Share Core")); pSC->m_pShm = (SHM_SHARED_MEMORY *)pIn->pShm; rc = pSC->DCS_Init(pTSWd, pTSWd->pSmInfo); pSC->m_pShm = NULL; if (rc) { // Initialized OK TRC_NRM((TB, "Share Class initialized, rc %d", rc)); pTSWd->shareClassInit = TRUE; } else { TRC_ERR((TB, "Failed to initialize Share Class")); status = STATUS_UNSUCCESSFUL; DC_QUIT; } } // If this is the primary stack, tell the display driver the desktop // width/height we need to use. if ((pTSWd->StackClass == Stack_Primary) || (pTSWd->StackClass == Stack_Console)) { pOut->desktopHeight = pTSWd->desktopHeight; pOut->desktopWidth = pTSWd->desktopWidth; // Share's on its way up, so give the key values back to the DD. pOut->pTSWd = (PVOID)pTSWd; pOut->pProtocolStatus = pTSWd->pProtocolStatus; TRC_ERR((TB, "Stored pTSWD %p, protocol status %p", pTSWd, pTSWd->pProtocolStatus)); } else { // For shadow connects, the DD tells the shadow WD the width/height // of the shadow target's desktop such that input from the shadow client // can be scaled appropriately. /********************************************************************/ /* See if the shadowing client supports dynamic resizing. First we */ /* need to extract the bitmap caps from the connect data */ /********************************************************************/ pBitmapCaps = (PTS_BITMAP_CAPABILITYSET) WDW_GetCapSet( pTSWd, TS_CAPSETTYPE_BITMAP, &pIn->pVirtModuleData->combinedCapabilities, pIn->pVirtModuleData->capsLength); /********************************************************************/ /* If we found the bitmap caps, and the client does support dynamic */ /* resizing, then just go ahead and assign the size. */ /********************************************************************/ if (pBitmapCaps && (pBitmapCaps->desktopResizeFlag == TS_CAPSFLAG_SUPPORTED)) { TRC_ALT((TB, "Client supports dynamic resizing")); pTSWd->desktopHeight = pIn->desktopHeight; pTSWd->desktopWidth = pIn->desktopWidth; pSC->m_desktopHeight = pIn->desktopHeight; pSC->m_desktopWidth = pIn->desktopWidth; } /********************************************************************/ /* If the client does NOT support dynamic resizing, then make sure */ /* that the shadower client is at least as big as the shadow target */ /* client - or the shadower client will trap */ /********************************************************************/ else if ((pTSWd->desktopHeight >= pIn->desktopHeight) && (pTSWd->desktopWidth >= pIn->desktopWidth)) { pTSWd->desktopHeight = pIn->desktopHeight; pTSWd->desktopWidth = pIn->desktopWidth; pSC->m_desktopHeight = pIn->desktopHeight; pSC->m_desktopWidth = pIn->desktopWidth; } else { TRC_ERR((TB, "Rejecting attempt to shadow a higher res client")); status = STATUS_UNSUCCESSFUL; DC_QUIT; } #ifdef DC_HICOLOR /********************************************************************/ /* Can the shadower cope with the target BPP? /********************************************************************/ TRC_ALT((TB, "Shadower WD: %d bpp", pTSWd->desktopBpp )); TRC_ALT((TB, "Target WD: %d bpp", pIn->desktopBpp )); if (pTSWd->desktopBpp == pIn->desktopBpp) { TRC_ALT((TB, "Color depths match - ok")); pSC->m_desktopBpp = pIn->desktopBpp; } else { TRC_ALT((TB, "Color depth mismatch")); /****************************************************************/ /* Test the shadower's supported color depths */ /****************************************************************/ status = STATUS_SUCCESS; switch (pIn->desktopBpp) { case 24: { if (pTSWd->supportedBpps & RNS_UD_24BPP_SUPPORT) { TRC_DBG((TB, "24bpp supported")); break; } status = STATUS_UNSUCCESSFUL; } break; case 16: { if (pTSWd->supportedBpps & RNS_UD_16BPP_SUPPORT) { TRC_DBG((TB, "16bpp supported")); break; } status = STATUS_UNSUCCESSFUL; } break; case 15: { if (pTSWd->supportedBpps & RNS_UD_15BPP_SUPPORT) { TRC_DBG((TB, "15bpp supported")); break; } status = STATUS_UNSUCCESSFUL; } break; case 8: case 4: { TRC_DBG((TB, "8/4 bpp supported")); } break; default: { TRC_ASSERT((FALSE), (TB, "Attempt to shadow unknown" \ " target color depth %d", pIn->desktopBpp)); } break; } /****************************************************************/ /* Did they support it? */ /****************************************************************/ if (status == STATUS_UNSUCCESSFUL) { TRC_ERR((TB, "Rejecting shadow: unsupported color depth")); DC_QUIT; } else { TRC_ALT((TB, "but client claims to cope...")); pTSWd->desktopBpp = pIn->desktopBpp; pSC->m_desktopBpp = pIn->desktopBpp; } } #endif } /************************************************************************/ /* Share Core is no longer dead */ /************************************************************************/ // Make sure that Domain.StatusDead is consistent with TSWd.dead pTSWd->dead = FALSE; ((PDomain)(pTSWd->hDomainKernel))->StatusDead = FALSE; SM_Dead(pTSWd->pSmInfo, FALSE); /************************************************************************/ /* Clear the create event before creating the Share */ /************************************************************************/ KeClearEvent(pTSWd->pCreateEvent); /************************************************************************/ /* Now create a Share */ /************************************************************************/ #ifdef DC_HICOLOR TRC_ALT((TB, "Creating share at %d bpp", pTSWd->desktopBpp )); #endif rc = pSC->SC_CreateShare(); if (rc) { // Initialized OK - save the Shared Memory pointer. TRC_NRM((TB, "Share create started")); } else { TRC_ERR((TB, "Failed to create Share")); status = STATUS_CONNECTION_DISCONNECTED; DC_QUIT; } /************************************************************************/ /* Wait for Share creation to complete before returning to TShareDD */ /************************************************************************/ TRC_NRM((TB, "Wait for Share Core to create the Share")); waitStatus = WDW_WaitForConnectionEvent(pTSWd, pTSWd->pCreateEvent, 60000L); /************************************************************************/ /* It is possible that the WD has been closed while we were waiting for */ /* the Share creation to complete. If this is the case, the Share */ /* class will have been deleted, so we can't continue. Return a */ /* failure to TShareDD. */ /************************************************************************/ if (pTSWd->dcShare == NULL) { TRC_ERR((TB, "Share Class ended while waiting for Share creation")); status = STATUS_CONNECTION_DISCONNECTED; DC_QUIT; } if (waitStatus == STATUS_TIMEOUT) { /********************************************************************/ /* The wait timed out - probably because the connection was */ /* disconnected before the Share was created. Tidy up the Share. */ /********************************************************************/ TRC_ERR((TB, "Timeout waiting for Share creation")); pSC->m_pShm = (SHM_SHARED_MEMORY *)pIn->pShm; pSC->SC_EndShare(FALSE); pSC->m_pShm = NULL; TRC_NRM((TB, "Share ended")); status = STATUS_CONNECTION_DISCONNECTED; // Can no longer accept input from RDPDD or PDMCS. if (pTSWd->shadowState != SHADOW_CLIENT) { // Make sure that Domain.StatusDead is consistent with TSWd.dead pTSWd->dead = TRUE; ((PDomain)(pTSWd->hDomainKernel))->StatusDead = TRUE; SM_Dead(pTSWd->pSmInfo, TRUE); } else { // The client shadow stack is disconnected from its display driver // during a shadow, but must still be able to send/receive data // to/from the target shadow stack and shadow client. TRC_ALT((TB, "In shadow: leaving SM active")); } DC_QUIT; } /************************************************************************/ /* Check whether the Share was created OK. If not, quit now. */ /************************************************************************/ if (!pTSWd->shareCreated) { TRC_ERR((TB, "Share creation failed")); status = STATUS_UNSUCCESSFUL; DC_QUIT; } // We have successfully received the initial share creation PDUs. // Update the received info for the DD to use. pSC->SBC_GetBitmapKeyDatabase(&pOut->bitmapKeyDatabaseSize, &pOut->bitmapKeyDatabase); // For shadow connects, we need to add in the remote party to negotiate // capabilities correctly. if ((pSdIoctl->IoControlCode == IOCTL_WDTS_DD_SHADOW_CONNECT) && ((pTSWd->StackClass == Stack_Primary) || (pTSWd->StackClass == Stack_Console))) { TRC_ALT((TB, "Negotiating shadow capabilities")); status = pSC->SC_AddPartyToShare( SC_SHADOW_PERSON_ID, &pIn->pVirtModuleData->combinedCapabilities, pIn->pVirtModuleData->capsLength); if (status != STATUS_SUCCESS) { TRC_ERR((TB, "Failed to negotiate shadow capabilities: %lx", status)); DC_QUIT; } } /************************************************************************/ /* By now, the capabilities have been exchanged with the Client. Call */ /* the core to update SHM. */ /************************************************************************/ if ((pTSWd->StackClass == Stack_Primary) || (pTSWd->StackClass == Stack_Console)) { TRC_NRM((TB, "Update SHM")); pSC->m_pShm = (SHM_SHARED_MEMORY *)pIn->pShm; pSC->DCS_UpdateShm(); pSC->m_pShm = NULL; #ifdef DC_DEBUG // Make sure trace config is updated in SHM. pTSWd->trcShmNeedsUpdate = TRUE; TRC_MaybeCopyConfig(pTSWd, &(((SHM_SHARED_MEMORY *)(pIn->pShm))->trc)); #endif } // All worked OK. TRC_NRM((TB, "Share created")); status = STATUS_SUCCESS; DC_EXIT_POINT: // record the individual connection status of each stack. if (pTSWd->StackClass == Stack_Primary) pOut->primaryStatus = status; else pOut->secondaryStatus |= status; DC_END_FN(); return (status); } /* WDWDDConnect */ /****************************************************************************/ /* Name: WDWDDDisconnect */ /* */ /* Purpose: Handle the disconnect IOCtl from the DD */ /* */ /* Params: IN pTSWd - pointer to WD struct */ /* INOUT PSD_IOCTL - pointer to received IOCtl */ /* IN bForce - used by shadow to force sending of a */ /* deactivate all PDU. */ /****************************************************************************/ NTSTATUS WDWDDDisconnect(PTSHARE_WD pTSWd, PSD_IOCTL pSdIoctl, BOOLEAN bForce) { ShareClass *pSC = (ShareClass *)pTSWd->dcShare; PTSHARE_DD_DISCONNECT_IN pIn = (PTSHARE_DD_DISCONNECT_IN)pSdIoctl->InputBuffer; DC_BEGIN_FN("WDWDDDisconnect"); TRC_ASSERT((pTSWd->dcShare != NULL), (TB,"Got a disconnect with no share obj!")); // Remove all references to WinStation resources. WDWStopRITTimer(pTSWd); pTSWd->ritTimer = NULL; // Dump the bitmap cache key database to system memory. If this disconnect // is in preparation for a reconnect, the database will allow us to // preserve the bitmap cache state. // // If this is a disconnect in preparation for a shadow, then we can't save // off the keys. For the shadow target, bShadowDisconnect will be set by // the DD in DrvShadowConnect() processing. The shadow state for the shadow // client will be other than NONE since we would have seen hotkey enable // requests prior to the disconnect. pSC->m_pShm = (SHM_SHARED_MEMORY *)pIn->pShm; if (pSC->m_pShm != NULL) { pSC->SBC_DumpBitmapKeyDatabase(!pIn->bShadowDisconnect && (pTSWd->shadowState == SHADOW_NONE)); } // First of all, end the Share. pSC->SC_EndShare(bForce); TRC_NRM((TB, "Share ended")); // Can no longer accept input from RDPDD or PDMCS. if (pTSWd->shadowState != SHADOW_CLIENT) { // Make sure that Domain.StatusDead is consistent with TSWd.dead pTSWd->dead = TRUE; ((PDomain)(pTSWd->hDomainKernel))->StatusDead = TRUE; SM_Dead(pTSWd->pSmInfo, TRUE); } else { // The client shadow stack is disconnected from its display driver // during a shadow, but must still be able to send/receive data // to/from the target shadow stack and shadow client. TRC_ALT((TB, "In shadow: leaving SM active")); } // Tell Share Class to disconnect. pSC->DCS_Disconnect(); TRC_NRM((TB, "Share Class disconnected")); pSC->m_pShm = NULL; DC_END_FN(); return STATUS_SUCCESS; } /* WDWDDDisconnect */ /****************************************************************************/ /* Name: WDWDDShadowConnect */ /* */ /* Purpose: Process an IOCTL_WDTS_DD_SHADOW_CONNECT from the DD */ /* */ /* Params: IN pTSWd - pointer to WD struct */ /* INOUT PSD_IOCTL - pointer to received IOCtl */ /* */ /* Operation: Initialize either the primary stack or the shadow stack for */ /* a shadowing session. */ /****************************************************************************/ NTSTATUS WDWDDShadowConnect(PTSHARE_WD pTSWd, PSD_IOCTL pSdIoctl) { NTSTATUS status = STATUS_SUCCESS; PTSHARE_DD_CONNECT_IN pIn = (PTSHARE_DD_CONNECT_IN)pSdIoctl->InputBuffer; PTSHARE_DD_CONNECT_OUT pOut = (PTSHARE_DD_CONNECT_OUT)pSdIoctl->OutputBuffer; PSHM_SHARED_MEMORY pShm = (PSHM_SHARED_MEMORY) pIn->pShm; DC_BEGIN_FN("WDWDDShadowConnect"); switch (pTSWd->StackClass) { case Stack_Primary: case Stack_Console: // Reconnect the primary stack status = WDWDDConnect(pTSWd, pSdIoctl, TRUE); if (NT_SUCCESS(status)) { TRC_ALT((TB, "Primary target stack reconnected!")); } else { TRC_ERR((TB, "Primary target stack could not reconnect: %lx)", status)); DC_QUIT; } // Set up the shadow data buffer. The primary stack will copy output to // this location so all other shadow stacks can just squirt it. #ifdef DC_HICOLOR pTSWd->pShadowInfo = (PSHADOW_INFO)COM_Malloc(2 * WD_MAX_SHADOW_BUFFER); #else pTSWd->pShadowInfo = (PSHADOW_INFO)COM_Malloc(WD_MAX_SHADOW_BUFFER); #endif // Stash the shadow buffer so the DD can pass it to all shadow stacks // via the Shm. if (pTSWd->pShadowInfo != NULL) { pTSWd->shadowState = SHADOW_TARGET; memset(pTSWd->pShadowInfo, 0, sizeof(SHADOW_INFO)); pShm->pShadowInfo = pTSWd->pShadowInfo; #ifdef DC_HICOLOR TRC_ALT((TB, "Primary stack allocated shadow info: %p[%ld]", pTSWd->pShadowInfo, 2 * WD_MAX_SHADOW_BUFFER)); #else TRC_ALT((TB, "Primary stack allocated shadow info: %p[%ld]", pTSWd->pShadowInfo, WD_MAX_SHADOW_BUFFER)); #endif } // Primary stack is really OK in this scenario, however it is fatal // for the shadow stack else { pTSWd->pShadowInfo = NULL; pShm->pShadowInfo = NULL; pOut->secondaryStatus = STATUS_NO_MEMORY; TRC_ERR((TB, "Could not allocate shadow data buffer")); DC_QUIT; } break; // Drive the shadow stack thru the normal connection phase case Stack_Shadow: status = WDWDDConnect(pTSWd, pSdIoctl, FALSE); if (NT_SUCCESS(status)) { TRC_ALT((TB, "Shadow stack connected!")); } else { TRC_ERR((TB, "Shadow stack could not connect: %lx", status)); } break; default: TRC_ERR((TB, "Unknown stack class: %ld", pTSWd->StackClass)); status = STATUS_INVALID_PARAMETER; break; } DC_EXIT_POINT: DC_END_FN(); return (status); } /****************************************************************************/ /* Name: WDWDDShadowDisconnect */ /* */ /* Purpose: Process an IOCTL_WDTS_DD_SHADOW_DISCONNECT from the DD */ /* */ /* Params: IN pTSWd - pointer to WD struct */ /* INOUT PSD_IOCTL - pointer to received IOCtl */ /* */ /* Operation: Stop shadowing on the primary stack. */ /****************************************************************************/ NTSTATUS WDWDDShadowDisconnect(PTSHARE_WD pTSWd, PSD_IOCTL pSdIoctl) { NTSTATUS status = STATUS_SUCCESS; ShareClass *pSC = (ShareClass *)pTSWd->dcShare; PTSHARE_DD_DISCONNECT_IN pIn = (PTSHARE_DD_DISCONNECT_IN)pSdIoctl->InputBuffer; DC_BEGIN_FN("WDWDDShadowDisconnect"); switch (pTSWd->StackClass) { // Deallocate the shadow buffer case Stack_Primary: case Stack_Console: pTSWd->shadowState = SHADOW_NONE; if (pTSWd->pShadowInfo != NULL) COM_Free(pTSWd->pShadowInfo); pTSWd->pShadowInfo = NULL; if (pTSWd->bCompress == TRUE) { unsigned MPPCCompressionLevel; // Negotiate down to our highest level of compression support // if we receive a larger number. MPPCCompressionLevel = (pTSWd->pInfoPkt->flags & RNS_INFO_COMPR_TYPE_MASK) >> RNS_INFO_COMPR_TYPE_SHIFT; if (MPPCCompressionLevel > PACKET_COMPR_TYPE_MAX) MPPCCompressionLevel = PACKET_COMPR_TYPE_MAX; // the compression history will be flushed pTSWd->bFlushed = PACKET_FLUSHED; // the compression will restart over initsendcontext(pTSWd->pMPPCContext, MPPCCompressionLevel); } pSC->SC_RemovePartyFromShare(SC_SHADOW_PERSON_ID); TRC_ALT((TB, "Update SHM after party left share")); pSC->m_pShm = (SHM_SHARED_MEMORY *)pIn->pShm; // Bump up the share ID to invalidate any old GRE cache entries for // glyphs or brushes. This is necessary when RDP caches are destroyed // since GRE may keep around the brush or font structure in its cache pTSWd->shareId = InterlockedIncrement(&WD_ShareId); pSC->m_pShm->shareId = pTSWd->shareId; pSC->SC_Update(); pSC->DCS_UpdateShm(); pSC->m_pShm = NULL; TRC_ALT((TB, "TSHARE_DD_SHADOW_DISCONNECT: Primary target stack")); break; // By the time this ioctl arrives TermDD should have already stopped echoing // calls to the shadow stack. case Stack_Shadow: TRC_ERR((TB, "Shadow stack received an unexpected disconnect!")); break; default: TRC_ERR((TB, "Unexpected stack class: %ld", pTSWd->StackClass)); break; } DC_END_FN(); return status; } /****************************************************************************/ /* Name: WDWUserLoggedOn */ /* */ /* Purpose: Notify the core that a user has logged on */ /****************************************************************************/ void WDWUserLoggedOn(PTSHARE_WD pTSWd, PSD_IOCTL pSdIoctl) { ShareClass *pSC = (ShareClass *)pTSWd->dcShare; DC_BEGIN_FN("WDWUserLoggedOn"); TRC_ASSERT((pSC != NULL), (TB, "NULL Share Class")); TRC_ASSERT((pSdIoctl->InputBufferLength == sizeof(LOGONINFO)), (TB, "Bad LogonInfo")); pSC->DCS_UserLoggedOn((PLOGONINFO)pSdIoctl->InputBuffer); DC_END_FN(); } /* WDWUserLoggedOn */ /****************************************************************************/ /* Name: WDWKeyboardSetIndicators */ /* */ /* Purpose: Notify the core of new keyboard indicators */ /****************************************************************************/ void WDWKeyboardSetIndicators(PTSHARE_WD pTSWd) { ShareClass *pSC = (ShareClass *)pTSWd->dcShare; DC_BEGIN_FN("WDWUserLoggedOn"); TRC_ASSERT((pSC != NULL), (TB, "NULL Share Class")); pSC->DCS_WDWKeyboardSetIndicators(); DC_END_FN(); } /* WDWKeyboardSetIndicators */ /****************************************************************************/ /* Name: WDWKeyboardSetImeStatus */ /* */ /* Purpose: Notify the core of new ime status */ /****************************************************************************/ void WDWKeyboardSetImeStatus(PTSHARE_WD pTSWd) { ShareClass *dcShare = (ShareClass *)pTSWd->dcShare; DC_BEGIN_FN("WDWKeyboardSetImeStatus"); TRC_ASSERT((dcShare != NULL), (TB, "NULL Share Class")); dcShare->DCS_WDWKeyboardSetImeStatus(); DC_END_FN(); } /* WDWKeyboardSetImeStatus */ /****************************************************************************/ /* Name: WDWSendBeep */ /* */ /* Purpose: Send a beep PDU to the client */ /* */ /* Params: IN pTSWd - pointer to WD struct */ /* INOUT PSD_IOCTL - pointer to received IOCtl */ /* */ /* Operation: Check validity of IOCtl & call through to UP. */ /****************************************************************************/ NTSTATUS WDWSendBeep(PTSHARE_WD pTSWd, PSD_IOCTL pSdIoctl) { NTSTATUS status = STATUS_UNSUCCESSFUL; ShareClass *pSC = (ShareClass *)pTSWd->dcShare; DC_BEGIN_FN("WDWSendBeep"); if (pSdIoctl->InputBufferLength == sizeof(BEEP_SET_PARAMETERS) && pSdIoctl->InputBuffer != NULL) { /************************************************************************/ /* Call into the Share Class to allocate and send the beep PDU */ /************************************************************************/ if (pSC != NULL && pTSWd->shareClassInit) { if (pSC->UP_SendBeep( ((PBEEP_SET_PARAMETERS)pSdIoctl->InputBuffer)->Duration, ((PBEEP_SET_PARAMETERS)pSdIoctl->InputBuffer)->Frequency)) status = STATUS_SUCCESS; } pSdIoctl->BytesReturned = 0; } else { TRC_ASSERT((TRUE), (TB,"Got Beep Ioctl but input buffer too small")); } DC_END_FN(); return status; } /* WDWSendBeep */ /****************************************************************************/ /* Name: WDWGetModuleData */ /* */ /* Purpose: Processes an IOCTL_ICA_STACK_QUERY_MODULE_DATA from Termsrv. */ /* */ /* Params: IN pTSWd - pointer to WD struct */ /* INOUT PSD_IOCTL - pointer to received IOCtl */ /* */ /* Operation: return all the relevant conference creation info */ /****************************************************************************/ NTSTATUS WDWGetModuleData(PTSHARE_WD pTSWd, PSD_IOCTL pSdIoctl) { NTSTATUS status = STATUS_SUCCESS; ShareClass *dcShare = (ShareClass *)pTSWd->dcShare; PTSHARE_MODULE_DATA pModuleData = (PTSHARE_MODULE_DATA) pSdIoctl->OutputBuffer; PBYTE pData; ULONG ulDataSize; PRNS_UD_CS_CORE pCoreData; PRNS_UD_CS_SEC pSecurityData; DC_BEGIN_FN("WDWGetModuleData"); ulDataSize = sizeof(TSHARE_MODULE_DATA) - sizeof(RNS_UD_HEADER) + sizeof(RNS_UD_CS_CORE) + sizeof(RNS_UD_CS_SEC); // Make sure the output buffer is big enough! pSdIoctl->BytesReturned = ulDataSize; if (pSdIoctl->OutputBufferLength < ulDataSize) { status = STATUS_BUFFER_TOO_SMALL; DC_QUIT; } pModuleData->ulLength = ulDataSize; pModuleData->ulVersion = 2; pModuleData->userDataLen = sizeof(RNS_UD_CS_CORE) + sizeof(RNS_UD_CS_SEC); pData = (PBYTE) &pModuleData->userData; // Client to server core data pCoreData = (PRNS_UD_CS_CORE) pData; pCoreData->header.type = RNS_UD_CS_CORE_ID; pCoreData->header.length = sizeof(RNS_UD_CS_CORE); pCoreData->version = RNS_UD_VERSION; pCoreData->desktopWidth = (UINT16)pTSWd->desktopWidth; pCoreData->desktopHeight = (UINT16)pTSWd->desktopHeight; // Re-munge the color depth switch (pTSWd->desktopBpp) { case 8: pCoreData->colorDepth = RNS_UD_COLOR_8BPP; break; case 4: pCoreData->colorDepth = RNS_UD_COLOR_4BPP; break; #ifdef DC_HICOLOR case 15: pCoreData->colorDepth = RNS_UD_COLOR_16BPP_555; break; #endif case 16: pCoreData->colorDepth = RNS_UD_COLOR_16BPP_565; break; case 24: pCoreData->colorDepth = RNS_UD_COLOR_24BPP; break; default: status = STATUS_UNSUCCESSFUL; DC_QUIT; } pCoreData->postBeta2ColorDepth = pCoreData->colorDepth; #ifdef DC_HICOLOR /************************************************************************/ /* Copy across the current color depth */ /************************************************************************/ pCoreData->highColorDepth = (TSUINT16)pTSWd->desktopBpp; pCoreData->supportedColorDepths = (TSUINT16)pTSWd->supportedBpps; #endif // Other useful stuff from user data pCoreData->version = pTSWd->version; pCoreData->SASSequence = pTSWd->sas; pCoreData->keyboardLayout = pTSWd->kbdLayout; pCoreData->clientBuild = pTSWd->clientBuild; memcpy(pCoreData->clientName, pTSWd->clientName, sizeof(pTSWd->clientName)); //Whistler post Beta2 - shadow loop fix memcpy( pCoreData->clientDigProductId, pTSWd->clientDigProductId, sizeof( pTSWd->clientDigProductId )); // FE data pCoreData->keyboardType = pTSWd->keyboardType; pCoreData->keyboardSubType = pTSWd->keyboardSubType; pCoreData->keyboardFunctionKey = pTSWd->keyboardFunctionKey; memcpy(pCoreData->imeFileName, pTSWd->imeFileName, sizeof(pTSWd->imeFileName)); // Win2000 Post Beta3 fields added pCoreData->clientProductId = pTSWd->clientProductId; pCoreData->serialNumber = pTSWd->serialNumber; // client to server security data pSecurityData = (PRNS_UD_CS_SEC) (pCoreData + 1); pSecurityData->header.type = RNS_UD_CS_SEC_ID; pSecurityData->header.length = sizeof(RNS_UD_CS_SEC); SM_GetEncryptionMethods(pTSWd->pSmInfo, pSecurityData ); // UGH! Now copy this data in a redundant form so we won't break // compatibility with TS5 B3. memcpy(&pModuleData->clientCoreData, pCoreData, sizeof(RNS_UD_CS_CORE_V0)); memcpy(&pModuleData->clientSecurityData, pSecurityData, sizeof(RNS_UD_CS_SEC_V0)); MCSGetDefaultDomain(pTSWd->pContext, &pModuleData->DomParams, &pModuleData->MaxSendSize, &pModuleData->MaxX224DataSize, &pModuleData->X224SourcePort); pModuleData->shareId = pTSWd->shareId; DC_EXIT_POINT: DC_END_FN(); return status; } /* WDWGetModuleData */ /****************************************************************************/ /* Name: WDW_GetCapSet */ /* */ /* Purpose: Extract the specific capabilities from a combined */ /* capabilities set */ /* */ /* Returns: pointer to caps or NULL if not found */ /* */ /* Params: IN pTSWd - pointer to WD struct */ /* IN CapSetType - type of capability set */ /* IN pCombinedCaps - pointer to combined capabilites */ /* IN lengthCaps - length of supplied caps */ /* */ /* Operation: find the specific caps in supplied capability set */ /****************************************************************************/ PTS_CAPABILITYHEADER WDW_GetCapSet( PTSHARE_WD pTSWd, UINT32 CapSetType, PTS_COMBINED_CAPABILITIES pCaps, UINT32 capsLength) { PTS_CAPABILITYHEADER pCapsHeader = NULL; UINT32 capsOffset; DC_BEGIN_FN("WDW_GetCapSet"); /************************************************************************/ /* Set up the pointer to the first capability set, and check that there */ /* is at least one set of caps! */ /************************************************************************/ pCapsHeader = (PTS_CAPABILITYHEADER)pCaps->data; capsOffset = sizeof(TS_COMBINED_CAPABILITIES) - 1; if (capsOffset >= capsLength) { TRC_NRM((TB, "No Caps found")); DC_QUIT; } /************************************************************************/ /* Now loop through all the caps, looking for the specified capabilities*/ /************************************************************************/ while (pCapsHeader->capabilitySetType != CapSetType) { /****************************************************************/ /* Add the length of this capability to the offset, to keep */ /* track of how much of the combined caps we have processed. */ /****************************************************************/ capsOffset += pCapsHeader->lengthCapability; if (capsOffset >= capsLength) { TRC_NRM((TB, "Bitmap Caps not found")); pCapsHeader = NULL; break; } /****************************************************************/ /* Add the length of this capability to the header pointer, so */ /* it points to the next capability set. */ /****************************************************************/ pCapsHeader = (PTS_CAPABILITYHEADER) (((PBYTE)pCapsHeader) + pCapsHeader->lengthCapability); TRC_NRM((TB, "Next set: %u", pCapsHeader->capabilitySetType)); } /************************************************************************/ /* pCapsHeader is either NULL or a pointer to the desired caps - which */ /* is what we want to return */ /************************************************************************/ DC_EXIT_POINT: DC_END_FN(); return(pCapsHeader); } /* WDW_GetCapSet */ /****************************************************************************/ /* Name: WDWGetDefaultCoreParams */ /* */ /* Purpose: Defaults the core params used by the shadow stacks */ /* */ /* Params: OUT pClientCoreData - WD core data */ /* */ /* Operation: Default the core params used by the shadow stacks */ /****************************************************************************/ NTSTATUS WDWGetDefaultCoreParams(PRNS_UD_CS_CORE pClientCoreData) { DC_BEGIN_FN("WDWGetDefaultCoreParams"); // Client to server core data pClientCoreData->header.type = RNS_UD_CS_CORE_ID; pClientCoreData->header.length = sizeof(RNS_UD_CS_CORE); // Desktop parameters pClientCoreData->desktopHeight = 640; pClientCoreData->desktopWidth = 480; pClientCoreData->colorDepth = RNS_UD_COLOR_8BPP; pClientCoreData->postBeta2ColorDepth = RNS_UD_COLOR_8BPP; #ifdef DC_HICOLOR pClientCoreData->highColorDepth = 15; pClientCoreData->supportedColorDepths = RNS_UD_24BPP_SUPPORT || RNS_UD_16BPP_SUPPORT || RNS_UD_15BPP_SUPPORT; #endif // Other useful stuff from user data pClientCoreData->version = RNS_TERMSRV_40_UD_VERSION; pClientCoreData->SASSequence = RNS_UD_SAS_NONE; pClientCoreData->keyboardLayout = 0; pClientCoreData->clientBuild = VER_PRODUCTBUILD; memcpy(pClientCoreData->clientName, L"Passthru Stack", sizeof(L"Passthru Stack")); //Whistler post Beta2 - shadow loop fix pClientCoreData->clientDigProductId[0] = 0; // FE data pClientCoreData->keyboardType = 0; pClientCoreData->keyboardSubType = 0; pClientCoreData->keyboardFunctionKey = 0; memset(pClientCoreData->imeFileName, 0, sizeof(pClientCoreData->imeFileName)); return STATUS_SUCCESS; } /****************************************************************************/ // Name: WDWSetConfigData // // Purpose: Sets the stack configuration/Policy settings from winstation // // Params: IN pConfigData /****************************************************************************/ NTSTATUS WDWSetConfigData(PTSHARE_WD pTSWd, PICA_STACK_CONFIG_DATA pConfigData) { PSM_HANDLE_DATA pRealSMHandle = (PSM_HANDLE_DATA)pTSWd->pSmInfo; DC_BEGIN_FN("WDWSetConfigData"); if (pConfigData->colorDepth == TS_24BPP_SUPPORT) { pTSWd->maxServerBpp = 24; } else if (pConfigData->colorDepth == TS_16BPP_SUPPORT) { pTSWd->maxServerBpp = 16; } else if (pConfigData->colorDepth == TS_15BPP_SUPPORT) { pTSWd->maxServerBpp = 15; } else { pTSWd->maxServerBpp = 8; } TRC_DBG((TB, "Max Color Depth support: %d", pTSWd->maxServerBpp)); pRealSMHandle->encryptionLevel = pConfigData->encryptionLevel; pRealSMHandle->encryptAfterLogon = (pConfigData->fDisableEncryption == 0) ? TRUE : FALSE; TRC_DBG((TB, "Encryption after logon: %d", pRealSMHandle->encryptAfterLogon)); TRC_DBG((TB, "Encryption level: %d", pRealSMHandle->encryptionLevel)); pTSWd->fPolicyDisablesArc = pConfigData->fDisableAutoReconnect; TRC_DBG((TB, "AutoReconnect disabled: %d", pTSWd->fPolicyDisablesArc)); DC_END_FN(); return STATUS_SUCCESS; } /****************************************************************************/ /* Name: WDWSetErrorInfo */ /* */ /* Purpose: Send error info to the client */ /****************************************************************************/ NTSTATUS WDWSetErrorInfo(PTSHARE_WD pTSWd, PSD_IOCTL pSdIoctl) { ShareClass *pSC = (ShareClass *)pTSWd->dcShare; DC_BEGIN_FN("WDWSetErrorInfo"); TRC_ASSERT((pSC != NULL), (TB, "NULL Share Class")); TRC_ASSERT((pSdIoctl->InputBufferLength == sizeof(TSUINT32)), (TB, "Bad ErrorInfo")); if(pSC) { if(pTSWd->bSupportErrorInfoPDU) { pSC->DCS_SendErrorInfo(*((PTSUINT32)pSdIoctl->InputBuffer)); } else { TRC_NRM((TB,"SetErrorInfo called but client doesn't support error PDU")); } } DC_END_FN(); return STATUS_SUCCESS; } /* WDWSetErrorInfo */ /****************************************************************************/ /* Name: WDWSendArcStatus /* /* Purpose: Send autoreconnect status update to client /****************************************************************************/ NTSTATUS WDWSendArcStatus(PTSHARE_WD pTSWd, PSD_IOCTL pSdIoctl) { ShareClass *pSC = (ShareClass *)pTSWd->dcShare; DC_BEGIN_FN("WDWSendArcStatus"); TRC_ASSERT((pSC != NULL), (TB, "NULL Share Class")); TRC_ASSERT((pSdIoctl->InputBufferLength == sizeof(TSUINT32)), (TB, "Bad ErrorInfo")); if(pSC) { if(pSC->SC_IsAutoReconnectEnabled()) { pSC->DCS_SendAutoReconnectStatus(*((PTSUINT32)pSdIoctl->InputBuffer)); } else { TRC_NRM((TB,"SetErrorInfo called but client doesn't ARC error PDU")); } } DC_END_FN(); return STATUS_SUCCESS; } /* WDWSendArcStatus */ /****************************************************************************/ /* Name: WDW_LogAndDisconnect */ /* */ /* Purpose: Log an event and disconnect the Client */ /* */ /* Returns: none */ /* */ /* Params: pTSWd */ /* errDetailCode - error code to log */ /* pDetailData - additional data */ /* detailDataLen - length of additional data */ /* */ /****************************************************************************/ void RDPCALL WDW_LogAndDisconnect( PTSHARE_WD pTSWd, BOOL fSendErrorToClient, unsigned errDetailCode, PBYTE pDetailData, unsigned detailDataLen) { DC_BEGIN_FN("WDW_LogAndDisconnect"); //Report the error code back to the client ShareClass *pSC = (ShareClass *)pTSWd->dcShare; if(pSC) { if(fSendErrorToClient && pTSWd->bSupportErrorInfoPDU) { pSC->DCS_SendErrorInfo( (errDetailCode + TS_ERRINFO_PROTOCOL_BASE)); } else { if( fSendErrorToClient ) { TRC_NRM((TB,"SetErrorInfo called but client doesn't support error PDU")); } else { TRC_NRM((TB,"SetErrorInfo called but asked not to send error code to client")); } } } if( !pTSWd->dead ) MCSProtocolErrorEvent(pTSWd->pContext, pTSWd->pProtocolStatus, errDetailCode, pDetailData, detailDataLen); DC_END_FN(); } } /* extern "C" */