|
|
/* (C) 1997-1999 Microsoft Corp.
* * file : Decode.c * author : Erik Mavrinac * * description: Decoding logic for MCS PDUs, for passing on to handlers * in ConPDU.c for connect PDUs and DomPDU.c and other files for domain * PDUs. */
#include "precomp.h"
#pragma hdrstop
#include <mcsimpl.h>
#include <at128.h>
/*
* Defines */
// Leading PDU byte for connect PDUs.
#define CONNECT_PDU 0x7F
// Size of the X.224 RFC1006 header.
#define RFC1006HeaderSize 4
// Constant for an incompletely-received input data header.
#define IncompleteHeader 0xFFFFFFFF
// Sizes for fast-path headers.
#define FastPathBaseHeaderSize 2
/*
* Prototypes for external functions for which we have no header. */ void __stdcall SM_DecodeFastPathInput(void *, BYTE *, unsigned, unsigned, unsigned, unsigned);
/*
* Prototypes for locally-defined functions */ BOOLEAN __fastcall RecognizeMCSFrame(PDomain, BYTE *, int, unsigned *); MCSError __fastcall DeliverShadowData(PDomain, BYTE *, unsigned, ChannelID);
/*
* Logs an error to the system event log and drops the connection. * ErrDetailCodes are from inc\LogErr.h. */
// Defined maximum size for caller-provided data to be sent to system event
// log. Max data size is 256, IcaLogError() includes a Unicode string
// "WinStation" which takes up some of the space, as does the unsigned
// subcode used here.
#define MaxEventDataLen (234 - sizeof(unsigned))
void APIENTRY MCSProtocolErrorEvent( PSDCONTEXT pContext, PPROTOCOLSTATUS pStat, unsigned ErrDetailCode, BYTE *pDetailData, unsigned DetailDataLen) { BYTE SpewBuf[256]; WCHAR *StringParams; unsigned DataLen; NTSTATUS Status; UNICODE_STRING EventLogName; ICA_CHANNEL_COMMAND Command;
// Increment the error counter.
pStat->Input.Errors++;
// Set the facility name based on the ErrDetailCode. More will need to be
// added here as we get more facilties using this function.
if (ErrDetailCode == Log_Null_Base) StringParams = L"NULL"; else if (ErrDetailCode >= Log_X224_Base && ErrDetailCode < Log_MCS_Base) StringParams = L"X.224"; else if (ErrDetailCode >= Log_MCS_Base && ErrDetailCode < Log_RDP_Base) StringParams = L"MCS"; else if (ErrDetailCode >= Log_RDP_Base && ErrDetailCode < Log_RDP_ENC_Base) StringParams = L"WD"; else if (ErrDetailCode >= Log_RDP_ENC_Base) // Add new facility here...
StringParams = L"\"DATA ENCRYPTION\"";
// ErrDetailCode is designated as the first unsigned in the extra buffer.
*((unsigned *)SpewBuf) = ErrDetailCode;
// Limit data according to max size.
DataLen = (DetailDataLen < MaxEventDataLen ? DetailDataLen : MaxEventDataLen); if (pDetailData != NULL) memcpy(SpewBuf + sizeof(unsigned), pDetailData, DataLen); DataLen += sizeof(unsigned);
IcaLogError(pContext, STATUS_RDP_PROTOCOL_ERROR, &StringParams, 1, SpewBuf, DataLen);
// Signal that we need to drop the link.
Command.Header.Command = ICA_COMMAND_BROKEN_CONNECTION; Command.BrokenConnection.Reason = Broken_Unexpected; Command.BrokenConnection.Source = BrokenSource_Server;
Status = IcaChannelInput(pContext, Channel_Command, 0, NULL, (BYTE *)&Command, sizeof(Command)); if (!NT_SUCCESS(Status)) ErrOut(pContext, "MCSProtocolErrorEvent(): Could not send BROKEN_CONN " "upward"); }
/*
* Utility function to send an X.224 connection response. Used by * DecodeWireData() and when a T120_START is sent indicating the * stack is up. */ NTSTATUS SendX224Confirm(Domain *pDomain) { POUTBUF pOutBuf; NTSTATUS Status;
pDomain->State = State_X224_Connected;
// This PDU send is vital to the connection and must succeed.
// Keep retrying the allocation until it succeeds.
do { // Allow the call to wait for a buffer.
Status = IcaBufferAlloc(pDomain->pContext, TRUE, FALSE, X224_ConnectionConPacketSize, NULL, &pOutBuf); if (Status != STATUS_SUCCESS) // NT_SUCCESS() does not fail STATUS_TIMEOUT
ErrOut(pDomain->pContext, "Could not alloc X.224 connect-confirm OutBuf, retrying"); } while (Status != STATUS_SUCCESS);
// Use a bogus source port number for the confirm. This is
// not used by either side.
CreateX224ConnectionConfirmPacket(pOutBuf->pBuffer, pDomain->X224SourcePort, 0x1234); pOutBuf->ByteCount = X224_ConnectionConPacketSize;
Status = SendOutBuf(pDomain, pOutBuf); if (!NT_SUCCESS(Status)) { ErrOut(pDomain->pContext, "Unable to send X.224 connection-confirm"); return Status; // Intended receiver receives silence.
}
return STATUS_SUCCESS; }
/*
* Connect-request-specific bytes: * Byte Contents * ---- -------- * 6 MSB of destination (answering) socket/port #, * should be 0 * 7 LSB of destination socket/port #, should be 0 * 8 MSB of source (calling) socket/port # * 9 LSB of source socket/port # * 10 Data class, should be 0x00 for X.224 class 0. * * Following are an optional TPDU size (incl. RFC1006 header size * of 4 bytes but not incl. X.224 3-byte data header) * negotiation block. * If this block is not present, an RFC1006 default is assumed * (65531, minus 3 bytes for the rest of the data packet * header) * Only present if LenInd is 2: * 11 TPDU type (only TPDU_SIZE (0xC0) supported) * 12 Info length (must be 0x01 for TPDU_SIZE) * 13 Encoded per X.224 sec 13.3.4(b), as power of 2 in * range 7..11 for TPDU size */
NTSTATUS HandleX224ConnectReq( Domain *pDomain, BYTE *pBuffer, unsigned PacketLen) { POUTBUF pOutBuf; NTSTATUS Status; unsigned LenInd;
if (pDomain->State != State_Unconnected) { ErrOut(pDomain->pContext, "X.224 ConnectionRequest received unexpectedly"); MCSProtocolErrorEvent(pDomain->pContext, pDomain->pStat, Log_X224_ConnectReceivedAfterConnected, pBuffer, PacketLen); return STATUS_RDP_PROTOCOL_ERROR; }
// Decode the length indicator in byte 4. Should be equal to the
// remaining packet size after the RFC1006 header and LenInd byte.
LenInd = pBuffer[4]; if (LenInd != (PacketLen - RFC1006HeaderSize - 1)) { ErrOut(pDomain->pContext, "X.224 Connect LenInd does not match packet length"); MCSProtocolErrorEvent(pDomain->pContext, pDomain->pStat, Log_X224_ConnectLenIndNotMatchingPacketLen, pBuffer, PacketLen); return STATUS_RDP_PROTOCOL_ERROR; }
// Check for possible denial-of-service attack or malformed packet.
if (PacketLen < 11 || LenInd < 6) { ErrOut(pDomain->pContext, "HandleX224ConnectReq(): Header length " "or LenInd encoded in X.224 header too short"); MCSProtocolErrorEvent(pDomain->pContext, pDomain->pStat, Log_X224_ConnectHeaderLenNotRequiredSize, pBuffer, PacketLen); return STATUS_RDP_PROTOCOL_ERROR; }
// Verify that dst port is set per standard.
if (pBuffer[6] != 0x00 || pBuffer[7] != 0x00) WarnOut(pDomain->pContext, "HandleX224ConnectReq(): Dest port not " "0x0000");
// Save src port.
pDomain->X224SourcePort = (pBuffer[8] << 8) + pBuffer[9];
// Must be class 0 connection per standard.
if (pBuffer[10] != 0x00) { ErrOut(pDomain->pContext, "HandleX224ConnectReq(): Data class not " "0x00 (X.224 class 0)"); MCSProtocolErrorEvent(pDomain->pContext, pDomain->pStat, Log_X224_ConnectNotClassZero, pBuffer, PacketLen); return STATUS_RDP_PROTOCOL_ERROR; }
// Set the default RFC1006 data size.
pDomain->MaxX224DataSize = X224_DefaultDataSize;
// Check for optional parameters.
if (LenInd == 6) goto FinishedDecoding;
// TPDU_SIZE is 3 bytes.
if (PacketLen < 14 || LenInd < 9) { ErrOut(pDomain->pContext, "HandleX224ConnectReq(): Header length(s) " "encoded in CR header too short for TPDU_SIZE"); goto FinishedDecoding; }
//MCS FUTURE: X.224 class 0 defined a couple more codes here;
// should we handle them in the future?
if (pBuffer[11] == TPDU_SIZE) { if (pBuffer[12] != 0x01) { ErrOut(pDomain->pContext, "HandleX224ConnectReq(): Illegal data " "size field in TPDU_SIZE block"); MCSProtocolErrorEvent(pDomain->pContext, pDomain->pStat, Log_X224_ConnectTPDU_SIZELengthFieldIllegalValue, pBuffer, PacketLen); return STATUS_RDP_PROTOCOL_ERROR; }
// Must conform to X.224 class 0 constraints of 7..11.
if (pBuffer[13] < 7 || pBuffer[13] > 11) { ErrOut(pDomain->pContext, "HandleX224ConnectReq(): Illegal data " "size field in TPDU size block"); MCSProtocolErrorEvent(pDomain->pContext, pDomain->pStat, Log_X224_ConnectTPDU_SIZENotLegalRange, pBuffer, PacketLen); return STATUS_RDP_PROTOCOL_ERROR; }
// Size is power of 2 -- 128..2048, minus 3 bytes for X.224
// Data TPDU header size.
pDomain->MaxX224DataSize = (1 << pBuffer[13]) - 3;
if (PacketLen > 14) WarnOut(pDomain->pContext, "HandleX224ConnectReq(): Frame size " "greater than TPDU data bytes (incl. TPDU-SIZE) for " "connection-request"); }
if (LenInd > 9) WarnOut(pDomain->pContext, "X224Recognize(): Extra optional " "fields present in TPDU, we are not handling!");
FinishedDecoding: // If the virtual channels have already been bound, and the
// stack has been given permission to send, send the
// X.224 response to start the client data flow.
if (pDomain->bChannelBound && pDomain->bCanSendData) { TraceOut(pDomain->pContext, "DecodeWireData(): Sending X.224 response"); Status = SendX224Confirm(pDomain); // Ignore errors. Should only occur if the stack is
// going down.
} else { // Set up for later with indication that X.224 is waiting.
pDomain->State = State_X224_Requesting; }
return STATUS_SUCCESS; }
/*
* Disconnect-request-specific bytes: * Byte Contents * ---- -------- * 6 MSB of destination socket/port # * 7 LSB of destination socket/port # * 8 MSB of source (sending) socket/port # * 9 LSB of source socket/port # * 10 Reason code: * 0 : not specified * 1 : congestion at sending machine * 2 : no session manager for data at sender * 3 : address unknown * * NOTE: We do not use any of these fields. */
NTSTATUS HandleX224Disconnect( Domain *pDomain, BYTE *pBuffer, unsigned PacketLen) { unsigned LenInd;
if (pDomain->State == State_Unconnected) { ErrOut(pDomain->pContext, "HandleX224Disconnect(): Disconnect " "received when not connected"); MCSProtocolErrorEvent(pDomain->pContext, pDomain->pStat, Log_X224_DisconnectWithoutConnection, pBuffer, PacketLen); return STATUS_RDP_PROTOCOL_ERROR; }
if (pDomain->State == State_MCS_Connected) { // Not a serious error since we just dropped X.224 below MCS
// without first dropping MCS.
WarnOut(pDomain->pContext, "X.224 Disconnect received, " "MCS was in connected state"); SignalBrokenConnection(pDomain); }
pDomain->State = State_Disconnected; pDomain->bEndConnectionPacketReceived = TRUE;
// Decode the length indicator in byte 4. Should be equal to the
// remaining packet size after the RFC1006 header and LenInd byte.
LenInd = pBuffer[4]; if (LenInd != (PacketLen - RFC1006HeaderSize - 1)) { ErrOut(pDomain->pContext, "X.224 Disconnect LenInd does not match packet length"); MCSProtocolErrorEvent(pDomain->pContext, pDomain->pStat, Log_X224_DisconnectLenIndNotMatchingPacketLen, pBuffer, PacketLen); return STATUS_RDP_PROTOCOL_ERROR; }
// Possible denial-of-service attack or malformed packet.
if (PacketLen != 11 || LenInd != 6) { ErrOut(pDomain->pContext, "HandleX224Disconnect(): Overall header " "length or LenInd encoded in X.224 Disconnect wrong size"); MCSProtocolErrorEvent(pDomain->pContext, pDomain->pStat, Log_X224_DisconnectHeaderLenNotRequiredSize, pBuffer, PacketLen); return STATUS_RDP_PROTOCOL_ERROR; }
return STATUS_SUCCESS; }
/*
* Main entry point for data arriving from transport via IcaRawInput() path. * Decode data passed up from the transport. pBuffer is assumed not to be * usable beyond the return from this function, so that data copying is * done as necessary. It is possible for incomplete frames to be passed * in, so a packet reassembly buffer is maintained. * This function is called directly by ICADD with a pointer to the WD data * structure. By convention, we assume that the DomainHandle is first in * that struct so we can simply do a double-indirection to get to our data. * Assumes the presence of X.224 framing headers at the start of all * data. * * General X.224 header is laid out as follows, with specific TPDU bytes * following: * Byte Contents * ---- -------- * 0 RFC1006 version number, must be 0x03. * 1 RFC1006 Reserved, must be 0x00. * 2 RFC1006 MSB of word-sized total-frame length (incl. whole X.224 * header). * 3 RFC1006 LSB of word-sized total-frame length. * 4 Length Indicator, the size of the header bytes following. * 5 Packet type indicator. Only 4 most sig. bits are type code, but * X.224 class 0 specifies lower 4 bits to be 0 anyway. */
NTSTATUS MCSIcaRawInput( void *pTSWd, PINBUF pInBuf, BYTE *pBuf, ULONG BytesLeft) { BYTE *pBuffer; Domain *pDomain; BOOLEAN bUsingReassemblyBuf, bMCSRecognizedFrame; unsigned NBytesConsumed_MCS, Diff, X224TPDUType, PacketLength;
// We assume we will not use InBufs.
ASSERT(pInBuf == NULL); ASSERT(pBuf != NULL); ASSERT(BytesLeft != 0);
// We actually receive a pointer to WD instance data. The first element in
// that data is a DomainHandle, i.e. a pointer to a Domain.
pDomain = *((Domain **)pTSWd);
// Increment protocol counters.
pDomain->pStat->Input.WdBytes += BytesLeft; pDomain->pStat->Input.WdFrames++;
#ifdef DUMP_RAW_INPUT
DbgPrint("Received raw input, len=%d\n", BytesLeft); if (pDomain->Ptrs[0] == NULL) pDomain->Ptrs[0] = pDomain->FooBuf; memcpy(pDomain->Ptrs[pDomain->NumPtrs], pBuf, BytesLeft); pDomain->NumPtrs++;
pDomain->Ptrs[pDomain->NumPtrs] = (BYTE *)pDomain->Ptrs[pDomain->NumPtrs - 1] + BytesLeft; #endif
/*
* Check for previous data in reassembly buffer. Prepare whole X.224 * packet for decode loop. */ if (pDomain->StackClass != Stack_Shadow) { if (pDomain->StoredDataLength == 0) { // We'll handle setup of this case just inside the decode loop,
// triggered by this value being FALSE.
bUsingReassemblyBuf = FALSE; } else { ASSERT(pDomain->pReassembleData != NULL); if (pDomain->PacketDataLength == IncompleteHeader) { ASSERT(pDomain->pReassembleData == pDomain->PacketBuf); // We did not have enough of a header last time to
// determine the packet size. Try to reassemble enough of one
// to get the size. We only need the first 4 bytes to get
// the size from the X.224 RFC1006 header, or 2-3 bytes
// for the fastpath header. We know the packet type based
// on the first byte, and must have received at least
// one byte in the last round.
if ((BytesLeft + pDomain->StoredDataLength) < pDomain->PacketHeaderLength) { // Copy what little we got and return -- we still don't
// have a header.
memcpy(pDomain->pReassembleData + pDomain->StoredDataLength, pBuf, BytesLeft); pDomain->StoredDataLength += BytesLeft; return STATUS_SUCCESS; } if (pDomain->PacketHeaderLength < pDomain->StoredDataLength) { pBuffer = pDomain->pReassembleData; goto X224BadPktType; } memcpy(pDomain->pReassembleData + pDomain->StoredDataLength, pBuf, pDomain->PacketHeaderLength - pDomain->StoredDataLength); BytesLeft -= (pDomain->PacketHeaderLength - pDomain->StoredDataLength); pBuf += (pDomain->PacketHeaderLength - pDomain->StoredDataLength); pDomain->StoredDataLength = pDomain->PacketHeaderLength; // Get the size.
if (pDomain->bCurrentPacketFastPath) { // Total packet length is in the second and, possibly,
// third bytes.of the packet. Format is similar to
// ASN.1 PER - high bit of first byte set means length is
// contained in the low 7 bits of current byte as the
// most significant bits, plus the 8 bits of the
// next byte for a total size of 15 bits. Otherwise,
// the packet size is contained within the low 7 bits
// of the second byte only.
if (!(pDomain->pReassembleData[1] & 0x80)) { pDomain->PacketDataLength = pDomain->pReassembleData[1]; } else { // We need a third byte. We don't assemble it the
// first time around to keep from corrupting the
// stream if we received a 1-byte size with no
// contents. Most often we will be receiving
// a 1-byte size anyway, so this code is little
// executed.
pDomain->PacketHeaderLength = 3; if (BytesLeft) { pDomain->pReassembleData[2] = *pBuf; pDomain->PacketDataLength = (pDomain->pReassembleData[1] & 0x7F) << 8 | *pBuf; BytesLeft--; pBuf++; } else { // No data left, try again later.
// IncompleteHeader is already in PacketDataLength.
return STATUS_SUCCESS; } } } else { // X.224 packet, length is in third and fourth bytes.
pDomain->PacketDataLength = (pDomain->pReassembleData[2] << 8) + pDomain->pReassembleData[3]; }
// Dynamically allocate a buffer if size is too big.
if (pDomain->PacketDataLength > pDomain->ReceiveBufSize) { TraceOut1(pDomain->pContext, "MCSIcaRawInput(): " "Allocating large [%ld] X.224 reassembly buf (path1)!", pDomain->PacketDataLength); pDomain->pReassembleData = ExAllocatePoolWithTag(PagedPool, pDomain->PacketDataLength + INPUT_BUFFER_BIAS, MCS_POOL_TAG); if (pDomain->pReassembleData != NULL) { // Copy the assembled header.
memcpy(pDomain->pReassembleData, pDomain->PacketBuf, pDomain->PacketHeaderLength); } else { // We are trying to parse the beginning of a net frame
// and have run out of memory. Set to read from the
// RFC1006 header if we are called again.
ErrOut(pDomain->pContext, "MCSIcaRawInput(): " "Failed to alloc large X.224 reassembly buf"); pDomain->pReassembleData = pDomain->PacketBuf; return STATUS_NO_MEMORY; } } } if ((pDomain->StoredDataLength + BytesLeft) < pDomain->PacketDataLength) { // We still don't have enough data. Copy what we have
// and return.
memcpy(pDomain->pReassembleData + pDomain->StoredDataLength, pBuf, BytesLeft); pDomain->StoredDataLength += BytesLeft; return STATUS_SUCCESS; } // We have at least enough data for this packet. Only copy
// up to the end of this particular packet. We'll handle
// any later data below.
if (pDomain->StoredDataLength > pDomain->PacketDataLength) { // We received a bad packet. Get out of here.
pBuffer = pDomain->pReassembleData; goto X224BadPktType; }
Diff = pDomain->PacketDataLength - pDomain->StoredDataLength; memcpy(pDomain->pReassembleData + pDomain->StoredDataLength, pBuf, Diff); pBuf += Diff; BytesLeft -= Diff; pDomain->StoredDataLength = pDomain->PacketDataLength; // Set decode data.
pBuffer = pDomain->pReassembleData; PacketLength = pDomain->PacketDataLength; // This will prevent us from doing the default input-buffer setup
// below.
bUsingReassemblyBuf = TRUE; }
/*
* Main decode loop. * Loops as long as there are complete X.224 packets to decode. */ for (;;) { /*
* Handle the general case of data being used directly from the * inbound data buffer. */ if (!bUsingReassemblyBuf) { // We must have at least one byte. Determine the packet type.
if ((pBuf[0] & TS_INPUT_FASTPATH_ACTION_MASK) == TS_INPUT_FASTPATH_ACTION_FASTPATH) { // Fast-path packet (low 2 bits = 00). Set up the minimum
// header length.
pDomain->PacketHeaderLength = 2; pDomain->bCurrentPacketFastPath = TRUE; } else if ((pBuf[0] & TS_INPUT_FASTPATH_ACTION_MASK) == TS_INPUT_FASTPATH_ACTION_X224) { // X.224. Use 4-byte fixed header length.
pDomain->PacketHeaderLength = RFC1006HeaderSize; pDomain->bCurrentPacketFastPath = FALSE; } else { // Bad low bits of first byte.
pBuffer = pBuf; goto X224BadPktType; }
// Check we have enough for the minimum header.
if (BytesLeft >= pDomain->PacketHeaderLength) { // Get the size.
if (pDomain->bCurrentPacketFastPath) { // Total packet length is in the second and, possibly,
// third bytes.of the packet. Format is similar to
// ASN.1 PER - high bit of first byte set means length is
// contained in the low 7 bits of current byte as the
// most significant bits, plus the 8 bits of the
// next byte for a total size of 15 bits. Otherwise,
// the packet size is contained within the low 7 bits
// of the second byte only.
if (!(pBuf[1] & 0x80)) { PacketLength = pBuf[1]; } else { // We need a third byte. We don't assemble it the
// first time around to keep from corrupting the
// stream if we received a 1-byte size with no
// contents. Most often we will be receiving
// a 1-byte size anyway, so this code is little
// executed.
pDomain->PacketHeaderLength = 3; if (BytesLeft >= 3) { PacketLength = (pBuf[1] & 0x7F) << 8 | pBuf[2]; } else { // We don't have enough for the minimum size
// header, store the little bit we do have.
pDomain->pReassembleData = pDomain->PacketBuf; pDomain->StoredDataLength = BytesLeft; pDomain->PacketDataLength = IncompleteHeader; pDomain->pReassembleData[0] = *pBuf; pDomain->pReassembleData[1] = pBuf[1]; break; } } } else { // Get the X.224 size from the third and fourth bytes.
PacketLength = (pBuf[2] << 8) + pBuf[3]; } } else { // We don't have enough for the minimum size header, store
// the little bit we do have.
pDomain->pReassembleData = pDomain->PacketBuf; pDomain->StoredDataLength = BytesLeft; pDomain->PacketDataLength = IncompleteHeader; memcpy(pDomain->pReassembleData, pBuf, BytesLeft); break; } // Make sure we have a whole packet.
if (PacketLength <= BytesLeft) { // Set decode data.
pBuffer = (BYTE *)pBuf;
// Skip the bytes we're about to consume.
pBuf += PacketLength; BytesLeft -= PacketLength; } else { // We don't have a whole packet, store what we do have
// and return.
pDomain->PacketDataLength = PacketLength; pDomain->StoredDataLength = BytesLeft; if (PacketLength <= pDomain->ReceiveBufSize) { pDomain->pReassembleData = pDomain->PacketBuf; } else { // Size is too big for the standard buffer, alloc one.
TraceOut1(pDomain->pContext, "MCSIcaRawInput(): " "Allocating large [%ld] X.224 reassembly buf (path2)!", pDomain->PacketDataLength); pDomain->pReassembleData = ExAllocatePoolWithTag( PagedPool, pDomain->PacketDataLength + INPUT_BUFFER_BIAS, MCS_POOL_TAG);
if (pDomain->pReassembleData == NULL) { // We failed to allocate, and we're in the middle of
// an X.224 frame. Store no bytes, and return an
// error to the transport.
ErrOut(pDomain->pContext, "MCSIcaRawInput(): " "Failed to alloc large X.224 reassembly buf"); pDomain->PacketDataLength = 0; return STATUS_NO_MEMORY; } } memcpy(pDomain->pReassembleData, pBuf, BytesLeft); break; } } /*
* Time to decode. Different handling for fast-path vs. X.224. */ if (pDomain->bCurrentPacketFastPath) { // Verify that the sent size covers at least the header.
if (PacketLength > pDomain->PacketHeaderLength) { // Let SM decrypt if need be, and pass to IM.
SM_DecodeFastPathInput(pDomain->pSMData, pBuffer + pDomain->PacketHeaderLength, PacketLength - pDomain->PacketHeaderLength, pBuffer[0] & TS_INPUT_FASTPATH_ENCRYPTED, (pBuffer[0] & TS_INPUT_FASTPATH_NUMEVENTS_MASK) >> 2, pBuffer[0] & TS_INPUT_FASTPATH_SECURE_CHECKSUM); goto PostDecode; } else { goto X224BadPktType; } }
// X.224.
//
// Verify all TPKT data up through the TPDU type code. This code
// is performance path, so segregate the error handling code outside
// the main paths.
if (pBuffer[0] == 0x03 && pBuffer[1] == 0x00 && PacketLength > RFC1006HeaderSize) { // Decode the X.224 PDU type contained in the 6th byte.
X224TPDUType = pBuffer[5]; // Most oft-used case is data.
// Further bytes in data:
// Byte Contents
// ---- --------
// 6 EOT flag -- 0x80 if end of sequence, 0x00 if not
// 7+ Start of user data bytes
if (X224TPDUType == X224_Data) { if (pDomain->State != State_Unconnected && pDomain->State != State_Disconnected && PacketLength > X224_DataHeaderSize && pBuffer[4] == 0x02 && pBuffer[6] == X224_EOT) { bMCSRecognizedFrame = RecognizeMCSFrame(pDomain, pBuffer + X224_DataHeaderSize, PacketLength - X224_DataHeaderSize, &NBytesConsumed_MCS); if (bMCSRecognizedFrame && (NBytesConsumed_MCS >= (PacketLength - X224_DataHeaderSize))) { // TraceOut2(pDomain->pContext, "MCS accepted %d "
// "bytes (Domain %X)", NBytesConsumed_MCS,
// pDomain);
} else { goto MCSRecognizeErr; } } else { goto DataPktProtocolErr; } } else { // Control-type PDUs, not a perf path.
switch (X224TPDUType) { case X224_ConnectionReq: // Noncritical path, throw to another function to handle.
if (HandleX224ConnectReq(pDomain, pBuffer, PacketLength) != STATUS_SUCCESS) goto ReturnErr; break; case X224_Disconnect: // Noncritical path, throw to another function to handle.
if (HandleX224Disconnect(pDomain, pBuffer, PacketLength) != STATUS_SUCCESS) goto ReturnErr; break; default: ErrOut1(pDomain->pContext, "Unsupported X.224 TPDU type %d received", X224TPDUType); goto X224BadPktType; } } } else { goto RFC1006ProtocolErr; }
PostDecode: // Free dynamic reassembly buf if allocated.
if (bUsingReassemblyBuf && pDomain->pReassembleData != pDomain->PacketBuf && NULL != pDomain->pReassembleData) { ExFreePool(pDomain->pReassembleData); pDomain->pReassembleData = NULL; }
// Force next loop iteration to use the PDU input buffer.
bUsingReassemblyBuf = FALSE; pDomain->StoredDataLength = 0; // If we consumed exactly what came in on the wire, we're done.
if (BytesLeft == 0) { pDomain->StoredDataLength = 0; break; } } }
// This is shadow stack, so deliver the data the the requested channel
else { MCSError MCSErr; MCSErr = DeliverShadowData(pDomain, pBuf, BytesLeft, pDomain->shadowChannel); if (MCSErr == MCS_NO_ERROR) return STATUS_SUCCESS; else return STATUS_RDP_PROTOCOL_ERROR; }
return STATUS_SUCCESS;
/*
* Protocol error handling code, segregated for performance. */
X224BadPktType: MCSProtocolErrorEvent(pDomain->pContext, pDomain->pStat, Log_X224_UnknownPacketType, pBuffer, 7); goto ReturnErr;
MCSRecognizeErr: if (!bMCSRecognizedFrame) { //pTSWd->hDomainKernel might be cleaned by WD_Close
if (*((Domain **)pTSWd)) { ErrOut(pDomain->pContext, "MCSIcaRawInput(): X.224 data " "packet contains an incomplete MCS PDU!");
if (pDomain->bCanSendData) { MCSProtocolErrorEvent(pDomain->pContext, pDomain->pStat, Log_X224_DataIncompleteMCSPacketNotSupported, pBuffer, PacketLength); } } else goto MCSQuit; }
if (NBytesConsumed_MCS < (PacketLength - X224_DataHeaderSize)) { ErrOut1(pDomain->pContext, "MCS did not consume %d bytes " "in X.224 data TPKT", (PacketLength - X224_DataHeaderSize - NBytesConsumed_MCS)); MCSProtocolErrorEvent(pDomain->pContext, pDomain->pStat, Log_X224_DataMultipleMCSPDUsNotSupported, pBuffer, PacketLength); goto ReturnErr; // MCS FUTURE: Implement parsing more than 1 MCS PDU per
// X.224 TPKT.
}
DataPktProtocolErr: if (*((Domain **)pTSWd) == NULL) { goto MCSQuit; }
if (pDomain->State == State_Unconnected) { ErrOut(pDomain->pContext, "X.224 Data received before X.224 " "Connect."); MCSProtocolErrorEvent(pDomain->pContext, pDomain->pStat, Log_X224_ReceivedDataBeforeConnected, pBuffer, PacketLength); goto ReturnErr; }
if (pDomain->State == State_Disconnected) { if (!pDomain->bEndConnectionPacketReceived) { TraceOut(pDomain->pContext, "X.224 Data received after X.224 Disconnect or " "DPum"); goto ReturnErr; } else { TraceOut(pDomain->pContext, "X.224 Data received after " "local disconnect, ignoring"); goto ReturnErr; } } // Possible denial-of-service attack, malformed or null packet.
if (PacketLength <= X224_DataHeaderSize) { ErrOut(pDomain->pContext, "X224Recognize(): Data header len " "wrong or null packet"); MCSProtocolErrorEvent(pDomain->pContext, pDomain->pStat, Log_X224_PacketLengthLessThanHeader, pBuffer, 4); goto ReturnErr; }
// TPDU length indicator should be 2 bytes.
if (pBuffer[4] != 0x02) { ErrOut(pDomain->pContext, "MCSIcaRawInput(): X.224 data " "packet contains length indicator not set to 2"); MCSProtocolErrorEvent(pDomain->pContext, pDomain->pStat, Log_X224_DataLenIndNotRequiredSize, pBuffer, 5); goto ReturnErr; }
// We don't handle fragmented X.224 packets.
if (pBuffer[6] != X224_EOT) { ErrOut(pDomain->pContext, "MCSIcaRawInput(): X.224 data " "packet does not have EOT bit set, not supported"); MCSProtocolErrorEvent(pDomain->pContext, pDomain->pStat, Log_X224_DataFalseEOTNotSupported, pBuffer, 7); goto ReturnErr; }
RFC1006ProtocolErr: if (pBuffer[0] != 0x03 || pBuffer[1] != 0x00) { ErrOut1(pDomain->pContext, "X.224 RFC1006 version not correct, " "skipping %d bytes", PacketLength); MCSProtocolErrorEvent(pDomain->pContext, pDomain->pStat, Log_X224_RFC1006HeaderVersionNotCorrect, pBuffer, 2); goto ReturnErr; }
// Null TPKTs.
if (PacketLength <= RFC1006HeaderSize) { ErrOut(pDomain->pContext, "X224Recognize(): Header len " "given is <= 4 (RFC header only)"); MCSProtocolErrorEvent(pDomain->pContext, pDomain->pStat, Log_X224_PacketLengthLessThanHeader, pBuffer, RFC1006HeaderSize); goto ReturnErr; }
ReturnErr: // Void any held packet data. The stream is considered corrupted now.
pDomain->StoredDataLength = 0; MCSQuit: return STATUS_RDP_PROTOCOL_ERROR; }
/*
* Decodes MCS data. Assumes that X.224 headers have already been interpreted * such that pBuffer points to the start of the MCS data. Returns FALSE if * the frame data was too short. */
BOOLEAN __fastcall RecognizeMCSFrame( PDomain pDomain, BYTE *pBuffer, int BytesLeft, unsigned *pNBytesConsumed) { int Result, PDUType; unsigned NBytesConsumed;
*pNBytesConsumed = 0;
if (BytesLeft >= 1) { if (*pBuffer != CONNECT_PDU) { // Domain PDUs include Data PDUs and are a perf path.
// This must be a domain PDU. Domain PDU enumeration code is
// stored in the high 6 bits of the first byte.
PDUType = *pBuffer >> 2;
// Special-case the almost-always data case.
if (PDUType == MCS_SEND_DATA_REQUEST_ENUM) { return HandleAllSendDataPDUs(pDomain, pBuffer, BytesLeft, pNBytesConsumed); } else if (PDUType <= MaxDomainPDU) { // Domain PDUs are in the range 0..42, so simply index
// into table and call handler. Note that we cannot skip
// any bytes when passing to handler, since the last 2
// bits of the initial byte can be used as information
// by ASN.1 PER encoding.
if (DomainPDUTable[PDUType].HandlePDUFunc != NULL) return DomainPDUTable[PDUType].HandlePDUFunc(pDomain, pBuffer, BytesLeft, pNBytesConsumed); else goto DomainPDURangeErr; } else { goto DomainPDURangeErr; } } else { // Not a performance path, PDUs on this path are control PDUs
// used at the beginning of a connection sequence.
// The first byte on a connect PDU is 0x7F, so that the actual
// PDU code is in the second byte.
if (BytesLeft < 2) return FALSE;
PDUType = pBuffer[1]; if (PDUType >= MinConnectPDU && PDUType <= MaxConnectPDU) { // Connect PDUs are in the range 101..104, so normalize to zero
// and call from table. Note that we can skip 1st byte because
// it has been completely claimed. 2nd byte is needed to unpack
// the PDU size.
PDUType = pBuffer[1] - MinConnectPDU; if (ConnectPDUTable[PDUType].HandlePDUFunc != NULL) { if (ConnectPDUTable[PDUType].HandlePDUFunc(pDomain, pBuffer + 1, BytesLeft - 1, pNBytesConsumed)) { (*pNBytesConsumed)++; // Add in the CONNECT_PDU byte.
return TRUE; } else { return FALSE; } } else { goto ConnectPDURangeErr; } } else { goto ConnectPDURangeErr; } }
return TRUE; } else { return FALSE; }
/*
* Protocol error handling code. */
DomainPDURangeErr: if (PDUType > MaxDomainPDU) { ErrOut1(pDomain->pContext, "RecognizeMCSFrame(): Received bad " "domain PDU number %d", PDUType); MCSProtocolErrorEvent(pDomain->pContext, pDomain->pStat, Log_MCS_BadDomainPDUType, pBuffer, BytesLeft); goto ReturnErr; }
ConnectPDURangeErr: if (PDUType < MinConnectPDU || PDUType > MaxConnectPDU) { ErrOut1(pDomain->pContext, "RecognizeMCSFrame(): Received bad " "connect PDU number %d", PDUType); MCSProtocolErrorEvent(pDomain->pContext, pDomain->pStat, Log_MCS_BadConnectPDUType, pBuffer, BytesLeft); goto ReturnErr; }
ReturnErr: // Skip everything we received.
*pNBytesConsumed = BytesLeft; return TRUE; }
/*
* Called by MCSICARawInput during shadow sessions to deliver shadow data to * any registered user attachements. */ MCSError __fastcall DeliverShadowData( PDomain pDomain, BYTE *Frame, unsigned DataLength, ChannelID shadowChannel) { MCSChannel *pMCSChannel; unsigned CurUserID; UserAttachment *pUA;
TraceOut(pDomain->pContext, "MCSDeliverShadowData(): entry");
// Find channel in channel list.
if (!SListGetByKey(&pDomain->ChannelList, shadowChannel, &pMCSChannel)) { // Ignore sends on missing channels. This means that no one
// has joined the channel. Give a warning only.
WarnOut1(pDomain->pContext, "Shadow ChannelID %d PDU does not exist", shadowChannel);
return MCS_NO_SUCH_CHANNEL; }
// Send indication to all local attachments.
SListResetIteration(&pMCSChannel->UserList); while (SListIterate(&pMCSChannel->UserList, (UINT_PTR *)&CurUserID, &pUA)) if (pUA->bLocal) (pUA->SDCallback)( Frame, // pData
DataLength, pUA->UserDefined, // UserDefined
pUA, // hUser
FALSE, // bUniform
pMCSChannel, // hChannel
MCS_TOP_PRIORITY, 1004, // SenderID
SEGMENTATION_BEGIN | SEGMENTATION_END); // Segmentation
return MCS_NO_ERROR; }
|