diff --git a/SOURCES/Add-checks-for-bitmap-and-glyph-width-heigth-values.patch b/SOURCES/Add-checks-for-bitmap-and-glyph-width-heigth-values.patch new file mode 100644 index 0000000..cde5535 --- /dev/null +++ b/SOURCES/Add-checks-for-bitmap-and-glyph-width-heigth-values.patch @@ -0,0 +1,146 @@ +From 4a4ecc32b160e93ee1e37e5617a0197e2d022643 Mon Sep 17 00:00:00 2001 +From: Armin Novak +Date: Thu, 4 Nov 2021 11:07:41 +0100 +Subject: [PATCH] Add checks for bitmap and glyph width/heigth values + +CVE-2021-41160 + +https://github.com/FreeRDP/FreeRDP/pull/7349 + +Signed-off-by: Felipe Borges +--- + libfreerdp/core/orders.c | 14 ++++++++++++ + libfreerdp/core/surface.c | 45 +++++++++++++++++++++++++++++++++++++++ + libfreerdp/core/update.c | 7 ++++++ + 3 files changed, 66 insertions(+) + +diff --git a/libfreerdp/core/orders.c b/libfreerdp/core/orders.c +index 74870fae6..44d23a61a 100644 +--- a/libfreerdp/core/orders.c ++++ b/libfreerdp/core/orders.c +@@ -1848,6 +1848,13 @@ static BOOL update_read_fast_glyph_order(wStream* s, const ORDER_INFO* orderInfo + new_cb = ((glyph->cx + 7) / 8) * glyph->cy; + new_cb += ((new_cb % 4) > 0) ? 4 - (new_cb % 4) : 0; + ++ if ((glyph->cx == 0) || (glyph->cy == 0)) ++ { ++ WLog_ERR(TAG, "GLYPH_DATA_V2::cx=%" PRIu32 ", GLYPH_DATA_V2::cy=%" PRIu32, ++ glyph->cx, glyph->cy); ++ return FALSE; ++ } ++ + if (fastGlyph->cbData < new_cb) + return FALSE; + +@@ -2825,6 +2832,13 @@ update_read_create_offscreen_bitmap_order(wStream* s, + Stream_Read_UINT16(s, create_offscreen_bitmap->cy); /* cy (2 bytes) */ + deleteList = &(create_offscreen_bitmap->deleteList); + ++ if ((create_offscreen_bitmap->cx == 0) || (create_offscreen_bitmap->cy == 0)) ++ { ++ WLog_ERR(TAG, "Invalid OFFSCREEN_DELETE_LIST: cx=%" PRIu16 ", cy=%" PRIu16, ++ create_offscreen_bitmap->cx, create_offscreen_bitmap->cy); ++ return FALSE; ++ } ++ + if (deleteListPresent) + { + UINT32 i; +diff --git a/libfreerdp/core/surface.c b/libfreerdp/core/surface.c +index d5c709885..ca89d230c 100644 +--- a/libfreerdp/core/surface.c ++++ b/libfreerdp/core/surface.c +@@ -21,6 +21,8 @@ + #include "config.h" + #endif + ++#include ++ + #include + #include + +@@ -62,6 +64,13 @@ static BOOL update_recv_surfcmd_bitmap_ex(wStream* s, TS_BITMAP_DATA_EX* bmp) + Stream_Read_UINT16(s, bmp->height); + Stream_Read_UINT32(s, bmp->bitmapDataLength); + ++ if ((bmp->width == 0) || (bmp->height == 0)) ++ { ++ WLog_ERR(TAG, "invalid size value width=%" PRIu16 ", height=%" PRIu16, bmp->width, ++ bmp->height); ++ return FALSE; ++ } ++ + if ((bmp->bpp < 1) || (bmp->bpp > 32)) + { + WLog_ERR(TAG, "invalid bpp value %" PRIu32 "", bmp->bpp); +@@ -85,6 +94,39 @@ static BOOL update_recv_surfcmd_bitmap_ex(wStream* s, TS_BITMAP_DATA_EX* bmp) + return TRUE; + } + ++static BOOL update_recv_surfcmd_is_rect_valid(const rdpContext* context, ++ const SURFACE_BITS_COMMAND* cmd) ++{ ++ assert(context); ++ assert(context->settings); ++ assert(cmd); ++ ++ /* We need a rectangle with left/top being smaller than right/bottom. ++ * Also do not allow empty rectangles. */ ++ if ((cmd->destTop >= cmd->destBottom) || (cmd->destLeft >= cmd->destRight)) ++ { ++ WLog_WARN(TAG, ++ "Empty surface bits command rectangle: %" PRIu16 "x%" PRIu16 "-%" PRIu16 ++ "x%" PRIu16, ++ cmd->destLeft, cmd->destTop, cmd->destRight, cmd->destBottom); ++ return FALSE; ++ } ++ ++ /* The rectangle needs to fit into our session size */ ++ if ((cmd->destRight > context->settings->DesktopWidth) || ++ (cmd->destBottom > context->settings->DesktopHeight)) ++ { ++ WLog_WARN(TAG, ++ "Invalid surface bits command rectangle: %" PRIu16 "x%" PRIu16 "-%" PRIu16 ++ "x%" PRIu16 " does not fit %" PRIu32 "x%" PRIu32, ++ cmd->destLeft, cmd->destTop, cmd->destRight, cmd->destBottom, ++ context->settings->DesktopWidth, context->settings->DesktopHeight); ++ return FALSE; ++ } ++ ++ return TRUE; ++} ++ + static BOOL update_recv_surfcmd_surface_bits(rdpUpdate* update, wStream* s, UINT16 cmdType) + { + SURFACE_BITS_COMMAND cmd = { 0 }; +@@ -98,6 +140,9 @@ static BOOL update_recv_surfcmd_surface_bits(rdpUpdate* update, wStream* s, UINT + Stream_Read_UINT16(s, cmd.destRight); + Stream_Read_UINT16(s, cmd.destBottom); + ++ if (!update_recv_surfcmd_is_rect_valid(update->context, &cmd)) ++ goto fail; ++ + if (!update_recv_surfcmd_bitmap_ex(s, &cmd.bmp)) + goto fail; + +diff --git a/libfreerdp/core/update.c b/libfreerdp/core/update.c +index ebb82fc2d..e137c3de8 100644 +--- a/libfreerdp/core/update.c ++++ b/libfreerdp/core/update.c +@@ -99,6 +99,13 @@ static BOOL update_read_bitmap_data(rdpUpdate* update, wStream* s, BITMAP_DATA* + Stream_Read_UINT16(s, bitmapData->flags); + Stream_Read_UINT16(s, bitmapData->bitmapLength); + ++ if ((bitmapData->width == 0) || (bitmapData->height == 0)) ++ { ++ WLog_ERR(TAG, "Invalid BITMAP_DATA: width=%" PRIu16 ", height=%" PRIu16, bitmapData->width, ++ bitmapData->height); ++ return FALSE; ++ } ++ + if (bitmapData->flags & BITMAP_COMPRESSION) + { + if (!(bitmapData->flags & NO_BITMAP_COMPRESSION_HDR)) +-- +2.32.0 + diff --git a/SOURCES/Implement-missing-TSG-debug-functions.patch b/SOURCES/Implement-missing-TSG-debug-functions.patch new file mode 100644 index 0000000..45f8c0a --- /dev/null +++ b/SOURCES/Implement-missing-TSG-debug-functions.patch @@ -0,0 +1,433 @@ +From ea20ba13ec764b30d6642cd570929e71e97d00f1 Mon Sep 17 00:00:00 2001 +From: akallabeth +Date: Tue, 12 Oct 2021 13:33:41 +0200 +Subject: [PATCH] Implemented missing TSG debug functions + +(cherry picked from commit c06c4638061b0305a269dbb8f7c87009cf886f02) +--- + libfreerdp/core/gateway/tsg.c | 330 ++++++++++++++++++++++++++++++++-- + 1 file changed, 313 insertions(+), 17 deletions(-) + +diff --git a/libfreerdp/core/gateway/tsg.c b/libfreerdp/core/gateway/tsg.c +index 1626b91..3376fb0 100644 +--- a/libfreerdp/core/gateway/tsg.c ++++ b/libfreerdp/core/gateway/tsg.c +@@ -24,6 +24,7 @@ + #include "config.h" + #endif + ++#include + #include + #include + #include +@@ -39,13 +40,14 @@ + + #define TAG FREERDP_TAG("core.gateway.tsg") + ++#define TSG_CAPABILITY_TYPE_NAP 0x00000001 ++ + #define TSG_PACKET_TYPE_HEADER 0x00004844 + #define TSG_PACKET_TYPE_VERSIONCAPS 0x00005643 + #define TSG_PACKET_TYPE_QUARCONFIGREQUEST 0x00005143 + #define TSG_PACKET_TYPE_QUARREQUEST 0x00005152 + #define TSG_PACKET_TYPE_RESPONSE 0x00005052 + #define TSG_PACKET_TYPE_QUARENC_RESPONSE 0x00004552 +-#define TSG_CAPABILITY_TYPE_NAP 0x00000001 + #define TSG_PACKET_TYPE_CAPS_RESPONSE 0x00004350 + #define TSG_PACKET_TYPE_MSGREQUEST_PACKET 0x00004752 + #define TSG_PACKET_TYPE_MESSAGE_PACKET 0x00004750 +@@ -308,16 +310,35 @@ static BOOL tsg_print(char** buffer, size_t* len, const char* fmt, ...) + static BOOL tsg_packet_header_to_string(char** buffer, size_t* length, + const TSG_PACKET_HEADER* header) + { ++ WINPR_ASSERT(buffer); ++ WINPR_ASSERT(length); ++ WINPR_ASSERT(header); ++ + return tsg_print(buffer, length, + "header { ComponentId=0x%04" PRIx16 ", PacketId=0x%04" PRIx16 " }", + header->ComponentId, header->PacketId); + } + ++static BOOL tsg_type_capability_nap_to_string(char** buffer, size_t* length, ++ const TSG_CAPABILITY_NAP* cur) ++{ ++ WINPR_ASSERT(buffer); ++ WINPR_ASSERT(length); ++ WINPR_ASSERT(cur); ++ ++ return tsg_print(buffer, length, "%s { capabilities=0x%08" PRIx32 " }", ++ tsg_packet_id_to_string(TSG_CAPABILITY_TYPE_NAP), cur->capabilities); ++} ++ + static BOOL tsg_packet_capabilities_to_string(char** buffer, size_t* length, + const TSG_PACKET_CAPABILITIES* caps, UINT32 numCaps) + { + UINT32 x; + ++ WINPR_ASSERT(buffer); ++ WINPR_ASSERT(length); ++ WINPR_ASSERT(caps); ++ + if (!tsg_print(buffer, length, "capabilities { ")) + return FALSE; + +@@ -327,9 +348,7 @@ static BOOL tsg_packet_capabilities_to_string(char** buffer, size_t* length, + switch (cur->capabilityType) + { + case TSG_CAPABILITY_TYPE_NAP: +- if (!tsg_print(buffer, length, "%s { capabilities=0x%08" PRIx32 " }", +- tsg_packet_id_to_string(cur->capabilityType), +- cur->tsgPacket.tsgCapNap.capabilities)) ++ if (!tsg_type_capability_nap_to_string(buffer, length, &cur->tsgPacket.tsgCapNap)) + return FALSE; + break; + default: +@@ -344,6 +363,10 @@ static BOOL tsg_packet_capabilities_to_string(char** buffer, size_t* length, + static BOOL tsg_packet_versioncaps_to_string(char** buffer, size_t* length, + const TSG_PACKET_VERSIONCAPS* caps) + { ++ WINPR_ASSERT(buffer); ++ WINPR_ASSERT(length); ++ WINPR_ASSERT(caps); ++ + if (!tsg_print(buffer, length, "versioncaps { ")) + return FALSE; + if (!tsg_packet_header_to_string(buffer, length, &caps->tsgHeader)) +@@ -365,6 +388,277 @@ static BOOL tsg_packet_versioncaps_to_string(char** buffer, size_t* length, + return tsg_print(buffer, length, " }"); + } + ++static BOOL tsg_packet_quarconfigrequest_to_string(char** buffer, size_t* length, ++ const TSG_PACKET_QUARCONFIGREQUEST* caps) ++{ ++ WINPR_ASSERT(buffer); ++ WINPR_ASSERT(length); ++ WINPR_ASSERT(caps); ++ ++ if (!tsg_print(buffer, length, "quarconfigrequest { ")) ++ return FALSE; ++ ++ if (!tsg_print(buffer, length, " ")) ++ return FALSE; ++ ++ if (!tsg_print(buffer, length, " flags=0x%08" PRIx32, caps->flags)) ++ return FALSE; ++ ++ return tsg_print(buffer, length, " }"); ++} ++ ++static BOOL tsg_packet_quarrequest_to_string(char** buffer, size_t* length, ++ const TSG_PACKET_QUARREQUEST* caps) ++{ ++ BOOL rc = FALSE; ++ char* name = NULL; ++ char* strdata = NULL; ++ ++ WINPR_ASSERT(buffer); ++ WINPR_ASSERT(length); ++ WINPR_ASSERT(caps); ++ ++ if (!tsg_print(buffer, length, "quarrequest { ")) ++ return FALSE; ++ ++ if (!tsg_print(buffer, length, " ")) ++ return FALSE; ++ ++ if (caps->nameLength > 0) ++ { ++ if (ConvertFromUnicode(CP_UTF8, 0, caps->machineName, caps->nameLength, &name, 0, NULL, ++ NULL) < 0) ++ return FALSE; ++ } ++ ++ strdata = winpr_BinToHexString(caps->data, caps->dataLen, TRUE); ++ if (strdata || (caps->dataLen == 0)) ++ rc = tsg_print(buffer, length, ++ " flags=0x%08" PRIx32 ", machineName=%s [%" PRIu32 "], data[%" PRIu32 "]=%s", ++ caps->flags, name, caps->nameLength, caps->dataLen, strdata); ++ free(name); ++ free(strdata); ++ if (!rc) ++ return FALSE; ++ ++ return tsg_print(buffer, length, " }"); ++} ++ ++static const char* tsg_bool_to_string(BOOL val) ++{ ++ if (val) ++ return "true"; ++ return "false"; ++} ++ ++static const char* tsg_redirection_flags_to_string(char* buffer, size_t size, ++ const TSG_REDIRECTION_FLAGS* flags) ++{ ++ WINPR_ASSERT(buffer || (size == 0)); ++ WINPR_ASSERT(flags); ++ ++ _snprintf(buffer, size, ++ "enableAllRedirections=%s, disableAllRedirections=%s, driveRedirectionDisabled=%s, " ++ "printerRedirectionDisabled=%s, portRedirectionDisabled=%s, reserved=%s, " ++ "clipboardRedirectionDisabled=%s, pnpRedirectionDisabled=%s", ++ tsg_bool_to_string(flags->enableAllRedirections), ++ tsg_bool_to_string(flags->disableAllRedirections), ++ tsg_bool_to_string(flags->driveRedirectionDisabled), ++ tsg_bool_to_string(flags->printerRedirectionDisabled), ++ tsg_bool_to_string(flags->portRedirectionDisabled), ++ tsg_bool_to_string(flags->reserved), ++ tsg_bool_to_string(flags->clipboardRedirectionDisabled), ++ tsg_bool_to_string(flags->pnpRedirectionDisabled)); ++ return buffer; ++} ++ ++static BOOL tsg_packet_response_to_string(char** buffer, size_t* length, ++ const TSG_PACKET_RESPONSE* caps) ++{ ++ BOOL rc = FALSE; ++ char* strdata = NULL; ++ char tbuffer[8192] = { 0 }; ++ ++ WINPR_ASSERT(buffer); ++ WINPR_ASSERT(length); ++ WINPR_ASSERT(caps); ++ ++ if (!tsg_print(buffer, length, "response { ")) ++ return FALSE; ++ ++ if (!tsg_print(buffer, length, " ")) ++ return FALSE; ++ ++ strdata = winpr_BinToHexString(caps->responseData, caps->responseDataLen, TRUE); ++ if (strdata || (caps->responseDataLen == 0)) ++ rc = tsg_print( ++ buffer, length, ++ " flags=0x%08" PRIx32 ", reserved=0x%08" PRIx32 ", responseData[%" PRIu32 ++ "]=%s, redirectionFlags={ %s }", ++ caps->flags, caps->reserved, caps->responseDataLen, strdata, ++ tsg_redirection_flags_to_string(tbuffer, ARRAYSIZE(tbuffer), &caps->redirectionFlags)); ++ free(strdata); ++ if (!rc) ++ return FALSE; ++ ++ return tsg_print(buffer, length, " }"); ++} ++ ++static BOOL tsg_packet_quarenc_response_to_string(char** buffer, size_t* length, ++ const TSG_PACKET_QUARENC_RESPONSE* caps) ++{ ++ BOOL rc = FALSE; ++ char* strdata = NULL; ++ RPC_CSTR uuid; ++ char tbuffer[8192] = { 0 }; ++ size_t size = ARRAYSIZE(tbuffer); ++ char* ptbuffer = tbuffer; ++ ++ WINPR_ASSERT(buffer); ++ WINPR_ASSERT(length); ++ WINPR_ASSERT(caps); ++ ++ if (!tsg_print(buffer, length, "quarenc_response { ")) ++ return FALSE; ++ ++ if (!tsg_print(buffer, length, " ")) ++ return FALSE; ++ ++ if (caps->certChainLen > 0) ++ { ++ if (ConvertFromUnicode(CP_UTF8, 0, caps->certChainData, caps->certChainLen, &strdata, 0, ++ NULL, NULL) <= 0) ++ return FALSE; ++ } ++ ++ tsg_packet_versioncaps_to_string(&ptbuffer, &size, caps->versionCaps); ++ UuidToStringA(&caps->nonce, &uuid); ++ if (strdata || (caps->certChainLen == 0)) ++ rc = ++ tsg_print(buffer, length, ++ " flags=0x%08" PRIx32 ", certChain[%" PRIu32 "]=%s, nonce=%s, versionCaps=%s", ++ caps->flags, caps->certChainLen, strdata, uuid, tbuffer); ++ free(strdata); ++ free(uuid); ++ if (!rc) ++ return FALSE; ++ ++ return tsg_print(buffer, length, " }"); ++} ++ ++static BOOL tsg_packet_message_response_to_string(char** buffer, size_t* length, ++ const TSG_PACKET_MSG_RESPONSE* caps) ++{ ++ WINPR_ASSERT(buffer); ++ WINPR_ASSERT(length); ++ WINPR_ASSERT(caps); ++ ++ if (!tsg_print(buffer, length, "msg_response { ")) ++ return FALSE; ++ ++ if (!tsg_print(buffer, length, ++ " msgID=0x%08" PRIx32 ", msgType=0x%08" PRIx32 ", isMsgPresent=%" PRId32, ++ caps->msgID, caps->msgType, caps->isMsgPresent)) ++ return FALSE; ++ ++ return tsg_print(buffer, length, " }"); ++} ++ ++static BOOL tsg_packet_caps_response_to_string(char** buffer, size_t* length, ++ const TSG_PACKET_CAPS_RESPONSE* caps) ++{ ++ WINPR_ASSERT(buffer); ++ WINPR_ASSERT(length); ++ WINPR_ASSERT(caps); ++ ++ if (!tsg_print(buffer, length, "caps_response { ")) ++ return FALSE; ++ ++ if (!tsg_packet_quarenc_response_to_string(buffer, length, &caps->pktQuarEncResponse)) ++ return FALSE; ++ ++ if (!tsg_packet_message_response_to_string(buffer, length, &caps->pktConsentMessage)) ++ return FALSE; ++ ++ return tsg_print(buffer, length, " }"); ++} ++ ++static BOOL tsg_packet_message_request_to_string(char** buffer, size_t* length, ++ const TSG_PACKET_MSG_REQUEST* caps) ++{ ++ WINPR_ASSERT(buffer); ++ WINPR_ASSERT(length); ++ WINPR_ASSERT(caps); ++ ++ if (!tsg_print(buffer, length, "caps_message_request { ")) ++ return FALSE; ++ ++ if (!tsg_print(buffer, length, " maxMessagesPerBatch=%" PRIu32, caps->maxMessagesPerBatch)) ++ return FALSE; ++ ++ return tsg_print(buffer, length, " }"); ++} ++ ++static BOOL tsg_packet_auth_to_string(char** buffer, size_t* length, const TSG_PACKET_AUTH* caps) ++{ ++ BOOL rc = FALSE; ++ char* strdata = NULL; ++ WINPR_ASSERT(buffer); ++ WINPR_ASSERT(length); ++ WINPR_ASSERT(caps); ++ ++ if (!tsg_print(buffer, length, "caps_message_request { ")) ++ return FALSE; ++ ++ if (!tsg_packet_versioncaps_to_string(buffer, length, &caps->tsgVersionCaps)) ++ return FALSE; ++ ++ strdata = winpr_BinToHexString(caps->cookie, caps->cookieLen, TRUE); ++ if (strdata || (caps->cookieLen == 0)) ++ rc = tsg_print(buffer, length, " cookie[%" PRIu32 "]=%s", caps->cookieLen, strdata); ++ free(strdata); ++ if (!rc) ++ return FALSE; ++ ++ return tsg_print(buffer, length, " }"); ++} ++ ++static BOOL tsg_packet_reauth_to_string(char** buffer, size_t* length, ++ const TSG_PACKET_REAUTH* caps) ++{ ++ BOOL rc = FALSE; ++ WINPR_ASSERT(buffer); ++ WINPR_ASSERT(length); ++ WINPR_ASSERT(caps); ++ ++ if (!tsg_print(buffer, length, "caps_message_request { ")) ++ return FALSE; ++ ++ if (!tsg_print(buffer, length, " tunnelContext=0x%08" PRIx32 ", packetId=%s [0x%08" PRIx32 "]", ++ caps->tunnelContext, tsg_packet_id_to_string(caps->packetId), caps->packetId)) ++ return FALSE; ++ ++ switch (caps->packetId) ++ { ++ case TSG_PACKET_TYPE_VERSIONCAPS: ++ rc = tsg_packet_versioncaps_to_string(buffer, length, ++ caps->tsgInitialPacket.packetVersionCaps); ++ break; ++ case TSG_PACKET_TYPE_AUTH: ++ rc = tsg_packet_auth_to_string(buffer, length, caps->tsgInitialPacket.packetAuth); ++ break; ++ default: ++ rc = tsg_print(buffer, length, "TODO: Unhandled packet type %s [0x%08" PRIx32 "]", ++ tsg_packet_id_to_string(caps->packetId), caps->packetId); ++ break; ++ } ++ ++ if (!rc) ++ return FALSE; ++ ++ return tsg_print(buffer, length, " }"); ++} ++ + static const char* tsg_packet_to_string(const TSG_PACKET* packet) + { + size_t len = 8192; +@@ -387,43 +681,45 @@ static const char* tsg_packet_to_string(const TSG_PACKET* packet) + goto fail; + break; + case TSG_PACKET_TYPE_QUARCONFIGREQUEST: +- if (!tsg_print(&buffer, &len, "TODO")) ++ if (!tsg_packet_quarconfigrequest_to_string(&buffer, &len, ++ packet->tsgPacket.packetQuarConfigRequest)) + goto fail; + break; + case TSG_PACKET_TYPE_QUARREQUEST: +- if (!tsg_print(&buffer, &len, "TODO")) ++ if (!tsg_packet_quarrequest_to_string(&buffer, &len, ++ packet->tsgPacket.packetQuarRequest)) + goto fail; + break; + case TSG_PACKET_TYPE_RESPONSE: +- if (!tsg_print(&buffer, &len, "TODO")) ++ if (!tsg_packet_response_to_string(&buffer, &len, packet->tsgPacket.packetResponse)) + goto fail; + break; + case TSG_PACKET_TYPE_QUARENC_RESPONSE: +- if (!tsg_print(&buffer, &len, "TODO")) +- goto fail; +- break; +- case TSG_CAPABILITY_TYPE_NAP: +- if (!tsg_print(&buffer, &len, "TODO")) ++ if (!tsg_packet_quarenc_response_to_string(&buffer, &len, ++ packet->tsgPacket.packetQuarEncResponse)) + goto fail; + break; + case TSG_PACKET_TYPE_CAPS_RESPONSE: +- if (!tsg_print(&buffer, &len, "TODO")) ++ if (!tsg_packet_caps_response_to_string(&buffer, &len, ++ packet->tsgPacket.packetCapsResponse)) + goto fail; + break; + case TSG_PACKET_TYPE_MSGREQUEST_PACKET: +- if (!tsg_print(&buffer, &len, "TODO")) ++ if (!tsg_packet_message_request_to_string(&buffer, &len, ++ packet->tsgPacket.packetMsgRequest)) + goto fail; + break; + case TSG_PACKET_TYPE_MESSAGE_PACKET: +- if (!tsg_print(&buffer, &len, "TODO")) ++ if (!tsg_packet_message_response_to_string(&buffer, &len, ++ packet->tsgPacket.packetMsgResponse)) + goto fail; + break; + case TSG_PACKET_TYPE_AUTH: +- if (!tsg_print(&buffer, &len, "TODO")) ++ if (!tsg_packet_auth_to_string(&buffer, &len, packet->tsgPacket.packetAuth)) + goto fail; + break; + case TSG_PACKET_TYPE_REAUTH: +- if (!tsg_print(&buffer, &len, "TODO")) ++ if (!tsg_packet_reauth_to_string(&buffer, &len, packet->tsgPacket.packetReauth)) + goto fail; + break; + default: diff --git a/SOURCES/Refactored-RPC-gateway-parser.patch b/SOURCES/Refactored-RPC-gateway-parser.patch new file mode 100644 index 0000000..2c0bd90 --- /dev/null +++ b/SOURCES/Refactored-RPC-gateway-parser.patch @@ -0,0 +1,4950 @@ +From f0a0683fa6a3f696c4bc5ba88c128bc781c54895 Mon Sep 17 00:00:00 2001 +From: Armin Novak +Date: Mon, 11 Oct 2021 12:19:40 +0200 +Subject: [PATCH] Refactored RPC gateway parser + +Utilize wStream instead of custom binary parsing code, add proper +input validation. +Reported by Sunglin from the Knownsec 404 team & 0103 sec team + +(cherry picked from commit f0b44da67c09488178000725ff9f2729ccfdf9fe) +--- + libfreerdp/core/gateway/ncacn_http.c | 6 +- + libfreerdp/core/gateway/rdg.c | 77 +- + libfreerdp/core/gateway/rpc.c | 134 +- + libfreerdp/core/gateway/rpc.h | 18 +- + libfreerdp/core/gateway/rpc_bind.c | 253 +-- + libfreerdp/core/gateway/rpc_bind.h | 2 +- + libfreerdp/core/gateway/rpc_client.c | 265 +-- + libfreerdp/core/gateway/rpc_client.h | 3 +- + libfreerdp/core/gateway/rpc_fault.c | 48 +- + libfreerdp/core/gateway/rts.c | 2085 ++++++++++++++++++----- + libfreerdp/core/gateway/rts.h | 29 +- + libfreerdp/core/gateway/rts_signature.c | 127 +- + libfreerdp/core/gateway/rts_signature.h | 8 +- + libfreerdp/core/gateway/tsg.c | 305 ++-- + libfreerdp/core/surface.c | 8 +- + 15 files changed, 2375 insertions(+), 993 deletions(-) + +diff --git a/libfreerdp/core/gateway/ncacn_http.c b/libfreerdp/core/gateway/ncacn_http.c +index cffb378..f288a0f 100644 +--- a/libfreerdp/core/gateway/ncacn_http.c ++++ b/libfreerdp/core/gateway/ncacn_http.c +@@ -121,6 +121,7 @@ BOOL rpc_ncacn_http_recv_in_channel_response(RpcChannel* inChannel, HttpResponse + if (ntlmTokenData && ntlmTokenLength) + return ntlm_client_set_input_buffer(ntlm, FALSE, ntlmTokenData, ntlmTokenLength); + ++ free(ntlmTokenData); + return TRUE; + } + +@@ -274,5 +275,6 @@ BOOL rpc_ncacn_http_recv_out_channel_response(RpcChannel* outChannel, HttpRespon + if (ntlmTokenData && ntlmTokenLength) + return ntlm_client_set_input_buffer(ntlm, FALSE, ntlmTokenData, ntlmTokenLength); + ++ free(ntlmTokenData); + return TRUE; + } +diff --git a/libfreerdp/core/gateway/rdg.c b/libfreerdp/core/gateway/rdg.c +index 107e396..d333880 100644 +--- a/libfreerdp/core/gateway/rdg.c ++++ b/libfreerdp/core/gateway/rdg.c +@@ -1159,6 +1159,9 @@ static BOOL rdg_tunnel_connect(rdpRdg* rdg) + + if (!status) + { ++ assert(rdg); ++ assert(rdg->context); ++ assert(rdg->context->rdp); + rdg->context->rdp->transport->layer = TRANSPORT_LAYER_CLOSED; + return FALSE; + } +@@ -1190,6 +1193,9 @@ BOOL rdg_connect(rdpRdg* rdg, DWORD timeout, BOOL* rpcFallback) + + if (!status) + { ++ assert(rdg); ++ assert(rdg->context); ++ assert(rdg->context->rdp); + rdg->context->rdp->transport->layer = TRANSPORT_LAYER_CLOSED; + return FALSE; + } +@@ -1535,10 +1541,10 @@ static int rdg_bio_gets(BIO* bio, char* str, int size) + return -2; + } + +-static long rdg_bio_ctrl(BIO* bio, int cmd, long arg1, void* arg2) ++static long rdg_bio_ctrl(BIO* in_bio, int cmd, long arg1, void* arg2) + { + long status = -1; +- rdpRdg* rdg = (rdpRdg*)BIO_get_data(bio); ++ rdpRdg* rdg = (rdpRdg*)BIO_get_data(in_bio); + rdpTls* tlsOut = rdg->tlsOut; + rdpTls* tlsIn = rdg->tlsIn; + +diff --git a/libfreerdp/core/gateway/rpc.c b/libfreerdp/core/gateway/rpc.c +index 218a407..4ba52f3 100644 +--- a/libfreerdp/core/gateway/rpc.c ++++ b/libfreerdp/core/gateway/rpc.c +@@ -24,6 +24,7 @@ + #endif + + #include ++#include + #include + #include + #include +@@ -46,6 +47,7 @@ + #include "rpc_client.h" + + #include "rpc.h" ++#include "rts.h" + + #define TAG FREERDP_TAG("core.gateway.rpc") + +@@ -88,8 +90,10 @@ static const char* PTYPE_STRINGS[] = { "PTYPE_REQUEST", "PTYPE_PING", + * + */ + +-void rpc_pdu_header_print(rpcconn_hdr_t* header) ++void rpc_pdu_header_print(const rpcconn_hdr_t* header) + { ++ assert(header); ++ + WLog_INFO(TAG, "rpc_vers: %" PRIu8 "", header->common.rpc_vers); + WLog_INFO(TAG, "rpc_vers_minor: %" PRIu8 "", header->common.rpc_vers_minor); + +@@ -139,26 +143,30 @@ void rpc_pdu_header_print(rpcconn_hdr_t* header) + } + } + +-void rpc_pdu_header_init(rdpRpc* rpc, rpcconn_common_hdr_t* header) ++rpcconn_common_hdr_t rpc_pdu_header_init(const rdpRpc* rpc) + { +- header->rpc_vers = rpc->rpc_vers; +- header->rpc_vers_minor = rpc->rpc_vers_minor; +- header->packed_drep[0] = rpc->packed_drep[0]; +- header->packed_drep[1] = rpc->packed_drep[1]; +- header->packed_drep[2] = rpc->packed_drep[2]; +- header->packed_drep[3] = rpc->packed_drep[3]; ++ rpcconn_common_hdr_t header = { 0 }; ++ assert(rpc); ++ ++ header.rpc_vers = rpc->rpc_vers; ++ header.rpc_vers_minor = rpc->rpc_vers_minor; ++ header.packed_drep[0] = rpc->packed_drep[0]; ++ header.packed_drep[1] = rpc->packed_drep[1]; ++ header.packed_drep[2] = rpc->packed_drep[2]; ++ header.packed_drep[3] = rpc->packed_drep[3]; ++ return header; + } + +-UINT32 rpc_offset_align(UINT32* offset, UINT32 alignment) ++size_t rpc_offset_align(size_t* offset, size_t alignment) + { +- UINT32 pad; ++ size_t pad; + pad = *offset; + *offset = (*offset + alignment - 1) & ~(alignment - 1); + pad = *offset - pad; + return pad; + } + +-UINT32 rpc_offset_pad(UINT32* offset, UINT32 pad) ++size_t rpc_offset_pad(size_t* offset, size_t pad) + { + *offset += pad; + return pad; +@@ -239,64 +247,67 @@ UINT32 rpc_offset_pad(UINT32* offset, UINT32 pad) + * + */ + +-BOOL rpc_get_stub_data_info(rdpRpc* rpc, BYTE* buffer, UINT32* offset, UINT32* length) ++BOOL rpc_get_stub_data_info(const rpcconn_hdr_t* header, size_t* poffset, size_t* length) + { +- UINT32 alloc_hint = 0; +- rpcconn_hdr_t* header; ++ size_t used = 0; ++ size_t offset = 0; ++ BOOL rc = FALSE; + UINT32 frag_length; + UINT32 auth_length; +- UINT32 auth_pad_length; ++ UINT32 auth_pad_length = 0; + UINT32 sec_trailer_offset; +- rpc_sec_trailer* sec_trailer; +- *offset = RPC_COMMON_FIELDS_LENGTH; +- header = ((rpcconn_hdr_t*)buffer); ++ const rpc_sec_trailer* sec_trailer = NULL; ++ ++ assert(header); ++ assert(poffset); ++ assert(length); ++ ++ offset = RPC_COMMON_FIELDS_LENGTH; + + switch (header->common.ptype) + { + case PTYPE_RESPONSE: +- *offset += 8; +- rpc_offset_align(offset, 8); +- alloc_hint = header->response.alloc_hint; ++ offset += 8; ++ rpc_offset_align(&offset, 8); ++ sec_trailer = &header->response.auth_verifier; + break; + + case PTYPE_REQUEST: +- *offset += 4; +- rpc_offset_align(offset, 8); +- alloc_hint = header->request.alloc_hint; ++ offset += 4; ++ rpc_offset_align(&offset, 8); ++ sec_trailer = &header->request.auth_verifier; + break; + + case PTYPE_RTS: +- *offset += 4; ++ offset += 4; + break; + + default: + WLog_ERR(TAG, "Unknown PTYPE: 0x%02" PRIX8 "", header->common.ptype); +- return FALSE; ++ goto fail; + } + +- if (!length) +- return TRUE; ++ frag_length = header->common.frag_length; ++ auth_length = header->common.auth_length; ++ ++ if (poffset) ++ *poffset = offset; + +- if (header->common.ptype == PTYPE_REQUEST) ++ /* The fragment must be larger than the authentication trailer */ ++ used = offset + auth_length + 8ull; ++ if (sec_trailer) + { +- UINT32 sec_trailer_offset; +- sec_trailer_offset = header->common.frag_length - header->common.auth_length - 8; +- *length = sec_trailer_offset - *offset; +- return TRUE; ++ auth_pad_length = sec_trailer->auth_pad_length; ++ used += sec_trailer->auth_pad_length; + } + +- frag_length = header->common.frag_length; +- auth_length = header->common.auth_length; ++ if (frag_length < used) ++ goto fail; ++ ++ if (!length) ++ return TRUE; ++ + sec_trailer_offset = frag_length - auth_length - 8; +- sec_trailer = (rpc_sec_trailer*)&buffer[sec_trailer_offset]; +- auth_pad_length = sec_trailer->auth_pad_length; +-#if 0 +- WLog_DBG(TAG, +- "sec_trailer: type: %"PRIu8" level: %"PRIu8" pad_length: %"PRIu8" reserved: %"PRIu8" context_id: %"PRIu32"", +- sec_trailer->auth_type, sec_trailer->auth_level, +- sec_trailer->auth_pad_length, sec_trailer->auth_reserved, +- sec_trailer->auth_context_id); +-#endif + + /** + * According to [MS-RPCE], auth_pad_length is the number of padding +@@ -310,18 +321,21 @@ BOOL rpc_get_stub_data_info(rdpRpc* rpc, BYTE* buffer, UINT32* offset, UINT32* l + auth_length, (frag_length - (sec_trailer_offset + 8))); + } + +- *length = frag_length - auth_length - 24 - 8 - auth_pad_length; +- return TRUE; ++ *length = sec_trailer_offset - auth_pad_length - offset; ++ ++ rc = TRUE; ++fail: ++ return rc; + } + + SSIZE_T rpc_channel_read(RpcChannel* channel, wStream* s, size_t length) + { + int status; + +- if (!channel) ++ if (!channel || (length > INT32_MAX)) + return -1; + +- status = BIO_read(channel->tls->bio, Stream_Pointer(s), length); ++ status = BIO_read(channel->tls->bio, Stream_Pointer(s), (INT32)length); + + if (status > 0) + { +@@ -340,10 +354,10 @@ SSIZE_T rpc_channel_write(RpcChannel* channel, const BYTE* data, size_t length) + { + int status; + +- if (!channel) ++ if (!channel || (length > INT32_MAX)) + return -1; + +- status = tls_write_all(channel->tls, data, length); ++ status = tls_write_all(channel->tls, data, (INT32)length); + return status; + } + +@@ -629,7 +643,7 @@ static void rpc_virtual_connection_free(RpcVirtualConnection* connection) + free(connection); + } + +-static BOOL rpc_channel_tls_connect(RpcChannel* channel, int timeout) ++static BOOL rpc_channel_tls_connect(RpcChannel* channel, UINT32 timeout) + { + int sockfd; + rdpTls* tls; +@@ -719,7 +733,7 @@ static BOOL rpc_channel_tls_connect(RpcChannel* channel, int timeout) + return TRUE; + } + +-static int rpc_in_channel_connect(RpcInChannel* inChannel, int timeout) ++static int rpc_in_channel_connect(RpcInChannel* inChannel, UINT32 timeout) + { + rdpContext* context; + +@@ -814,7 +828,7 @@ int rpc_out_channel_replacement_connect(RpcOutChannel* outChannel, int timeout) + return 1; + } + +-BOOL rpc_connect(rdpRpc* rpc, int timeout) ++BOOL rpc_connect(rdpRpc* rpc, UINT32 timeout) + { + RpcInChannel* inChannel; + RpcOutChannel* outChannel; +@@ -840,7 +854,15 @@ BOOL rpc_connect(rdpRpc* rpc, int timeout) + + rdpRpc* rpc_new(rdpTransport* transport) + { +- rdpRpc* rpc = (rdpRpc*)calloc(1, sizeof(rdpRpc)); ++ rdpContext* context; ++ rdpRpc* rpc; ++ ++ assert(transport); ++ ++ context = transport->context; ++ assert(context); ++ ++ rpc = (rdpRpc*)calloc(1, sizeof(rdpRpc)); + + if (!rpc) + return NULL; +@@ -848,7 +870,7 @@ rdpRpc* rpc_new(rdpTransport* transport) + rpc->State = RPC_CLIENT_STATE_INITIAL; + rpc->transport = transport; + rpc->settings = transport->settings; +- rpc->context = transport->context; ++ rpc->context = context; + rpc->SendSeqNum = 0; + rpc->ntlm = ntlm_new(); + +@@ -873,7 +895,7 @@ rdpRpc* rpc_new(rdpTransport* transport) + rpc->CurrentKeepAliveInterval = rpc->KeepAliveInterval; + rpc->CurrentKeepAliveTime = 0; + rpc->CallId = 2; +- rpc->client = rpc_client_new(rpc->context, rpc->max_recv_frag); ++ rpc->client = rpc_client_new(context, rpc->max_recv_frag); + + if (!rpc->client) + goto out_free; +diff --git a/libfreerdp/core/gateway/rpc.h b/libfreerdp/core/gateway/rpc.h +index 3ca18a7..28f7f30 100644 +--- a/libfreerdp/core/gateway/rpc.h ++++ b/libfreerdp/core/gateway/rpc.h +@@ -73,7 +73,6 @@ typedef struct _RPC_PDU + #include "../tcp.h" + #include "../transport.h" + +-#include "rts.h" + #include "http.h" + #include "ntlm.h" + +@@ -522,7 +521,8 @@ typedef struct + rpcconn_common_hdr_t header; + } rpcconn_shutdown_hdr_t; + +-typedef union { ++typedef union ++{ + rpcconn_common_hdr_t common; + rpcconn_alter_context_hdr_t alter_context; + rpcconn_alter_context_response_hdr_t alter_context_response; +@@ -764,14 +764,14 @@ struct rdp_rpc + RpcVirtualConnection* VirtualConnection; + }; + +-FREERDP_LOCAL void rpc_pdu_header_print(rpcconn_hdr_t* header); +-FREERDP_LOCAL void rpc_pdu_header_init(rdpRpc* rpc, rpcconn_common_hdr_t* header); ++FREERDP_LOCAL void rpc_pdu_header_print(const rpcconn_hdr_t* header); ++FREERDP_LOCAL rpcconn_common_hdr_t rpc_pdu_header_init(const rdpRpc* rpc); + +-FREERDP_LOCAL UINT32 rpc_offset_align(UINT32* offset, UINT32 alignment); +-FREERDP_LOCAL UINT32 rpc_offset_pad(UINT32* offset, UINT32 pad); ++FREERDP_LOCAL size_t rpc_offset_align(size_t* offset, size_t alignment); ++FREERDP_LOCAL size_t rpc_offset_pad(size_t* offset, size_t pad); + +-FREERDP_LOCAL BOOL rpc_get_stub_data_info(rdpRpc* rpc, BYTE* header, UINT32* offset, +- UINT32* length); ++FREERDP_LOCAL BOOL rpc_get_stub_data_info(const rpcconn_hdr_t* header, size_t* offset, ++ size_t* length); + + FREERDP_LOCAL SSIZE_T rpc_channel_write(RpcChannel* channel, const BYTE* data, size_t length); + +@@ -791,7 +791,7 @@ FREERDP_LOCAL BOOL rpc_virtual_connection_transition_to_state(rdpRpc* rpc, + RpcVirtualConnection* connection, + VIRTUAL_CONNECTION_STATE state); + +-FREERDP_LOCAL BOOL rpc_connect(rdpRpc* rpc, int timeout); ++FREERDP_LOCAL BOOL rpc_connect(rdpRpc* rpc, UINT32 timeout); + + FREERDP_LOCAL rdpRpc* rpc_new(rdpTransport* transport); + FREERDP_LOCAL void rpc_free(rdpRpc* rpc); +diff --git a/libfreerdp/core/gateway/rpc_bind.c b/libfreerdp/core/gateway/rpc_bind.c +index 98ed9a9..bdf54d5 100644 +--- a/libfreerdp/core/gateway/rpc_bind.c ++++ b/libfreerdp/core/gateway/rpc_bind.c +@@ -22,11 +22,14 @@ + #endif + + #include ++#include + + #include + + #include "rpc_client.h" + ++#include "rts.h" ++ + #include "rpc_bind.h" + + #define TAG FREERDP_TAG("core.gateway.rpc") +@@ -106,18 +109,32 @@ int rpc_send_bind_pdu(rdpRpc* rpc) + { + BOOL continueNeeded = FALSE; + int status = -1; +- BYTE* buffer = NULL; ++ wStream* buffer = NULL; + UINT32 offset; +- UINT32 length; + RpcClientCall* clientCall; + p_cont_elem_t* p_cont_elem; +- rpcconn_bind_hdr_t* bind_pdu = NULL; ++ rpcconn_bind_hdr_t bind_pdu = { 0 }; + BOOL promptPassword = FALSE; +- rdpSettings* settings = rpc->settings; +- freerdp* instance = (freerdp*)settings->instance; +- RpcVirtualConnection* connection = rpc->VirtualConnection; +- RpcInChannel* inChannel = connection->DefaultInChannel; ++ rdpSettings* settings; ++ freerdp* instance; ++ RpcVirtualConnection* connection; ++ RpcInChannel* inChannel; + const SecBuffer* sbuffer = NULL; ++ ++ assert(rpc); ++ ++ settings = rpc->settings; ++ assert(settings); ++ ++ instance = (freerdp*)settings->instance; ++ assert(instance); ++ ++ connection = rpc->VirtualConnection; ++ ++ assert(connection); ++ ++ inChannel = connection->DefaultInChannel; ++ + WLog_DBG(TAG, "Sending Bind PDU"); + ntlm_free(rpc->ntlm); + rpc->ntlm = ntlm_new(); +@@ -180,36 +197,31 @@ int rpc_send_bind_pdu(rdpRpc* rpc) + if (!continueNeeded) + goto fail; + +- bind_pdu = (rpcconn_bind_hdr_t*)calloc(1, sizeof(rpcconn_bind_hdr_t)); +- +- if (!bind_pdu) +- goto fail; +- + sbuffer = ntlm_client_get_output_buffer(rpc->ntlm); + + if (!sbuffer) + goto fail; + +- rpc_pdu_header_init(rpc, &bind_pdu->header); +- bind_pdu->header.auth_length = (UINT16)sbuffer->cbBuffer; +- bind_pdu->auth_verifier.auth_value = sbuffer->pvBuffer; +- bind_pdu->header.ptype = PTYPE_BIND; +- bind_pdu->header.pfc_flags = ++ bind_pdu.header = rpc_pdu_header_init(rpc); ++ bind_pdu.header.auth_length = (UINT16)sbuffer->cbBuffer; ++ bind_pdu.auth_verifier.auth_value = sbuffer->pvBuffer; ++ bind_pdu.header.ptype = PTYPE_BIND; ++ bind_pdu.header.pfc_flags = + PFC_FIRST_FRAG | PFC_LAST_FRAG | PFC_SUPPORT_HEADER_SIGN | PFC_CONC_MPX; +- bind_pdu->header.call_id = 2; +- bind_pdu->max_xmit_frag = rpc->max_xmit_frag; +- bind_pdu->max_recv_frag = rpc->max_recv_frag; +- bind_pdu->assoc_group_id = 0; +- bind_pdu->p_context_elem.n_context_elem = 2; +- bind_pdu->p_context_elem.reserved = 0; +- bind_pdu->p_context_elem.reserved2 = 0; +- bind_pdu->p_context_elem.p_cont_elem = +- calloc(bind_pdu->p_context_elem.n_context_elem, sizeof(p_cont_elem_t)); +- +- if (!bind_pdu->p_context_elem.p_cont_elem) ++ bind_pdu.header.call_id = 2; ++ bind_pdu.max_xmit_frag = rpc->max_xmit_frag; ++ bind_pdu.max_recv_frag = rpc->max_recv_frag; ++ bind_pdu.assoc_group_id = 0; ++ bind_pdu.p_context_elem.n_context_elem = 2; ++ bind_pdu.p_context_elem.reserved = 0; ++ bind_pdu.p_context_elem.reserved2 = 0; ++ bind_pdu.p_context_elem.p_cont_elem = ++ calloc(bind_pdu.p_context_elem.n_context_elem, sizeof(p_cont_elem_t)); ++ ++ if (!bind_pdu.p_context_elem.p_cont_elem) + goto fail; + +- p_cont_elem = &bind_pdu->p_context_elem.p_cont_elem[0]; ++ p_cont_elem = &bind_pdu.p_context_elem.p_cont_elem[0]; + p_cont_elem->p_cont_id = 0; + p_cont_elem->n_transfer_syn = 1; + p_cont_elem->reserved = 0; +@@ -222,7 +234,7 @@ int rpc_send_bind_pdu(rdpRpc* rpc) + + CopyMemory(&(p_cont_elem->transfer_syntaxes[0].if_uuid), &NDR_UUID, sizeof(p_uuid_t)); + p_cont_elem->transfer_syntaxes[0].if_version = NDR_SYNTAX_IF_VERSION; +- p_cont_elem = &bind_pdu->p_context_elem.p_cont_elem[1]; ++ p_cont_elem = &bind_pdu.p_context_elem.p_cont_elem[1]; + p_cont_elem->p_cont_id = 1; + p_cont_elem->n_transfer_syn = 1; + p_cont_elem->reserved = 0; +@@ -236,32 +248,23 @@ int rpc_send_bind_pdu(rdpRpc* rpc) + CopyMemory(&(p_cont_elem->transfer_syntaxes[0].if_uuid), &BTFN_UUID, sizeof(p_uuid_t)); + p_cont_elem->transfer_syntaxes[0].if_version = BTFN_SYNTAX_IF_VERSION; + offset = 116; +- bind_pdu->auth_verifier.auth_pad_length = rpc_offset_align(&offset, 4); +- bind_pdu->auth_verifier.auth_type = RPC_C_AUTHN_WINNT; +- bind_pdu->auth_verifier.auth_level = RPC_C_AUTHN_LEVEL_PKT_INTEGRITY; +- bind_pdu->auth_verifier.auth_reserved = 0x00; +- bind_pdu->auth_verifier.auth_context_id = 0x00000000; +- offset += (8 + bind_pdu->header.auth_length); +- bind_pdu->header.frag_length = offset; +- buffer = (BYTE*)malloc(bind_pdu->header.frag_length); ++ ++ bind_pdu.auth_verifier.auth_type = RPC_C_AUTHN_WINNT; ++ bind_pdu.auth_verifier.auth_level = RPC_C_AUTHN_LEVEL_PKT_INTEGRITY; ++ bind_pdu.auth_verifier.auth_reserved = 0x00; ++ bind_pdu.auth_verifier.auth_context_id = 0x00000000; ++ offset += (8 + bind_pdu.header.auth_length); ++ bind_pdu.header.frag_length = offset; ++ ++ buffer = Stream_New(NULL, bind_pdu.header.frag_length); + + if (!buffer) + goto fail; + +- CopyMemory(buffer, bind_pdu, 24); +- CopyMemory(&buffer[24], &bind_pdu->p_context_elem, 4); +- CopyMemory(&buffer[28], &bind_pdu->p_context_elem.p_cont_elem[0], 24); +- CopyMemory(&buffer[52], bind_pdu->p_context_elem.p_cont_elem[0].transfer_syntaxes, 20); +- CopyMemory(&buffer[72], &bind_pdu->p_context_elem.p_cont_elem[1], 24); +- CopyMemory(&buffer[96], bind_pdu->p_context_elem.p_cont_elem[1].transfer_syntaxes, 20); +- offset = 116; +- rpc_offset_pad(&offset, bind_pdu->auth_verifier.auth_pad_length); +- CopyMemory(&buffer[offset], &bind_pdu->auth_verifier.auth_type, 8); +- CopyMemory(&buffer[offset + 8], bind_pdu->auth_verifier.auth_value, +- bind_pdu->header.auth_length); +- offset += (8 + bind_pdu->header.auth_length); +- length = bind_pdu->header.frag_length; +- clientCall = rpc_client_call_new(bind_pdu->header.call_id, 0); ++ if (!rts_write_pdu_bind(buffer, &bind_pdu)) ++ goto fail; ++ ++ clientCall = rpc_client_call_new(bind_pdu.header.call_id, 0); + + if (!clientCall) + goto fail; +@@ -272,22 +275,19 @@ int rpc_send_bind_pdu(rdpRpc* rpc) + goto fail; + } + +- status = rpc_in_channel_send_pdu(inChannel, buffer, length); ++ Stream_SealLength(buffer); ++ status = rpc_in_channel_send_pdu(inChannel, Stream_Buffer(buffer), Stream_Length(buffer)); + fail: + +- if (bind_pdu) ++ if (bind_pdu.p_context_elem.p_cont_elem) + { +- if (bind_pdu->p_context_elem.p_cont_elem) +- { +- free(bind_pdu->p_context_elem.p_cont_elem[0].transfer_syntaxes); +- free(bind_pdu->p_context_elem.p_cont_elem[1].transfer_syntaxes); +- } +- +- free(bind_pdu->p_context_elem.p_cont_elem); ++ free(bind_pdu.p_context_elem.p_cont_elem[0].transfer_syntaxes); ++ free(bind_pdu.p_context_elem.p_cont_elem[1].transfer_syntaxes); + } + +- free(bind_pdu); +- free(buffer); ++ free(bind_pdu.p_context_elem.p_cont_elem); ++ ++ Stream_Free(buffer, TRUE); + return (status > 0) ? 1 : -1; + } + +@@ -317,31 +317,47 @@ fail: + * example. + */ + +-int rpc_recv_bind_ack_pdu(rdpRpc* rpc, BYTE* buffer, UINT32 length) ++BOOL rpc_recv_bind_ack_pdu(rdpRpc* rpc, wStream* s) + { ++ BOOL rc = FALSE; + BOOL continueNeeded = FALSE; +- BYTE* auth_data; +- rpcconn_hdr_t* header; +- header = (rpcconn_hdr_t*)buffer; ++ const BYTE* auth_data; ++ size_t pos, end; ++ rpcconn_hdr_t header = { 0 }; ++ ++ assert(rpc); ++ assert(rpc->ntlm); ++ assert(s); ++ ++ pos = Stream_GetPosition(s); ++ if (!rts_read_pdu_header(s, &header)) ++ goto fail; ++ + WLog_DBG(TAG, "Receiving BindAck PDU"); + +- if (!rpc || !rpc->ntlm) +- return -1; ++ rpc->max_recv_frag = header.bind_ack.max_xmit_frag; ++ rpc->max_xmit_frag = header.bind_ack.max_recv_frag; + +- rpc->max_recv_frag = header->bind_ack.max_xmit_frag; +- rpc->max_xmit_frag = header->bind_ack.max_recv_frag; +- auth_data = buffer + (header->common.frag_length - header->common.auth_length); ++ /* Get the correct offset in the input data and pass that on as input buffer. ++ * rts_read_pdu_header did already do consistency checks */ ++ end = Stream_GetPosition(s); ++ Stream_SetPosition(s, pos + header.common.frag_length - header.common.auth_length); ++ auth_data = Stream_Pointer(s); ++ Stream_SetPosition(s, end); + +- if (!ntlm_client_set_input_buffer(rpc->ntlm, TRUE, auth_data, header->common.auth_length)) +- return -1; ++ if (!ntlm_client_set_input_buffer(rpc->ntlm, TRUE, auth_data, header.common.auth_length)) ++ goto fail; + + if (!ntlm_authenticate(rpc->ntlm, &continueNeeded)) +- return -1; ++ goto fail; + + if (continueNeeded) +- return -1; ++ goto fail; + +- return (int)length; ++ rc = TRUE; ++fail: ++ rts_free_pdu_header(&header, FALSE); ++ return rc; + } + + /** +@@ -354,68 +370,63 @@ int rpc_recv_bind_ack_pdu(rdpRpc* rpc, BYTE* buffer, UINT32 length) + int rpc_send_rpc_auth_3_pdu(rdpRpc* rpc) + { + int status = -1; +- BYTE* buffer; +- UINT32 offset; +- UINT32 length; ++ wStream* buffer; ++ size_t offset; + const SecBuffer* sbuffer; + RpcClientCall* clientCall; +- rpcconn_rpc_auth_3_hdr_t* auth_3_pdu; +- RpcVirtualConnection* connection = rpc->VirtualConnection; +- RpcInChannel* inChannel = connection->DefaultInChannel; +- WLog_DBG(TAG, "Sending RpcAuth3 PDU"); +- auth_3_pdu = (rpcconn_rpc_auth_3_hdr_t*)calloc(1, sizeof(rpcconn_rpc_auth_3_hdr_t)); ++ rpcconn_rpc_auth_3_hdr_t auth_3_pdu = { 0 }; ++ RpcVirtualConnection* connection; ++ RpcInChannel* inChannel; + +- if (!auth_3_pdu) +- return -1; ++ assert(rpc); ++ ++ connection = rpc->VirtualConnection; ++ assert(connection); ++ ++ inChannel = connection->DefaultInChannel; ++ assert(inChannel); ++ ++ WLog_DBG(TAG, "Sending RpcAuth3 PDU"); + + sbuffer = ntlm_client_get_output_buffer(rpc->ntlm); + + if (!sbuffer) +- { +- free(auth_3_pdu); + return -1; +- } + +- rpc_pdu_header_init(rpc, &auth_3_pdu->header); +- auth_3_pdu->header.auth_length = (UINT16)sbuffer->cbBuffer; +- auth_3_pdu->auth_verifier.auth_value = sbuffer->pvBuffer; +- auth_3_pdu->header.ptype = PTYPE_RPC_AUTH_3; +- auth_3_pdu->header.pfc_flags = PFC_FIRST_FRAG | PFC_LAST_FRAG | PFC_CONC_MPX; +- auth_3_pdu->header.call_id = 2; +- auth_3_pdu->max_xmit_frag = rpc->max_xmit_frag; +- auth_3_pdu->max_recv_frag = rpc->max_recv_frag; ++ auth_3_pdu.header = rpc_pdu_header_init(rpc); ++ auth_3_pdu.header.auth_length = (UINT16)sbuffer->cbBuffer; ++ auth_3_pdu.auth_verifier.auth_value = sbuffer->pvBuffer; ++ auth_3_pdu.header.ptype = PTYPE_RPC_AUTH_3; ++ auth_3_pdu.header.pfc_flags = PFC_FIRST_FRAG | PFC_LAST_FRAG | PFC_CONC_MPX; ++ auth_3_pdu.header.call_id = 2; ++ auth_3_pdu.max_xmit_frag = rpc->max_xmit_frag; ++ auth_3_pdu.max_recv_frag = rpc->max_recv_frag; + offset = 20; +- auth_3_pdu->auth_verifier.auth_pad_length = rpc_offset_align(&offset, 4); +- auth_3_pdu->auth_verifier.auth_type = RPC_C_AUTHN_WINNT; +- auth_3_pdu->auth_verifier.auth_level = RPC_C_AUTHN_LEVEL_PKT_INTEGRITY; +- auth_3_pdu->auth_verifier.auth_reserved = 0x00; +- auth_3_pdu->auth_verifier.auth_context_id = 0x00000000; +- offset += (8 + auth_3_pdu->header.auth_length); +- auth_3_pdu->header.frag_length = offset; +- buffer = (BYTE*)malloc(auth_3_pdu->header.frag_length); ++ auth_3_pdu.auth_verifier.auth_pad_length = rpc_offset_align(&offset, 4); ++ auth_3_pdu.auth_verifier.auth_type = RPC_C_AUTHN_WINNT; ++ auth_3_pdu.auth_verifier.auth_level = RPC_C_AUTHN_LEVEL_PKT_INTEGRITY; ++ auth_3_pdu.auth_verifier.auth_reserved = 0x00; ++ auth_3_pdu.auth_verifier.auth_context_id = 0x00000000; ++ offset += (8 + auth_3_pdu.header.auth_length); ++ auth_3_pdu.header.frag_length = offset; ++ ++ buffer = Stream_New(NULL, auth_3_pdu.header.frag_length); + + if (!buffer) +- { +- free(auth_3_pdu); + return -1; +- } + +- CopyMemory(buffer, auth_3_pdu, 20); +- offset = 20; +- rpc_offset_pad(&offset, auth_3_pdu->auth_verifier.auth_pad_length); +- CopyMemory(&buffer[offset], &auth_3_pdu->auth_verifier.auth_type, 8); +- CopyMemory(&buffer[offset + 8], auth_3_pdu->auth_verifier.auth_value, +- auth_3_pdu->header.auth_length); +- offset += (8 + auth_3_pdu->header.auth_length); +- length = auth_3_pdu->header.frag_length; +- clientCall = rpc_client_call_new(auth_3_pdu->header.call_id, 0); ++ if (!rts_write_pdu_auth3(buffer, &auth_3_pdu)) ++ goto fail; ++ ++ clientCall = rpc_client_call_new(auth_3_pdu.header.call_id, 0); + + if (ArrayList_Add(rpc->client->ClientCallList, clientCall) >= 0) + { +- status = rpc_in_channel_send_pdu(inChannel, buffer, length); ++ Stream_SealLength(buffer); ++ status = rpc_in_channel_send_pdu(inChannel, Stream_Buffer(buffer), Stream_Length(buffer)); + } + +- free(auth_3_pdu); +- free(buffer); ++fail: ++ Stream_Free(buffer, TRUE); + return (status > 0) ? 1 : -1; + } +diff --git a/libfreerdp/core/gateway/rpc_bind.h b/libfreerdp/core/gateway/rpc_bind.h +index 759555f..69758e5 100644 +--- a/libfreerdp/core/gateway/rpc_bind.h ++++ b/libfreerdp/core/gateway/rpc_bind.h +@@ -35,7 +35,7 @@ FREERDP_LOCAL extern const p_uuid_t BTFN_UUID; + #define BTFN_SYNTAX_IF_VERSION 0x00000001 + + FREERDP_LOCAL int rpc_send_bind_pdu(rdpRpc* rpc); +-FREERDP_LOCAL int rpc_recv_bind_ack_pdu(rdpRpc* rpc, BYTE* buffer, UINT32 length); ++FREERDP_LOCAL BOOL rpc_recv_bind_ack_pdu(rdpRpc* rpc, wStream* s); + FREERDP_LOCAL int rpc_send_rpc_auth_3_pdu(rdpRpc* rpc); + + #endif /* FREERDP_LIB_CORE_GATEWAY_RPC_BIND_H */ +diff --git a/libfreerdp/core/gateway/rpc_fault.c b/libfreerdp/core/gateway/rpc_fault.c +index 7259f04..c4cb086 100644 +--- a/libfreerdp/core/gateway/rpc_fault.c ++++ b/libfreerdp/core/gateway/rpc_fault.c +@@ -133,10 +133,7 @@ static const RPC_FAULT_CODE RPC_FAULT_CODES[] = { + CAT_GATEWAY) + DEFINE_RPC_FAULT_CODE( + RPC_S_INVALID_OBJECT, +- CAT_GATEWAY){ +- 0, +- NULL, +- NULL } ++ CAT_GATEWAY) + }; + + static const RPC_FAULT_CODE RPC_TSG_FAULT_CODES[] = { +@@ -222,9 +219,7 @@ static const RPC_FAULT_CODE RPC_TSG_FAULT_CODES[] = { + DEFINE_RPC_FAULT_CODE( + HRESULT_CODE( + RPC_S_CALL_CANCELLED), +- CAT_GATEWAY){ +- 0, NULL, +- NULL } ++ CAT_GATEWAY) + }; + + /** +@@ -377,22 +372,22 @@ const char* rpc_error_to_string(UINT32 code) + size_t index; + static char buffer[1024]; + +- for (index = 0; RPC_FAULT_CODES[index].name != NULL; index++) ++ for (index = 0; index < ARRAYSIZE(RPC_FAULT_CODES); index++) + { +- if (RPC_FAULT_CODES[index].code == code) ++ const RPC_FAULT_CODE* const current = &RPC_FAULT_CODES[index]; ++ if (current->code == code) + { +- sprintf_s(buffer, ARRAYSIZE(buffer), "%s [0x%08" PRIX32 "]", +- RPC_FAULT_CODES[index].name, code); ++ sprintf_s(buffer, ARRAYSIZE(buffer), "%s", current->name); + goto out; + } + } + +- for (index = 0; RPC_TSG_FAULT_CODES[index].name != NULL; index++) ++ for (index = 0; index < ARRAYSIZE(RPC_TSG_FAULT_CODES); index++) + { +- if (RPC_TSG_FAULT_CODES[index].code == code) ++ const RPC_FAULT_CODE* const current = &RPC_TSG_FAULT_CODES[index]; ++ if (current->code == code) + { +- sprintf_s(buffer, ARRAYSIZE(buffer), "%s [0x%08" PRIX32 "]", +- RPC_TSG_FAULT_CODES[index].name, code); ++ sprintf_s(buffer, ARRAYSIZE(buffer), "%s", current->name); + goto out; + } + } +@@ -406,16 +401,18 @@ const char* rpc_error_to_category(UINT32 code) + { + size_t index; + +- for (index = 0; RPC_FAULT_CODES[index].category != NULL; index++) ++ for (index = 0; index < ARRAYSIZE(RPC_FAULT_CODES); index++) + { +- if (RPC_FAULT_CODES[index].code == code) +- return RPC_FAULT_CODES[index].category; ++ const RPC_FAULT_CODE* const current = &RPC_FAULT_CODES[index]; ++ if (current->code == code) ++ return current->category; + } + +- for (index = 0; RPC_TSG_FAULT_CODES[index].category != NULL; index++) ++ for (index = 0; index < ARRAYSIZE(RPC_TSG_FAULT_CODES); index++) + { +- if (RPC_TSG_FAULT_CODES[index].code == code) +- return RPC_TSG_FAULT_CODES[index].category; ++ const RPC_FAULT_CODE* const current = &RPC_TSG_FAULT_CODES[index]; ++ if (current->code == code) ++ return current->category; + } + + return "UNKNOWN"; +diff --git a/libfreerdp/core/gateway/rpc_client.c b/libfreerdp/core/gateway/rpc_client.c +index d4432cb..50ec8e5 100644 +--- a/libfreerdp/core/gateway/rpc_client.c ++++ b/libfreerdp/core/gateway/rpc_client.c +@@ -24,6 +24,7 @@ + #include + + #include ++#include + #include + #include + #include +@@ -35,6 +36,8 @@ + #include "rpc_bind.h" + #include "rpc_fault.h" + #include "rpc_client.h" ++#include "rts_signature.h" ++ + #include "../rdp.h" + #include "../proxy.h" + +@@ -99,7 +102,7 @@ static int rpc_client_receive_pipe_write(RpcClient* client, const BYTE* buffer, + int rpc_client_receive_pipe_read(RpcClient* client, BYTE* buffer, size_t length) + { + int index = 0; +- int status = 0; ++ size_t status = 0; + int nchunks = 0; + DataChunk chunks[2]; + +@@ -122,7 +125,10 @@ int rpc_client_receive_pipe_read(RpcClient* client, BYTE* buffer, size_t length) + ResetEvent(client->PipeEvent); + + LeaveCriticalSection(&(client->PipeLock)); +- return status; ++ ++ if (status > INT_MAX) ++ return -1; ++ return (int)status; + } + + static int rpc_client_transition_to_state(rdpRpc* rpc, RPC_CLIENT_STATE state) +@@ -173,8 +179,15 @@ static int rpc_client_transition_to_state(rdpRpc* rpc, RPC_CLIENT_STATE state) + static int rpc_client_recv_pdu(rdpRpc* rpc, RPC_PDU* pdu) + { + int status = -1; +- rpcconn_rts_hdr_t* rts; +- rdpTsg* tsg = rpc->transport->tsg; ++ rdpTsg* tsg; ++ ++ assert(rpc); ++ assert(pdu); ++ ++ Stream_SealLength(pdu->s); ++ Stream_SetPosition(pdu->s, 0); ++ ++ tsg = rpc->transport->tsg; + + if (rpc->VirtualConnection->State < VIRTUAL_CONNECTION_STATE_OPENED) + { +@@ -187,17 +200,13 @@ static int rpc_client_recv_pdu(rdpRpc* rpc, RPC_PDU* pdu) + break; + + case VIRTUAL_CONNECTION_STATE_WAIT_A3W: +- rts = (rpcconn_rts_hdr_t*)Stream_Buffer(pdu->s); +- +- if (!rts_match_pdu_signature(&RTS_PDU_CONN_A3_SIGNATURE, rts)) ++ if (!rts_match_pdu_signature(&RTS_PDU_CONN_A3_SIGNATURE, pdu->s, NULL)) + { + WLog_ERR(TAG, "unexpected RTS PDU: Expected CONN/A3"); + return -1; + } + +- status = rts_recv_CONN_A3_pdu(rpc, Stream_Buffer(pdu->s), Stream_Length(pdu->s)); +- +- if (status < 0) ++ if (!rts_recv_CONN_A3_pdu(rpc, pdu->s)) + { + WLog_ERR(TAG, "rts_recv_CONN_A3_pdu failure"); + return -1; +@@ -209,17 +218,13 @@ static int rpc_client_recv_pdu(rdpRpc* rpc, RPC_PDU* pdu) + break; + + case VIRTUAL_CONNECTION_STATE_WAIT_C2: +- rts = (rpcconn_rts_hdr_t*)Stream_Buffer(pdu->s); +- +- if (!rts_match_pdu_signature(&RTS_PDU_CONN_C2_SIGNATURE, rts)) ++ if (!rts_match_pdu_signature(&RTS_PDU_CONN_C2_SIGNATURE, pdu->s, NULL)) + { + WLog_ERR(TAG, "unexpected RTS PDU: Expected CONN/C2"); + return -1; + } + +- status = rts_recv_CONN_C2_pdu(rpc, Stream_Buffer(pdu->s), Stream_Length(pdu->s)); +- +- if (status < 0) ++ if (!rts_recv_CONN_C2_pdu(rpc, pdu->s)) + { + WLog_ERR(TAG, "rts_recv_CONN_C2_pdu failure"); + return -1; +@@ -252,7 +257,7 @@ static int rpc_client_recv_pdu(rdpRpc* rpc, RPC_PDU* pdu) + { + if (pdu->Type == PTYPE_BIND_ACK) + { +- if (rpc_recv_bind_ack_pdu(rpc, Stream_Buffer(pdu->s), Stream_Length(pdu->s)) <= 0) ++ if (!rpc_recv_bind_ack_pdu(rpc, pdu->s)) + { + WLog_ERR(TAG, "rpc_recv_bind_ack_pdu failure"); + return -1; +@@ -301,89 +306,117 @@ static int rpc_client_recv_pdu(rdpRpc* rpc, RPC_PDU* pdu) + + static int rpc_client_recv_fragment(rdpRpc* rpc, wStream* fragment) + { +- BYTE* buffer; ++ int rc = -1; + RPC_PDU* pdu; +- UINT32 StubOffset; +- UINT32 StubLength; ++ size_t StubOffset; ++ size_t StubLength; + RpcClientCall* call; +- rpcconn_hdr_t* header; ++ rpcconn_hdr_t header = { 0 }; ++ ++ assert(rpc); ++ assert(rpc->client); ++ assert(fragment); ++ + pdu = rpc->client->pdu; +- buffer = (BYTE*)Stream_Buffer(fragment); +- header = (rpcconn_hdr_t*)Stream_Buffer(fragment); ++ assert(pdu); ++ ++ Stream_SealLength(fragment); ++ Stream_SetPosition(fragment, 0); ++ ++ if (!rts_read_pdu_header(fragment, &header)) ++ goto fail; + +- if (header->common.ptype == PTYPE_RESPONSE) ++ if (header.common.ptype == PTYPE_RESPONSE) + { +- rpc->VirtualConnection->DefaultOutChannel->BytesReceived += header->common.frag_length; ++ rpc->VirtualConnection->DefaultOutChannel->BytesReceived += header.common.frag_length; + rpc->VirtualConnection->DefaultOutChannel->ReceiverAvailableWindow -= +- header->common.frag_length; ++ header.common.frag_length; + + if (rpc->VirtualConnection->DefaultOutChannel->ReceiverAvailableWindow < + (rpc->ReceiveWindow / 2)) + { +- if (rts_send_flow_control_ack_pdu(rpc) < 0) +- return -1; ++ if (!rts_send_flow_control_ack_pdu(rpc)) ++ goto fail; + } + +- if (!rpc_get_stub_data_info(rpc, buffer, &StubOffset, &StubLength)) ++ if (!rpc_get_stub_data_info(&header, &StubOffset, &StubLength)) + { + WLog_ERR(TAG, "expected stub"); +- return -1; ++ goto fail; + } + + if (StubLength == 4) + { +- if ((header->common.call_id == rpc->PipeCallId) && +- (header->common.pfc_flags & PFC_LAST_FRAG)) ++ if ((header.common.call_id == rpc->PipeCallId) && ++ (header.common.pfc_flags & PFC_LAST_FRAG)) + { + /* End of TsProxySetupReceivePipe */ + TerminateEventArgs e; +- rpc->result = *((UINT32*)&buffer[StubOffset]); +- freerdp_abort_connect(rpc->context->instance); +- tsg_set_state(rpc->transport->tsg, TSG_STATE_TUNNEL_CLOSE_PENDING); ++ rdpContext* context = rpc->transport->context; ++ rdpTsg* tsg = rpc->transport->tsg; ++ ++ assert(context); ++ ++ if (Stream_Length(fragment) < StubOffset + 4) ++ goto fail; ++ Stream_SetPosition(fragment, StubOffset); ++ Stream_Read_UINT32(fragment, rpc->result); ++ ++ freerdp_abort_connect(context->instance); ++ tsg_set_state(tsg, TSG_STATE_TUNNEL_CLOSE_PENDING); + EventArgsInit(&e, "freerdp"); + e.code = 0; +- PubSub_OnTerminate(rpc->context->pubSub, rpc->context, &e); +- return 0; ++ PubSub_OnTerminate(context->pubSub, context, &e); ++ rc = 0; ++ goto success; + } + +- if (header->common.call_id != rpc->PipeCallId) ++ if (header.common.call_id != rpc->PipeCallId) + { + /* Ignoring non-TsProxySetupReceivePipe Response */ +- return 0; ++ rc = 0; ++ goto success; + } + } + + if (rpc->StubFragCount == 0) +- rpc->StubCallId = header->common.call_id; ++ rpc->StubCallId = header.common.call_id; + +- if (rpc->StubCallId != header->common.call_id) ++ if (rpc->StubCallId != header.common.call_id) + { + WLog_ERR(TAG, + "invalid call_id: actual: %" PRIu32 ", expected: %" PRIu32 + ", frag_count: %" PRIu32 "", +- rpc->StubCallId, header->common.call_id, rpc->StubFragCount); ++ rpc->StubCallId, header.common.call_id, rpc->StubFragCount); + } + + call = rpc_client_call_find_by_id(rpc->client, rpc->StubCallId); + + if (!call) +- return -1; ++ goto fail; + + if (call->OpNum != TsProxySetupReceivePipeOpnum) + { +- if (!Stream_EnsureCapacity(pdu->s, header->response.alloc_hint)) +- return -1; ++ const rpcconn_response_hdr_t* response = ++ (const rpcconn_response_hdr_t*)&header.response; ++ if (!Stream_EnsureCapacity(pdu->s, response->alloc_hint)) ++ goto fail; ++ ++ if (Stream_Length(fragment) < StubOffset + StubLength) ++ goto fail; + +- Stream_Write(pdu->s, &buffer[StubOffset], StubLength); ++ Stream_SetPosition(fragment, StubOffset); ++ Stream_Write(pdu->s, Stream_Pointer(fragment), StubLength); + rpc->StubFragCount++; + +- if (header->response.alloc_hint == StubLength) ++ if (response->alloc_hint == StubLength) + { + pdu->Flags = RPC_PDU_FLAG_STUB; + pdu->Type = PTYPE_RESPONSE; + pdu->CallId = rpc->StubCallId; +- Stream_SealLength(pdu->s); +- rpc_client_recv_pdu(rpc, pdu); ++ ++ if (rpc_client_recv_pdu(rpc, pdu) < 0) ++ goto fail; + rpc_pdu_reset(pdu); + rpc->StubFragCount = 0; + rpc->StubCallId = 0; +@@ -391,75 +424,84 @@ static int rpc_client_recv_fragment(rdpRpc* rpc, wStream* fragment) + } + else + { +- rpc_client_receive_pipe_write(rpc->client, &buffer[StubOffset], (size_t)StubLength); ++ const rpcconn_response_hdr_t* response = &header.response; ++ if (Stream_Length(fragment) < StubOffset + StubLength) ++ goto fail; ++ Stream_SetPosition(fragment, StubOffset); ++ rpc_client_receive_pipe_write(rpc->client, Stream_Pointer(fragment), ++ (size_t)StubLength); + rpc->StubFragCount++; + +- if (header->response.alloc_hint == StubLength) ++ if (response->alloc_hint == StubLength) + { + rpc->StubFragCount = 0; + rpc->StubCallId = 0; + } + } + +- return 1; ++ goto success; + } +- else if (header->common.ptype == PTYPE_RTS) ++ else if (header.common.ptype == PTYPE_RTS) + { + if (rpc->State < RPC_CLIENT_STATE_CONTEXT_NEGOTIATED) + { + pdu->Flags = 0; +- pdu->Type = header->common.ptype; +- pdu->CallId = header->common.call_id; ++ pdu->Type = header.common.ptype; ++ pdu->CallId = header.common.call_id; + + if (!Stream_EnsureCapacity(pdu->s, Stream_Length(fragment))) +- return -1; ++ goto fail; + +- Stream_Write(pdu->s, buffer, Stream_Length(fragment)); +- Stream_SealLength(pdu->s); ++ Stream_Write(pdu->s, Stream_Buffer(fragment), Stream_Length(fragment)); + + if (rpc_client_recv_pdu(rpc, pdu) < 0) +- return -1; ++ goto fail; + + rpc_pdu_reset(pdu); + } + else + { +- if (rts_recv_out_of_sequence_pdu(rpc, buffer, header->common.frag_length) < 0) +- return -1; ++ if (!rts_recv_out_of_sequence_pdu(rpc, fragment, &header)) ++ goto fail; + } + +- return 1; ++ goto success; + } +- else if (header->common.ptype == PTYPE_BIND_ACK) ++ else if (header.common.ptype == PTYPE_BIND_ACK) + { + pdu->Flags = 0; +- pdu->Type = header->common.ptype; +- pdu->CallId = header->common.call_id; ++ pdu->Type = header.common.ptype; ++ pdu->CallId = header.common.call_id; + + if (!Stream_EnsureCapacity(pdu->s, Stream_Length(fragment))) +- return -1; ++ goto fail; + +- Stream_Write(pdu->s, buffer, Stream_Length(fragment)); +- Stream_SealLength(pdu->s); ++ Stream_Write(pdu->s, Stream_Buffer(fragment), Stream_Length(fragment)); + + if (rpc_client_recv_pdu(rpc, pdu) < 0) +- return -1; ++ goto fail; + + rpc_pdu_reset(pdu); +- return 1; ++ goto success; + } +- else if (header->common.ptype == PTYPE_FAULT) ++ else if (header.common.ptype == PTYPE_FAULT) + { +- rpc_recv_fault_pdu(header->fault.status); +- return -1; ++ const rpcconn_fault_hdr_t* fault = (const rpcconn_fault_hdr_t*)&header.fault; ++ rpc_recv_fault_pdu(fault->status); ++ goto fail; + } + else + { +- WLog_ERR(TAG, "unexpected RPC PDU type 0x%02" PRIX8 "", header->common.ptype); +- return -1; ++ WLog_ERR(TAG, "unexpected RPC PDU type 0x%02" PRIX8 "", header.common.ptype); ++ goto fail; + } + +- return 1; ++success: ++ rc = (rc < 0) ? 1 : 0; /* In case of default error return change to 1, otherwise we already set ++ the return code */ ++fail: ++ rts_free_pdu_header(&header, FALSE); ++ return rc; + } + + static int rpc_client_default_out_channel_recv(rdpRpc* rpc) +@@ -509,7 +551,7 @@ static int rpc_client_default_out_channel_recv(rdpRpc* rpc) + + /* Send CONN/A1 PDU over OUT channel */ + +- if (rts_send_CONN_A1_pdu(rpc) < 0) ++ if (!rts_send_CONN_A1_pdu(rpc)) + { + http_response_free(response); + WLog_ERR(TAG, "rpc_send_CONN_A1_pdu error!"); +@@ -549,7 +591,8 @@ static int rpc_client_default_out_channel_recv(rdpRpc* rpc) + + if (statusCode == HTTP_STATUS_DENIED) + { +- freerdp_set_last_error_if_not(rpc->context, FREERDP_ERROR_AUTHENTICATION_FAILED); ++ rdpContext* context = rpc->context; ++ freerdp_set_last_error_if_not(context, FREERDP_ERROR_AUTHENTICATION_FAILED); + } + + http_response_free(response); +@@ -563,12 +606,13 @@ static int rpc_client_default_out_channel_recv(rdpRpc* rpc) + } + else + { +- wStream* fragment; +- rpcconn_common_hdr_t* header; +- fragment = rpc->client->ReceiveFragment; ++ wStream* fragment = rpc->client->ReceiveFragment; + + while (1) + { ++ size_t pos; ++ rpcconn_common_hdr_t header = { 0 }; ++ + while (Stream_GetPosition(fragment) < RPC_COMMON_FIELDS_LENGTH) + { + status = rpc_channel_read(&outChannel->common, fragment, +@@ -581,22 +625,27 @@ static int rpc_client_default_out_channel_recv(rdpRpc* rpc) + return 0; + } + +- header = (rpcconn_common_hdr_t*)Stream_Buffer(fragment); ++ pos = Stream_GetPosition(fragment); ++ Stream_SetPosition(fragment, 0); ++ ++ /* Ignore errors, the PDU might not be complete. */ ++ rts_read_common_pdu_header(fragment, &header); ++ Stream_SetPosition(fragment, pos); + +- if (header->frag_length > rpc->max_recv_frag) ++ if (header.frag_length > rpc->max_recv_frag) + { + WLog_ERR(TAG, + "rpc_client_recv: invalid fragment size: %" PRIu16 " (max: %" PRIu16 ")", +- header->frag_length, rpc->max_recv_frag); ++ header.frag_length, rpc->max_recv_frag); + winpr_HexDump(TAG, WLOG_ERROR, Stream_Buffer(fragment), + Stream_GetPosition(fragment)); + return -1; + } + +- while (Stream_GetPosition(fragment) < header->frag_length) ++ while (Stream_GetPosition(fragment) < header.frag_length) + { + status = rpc_channel_read(&outChannel->common, fragment, +- header->frag_length - Stream_GetPosition(fragment)); ++ header.frag_length - Stream_GetPosition(fragment)); + + if (status < 0) + { +@@ -604,14 +653,12 @@ static int rpc_client_default_out_channel_recv(rdpRpc* rpc) + return -1; + } + +- if (Stream_GetPosition(fragment) < header->frag_length) ++ if (Stream_GetPosition(fragment) < header.frag_length) + return 0; + } + + { + /* complete fragment received */ +- Stream_SealLength(fragment); +- Stream_SetPosition(fragment, 0); + status = rpc_client_recv_fragment(rpc, fragment); + + if (status < 0) +@@ -663,10 +710,10 @@ static int rpc_client_nondefault_out_channel_recv(rdpRpc* rpc) + if (rpc_ncacn_http_send_out_channel_request(&nextOutChannel->common, TRUE)) + { + rpc_ncacn_http_ntlm_uninit(&nextOutChannel->common); +- status = rts_send_OUT_R1_A3_pdu(rpc); + +- if (status >= 0) ++ if (rts_send_OUT_R1_A3_pdu(rpc)) + { ++ status = 1; + rpc_out_channel_transition_to_state( + nextOutChannel, CLIENT_OUT_CHANNEL_STATE_OPENED_A6W); + } +@@ -687,11 +734,14 @@ static int rpc_client_nondefault_out_channel_recv(rdpRpc* rpc) + + break; + ++ case CLIENT_OUT_CHANNEL_STATE_INITIAL: ++ case CLIENT_OUT_CHANNEL_STATE_CONNECTED: ++ case CLIENT_OUT_CHANNEL_STATE_NEGOTIATED: + default: + WLog_ERR(TAG, + "rpc_client_nondefault_out_channel_recv: Unexpected message %08" PRIx32, + nextOutChannel->State); +- return -1; ++ status = -1; + } + + http_response_free(response); +@@ -769,7 +819,7 @@ int rpc_client_in_channel_recv(rdpRpc* rpc) + + /* Send CONN/B1 PDU over IN channel */ + +- if (rts_send_CONN_B1_pdu(rpc) < 0) ++ if (!rts_send_CONN_B1_pdu(rpc)) + { + WLog_ERR(TAG, "rpc_send_CONN_B1_pdu error!"); + http_response_free(response); +@@ -810,8 +860,8 @@ int rpc_client_in_channel_recv(rdpRpc* rpc) + + RpcClientCall* rpc_client_call_find_by_id(RpcClient* client, UINT32 CallId) + { +- int index; +- int count; ++ size_t index; ++ size_t count; + RpcClientCall* clientCall = NULL; + + if (!client) +@@ -856,18 +906,23 @@ static void rpc_array_client_call_free(void* call) + rpc_client_call_free((RpcClientCall*)call); + } + +-int rpc_in_channel_send_pdu(RpcInChannel* inChannel, BYTE* buffer, UINT32 length) ++int rpc_in_channel_send_pdu(RpcInChannel* inChannel, const BYTE* buffer, size_t length) + { +- int status; ++ SSIZE_T status; + RpcClientCall* clientCall; +- rpcconn_common_hdr_t* header; ++ wStream s; ++ rpcconn_common_hdr_t header = { 0 }; ++ + status = rpc_channel_write(&inChannel->common, buffer, length); + + if (status <= 0) + return -1; + +- header = (rpcconn_common_hdr_t*)buffer; +- clientCall = rpc_client_call_find_by_id(inChannel->common.client, header->call_id); ++ Stream_StaticInit(&s, buffer, length); ++ if (!rts_read_common_pdu_header(&s, &header)) ++ return -1; ++ ++ clientCall = rpc_client_call_find_by_id(inChannel->common.client, header.call_id); + clientCall->State = RPC_CLIENT_CALL_STATE_DISPATCHED; + + /* +@@ -877,7 +932,7 @@ int rpc_in_channel_send_pdu(RpcInChannel* inChannel, BYTE* buffer, UINT32 length + * variables specified by this abstract data model. + */ + +- if (header->ptype == PTYPE_REQUEST) ++ if (header.ptype == PTYPE_REQUEST) + { + inChannel->BytesSent += status; + inChannel->SenderAvailableWindow -= status; +@@ -888,7 +943,7 @@ int rpc_in_channel_send_pdu(RpcInChannel* inChannel, BYTE* buffer, UINT32 length + + BOOL rpc_client_write_call(rdpRpc* rpc, wStream* s, UINT16 opnum) + { +- UINT32 offset; ++ size_t offset; + BYTE* buffer = NULL; + UINT32 stub_data_pad; + SecBuffer Buffers[2] = { 0 }; +@@ -936,7 +991,7 @@ BOOL rpc_client_write_call(rdpRpc* rpc, wStream* s, UINT16 opnum) + if (size < 0) + goto fail; + +- rpc_pdu_header_init(rpc, &request_pdu.header); ++ request_pdu.header = rpc_pdu_header_init(rpc); + request_pdu.header.ptype = PTYPE_REQUEST; + request_pdu.header.pfc_flags = PFC_FIRST_FRAG | PFC_LAST_FRAG; + request_pdu.header.auth_length = (UINT16)size; +@@ -1025,7 +1080,7 @@ static BOOL rpc_client_resolve_gateway(rdpSettings* settings, char** host, UINT1 + const char* peerHostname = settings->GatewayHostname; + const char* proxyUsername = settings->ProxyUsername; + const char* proxyPassword = settings->ProxyPassword; +- *port = settings->GatewayPort; ++ *port = (UINT16)settings->GatewayPort; + *isProxy = proxy_prepare(settings, &peerHostname, port, &proxyUsername, &proxyPassword); + result = freerdp_tcp_resolve_host(peerHostname, *port, 0); + +diff --git a/libfreerdp/core/gateway/rpc_client.h b/libfreerdp/core/gateway/rpc_client.h +index af0b8ce..7b509de 100644 +--- a/libfreerdp/core/gateway/rpc_client.h ++++ b/libfreerdp/core/gateway/rpc_client.h +@@ -31,7 +31,8 @@ FREERDP_LOCAL RpcClientCall* rpc_client_call_find_by_id(RpcClient* client, UINT3 + FREERDP_LOCAL RpcClientCall* rpc_client_call_new(UINT32 CallId, UINT32 OpNum); + FREERDP_LOCAL void rpc_client_call_free(RpcClientCall* client_call); + +-FREERDP_LOCAL int rpc_in_channel_send_pdu(RpcInChannel* inChannel, BYTE* buffer, UINT32 length); ++FREERDP_LOCAL int rpc_in_channel_send_pdu(RpcInChannel* inChannel, const BYTE* buffer, ++ size_t length); + + FREERDP_LOCAL int rpc_client_in_channel_recv(rdpRpc* rpc); + FREERDP_LOCAL int rpc_client_out_channel_recv(rdpRpc* rpc); +diff --git a/libfreerdp/core/gateway/rts.c b/libfreerdp/core/gateway/rts.c +index c003b1e..ccbe0be 100644 +--- a/libfreerdp/core/gateway/rts.c ++++ b/libfreerdp/core/gateway/rts.c +@@ -21,6 +21,7 @@ + #include "config.h" + #endif + ++#include + #include + #include + #include +@@ -29,6 +30,7 @@ + + #include "ncacn_http.h" + #include "rpc_client.h" ++#include "rts_signature.h" + + #include "rts.h" + +@@ -67,545 +69,1643 @@ + * + */ + ++static const char* rts_pdu_ptype_to_string(UINT32 ptype) ++{ ++ switch (ptype) ++ { ++ case PTYPE_REQUEST: ++ return "PTYPE_REQUEST"; ++ case PTYPE_PING: ++ return "PTYPE_PING"; ++ case PTYPE_RESPONSE: ++ return "PTYPE_RESPONSE"; ++ case PTYPE_FAULT: ++ return "PTYPE_FAULT"; ++ case PTYPE_WORKING: ++ return "PTYPE_WORKING"; ++ case PTYPE_NOCALL: ++ return "PTYPE_NOCALL"; ++ case PTYPE_REJECT: ++ return "PTYPE_REJECT"; ++ case PTYPE_ACK: ++ return "PTYPE_ACK"; ++ case PTYPE_CL_CANCEL: ++ return "PTYPE_CL_CANCEL"; ++ case PTYPE_FACK: ++ return "PTYPE_FACK"; ++ case PTYPE_CANCEL_ACK: ++ return "PTYPE_CANCEL_ACK"; ++ case PTYPE_BIND: ++ return "PTYPE_BIND"; ++ case PTYPE_BIND_ACK: ++ return "PTYPE_BIND_ACK"; ++ case PTYPE_BIND_NAK: ++ return "PTYPE_BIND_NAK"; ++ case PTYPE_ALTER_CONTEXT: ++ return "PTYPE_ALTER_CONTEXT"; ++ case PTYPE_ALTER_CONTEXT_RESP: ++ return "PTYPE_ALTER_CONTEXT_RESP"; ++ case PTYPE_RPC_AUTH_3: ++ return "PTYPE_RPC_AUTH_3"; ++ case PTYPE_SHUTDOWN: ++ return "PTYPE_SHUTDOWN"; ++ case PTYPE_CO_CANCEL: ++ return "PTYPE_CO_CANCEL"; ++ case PTYPE_ORPHANED: ++ return "PTYPE_ORPHANED"; ++ case PTYPE_RTS: ++ return "PTYPE_RTS"; ++ default: ++ return "UNKNOWN"; ++ } ++} + static rpcconn_rts_hdr_t rts_pdu_header_init(void) + { +- rpcconn_rts_hdr_t header = { 0 }; +- header.header.rpc_vers = 5; +- header.header.rpc_vers_minor = 0; +- header.header.ptype = PTYPE_RTS; +- header.header.packed_drep[0] = 0x10; +- header.header.packed_drep[1] = 0x00; +- header.header.packed_drep[2] = 0x00; +- header.header.packed_drep[3] = 0x00; +- header.header.pfc_flags = PFC_FIRST_FRAG | PFC_LAST_FRAG; +- header.header.auth_length = 0; +- header.header.call_id = 0; ++ rpcconn_rts_hdr_t header = { 0 }; ++ header.header.rpc_vers = 5; ++ header.header.rpc_vers_minor = 0; ++ header.header.ptype = PTYPE_RTS; ++ header.header.packed_drep[0] = 0x10; ++ header.header.packed_drep[1] = 0x00; ++ header.header.packed_drep[2] = 0x00; ++ header.header.packed_drep[3] = 0x00; ++ header.header.pfc_flags = PFC_FIRST_FRAG | PFC_LAST_FRAG; ++ header.header.auth_length = 0; ++ header.header.call_id = 0; ++ ++ return header; ++} ++ ++static BOOL rts_align_stream(wStream* s, size_t alignment) ++{ ++ size_t pos, pad; ++ ++ assert(s); ++ assert(alignment > 0); ++ ++ pos = Stream_GetPosition(s); ++ pad = rpc_offset_align(&pos, alignment); ++ return Stream_SafeSeek(s, pad); ++} ++ ++static char* sdup(const void* src, size_t length) ++{ ++ char* dst; ++ assert(src || (length == 0)); ++ if (length == 0) ++ return NULL; ++ ++ dst = calloc(length + 1, sizeof(char)); ++ if (!dst) ++ return NULL; ++ memcpy(dst, src, length); ++ return dst; ++} ++ ++static BOOL rts_write_common_pdu_header(wStream* s, const rpcconn_common_hdr_t* header) ++{ ++ assert(s); ++ assert(header); ++ if (!Stream_EnsureRemainingCapacity(s, sizeof(rpcconn_common_hdr_t))) ++ return FALSE; ++ ++ Stream_Write_UINT8(s, header->rpc_vers); ++ Stream_Write_UINT8(s, header->rpc_vers_minor); ++ Stream_Write_UINT8(s, header->ptype); ++ Stream_Write_UINT8(s, header->pfc_flags); ++ Stream_Write(s, header->packed_drep, ARRAYSIZE(header->packed_drep)); ++ Stream_Write_UINT16(s, header->frag_length); ++ Stream_Write_UINT16(s, header->auth_length); ++ Stream_Write_UINT32(s, header->call_id); ++ return TRUE; ++} ++ ++BOOL rts_read_common_pdu_header(wStream* s, rpcconn_common_hdr_t* header) ++{ ++ size_t left; ++ assert(s); ++ assert(header); ++ ++ if (Stream_GetRemainingLength(s) < sizeof(rpcconn_common_hdr_t)) ++ return FALSE; ++ ++ Stream_Read_UINT8(s, header->rpc_vers); ++ Stream_Read_UINT8(s, header->rpc_vers_minor); ++ Stream_Read_UINT8(s, header->ptype); ++ Stream_Read_UINT8(s, header->pfc_flags); ++ Stream_Read(s, header->packed_drep, ARRAYSIZE(header->packed_drep)); ++ Stream_Read_UINT16(s, header->frag_length); ++ Stream_Read_UINT16(s, header->auth_length); ++ Stream_Read_UINT32(s, header->call_id); ++ ++ if (header->frag_length < sizeof(rpcconn_common_hdr_t)) ++ return FALSE; ++ ++ left = Stream_GetRemainingLength(s); ++ if (left < header->frag_length - sizeof(rpcconn_common_hdr_t)) ++ return FALSE; ++ ++ return TRUE; ++} ++ ++static BOOL rts_read_auth_verifier_no_checks(wStream* s, auth_verifier_co_t* auth, ++ const rpcconn_common_hdr_t* header, size_t* startPos) ++{ ++ assert(s); ++ assert(auth); ++ assert(header); ++ ++ assert(header->frag_length > header->auth_length); ++ ++ if (startPos) ++ *startPos = Stream_GetPosition(s); ++ ++ /* Read the auth verifier and check padding matches frag_length */ ++ { ++ const size_t expected = header->frag_length - header->auth_length - 8; ++ ++ Stream_SetPosition(s, expected); ++ if (Stream_GetRemainingLength(s) < sizeof(auth_verifier_co_t)) ++ return FALSE; ++ ++ Stream_Read_UINT8(s, auth->auth_type); ++ Stream_Read_UINT8(s, auth->auth_level); ++ Stream_Read_UINT8(s, auth->auth_pad_length); ++ Stream_Read_UINT8(s, auth->auth_reserved); ++ Stream_Read_UINT32(s, auth->auth_context_id); ++ } ++ ++ if (header->auth_length != 0) ++ { ++ const void* ptr = Stream_Pointer(s); ++ if (!Stream_SafeSeek(s, header->auth_length)) ++ return FALSE; ++ auth->auth_value = (BYTE*)sdup(ptr, header->auth_length); ++ if (auth->auth_value == NULL) ++ return FALSE; ++ } ++ ++ return TRUE; ++} ++ ++static BOOL rts_read_auth_verifier(wStream* s, auth_verifier_co_t* auth, ++ const rpcconn_common_hdr_t* header) ++{ ++ size_t pos; ++ assert(s); ++ assert(auth); ++ assert(header); ++ ++ if (!rts_read_auth_verifier_no_checks(s, auth, header, &pos)) ++ return FALSE; ++ ++ { ++ const size_t expected = header->frag_length - header->auth_length - 8; ++ assert(pos + auth->auth_pad_length == expected); ++ } ++ ++ return TRUE; ++} ++ ++static BOOL rts_read_auth_verifier_with_stub(wStream* s, auth_verifier_co_t* auth, ++ rpcconn_common_hdr_t* header) ++{ ++ size_t pos; ++ size_t alloc_hint = 0; ++ BYTE** ptr = NULL; ++ ++ if (!rts_read_auth_verifier_no_checks(s, auth, header, &pos)) ++ return FALSE; ++ ++ switch (header->ptype) ++ { ++ case PTYPE_FAULT: ++ { ++ rpcconn_fault_hdr_t* hdr = (rpcconn_fault_hdr_t*)header; ++ alloc_hint = hdr->alloc_hint; ++ ptr = &hdr->stub_data; ++ } ++ break; ++ case PTYPE_RESPONSE: ++ { ++ rpcconn_response_hdr_t* hdr = (rpcconn_response_hdr_t*)header; ++ alloc_hint = hdr->alloc_hint; ++ ptr = &hdr->stub_data; ++ } ++ break; ++ case PTYPE_REQUEST: ++ { ++ rpcconn_request_hdr_t* hdr = (rpcconn_request_hdr_t*)header; ++ alloc_hint = hdr->alloc_hint; ++ ptr = &hdr->stub_data; ++ } ++ break; ++ default: ++ return FALSE; ++ } ++ ++ if (alloc_hint > 0) ++ { ++ const size_t size = ++ header->frag_length - header->auth_length - 8 - auth->auth_pad_length - pos; ++ const void* src = Stream_Buffer(s) + pos; ++ ++ *ptr = (BYTE*)sdup(src, size); ++ if (!*ptr) ++ return FALSE; ++ } ++ ++ return TRUE; ++} ++ ++static void rts_free_auth_verifier(auth_verifier_co_t* auth) ++{ ++ if (!auth) ++ return; ++ free(auth->auth_value); ++} ++ ++static BOOL rts_write_auth_verifier(wStream* s, const auth_verifier_co_t* auth, ++ const rpcconn_common_hdr_t* header) ++{ ++ size_t pos; ++ UINT8 auth_pad_length = 0; ++ ++ assert(s); ++ assert(auth); ++ assert(header); ++ ++ /* Align start to a multiple of 4 */ ++ pos = Stream_GetPosition(s); ++ if ((pos % 4) != 0) ++ { ++ auth_pad_length = 4 - (pos % 4); ++ if (!Stream_EnsureRemainingCapacity(s, auth_pad_length)) ++ return FALSE; ++ Stream_Zero(s, auth_pad_length); ++ } ++ ++ assert(header->frag_length + 8ull > header->auth_length); ++ { ++ size_t pos = Stream_GetPosition(s); ++ size_t expected = header->frag_length - header->auth_length - 8; ++ ++ assert(pos == expected); ++ } ++ ++ if (!Stream_EnsureRemainingCapacity(s, sizeof(auth_verifier_co_t))) ++ return FALSE; ++ ++ Stream_Write_UINT8(s, auth->auth_type); ++ Stream_Write_UINT8(s, auth->auth_level); ++ Stream_Write_UINT8(s, auth_pad_length); ++ Stream_Write_UINT8(s, 0); /* auth->auth_reserved */ ++ Stream_Write_UINT32(s, auth->auth_context_id); ++ ++ if (!Stream_EnsureRemainingCapacity(s, header->auth_length)) ++ return FALSE; ++ Stream_Write(s, auth->auth_value, header->auth_length); ++ return TRUE; ++} ++ ++static BOOL rts_read_version(wStream* s, p_rt_version_t* version) ++{ ++ assert(s); ++ assert(version); ++ ++ if (Stream_GetRemainingLength(s) < 2 * sizeof(UINT8)) ++ return FALSE; ++ Stream_Read_UINT8(s, version->major); ++ Stream_Read_UINT8(s, version->minor); ++ return TRUE; ++} ++ ++void rts_free_supported_versions(p_rt_versions_supported_t* versions) ++{ ++ if (!versions) ++ return; ++ free(versions->p_protocols); ++ versions->p_protocols = NULL; ++} ++ ++static BOOL rts_read_supported_versions(wStream* s, p_rt_versions_supported_t* versions) ++{ ++ BYTE x; ++ ++ assert(s); ++ assert(versions); ++ ++ if (Stream_GetRemainingLength(s) < sizeof(UINT8)) ++ return FALSE; ++ ++ Stream_Read_UINT8(s, versions->n_protocols); /* count */ ++ ++ if (versions->n_protocols > 0) ++ { ++ versions->p_protocols = calloc(versions->n_protocols, sizeof(p_rt_version_t)); ++ if (!versions->p_protocols) ++ return FALSE; ++ } ++ for (x = 0; x < versions->n_protocols; x++) ++ { ++ p_rt_version_t* version = &versions->p_protocols[x]; ++ if (!rts_read_version(s, version)) /* size_is(n_protocols) */ ++ { ++ rts_free_supported_versions(versions); ++ return FALSE; ++ } ++ } ++ ++ return TRUE; ++} ++ ++static BOOL rts_read_port_any(wStream* s, port_any_t* port) ++{ ++ const void* ptr; ++ ++ assert(s); ++ assert(port); ++ ++ if (Stream_GetRemainingLength(s) < sizeof(UINT16)) ++ return FALSE; ++ ++ Stream_Read_UINT16(s, port->length); ++ if (port->length == 0) ++ return TRUE; ++ ++ ptr = Stream_Pointer(s); ++ if (!Stream_SafeSeek(s, port->length)) ++ return FALSE; ++ port->port_spec = sdup(ptr, port->length); ++ return port->port_spec != NULL; ++} ++ ++static void rts_free_port_any(port_any_t* port) ++{ ++ if (!port) ++ return; ++ free(port->port_spec); ++} ++ ++static BOOL rts_read_uuid(wStream* s, p_uuid_t* uuid) ++{ ++ assert(s); ++ assert(uuid); ++ ++ if (Stream_GetRemainingLength(s) < sizeof(p_uuid_t)) ++ return FALSE; ++ ++ Stream_Read_UINT32(s, uuid->time_low); ++ Stream_Read_UINT16(s, uuid->time_mid); ++ Stream_Read_UINT16(s, uuid->time_hi_and_version); ++ Stream_Read_UINT8(s, uuid->clock_seq_hi_and_reserved); ++ Stream_Read_UINT8(s, uuid->clock_seq_low); ++ Stream_Read(s, uuid->node, ARRAYSIZE(uuid->node)); ++ return TRUE; ++} ++ ++static BOOL rts_write_uuid(wStream* s, const p_uuid_t* uuid) ++{ ++ assert(s); ++ assert(uuid); ++ ++ if (!Stream_EnsureRemainingCapacity(s, sizeof(p_uuid_t))) ++ return FALSE; ++ ++ Stream_Write_UINT32(s, uuid->time_low); ++ Stream_Write_UINT16(s, uuid->time_mid); ++ Stream_Write_UINT16(s, uuid->time_hi_and_version); ++ Stream_Write_UINT8(s, uuid->clock_seq_hi_and_reserved); ++ Stream_Write_UINT8(s, uuid->clock_seq_low); ++ Stream_Write(s, uuid->node, ARRAYSIZE(uuid->node)); ++ return TRUE; ++} ++ ++static p_syntax_id_t* rts_syntax_id_new(size_t count) ++{ ++ return calloc(count, sizeof(p_syntax_id_t)); ++} ++ ++static void rts_syntax_id_free(p_syntax_id_t* ptr) ++{ ++ free(ptr); ++} ++ ++static BOOL rts_read_syntax_id(wStream* s, p_syntax_id_t* syntax_id) ++{ ++ assert(s); ++ assert(syntax_id); ++ ++ if (!rts_read_uuid(s, &syntax_id->if_uuid)) ++ return FALSE; ++ ++ if (Stream_GetRemainingLength(s) < 4) ++ return FALSE; ++ ++ Stream_Read_UINT32(s, syntax_id->if_version); ++ return TRUE; ++} ++ ++static BOOL rts_write_syntax_id(wStream* s, const p_syntax_id_t* syntax_id) ++{ ++ assert(s); ++ assert(syntax_id); ++ ++ if (!rts_write_uuid(s, &syntax_id->if_uuid)) ++ return FALSE; ++ ++ if (!Stream_EnsureRemainingCapacity(s, 4)) ++ return FALSE; ++ ++ Stream_Write_UINT32(s, syntax_id->if_version); ++ return TRUE; ++} ++ ++p_cont_elem_t* rts_context_elem_new(size_t count) ++{ ++ p_cont_elem_t* ctx = calloc(count, sizeof(p_cont_elem_t)); ++ return ctx; ++} ++ ++void rts_context_elem_free(p_cont_elem_t* ptr) ++{ ++ if (!ptr) ++ return; ++ rts_syntax_id_free(ptr->transfer_syntaxes); ++ free(ptr); ++} ++ ++static BOOL rts_read_context_elem(wStream* s, p_cont_elem_t* element) ++{ ++ BYTE x; ++ assert(s); ++ assert(element); ++ ++ if (Stream_GetRemainingLength(s) < 4) ++ return FALSE; ++ ++ Stream_Read_UINT16(s, element->p_cont_id); ++ Stream_Read_UINT8(s, element->n_transfer_syn); /* number of items */ ++ Stream_Read_UINT8(s, element->reserved); /* alignment pad, m.b.z. */ ++ ++ if (!rts_read_syntax_id(s, &element->abstract_syntax)) /* transfer syntax list */ ++ return FALSE; ++ ++ if (element->n_transfer_syn > 0) ++ { ++ element->transfer_syntaxes = rts_syntax_id_new(element->n_transfer_syn); ++ if (!element->transfer_syntaxes) ++ return FALSE; ++ for (x = 0; x < element->n_transfer_syn; x++) ++ { ++ p_syntax_id_t* syn = &element->transfer_syntaxes[x]; ++ if (!rts_read_syntax_id(s, syn)) /* size_is(n_transfer_syn) */ ++ return FALSE; ++ } ++ } ++ ++ return TRUE; ++} ++ ++static BOOL rts_write_context_elem(wStream* s, const p_cont_elem_t* element) ++{ ++ BYTE x; ++ assert(s); ++ assert(element); ++ ++ if (!Stream_EnsureRemainingCapacity(s, 4)) ++ return FALSE; ++ Stream_Write_UINT16(s, element->p_cont_id); ++ Stream_Write_UINT8(s, element->n_transfer_syn); /* number of items */ ++ Stream_Write_UINT8(s, element->reserved); /* alignment pad, m.b.z. */ ++ if (!rts_write_syntax_id(s, &element->abstract_syntax)) /* transfer syntax list */ ++ return FALSE; ++ ++ for (x = 0; x < element->n_transfer_syn; x++) ++ { ++ const p_syntax_id_t* syn = &element->transfer_syntaxes[x]; ++ if (!rts_write_syntax_id(s, syn)) /* size_is(n_transfer_syn) */ ++ return FALSE; ++ } ++ ++ return TRUE; ++} ++ ++static BOOL rts_read_context_list(wStream* s, p_cont_list_t* list) ++{ ++ BYTE x; ++ ++ assert(s); ++ assert(list); ++ ++ if (Stream_GetRemainingLength(s) < 4) ++ return FALSE; ++ Stream_Read_UINT8(s, list->n_context_elem); /* number of items */ ++ Stream_Read_UINT8(s, list->reserved); /* alignment pad, m.b.z. */ ++ Stream_Read_UINT16(s, list->reserved2); /* alignment pad, m.b.z. */ ++ ++ if (list->n_context_elem > 0) ++ { ++ list->p_cont_elem = rts_context_elem_new(list->n_context_elem); ++ if (!list->p_cont_elem) ++ return FALSE; ++ for (x = 0; x < list->n_context_elem; x++) ++ { ++ p_cont_elem_t* element = &list->p_cont_elem[x]; ++ if (!rts_read_context_elem(s, element)) ++ return FALSE; ++ } ++ } ++ return TRUE; ++} ++ ++static void rts_free_context_list(p_cont_list_t* list) ++{ ++ if (!list) ++ return; ++ rts_context_elem_free(list->p_cont_elem); ++} ++ ++static BOOL rts_write_context_list(wStream* s, const p_cont_list_t* list) ++{ ++ BYTE x; ++ ++ assert(s); ++ assert(list); ++ ++ if (!Stream_EnsureRemainingCapacity(s, 4)) ++ return FALSE; ++ Stream_Write_UINT8(s, list->n_context_elem); /* number of items */ ++ Stream_Write_UINT8(s, 0); /* alignment pad, m.b.z. */ ++ Stream_Write_UINT16(s, 0); /* alignment pad, m.b.z. */ ++ ++ for (x = 0; x < list->n_context_elem; x++) ++ { ++ const p_cont_elem_t* element = &list->p_cont_elem[x]; ++ if (!rts_write_context_elem(s, element)) ++ return FALSE; ++ } ++ return TRUE; ++} ++ ++static p_result_t* rts_result_new(size_t count) ++{ ++ return calloc(count, sizeof(p_result_t)); ++} ++ ++static void rts_result_free(p_result_t* results) ++{ ++ if (!results) ++ return; ++ free(results); ++} ++ ++static BOOL rts_read_result(wStream* s, p_result_t* result) ++{ ++ assert(s); ++ assert(result); ++ ++ if (Stream_GetRemainingLength(s) < 2) ++ return FALSE; ++ Stream_Read_UINT16(s, result->result); ++ Stream_Read_UINT16(s, result->reason); ++ ++ return rts_read_syntax_id(s, &result->transfer_syntax); ++} ++ ++static void rts_free_result(p_result_t* result) ++{ ++ if (!result) ++ return; ++} ++ ++static BOOL rts_read_result_list(wStream* s, p_result_list_t* list) ++{ ++ BYTE x; ++ ++ assert(s); ++ assert(list); ++ ++ if (Stream_GetRemainingLength(s) < 4) ++ return FALSE; ++ Stream_Read_UINT8(s, list->n_results); /* count */ ++ Stream_Read_UINT8(s, list->reserved); /* alignment pad, m.b.z. */ ++ Stream_Read_UINT16(s, list->reserved2); /* alignment pad, m.b.z. */ ++ ++ if (list->n_results > 0) ++ { ++ list->p_results = rts_result_new(list->n_results); ++ if (!list->p_results) ++ return FALSE; ++ ++ for (x = 0; x < list->n_results; x++) ++ { ++ p_result_t* result = &list->p_results[x]; /* size_is(n_results) */ ++ if (!rts_read_result(s, result)) ++ return FALSE; ++ } ++ } ++ ++ return TRUE; ++} ++ ++static void rts_free_result_list(p_result_list_t* list) ++{ ++ BYTE x; ++ ++ if (!list) ++ return; ++ for (x = 0; x < list->n_results; x++) ++ { ++ p_result_t* result = &list->p_results[x]; ++ rts_free_result(result); ++ } ++ rts_result_free(list->p_results); ++} ++ ++static void rts_free_pdu_alter_context(rpcconn_alter_context_hdr_t* ctx) ++{ ++ if (!ctx) ++ return; ++ ++ rts_free_context_list(&ctx->p_context_elem); ++ rts_free_auth_verifier(&ctx->auth_verifier); ++} ++ ++static BOOL rts_read_pdu_alter_context(wStream* s, rpcconn_alter_context_hdr_t* ctx) ++{ ++ assert(s); ++ assert(ctx); ++ ++ if (Stream_GetRemainingLength(s) < ++ sizeof(rpcconn_alter_context_hdr_t) - sizeof(rpcconn_common_hdr_t)) ++ return FALSE; ++ ++ Stream_Read_UINT16(s, ctx->max_xmit_frag); ++ Stream_Read_UINT16(s, ctx->max_recv_frag); ++ Stream_Read_UINT32(s, ctx->assoc_group_id); ++ ++ if (!rts_read_context_list(s, &ctx->p_context_elem)) ++ return FALSE; ++ ++ if (!rts_read_auth_verifier(s, &ctx->auth_verifier, &ctx->header)) ++ return FALSE; ++ ++ return TRUE; ++} ++ ++static BOOL rts_read_pdu_alter_context_response(wStream* s, ++ rpcconn_alter_context_response_hdr_t* ctx) ++{ ++ assert(s); ++ assert(ctx); ++ ++ if (Stream_GetRemainingLength(s) < ++ sizeof(rpcconn_alter_context_response_hdr_t) - sizeof(rpcconn_common_hdr_t)) ++ return FALSE; ++ Stream_Read_UINT16(s, ctx->max_xmit_frag); ++ Stream_Read_UINT16(s, ctx->max_recv_frag); ++ Stream_Read_UINT32(s, ctx->assoc_group_id); ++ ++ if (!rts_read_port_any(s, &ctx->sec_addr)) ++ return FALSE; ++ ++ if (!rts_align_stream(s, 4)) ++ return FALSE; ++ ++ if (!rts_read_result_list(s, &ctx->p_result_list)) ++ return FALSE; ++ ++ if (!rts_read_auth_verifier(s, &ctx->auth_verifier, &ctx->header)) ++ return FALSE; ++ ++ return TRUE; ++} ++ ++static void rts_free_pdu_alter_context_response(rpcconn_alter_context_response_hdr_t* ctx) ++{ ++ if (!ctx) ++ return; ++ ++ rts_free_port_any(&ctx->sec_addr); ++ rts_free_result_list(&ctx->p_result_list); ++ rts_free_auth_verifier(&ctx->auth_verifier); ++} ++ ++static BOOL rts_read_pdu_bind(wStream* s, rpcconn_bind_hdr_t* ctx) ++{ ++ assert(s); ++ assert(ctx); ++ ++ if (Stream_GetRemainingLength(s) < sizeof(rpcconn_bind_hdr_t) - sizeof(rpcconn_common_hdr_t)) ++ return FALSE; ++ Stream_Read_UINT16(s, ctx->max_xmit_frag); ++ Stream_Read_UINT16(s, ctx->max_recv_frag); ++ Stream_Read_UINT32(s, ctx->assoc_group_id); ++ ++ if (!rts_read_context_list(s, &ctx->p_context_elem)) ++ return FALSE; ++ ++ if (!rts_read_auth_verifier(s, &ctx->auth_verifier, &ctx->header)) ++ return FALSE; ++ ++ return TRUE; ++} ++ ++static void rts_free_pdu_bind(rpcconn_bind_hdr_t* ctx) ++{ ++ if (!ctx) ++ return; ++ rts_free_context_list(&ctx->p_context_elem); ++ rts_free_auth_verifier(&ctx->auth_verifier); ++} ++ ++static BOOL rts_read_pdu_bind_ack(wStream* s, rpcconn_bind_ack_hdr_t* ctx) ++{ ++ assert(s); ++ assert(ctx); ++ ++ if (Stream_GetRemainingLength(s) < ++ sizeof(rpcconn_bind_ack_hdr_t) - sizeof(rpcconn_common_hdr_t)) ++ return FALSE; ++ Stream_Read_UINT16(s, ctx->max_xmit_frag); ++ Stream_Read_UINT16(s, ctx->max_recv_frag); ++ Stream_Read_UINT32(s, ctx->assoc_group_id); ++ ++ if (!rts_read_port_any(s, &ctx->sec_addr)) ++ return FALSE; ++ ++ if (!rts_align_stream(s, 4)) ++ return FALSE; ++ ++ if (!rts_read_result_list(s, &ctx->p_result_list)) ++ return FALSE; ++ ++ return rts_read_auth_verifier(s, &ctx->auth_verifier, &ctx->header); ++} ++ ++static void rts_free_pdu_bind_ack(rpcconn_bind_ack_hdr_t* ctx) ++{ ++ if (!ctx) ++ return; ++ rts_free_port_any(&ctx->sec_addr); ++ rts_free_result_list(&ctx->p_result_list); ++ rts_free_auth_verifier(&ctx->auth_verifier); ++} ++ ++static BOOL rts_read_pdu_bind_nak(wStream* s, rpcconn_bind_nak_hdr_t* ctx) ++{ ++ assert(s); ++ assert(ctx); ++ ++ if (Stream_GetRemainingLength(s) < ++ sizeof(rpcconn_bind_nak_hdr_t) - sizeof(rpcconn_common_hdr_t)) ++ return FALSE; ++ Stream_Read_UINT16(s, ctx->provider_reject_reason); ++ return rts_read_supported_versions(s, &ctx->versions); ++} ++ ++static void rts_free_pdu_bind_nak(rpcconn_bind_nak_hdr_t* ctx) ++{ ++ if (!ctx) ++ return; ++ ++ rts_free_supported_versions(&ctx->versions); ++} ++ ++static BOOL rts_read_pdu_auth3(wStream* s, rpcconn_rpc_auth_3_hdr_t* ctx) ++{ ++ assert(s); ++ assert(ctx); ++ ++ if (Stream_GetRemainingLength(s) < ++ sizeof(rpcconn_rpc_auth_3_hdr_t) - sizeof(rpcconn_common_hdr_t)) ++ return FALSE; ++ Stream_Read_UINT16(s, ctx->max_xmit_frag); ++ Stream_Read_UINT16(s, ctx->max_recv_frag); ++ ++ return rts_read_auth_verifier(s, &ctx->auth_verifier, &ctx->header); ++} ++ ++static void rts_free_pdu_auth3(rpcconn_rpc_auth_3_hdr_t* ctx) ++{ ++ if (!ctx) ++ return; ++ rts_free_auth_verifier(&ctx->auth_verifier); ++} ++ ++static BOOL rts_read_pdu_fault(wStream* s, rpcconn_fault_hdr_t* ctx) ++{ ++ assert(s); ++ assert(ctx); ++ ++ if (Stream_GetRemainingLength(s) < sizeof(rpcconn_fault_hdr_t) - sizeof(rpcconn_common_hdr_t)) ++ return FALSE; ++ Stream_Read_UINT32(s, ctx->alloc_hint); ++ Stream_Read_UINT16(s, ctx->p_cont_id); ++ Stream_Read_UINT8(s, ctx->cancel_count); ++ Stream_Read_UINT8(s, ctx->reserved); ++ Stream_Read_UINT32(s, ctx->status); ++ ++ return rts_read_auth_verifier_with_stub(s, &ctx->auth_verifier, &ctx->header); ++} ++ ++static void rts_free_pdu_fault(rpcconn_fault_hdr_t* ctx) ++{ ++ if (!ctx) ++ return; ++ rts_free_auth_verifier(&ctx->auth_verifier); ++} ++ ++static BOOL rts_read_pdu_cancel_ack(wStream* s, rpcconn_cancel_hdr_t* ctx) ++{ ++ assert(s); ++ assert(ctx); ++ ++ if (Stream_GetRemainingLength(s) < sizeof(rpcconn_cancel_hdr_t) - sizeof(rpcconn_common_hdr_t)) ++ return FALSE; ++ return rts_read_auth_verifier(s, &ctx->auth_verifier, &ctx->header); ++} ++ ++static void rts_free_pdu_cancel_ack(rpcconn_cancel_hdr_t* ctx) ++{ ++ if (!ctx) ++ return; ++ rts_free_auth_verifier(&ctx->auth_verifier); ++} ++ ++static BOOL rts_read_pdu_orphaned(wStream* s, rpcconn_orphaned_hdr_t* ctx) ++{ ++ assert(s); ++ assert(ctx); ++ ++ if (Stream_GetRemainingLength(s) < ++ sizeof(rpcconn_orphaned_hdr_t) - sizeof(rpcconn_common_hdr_t)) ++ return FALSE; ++ return rts_read_auth_verifier(s, &ctx->auth_verifier, &ctx->header); ++} ++ ++static void rts_free_pdu_orphaned(rpcconn_orphaned_hdr_t* ctx) ++{ ++ if (!ctx) ++ return; ++ rts_free_auth_verifier(&ctx->auth_verifier); ++} ++ ++static BOOL rts_read_pdu_request(wStream* s, rpcconn_request_hdr_t* ctx) ++{ ++ assert(s); ++ assert(ctx); ++ ++ if (Stream_GetRemainingLength(s) < sizeof(rpcconn_request_hdr_t) - sizeof(rpcconn_common_hdr_t)) ++ return FALSE; ++ Stream_Read_UINT32(s, ctx->alloc_hint); ++ Stream_Read_UINT16(s, ctx->p_cont_id); ++ Stream_Read_UINT16(s, ctx->opnum); ++ if (!rts_read_uuid(s, &ctx->object)) ++ return FALSE; ++ ++ return rts_read_auth_verifier_with_stub(s, &ctx->auth_verifier, &ctx->header); ++} ++ ++static void rts_free_pdu_request(rpcconn_request_hdr_t* ctx) ++{ ++ if (!ctx) ++ return; ++ rts_free_auth_verifier(&ctx->auth_verifier); ++} ++ ++static BOOL rts_read_pdu_response(wStream* s, rpcconn_response_hdr_t* ctx) ++{ ++ assert(s); ++ assert(ctx); ++ ++ if (Stream_GetRemainingLength(s) < ++ sizeof(rpcconn_response_hdr_t) - sizeof(rpcconn_common_hdr_t)) ++ return FALSE; ++ Stream_Read_UINT32(s, ctx->alloc_hint); ++ Stream_Read_UINT16(s, ctx->p_cont_id); ++ Stream_Read_UINT8(s, ctx->cancel_count); ++ Stream_Read_UINT8(s, ctx->reserved); ++ ++ if (!rts_align_stream(s, 8)) ++ return FALSE; ++ ++ return rts_read_auth_verifier_with_stub(s, &ctx->auth_verifier, &ctx->header); ++} ++ ++static void rts_free_pdu_response(rpcconn_response_hdr_t* ctx) ++{ ++ if (!ctx) ++ return; ++ free(ctx->stub_data); ++ rts_free_auth_verifier(&ctx->auth_verifier); ++} ++ ++static BOOL rts_read_pdu_rts(wStream* s, rpcconn_rts_hdr_t* ctx) ++{ ++ assert(s); ++ assert(ctx); ++ ++ if (Stream_GetRemainingLength(s) < sizeof(rpcconn_rts_hdr_t) - sizeof(rpcconn_common_hdr_t)) ++ return FALSE; ++ ++ Stream_Read_UINT16(s, ctx->Flags); ++ Stream_Read_UINT16(s, ctx->NumberOfCommands); ++ return TRUE; ++} ++ ++static void rts_free_pdu_rts(rpcconn_rts_hdr_t* ctx) ++{ ++ WINPR_UNUSED(ctx); ++} ++ ++void rts_free_pdu_header(rpcconn_hdr_t* header, BOOL allocated) ++{ ++ if (!header) ++ return; ++ ++ switch (header->common.ptype) ++ { ++ case PTYPE_ALTER_CONTEXT: ++ rts_free_pdu_alter_context(&header->alter_context); ++ break; ++ case PTYPE_ALTER_CONTEXT_RESP: ++ rts_free_pdu_alter_context_response(&header->alter_context_response); ++ break; ++ case PTYPE_BIND: ++ rts_free_pdu_bind(&header->bind); ++ break; ++ case PTYPE_BIND_ACK: ++ rts_free_pdu_bind_ack(&header->bind_ack); ++ break; ++ case PTYPE_BIND_NAK: ++ rts_free_pdu_bind_nak(&header->bind_nak); ++ break; ++ case PTYPE_RPC_AUTH_3: ++ rts_free_pdu_auth3(&header->rpc_auth_3); ++ break; ++ case PTYPE_CANCEL_ACK: ++ rts_free_pdu_cancel_ack(&header->cancel); ++ break; ++ case PTYPE_FAULT: ++ rts_free_pdu_fault(&header->fault); ++ break; ++ case PTYPE_ORPHANED: ++ rts_free_pdu_orphaned(&header->orphaned); ++ break; ++ case PTYPE_REQUEST: ++ rts_free_pdu_request(&header->request); ++ break; ++ case PTYPE_RESPONSE: ++ rts_free_pdu_response(&header->response); ++ break; ++ case PTYPE_RTS: ++ rts_free_pdu_rts(&header->rts); ++ break; ++ /* No extra fields */ ++ case PTYPE_SHUTDOWN: ++ break; ++ ++ /* not handled */ ++ case PTYPE_PING: ++ case PTYPE_WORKING: ++ case PTYPE_NOCALL: ++ case PTYPE_REJECT: ++ case PTYPE_ACK: ++ case PTYPE_CL_CANCEL: ++ case PTYPE_FACK: ++ case PTYPE_CO_CANCEL: ++ default: ++ break; ++ } ++ ++ if (allocated) ++ free(header); ++} ++ ++BOOL rts_read_pdu_header(wStream* s, rpcconn_hdr_t* header) ++{ ++ BOOL rc = FALSE; ++ assert(s); ++ assert(header); ++ ++ if (!rts_read_common_pdu_header(s, &header->common)) ++ return FALSE; ++ ++ WLog_DBG(TAG, "Reading PDU type %s", rts_pdu_ptype_to_string(header->common.ptype)); ++ fflush(stdout); ++ switch (header->common.ptype) ++ { ++ case PTYPE_ALTER_CONTEXT: ++ rc = rts_read_pdu_alter_context(s, &header->alter_context); ++ break; ++ case PTYPE_ALTER_CONTEXT_RESP: ++ rc = rts_read_pdu_alter_context_response(s, &header->alter_context_response); ++ break; ++ case PTYPE_BIND: ++ rc = rts_read_pdu_bind(s, &header->bind); ++ break; ++ case PTYPE_BIND_ACK: ++ rc = rts_read_pdu_bind_ack(s, &header->bind_ack); ++ break; ++ case PTYPE_BIND_NAK: ++ rc = rts_read_pdu_bind_nak(s, &header->bind_nak); ++ break; ++ case PTYPE_RPC_AUTH_3: ++ rc = rts_read_pdu_auth3(s, &header->rpc_auth_3); ++ break; ++ case PTYPE_CANCEL_ACK: ++ rc = rts_read_pdu_cancel_ack(s, &header->cancel); ++ break; ++ case PTYPE_FAULT: ++ rc = rts_read_pdu_fault(s, &header->fault); ++ break; ++ case PTYPE_ORPHANED: ++ rc = rts_read_pdu_orphaned(s, &header->orphaned); ++ break; ++ case PTYPE_REQUEST: ++ rc = rts_read_pdu_request(s, &header->request); ++ break; ++ case PTYPE_RESPONSE: ++ rc = rts_read_pdu_response(s, &header->response); ++ break; ++ case PTYPE_RTS: ++ rc = rts_read_pdu_rts(s, &header->rts); ++ break; ++ case PTYPE_SHUTDOWN: ++ rc = TRUE; /* No extra fields */ ++ break; ++ ++ /* not handled */ ++ case PTYPE_PING: ++ case PTYPE_WORKING: ++ case PTYPE_NOCALL: ++ case PTYPE_REJECT: ++ case PTYPE_ACK: ++ case PTYPE_CL_CANCEL: ++ case PTYPE_FACK: ++ case PTYPE_CO_CANCEL: ++ default: ++ break; ++ } ++ ++ return rc; ++} ++ ++static BOOL rts_write_pdu_header(wStream* s, const rpcconn_rts_hdr_t* header) ++{ ++ assert(s); ++ assert(header); ++ if (!Stream_EnsureRemainingCapacity(s, sizeof(rpcconn_rts_hdr_t))) ++ return FALSE; + +- return header; ++ if (!rts_write_common_pdu_header(s, &header->header)) ++ return FALSE; ++ ++ Stream_Write_UINT16(s, header->Flags); ++ Stream_Write_UINT16(s, header->NumberOfCommands); ++ return TRUE; + } + +-static int rts_receive_window_size_command_read(rdpRpc* rpc, BYTE* buffer, UINT32 length, ++static int rts_receive_window_size_command_read(rdpRpc* rpc, wStream* buffer, + UINT32* ReceiveWindowSize) + { ++ UINT32 val; ++ ++ assert(rpc); ++ assert(buffer); ++ ++ if (Stream_GetRemainingLength(buffer) < 4) ++ return -1; ++ Stream_Read_UINT32(buffer, val); + if (ReceiveWindowSize) +- *ReceiveWindowSize = *((UINT32*)&buffer[0]); /* ReceiveWindowSize (4 bytes) */ ++ *ReceiveWindowSize = val; /* ReceiveWindowSize (4 bytes) */ + + return 4; + } + +-static int rts_receive_window_size_command_write(BYTE* buffer, UINT32 ReceiveWindowSize) ++static BOOL rts_receive_window_size_command_write(wStream* s, UINT32 ReceiveWindowSize) + { +- if (buffer) +- { +- *((UINT32*)&buffer[0]) = RTS_CMD_RECEIVE_WINDOW_SIZE; /* CommandType (4 bytes) */ +- *((UINT32*)&buffer[4]) = ReceiveWindowSize; /* ReceiveWindowSize (4 bytes) */ +- } ++ assert(s); ++ ++ if (!Stream_EnsureRemainingCapacity(s, 2 * sizeof(UINT32))) ++ return FALSE; + +- return 8; ++ Stream_Write_UINT32(s, RTS_CMD_RECEIVE_WINDOW_SIZE); /* CommandType (4 bytes) */ ++ Stream_Write_UINT32(s, ReceiveWindowSize); /* ReceiveWindowSize (4 bytes) */ ++ ++ return TRUE; + } + +-static int rts_flow_control_ack_command_read(rdpRpc* rpc, BYTE* buffer, UINT32 length, +- UINT32* BytesReceived, UINT32* AvailableWindow, +- BYTE* ChannelCookie) ++static int rts_flow_control_ack_command_read(rdpRpc* rpc, wStream* buffer, UINT32* BytesReceived, ++ UINT32* AvailableWindow, BYTE* ChannelCookie) + { ++ UINT32 val; ++ assert(rpc); ++ assert(buffer); ++ + /* Ack (24 bytes) */ ++ if (Stream_GetRemainingLength(buffer) < 24) ++ return -1; ++ ++ Stream_Read_UINT32(buffer, val); + if (BytesReceived) +- *BytesReceived = *((UINT32*)&buffer[0]); /* BytesReceived (4 bytes) */ ++ *BytesReceived = val; /* BytesReceived (4 bytes) */ + ++ Stream_Read_UINT32(buffer, val); + if (AvailableWindow) +- *AvailableWindow = *((UINT32*)&buffer[4]); /* AvailableWindow (4 bytes) */ ++ *AvailableWindow = val; /* AvailableWindow (4 bytes) */ + + if (ChannelCookie) +- CopyMemory(ChannelCookie, &buffer[8], 16); /* ChannelCookie (16 bytes) */ +- ++ Stream_Read(buffer, ChannelCookie, 16); /* ChannelCookie (16 bytes) */ ++ else ++ Stream_Seek(buffer, 16); + return 24; + } + +-static int rts_flow_control_ack_command_write(BYTE* buffer, UINT32 BytesReceived, +- UINT32 AvailableWindow, BYTE* ChannelCookie) ++static BOOL rts_flow_control_ack_command_write(wStream* s, UINT32 BytesReceived, ++ UINT32 AvailableWindow, BYTE* ChannelCookie) + { +- if (buffer) +- { +- *((UINT32*)&buffer[0]) = RTS_CMD_FLOW_CONTROL_ACK; /* CommandType (4 bytes) */ +- /* Ack (24 bytes) */ +- *((UINT32*)&buffer[4]) = BytesReceived; /* BytesReceived (4 bytes) */ +- *((UINT32*)&buffer[8]) = AvailableWindow; /* AvailableWindow (4 bytes) */ +- CopyMemory(&buffer[12], ChannelCookie, 16); /* ChannelCookie (16 bytes) */ +- } ++ assert(s); + +- return 28; +-} ++ if (!Stream_EnsureRemainingCapacity(s, 28)) ++ return FALSE; + +-static int rts_connection_timeout_command_read(rdpRpc* rpc, BYTE* buffer, UINT32 length, +- UINT32* ConnectionTimeout) +-{ +- if (ConnectionTimeout) +- *ConnectionTimeout = *((UINT32*)&buffer[0]); /* ConnectionTimeout (4 bytes) */ ++ Stream_Write_UINT32(s, RTS_CMD_FLOW_CONTROL_ACK); /* CommandType (4 bytes) */ ++ Stream_Write_UINT32(s, BytesReceived); /* BytesReceived (4 bytes) */ ++ Stream_Write_UINT32(s, AvailableWindow); /* AvailableWindow (4 bytes) */ ++ Stream_Write(s, ChannelCookie, 16); /* ChannelCookie (16 bytes) */ + +- return 4; ++ return TRUE; + } + +-static int rts_connection_timeout_command_write(BYTE* buffer, UINT32 ConnectionTimeout) ++static BOOL rts_connection_timeout_command_read(rdpRpc* rpc, wStream* buffer, ++ UINT32* ConnectionTimeout) + { +- if (buffer) +- { +- *((UINT32*)&buffer[0]) = RTS_CMD_CONNECTION_TIMEOUT; /* CommandType (4 bytes) */ +- *((UINT32*)&buffer[4]) = ConnectionTimeout; /* ConnectionTimeout (4 bytes) */ +- } ++ UINT32 val; ++ assert(rpc); ++ assert(buffer); + +- return 8; +-} ++ if (Stream_GetRemainingLength(buffer) < 4) ++ return FALSE; + +-static int rts_cookie_command_read(rdpRpc* rpc, BYTE* buffer, UINT32 length) +-{ +- /* Cookie (16 bytes) */ +- return 16; ++ Stream_Read_UINT32(buffer, val); ++ if (ConnectionTimeout) ++ *ConnectionTimeout = val; /* ConnectionTimeout (4 bytes) */ ++ ++ return TRUE; + } + +-static int rts_cookie_command_write(BYTE* buffer, BYTE* Cookie) ++static BOOL rts_cookie_command_write(wStream* s, const BYTE* Cookie) + { +- if (buffer) +- { +- *((UINT32*)&buffer[0]) = RTS_CMD_COOKIE; /* CommandType (4 bytes) */ +- CopyMemory(&buffer[4], Cookie, 16); /* Cookie (16 bytes) */ +- } ++ assert(s); + +- return 20; +-} ++ if (!Stream_EnsureRemainingCapacity(s, 20)) ++ return FALSE; + +-static int rts_channel_lifetime_command_read(rdpRpc* rpc, BYTE* buffer, UINT32 length) +-{ +- /* ChannelLifetime (4 bytes) */ +- return 4; ++ Stream_Write_UINT32(s, RTS_CMD_COOKIE); /* CommandType (4 bytes) */ ++ Stream_Write(s, Cookie, 16); /* Cookie (16 bytes) */ ++ ++ return TRUE; + } + +-static int rts_channel_lifetime_command_write(BYTE* buffer, UINT32 ChannelLifetime) ++static BOOL rts_channel_lifetime_command_write(wStream* s, UINT32 ChannelLifetime) + { +- if (buffer) +- { +- *((UINT32*)&buffer[0]) = RTS_CMD_CHANNEL_LIFETIME; /* CommandType (4 bytes) */ +- *((UINT32*)&buffer[4]) = ChannelLifetime; /* ChannelLifetime (4 bytes) */ +- } ++ assert(s); + +- return 8; +-} ++ if (!Stream_EnsureRemainingCapacity(s, 8)) ++ return FALSE; ++ Stream_Write_UINT32(s, RTS_CMD_CHANNEL_LIFETIME); /* CommandType (4 bytes) */ ++ Stream_Write_UINT32(s, ChannelLifetime); /* ChannelLifetime (4 bytes) */ + +-static int rts_client_keepalive_command_read(rdpRpc* rpc, BYTE* buffer, UINT32 length) +-{ +- /* ClientKeepalive (4 bytes) */ +- return 4; ++ return TRUE; + } + +-static int rts_client_keepalive_command_write(BYTE* buffer, UINT32 ClientKeepalive) ++static BOOL rts_client_keepalive_command_write(wStream* s, UINT32 ClientKeepalive) + { ++ assert(s); ++ ++ if (!Stream_EnsureRemainingCapacity(s, 8)) ++ return FALSE; + /** + * An unsigned integer that specifies the keep-alive interval, in milliseconds, + * that this connection is configured to use. This value MUST be 0 or in the inclusive + * range of 60,000 through 4,294,967,295. If it is 0, it MUST be interpreted as 300,000. + */ +- if (buffer) +- { +- *((UINT32*)&buffer[0]) = RTS_CMD_CLIENT_KEEPALIVE; /* CommandType (4 bytes) */ +- *((UINT32*)&buffer[4]) = ClientKeepalive; /* ClientKeepalive (4 bytes) */ +- } + +- return 8; +-} ++ Stream_Write_UINT32(s, RTS_CMD_CLIENT_KEEPALIVE); /* CommandType (4 bytes) */ ++ Stream_Write_UINT32(s, ClientKeepalive); /* ClientKeepalive (4 bytes) */ + +-static int rts_version_command_read(rdpRpc* rpc, BYTE* buffer, UINT32 length) +-{ +- /* Version (4 bytes) */ +- return 4; ++ return TRUE; + } + +-static int rts_version_command_write(BYTE* buffer) ++static BOOL rts_version_command_read(rdpRpc* rpc, wStream* buffer) + { +- if (buffer) +- { +- *((UINT32*)&buffer[0]) = RTS_CMD_VERSION; /* CommandType (4 bytes) */ +- *((UINT32*)&buffer[4]) = 1; /* Version (4 bytes) */ +- } ++ assert(rpc); ++ assert(buffer); + +- return 8; +-} ++ if (!Stream_SafeSeek(buffer, 4)) ++ return FALSE; + +-static int rts_empty_command_read(rdpRpc* rpc, BYTE* buffer, UINT32 length) +-{ +- return 0; ++ /* Version (4 bytes) */ ++ return TRUE; + } + +-static int rts_empty_command_write(BYTE* buffer) ++static BOOL rts_version_command_write(wStream* buffer) + { +- if (buffer) +- { +- *((UINT32*)&buffer[0]) = RTS_CMD_EMPTY; /* CommandType (4 bytes) */ +- } +- +- return 4; +-} ++ assert(buffer); + +-static SSIZE_T rts_padding_command_read(const BYTE* buffer, size_t length) +-{ +- UINT32 ConformanceCount; +- ConformanceCount = *((UINT32*)&buffer[0]); /* ConformanceCount (4 bytes) */ +- /* Padding (variable) */ +- return ConformanceCount + 4; +-} ++ if (Stream_GetRemainingCapacity(buffer) < 8) ++ return FALSE; + +-static int rts_padding_command_write(BYTE* buffer, UINT32 ConformanceCount) +-{ +- if (buffer) +- { +- *((UINT32*)&buffer[0]) = RTS_CMD_PADDING; /* CommandType (4 bytes) */ +- *((UINT32*)&buffer[4]) = ConformanceCount; /* ConformanceCount (4 bytes) */ +- ZeroMemory(&buffer[8], ConformanceCount); /* Padding (variable) */ +- } ++ Stream_Write_UINT32(buffer, RTS_CMD_VERSION); /* CommandType (4 bytes) */ ++ Stream_Write_UINT32(buffer, 1); /* Version (4 bytes) */ + +- return 8 + ConformanceCount; ++ return TRUE; + } + +-static int rts_negative_ance_command_read(rdpRpc* rpc, BYTE* buffer, UINT32 length) ++static BOOL rts_empty_command_write(wStream* s) + { +- return 0; +-} ++ assert(s); + +-static int rts_negative_ance_command_write(BYTE* buffer) +-{ +- if (buffer) +- { +- *((UINT32*)&buffer[0]) = RTS_CMD_NEGATIVE_ANCE; /* CommandType (4 bytes) */ +- } ++ if (!Stream_EnsureRemainingCapacity(s, 8)) ++ return FALSE; + +- return 4; +-} ++ Stream_Write_UINT32(s, RTS_CMD_EMPTY); /* CommandType (4 bytes) */ + +-static int rts_ance_command_read(rdpRpc* rpc, BYTE* buffer, UINT32 length) +-{ +- return 0; ++ return TRUE; + } + +-static int rts_ance_command_write(BYTE* buffer) ++static BOOL rts_padding_command_read(wStream* s, size_t* length) + { +- if (buffer) +- { +- *((UINT32*)&buffer[0]) = RTS_CMD_ANCE; /* CommandType (4 bytes) */ +- } +- +- return 4; ++ UINT32 ConformanceCount; ++ assert(s); ++ assert(length); ++ if (Stream_GetRemainingLength(s) < 4) ++ return FALSE; ++ Stream_Read_UINT32(s, ConformanceCount); /* ConformanceCount (4 bytes) */ ++ *length = ConformanceCount + 4; ++ return TRUE; + } + +-static SSIZE_T rts_client_address_command_read(const BYTE* buffer, size_t length) ++static BOOL rts_client_address_command_read(wStream* s, size_t* length) + { + UINT32 AddressType; +- AddressType = *((UINT32*)&buffer[0]); /* AddressType (4 bytes) */ ++ ++ assert(s); ++ assert(length); ++ ++ if (Stream_GetRemainingLength(s) < 4) ++ return FALSE; ++ Stream_Read_UINT32(s, AddressType); /* AddressType (4 bytes) */ + + if (AddressType == 0) + { + /* ClientAddress (4 bytes) */ + /* padding (12 bytes) */ +- return 4 + 4 + 12; ++ *length = 4 + 4 + 12; + } + else + { + /* ClientAddress (16 bytes) */ + /* padding (12 bytes) */ +- return 4 + 16 + 12; ++ *length = 4 + 16 + 12; + } ++ return TRUE; + } + +-static int rts_client_address_command_write(BYTE* buffer, UINT32 AddressType, BYTE* ClientAddress) ++static BOOL rts_association_group_id_command_write(wStream* s, const BYTE* AssociationGroupId) + { +- if (buffer) +- { +- *((UINT32*)&buffer[0]) = RTS_CMD_CLIENT_ADDRESS; /* CommandType (4 bytes) */ +- *((UINT32*)&buffer[4]) = AddressType; /* AddressType (4 bytes) */ +- } ++ assert(s); + +- if (AddressType == 0) +- { +- if (buffer) +- { +- CopyMemory(&buffer[8], ClientAddress, 4); /* ClientAddress (4 bytes) */ +- ZeroMemory(&buffer[12], 12); /* padding (12 bytes) */ +- } +- +- return 24; +- } +- else +- { +- if (buffer) +- { +- CopyMemory(&buffer[8], ClientAddress, 16); /* ClientAddress (16 bytes) */ +- ZeroMemory(&buffer[24], 12); /* padding (12 bytes) */ +- } ++ if (!Stream_EnsureRemainingCapacity(s, 20)) ++ return FALSE; + +- return 36; +- } +-} ++ Stream_Write_UINT32(s, RTS_CMD_ASSOCIATION_GROUP_ID); /* CommandType (4 bytes) */ ++ Stream_Write(s, AssociationGroupId, 16); /* AssociationGroupId (16 bytes) */ + +-static int rts_association_group_id_command_read(rdpRpc* rpc, BYTE* buffer, UINT32 length) +-{ +- /* AssociationGroupId (16 bytes) */ +- return 16; ++ return TRUE; + } + +-static int rts_association_group_id_command_write(BYTE* buffer, BYTE* AssociationGroupId) ++static int rts_destination_command_read(rdpRpc* rpc, wStream* buffer, UINT32* Destination) + { +- if (buffer) +- { +- *((UINT32*)&buffer[0]) = RTS_CMD_ASSOCIATION_GROUP_ID; /* CommandType (4 bytes) */ +- CopyMemory(&buffer[4], AssociationGroupId, 16); /* AssociationGroupId (16 bytes) */ +- } +- +- return 20; +-} ++ UINT32 val; ++ assert(rpc); ++ assert(buffer); + +-static int rts_destination_command_read(rdpRpc* rpc, BYTE* buffer, UINT32 length, +- UINT32* Destination) +-{ ++ if (Stream_GetRemainingLength(buffer) < 4) ++ return -1; ++ Stream_Read_UINT32(buffer, val); + if (Destination) +- *Destination = *((UINT32*)&buffer[0]); /* Destination (4 bytes) */ ++ *Destination = val; /* Destination (4 bytes) */ + + return 4; + } + +-static int rts_destination_command_write(BYTE* buffer, UINT32 Destination) ++static BOOL rts_destination_command_write(wStream* s, UINT32 Destination) + { +- if (buffer) +- { +- *((UINT32*)&buffer[0]) = RTS_CMD_DESTINATION; /* CommandType (4 bytes) */ +- *((UINT32*)&buffer[4]) = Destination; /* Destination (4 bytes) */ +- } +- +- return 8; +-} ++ assert(s); + +-static int rts_ping_traffic_sent_notify_command_read(rdpRpc* rpc, BYTE* buffer, UINT32 length) +-{ +- /* PingTrafficSent (4 bytes) */ +- return 4; +-} ++ if (!Stream_EnsureRemainingCapacity(s, 8)) ++ return FALSE; + +-static int rts_ping_traffic_sent_notify_command_write(BYTE* buffer, UINT32 PingTrafficSent) +-{ +- if (buffer) +- { +- *((UINT32*)&buffer[0]) = RTS_CMD_PING_TRAFFIC_SENT_NOTIFY; /* CommandType (4 bytes) */ +- *((UINT32*)&buffer[4]) = PingTrafficSent; /* PingTrafficSent (4 bytes) */ +- } ++ Stream_Write_UINT32(s, RTS_CMD_DESTINATION); /* CommandType (4 bytes) */ ++ Stream_Write_UINT32(s, Destination); /* Destination (4 bytes) */ + +- return 8; ++ return TRUE; + } + + void rts_generate_cookie(BYTE* cookie) + { ++ assert(cookie); + winpr_RAND(cookie, 16); + } + ++static BOOL rts_send_buffer(RpcChannel* channel, wStream* s, size_t frag_length) ++{ ++ BOOL status = FALSE; ++ SSIZE_T rc; ++ ++ assert(channel); ++ assert(s); ++ ++ Stream_SealLength(s); ++ if (Stream_Length(s) < sizeof(rpcconn_common_hdr_t)) ++ goto fail; ++ if (Stream_Length(s) != frag_length) ++ goto fail; ++ ++ rc = rpc_channel_write(channel, Stream_Buffer(s), Stream_Length(s)); ++ if (rc < 0) ++ goto fail; ++ if ((size_t)rc != Stream_Length(s)) ++ goto fail; ++ status = TRUE; ++fail: ++ return status; ++} ++ + /* CONN/A Sequence */ + +-int rts_send_CONN_A1_pdu(rdpRpc* rpc) ++BOOL rts_send_CONN_A1_pdu(rdpRpc* rpc) + { +- int status; +- BYTE* buffer; ++ BOOL status = FALSE; ++ wStream* buffer; + rpcconn_rts_hdr_t header = rts_pdu_header_init(); + UINT32 ReceiveWindowSize; + BYTE* OUTChannelCookie; + BYTE* VirtualConnectionCookie; +- RpcVirtualConnection* connection = rpc->VirtualConnection; +- RpcOutChannel* outChannel = connection->DefaultOutChannel; ++ RpcVirtualConnection* connection; ++ RpcOutChannel* outChannel; ++ ++ assert(rpc); ++ ++ connection = rpc->VirtualConnection; ++ assert(connection); ++ ++ outChannel = connection->DefaultOutChannel; ++ assert(outChannel); + + header.header.frag_length = 76; + header.Flags = RTS_FLAG_NONE; + header.NumberOfCommands = 4; ++ + WLog_DBG(TAG, "Sending CONN/A1 RTS PDU"); + VirtualConnectionCookie = (BYTE*)&(connection->Cookie); + OUTChannelCookie = (BYTE*)&(outChannel->common.Cookie); + ReceiveWindowSize = outChannel->ReceiveWindow; +- buffer = (BYTE*)malloc(header.header.frag_length); ++ ++ buffer = Stream_New(NULL, header.header.frag_length); + + if (!buffer) + return -1; + +- CopyMemory(buffer, ((BYTE*)&header), 20); /* RTS Header (20 bytes) */ +- rts_version_command_write(&buffer[20]); /* Version (8 bytes) */ +- rts_cookie_command_write(&buffer[28], +- VirtualConnectionCookie); /* VirtualConnectionCookie (20 bytes) */ +- rts_cookie_command_write(&buffer[48], OUTChannelCookie); /* OUTChannelCookie (20 bytes) */ +- rts_receive_window_size_command_write(&buffer[68], +- ReceiveWindowSize); /* ReceiveWindowSize (8 bytes) */ +- status = rpc_channel_write(&outChannel->common, buffer, header.header.frag_length); +- free(buffer); +- return (status > 0) ? 1 : -1; ++ if (!rts_write_pdu_header(buffer, &header)) /* RTS Header (20 bytes) */ ++ goto fail; ++ status = rts_version_command_write(buffer); /* Version (8 bytes) */ ++ if (!status) ++ goto fail; ++ status = rts_cookie_command_write( ++ buffer, VirtualConnectionCookie); /* VirtualConnectionCookie (20 bytes) */ ++ if (!status) ++ goto fail; ++ status = rts_cookie_command_write(buffer, OUTChannelCookie); /* OUTChannelCookie (20 bytes) */ ++ if (!status) ++ goto fail; ++ status = rts_receive_window_size_command_write( ++ buffer, ReceiveWindowSize); /* ReceiveWindowSize (8 bytes) */ ++ if (!status) ++ goto fail; ++ status = rts_send_buffer(&outChannel->common, buffer, header.header.frag_length); ++fail: ++ Stream_Free(buffer, TRUE); ++ return status; + } + +-int rts_recv_CONN_A3_pdu(rdpRpc* rpc, BYTE* buffer, UINT32 length) ++BOOL rts_recv_CONN_A3_pdu(rdpRpc* rpc, wStream* buffer) + { ++ BOOL rc; + UINT32 ConnectionTimeout; +- rts_connection_timeout_command_read(rpc, &buffer[24], length - 24, &ConnectionTimeout); ++ ++ if (!Stream_SafeSeek(buffer, 24)) ++ return FALSE; ++ ++ rc = rts_connection_timeout_command_read(rpc, buffer, &ConnectionTimeout); ++ if (!rc) ++ return rc; ++ + WLog_DBG(TAG, "Receiving CONN/A3 RTS PDU: ConnectionTimeout: %" PRIu32 "", ConnectionTimeout); ++ ++ assert(rpc); ++ assert(rpc->VirtualConnection); ++ assert(rpc->VirtualConnection->DefaultInChannel); ++ + rpc->VirtualConnection->DefaultInChannel->PingOriginator.ConnectionTimeout = ConnectionTimeout; +- return 1; ++ return TRUE; + } + + /* CONN/B Sequence */ + +-int rts_send_CONN_B1_pdu(rdpRpc* rpc) ++BOOL rts_send_CONN_B1_pdu(rdpRpc* rpc) + { +- int status; +- BYTE* buffer; +- UINT32 length; ++ BOOL status = FALSE; ++ wStream* buffer; + rpcconn_rts_hdr_t header = rts_pdu_header_init(); + BYTE* INChannelCookie; + BYTE* AssociationGroupId; + BYTE* VirtualConnectionCookie; +- RpcVirtualConnection* connection = rpc->VirtualConnection; +- RpcInChannel* inChannel = connection->DefaultInChannel; ++ RpcVirtualConnection* connection; ++ RpcInChannel* inChannel; ++ ++ assert(rpc); ++ ++ connection = rpc->VirtualConnection; ++ assert(connection); ++ ++ inChannel = connection->DefaultInChannel; ++ assert(inChannel); + + header.header.frag_length = 104; + header.Flags = RTS_FLAG_NONE; + header.NumberOfCommands = 6; ++ + WLog_DBG(TAG, "Sending CONN/B1 RTS PDU"); ++ + VirtualConnectionCookie = (BYTE*)&(connection->Cookie); + INChannelCookie = (BYTE*)&(inChannel->common.Cookie); + AssociationGroupId = (BYTE*)&(connection->AssociationGroupId); +- buffer = (BYTE*)malloc(header.header.frag_length); ++ buffer = Stream_New(NULL, header.header.frag_length); + + if (!buffer) +- return -1; +- +- CopyMemory(buffer, ((BYTE*)&header), 20); /* RTS Header (20 bytes) */ +- rts_version_command_write(&buffer[20]); /* Version (8 bytes) */ +- rts_cookie_command_write(&buffer[28], +- VirtualConnectionCookie); /* VirtualConnectionCookie (20 bytes) */ +- rts_cookie_command_write(&buffer[48], INChannelCookie); /* INChannelCookie (20 bytes) */ +- rts_channel_lifetime_command_write(&buffer[68], +- rpc->ChannelLifetime); /* ChannelLifetime (8 bytes) */ +- rts_client_keepalive_command_write(&buffer[76], +- rpc->KeepAliveInterval); /* ClientKeepalive (8 bytes) */ +- rts_association_group_id_command_write(&buffer[84], +- AssociationGroupId); /* AssociationGroupId (20 bytes) */ +- length = header.header.frag_length; +- status = rpc_channel_write(&inChannel->common, buffer, length); +- free(buffer); +- return (status > 0) ? 1 : -1; ++ goto fail; ++ if (!rts_write_pdu_header(buffer, &header)) /* RTS Header (20 bytes) */ ++ goto fail; ++ if (!rts_version_command_write(buffer)) /* Version (8 bytes) */ ++ goto fail; ++ if (!rts_cookie_command_write(buffer, ++ VirtualConnectionCookie)) /* VirtualConnectionCookie (20 bytes) */ ++ goto fail; ++ if (!rts_cookie_command_write(buffer, INChannelCookie)) /* INChannelCookie (20 bytes) */ ++ goto fail; ++ if (!rts_channel_lifetime_command_write(buffer, ++ rpc->ChannelLifetime)) /* ChannelLifetime (8 bytes) */ ++ goto fail; ++ if (!rts_client_keepalive_command_write(buffer, ++ rpc->KeepAliveInterval)) /* ClientKeepalive (8 bytes) */ ++ goto fail; ++ if (!rts_association_group_id_command_write( ++ buffer, AssociationGroupId)) /* AssociationGroupId (20 bytes) */ ++ goto fail; ++ status = rts_send_buffer(&inChannel->common, buffer, header.header.frag_length); ++fail: ++ Stream_Free(buffer, TRUE); ++ return status; + } + + /* CONN/C Sequence */ + +-int rts_recv_CONN_C2_pdu(rdpRpc* rpc, BYTE* buffer, UINT32 length) ++BOOL rts_recv_CONN_C2_pdu(rdpRpc* rpc, wStream* buffer) + { +- UINT32 offset; ++ BOOL rc; + UINT32 ReceiveWindowSize; + UINT32 ConnectionTimeout; +- offset = 24; +- offset += rts_version_command_read(rpc, &buffer[offset], length - offset) + 4; +- offset += rts_receive_window_size_command_read(rpc, &buffer[offset], length - offset, +- &ReceiveWindowSize) + +- 4; +- offset += rts_connection_timeout_command_read(rpc, &buffer[offset], length - offset, +- &ConnectionTimeout) + +- 4; ++ ++ assert(rpc); ++ assert(buffer); ++ ++ if (!Stream_SafeSeek(buffer, 24)) ++ return FALSE; ++ ++ rc = rts_version_command_read(rpc, buffer); ++ if (rc < 0) ++ return rc; ++ rc = rts_receive_window_size_command_read(rpc, buffer, &ReceiveWindowSize); ++ if (rc < 0) ++ return rc; ++ rc = rts_connection_timeout_command_read(rpc, buffer, &ConnectionTimeout); ++ if (rc < 0) ++ return rc; + WLog_DBG(TAG, + "Receiving CONN/C2 RTS PDU: ConnectionTimeout: %" PRIu32 " ReceiveWindowSize: %" PRIu32 + "", + ConnectionTimeout, ReceiveWindowSize); ++ ++ assert(rpc); ++ assert(rpc->VirtualConnection); ++ assert(rpc->VirtualConnection->DefaultInChannel); ++ + rpc->VirtualConnection->DefaultInChannel->PingOriginator.ConnectionTimeout = ConnectionTimeout; + rpc->VirtualConnection->DefaultInChannel->PeerReceiveWindow = ReceiveWindowSize; +- return 1; ++ return TRUE; + } + + /* Out-of-Sequence PDUs */ + +-static int rts_send_keep_alive_pdu(rdpRpc* rpc) ++BOOL rts_send_flow_control_ack_pdu(rdpRpc* rpc) + { +- int status; +- BYTE* buffer; +- UINT32 length; ++ BOOL status = FALSE; ++ wStream* buffer; + rpcconn_rts_hdr_t header = rts_pdu_header_init(); +- RpcInChannel* inChannel = rpc->VirtualConnection->DefaultInChannel; ++ UINT32 BytesReceived; ++ UINT32 AvailableWindow; ++ BYTE* ChannelCookie; ++ RpcVirtualConnection* connection; ++ RpcInChannel* inChannel; ++ RpcOutChannel* outChannel; + +- header.header.frag_length = 28; +- header.Flags = RTS_FLAG_OTHER_CMD; +- header.NumberOfCommands = 1; +- WLog_DBG(TAG, "Sending Keep-Alive RTS PDU"); +- buffer = (BYTE*)malloc(header.header.frag_length); ++ assert(rpc); + +- if (!buffer) +- return -1; ++ connection = rpc->VirtualConnection; ++ assert(connection); + +- CopyMemory(buffer, ((BYTE*)&header), 20); /* RTS Header (20 bytes) */ +- rts_client_keepalive_command_write( +- &buffer[20], rpc->CurrentKeepAliveInterval); /* ClientKeepAlive (8 bytes) */ +- length = header.header.frag_length; +- status = rpc_channel_write(&inChannel->common, buffer, length); +- free(buffer); +- return (status > 0) ? 1 : -1; +-} ++ inChannel = connection->DefaultInChannel; ++ assert(inChannel); + +-int rts_send_flow_control_ack_pdu(rdpRpc* rpc) +-{ +- int status; +- BYTE* buffer; +- UINT32 length; +- rpcconn_rts_hdr_t header = rts_pdu_header_init(); +- UINT32 BytesReceived; +- UINT32 AvailableWindow; +- BYTE* ChannelCookie; +- RpcVirtualConnection* connection = rpc->VirtualConnection; +- RpcInChannel* inChannel = connection->DefaultInChannel; +- RpcOutChannel* outChannel = connection->DefaultOutChannel; ++ outChannel = connection->DefaultOutChannel; ++ assert(outChannel); + + header.header.frag_length = 56; + header.Flags = RTS_FLAG_OTHER_CMD; + header.NumberOfCommands = 2; ++ + WLog_DBG(TAG, "Sending FlowControlAck RTS PDU"); ++ + BytesReceived = outChannel->BytesReceived; + AvailableWindow = outChannel->AvailableWindowAdvertised; + ChannelCookie = (BYTE*)&(outChannel->common.Cookie); + outChannel->ReceiverAvailableWindow = outChannel->AvailableWindowAdvertised; +- buffer = (BYTE*)malloc(header.header.frag_length); ++ buffer = Stream_New(NULL, header.header.frag_length); + + if (!buffer) +- return -1; ++ goto fail; ++ ++ if (!rts_write_pdu_header(buffer, &header)) /* RTS Header (20 bytes) */ ++ goto fail; ++ if (!rts_destination_command_write(buffer, FDOutProxy)) /* Destination Command (8 bytes) */ ++ goto fail; + +- CopyMemory(buffer, ((BYTE*)&header), 20); /* RTS Header (20 bytes) */ +- rts_destination_command_write(&buffer[20], FDOutProxy); /* Destination Command (8 bytes) */ + /* FlowControlAck Command (28 bytes) */ +- rts_flow_control_ack_command_write(&buffer[28], BytesReceived, AvailableWindow, ChannelCookie); +- length = header.header.frag_length; +- status = rpc_channel_write(&inChannel->common, buffer, length); +- free(buffer); +- return (status > 0) ? 1 : -1; ++ if (!rts_flow_control_ack_command_write(buffer, BytesReceived, AvailableWindow, ChannelCookie)) ++ goto fail; ++ ++ status = rts_send_buffer(&inChannel->common, buffer, header.header.frag_length); ++fail: ++ Stream_Free(buffer, TRUE); ++ return status; + } + +-static int rts_recv_flow_control_ack_pdu(rdpRpc* rpc, BYTE* buffer, UINT32 length) ++static int rts_recv_flow_control_ack_pdu(rdpRpc* rpc, wStream* buffer) + { +- UINT32 offset; ++ int rc; + UINT32 BytesReceived; + UINT32 AvailableWindow; +- BYTE ChannelCookie[16]; +- offset = 24; +- offset += +- rts_flow_control_ack_command_read(rpc, &buffer[offset], length - offset, &BytesReceived, +- &AvailableWindow, (BYTE*)&ChannelCookie) + +- 4; ++ BYTE ChannelCookie[16] = { 0 }; ++ ++ rc = rts_flow_control_ack_command_read(rpc, buffer, &BytesReceived, &AvailableWindow, ++ (BYTE*)&ChannelCookie); ++ if (rc < 0) ++ return rc; + WLog_ERR(TAG, + "Receiving FlowControlAck RTS PDU: BytesReceived: %" PRIu32 + " AvailableWindow: %" PRIu32 "", + BytesReceived, AvailableWindow); ++ ++ assert(rpc->VirtualConnection); ++ assert(rpc->VirtualConnection->DefaultInChannel); ++ + rpc->VirtualConnection->DefaultInChannel->SenderAvailableWindow = + AvailableWindow - (rpc->VirtualConnection->DefaultInChannel->BytesSent - BytesReceived); + return 1; + } + +-static int rts_recv_flow_control_ack_with_destination_pdu(rdpRpc* rpc, BYTE* buffer, UINT32 length) ++static int rts_recv_flow_control_ack_with_destination_pdu(rdpRpc* rpc, wStream* buffer) + { +- UINT32 offset; ++ int rc; + UINT32 Destination; + UINT32 BytesReceived; + UINT32 AvailableWindow; +- BYTE ChannelCookie[16]; ++ BYTE ChannelCookie[16] = { 0 }; + /** + * When the sender receives a FlowControlAck RTS PDU, it MUST use the following formula to + * recalculate its Sender AvailableWindow variable: +@@ -622,16 +1722,23 @@ static int rts_recv_flow_control_ack_with_destination_pdu(rdpRpc* rpc, BYTE* buf + * structure in the PDU received. + * + */ +- offset = 24; +- offset += rts_destination_command_read(rpc, &buffer[offset], length - offset, &Destination) + 4; +- offset += +- rts_flow_control_ack_command_read(rpc, &buffer[offset], length - offset, &BytesReceived, +- &AvailableWindow, (BYTE*)&ChannelCookie) + +- 4; ++ ++ rc = rts_destination_command_read(rpc, buffer, &Destination); ++ if (rc < 0) ++ return rc; ++ ++ rc = rts_flow_control_ack_command_read(rpc, buffer, &BytesReceived, &AvailableWindow, ++ ChannelCookie); ++ if (rc < 0) ++ return rc; ++ + WLog_DBG(TAG, + "Receiving FlowControlAckWithDestination RTS PDU: BytesReceived: %" PRIu32 + " AvailableWindow: %" PRIu32 "", + BytesReceived, AvailableWindow); ++ ++ assert(rpc->VirtualConnection); ++ assert(rpc->VirtualConnection->DefaultInChannel); + rpc->VirtualConnection->DefaultInChannel->SenderAvailableWindow = + AvailableWindow - (rpc->VirtualConnection->DefaultInChannel->BytesSent - BytesReceived); + return 1; +@@ -639,31 +1746,41 @@ static int rts_recv_flow_control_ack_with_destination_pdu(rdpRpc* rpc, BYTE* buf + + static int rts_send_ping_pdu(rdpRpc* rpc) + { +- int status; +- BYTE* buffer; +- UINT32 length; ++ BOOL status = FALSE; ++ wStream* buffer; + rpcconn_rts_hdr_t header = rts_pdu_header_init(); +- RpcInChannel* inChannel = rpc->VirtualConnection->DefaultInChannel; ++ RpcInChannel* inChannel; ++ ++ assert(rpc); ++ assert(rpc->VirtualConnection); ++ ++ inChannel = rpc->VirtualConnection->DefaultInChannel; ++ assert(inChannel); + + header.header.frag_length = 20; + header.Flags = RTS_FLAG_PING; + header.NumberOfCommands = 0; ++ + WLog_DBG(TAG, "Sending Ping RTS PDU"); +- buffer = (BYTE*)malloc(header.header.frag_length); ++ buffer = Stream_New(NULL, header.header.frag_length); + + if (!buffer) +- return -1; +- +- CopyMemory(buffer, ((BYTE*)&header), 20); /* RTS Header (20 bytes) */ +- length = header.header.frag_length; +- status = rpc_channel_write(&inChannel->common, buffer, length); +- free(buffer); +- return (status > 0) ? 1 : -1; ++ goto fail; ++ ++ if (!rts_write_pdu_header(buffer, &header)) /* RTS Header (20 bytes) */ ++ goto fail; ++ status = rts_send_buffer(&inChannel->common, buffer, header.header.frag_length); ++fail: ++ Stream_Free(buffer, TRUE); ++ return (status) ? 1 : -1; + } + +-SSIZE_T rts_command_length(UINT32 CommandType, const BYTE* buffer, size_t length) ++BOOL rts_command_length(UINT32 CommandType, wStream* s, size_t* length) + { +- int CommandLength = 0; ++ size_t padding = 0; ++ size_t CommandLength = 0; ++ ++ assert(s); + + switch (CommandType) + { +@@ -700,7 +1817,8 @@ SSIZE_T rts_command_length(UINT32 CommandType, const BYTE* buffer, size_t length + break; + + case RTS_CMD_PADDING: /* variable-size */ +- CommandLength = rts_padding_command_read(buffer, length); ++ if (!rts_padding_command_read(s, &padding)) ++ return FALSE; + break; + + case RTS_CMD_NEGATIVE_ANCE: +@@ -712,7 +1830,8 @@ SSIZE_T rts_command_length(UINT32 CommandType, const BYTE* buffer, size_t length + break; + + case RTS_CMD_CLIENT_ADDRESS: /* variable-size */ +- CommandLength = rts_client_address_command_read(buffer, length); ++ if (!rts_client_address_command_read(s, &CommandLength)) ++ return FALSE; + break; + + case RTS_CMD_ASSOCIATION_GROUP_ID: +@@ -729,118 +1848,176 @@ SSIZE_T rts_command_length(UINT32 CommandType, const BYTE* buffer, size_t length + + default: + WLog_ERR(TAG, "Error: Unknown RTS Command Type: 0x%" PRIx32 "", CommandType); +- return -1; ++ return FALSE; + } + +- return CommandLength; ++ CommandLength += padding; ++ if (Stream_GetRemainingLength(s) < CommandLength) ++ return FALSE; ++ ++ if (length) ++ *length = CommandLength; ++ return TRUE; + } + + static int rts_send_OUT_R2_A7_pdu(rdpRpc* rpc) + { +- int status; +- BYTE* buffer; ++ BOOL status = FALSE; ++ wStream* buffer; + rpcconn_rts_hdr_t header = rts_pdu_header_init(); + BYTE* SuccessorChannelCookie; +- RpcInChannel* inChannel = rpc->VirtualConnection->DefaultInChannel; +- RpcOutChannel* nextOutChannel = rpc->VirtualConnection->NonDefaultOutChannel; ++ RpcInChannel* inChannel; ++ RpcOutChannel* nextOutChannel; ++ ++ assert(rpc); ++ assert(rpc->VirtualConnection); ++ ++ inChannel = rpc->VirtualConnection->DefaultInChannel; ++ assert(inChannel); ++ ++ nextOutChannel = rpc->VirtualConnection->NonDefaultOutChannel; ++ assert(nextOutChannel); + + header.header.frag_length = 56; + header.Flags = RTS_FLAG_OUT_CHANNEL; + header.NumberOfCommands = 3; ++ + WLog_DBG(TAG, "Sending OUT_R2/A7 RTS PDU"); ++ + SuccessorChannelCookie = (BYTE*)&(nextOutChannel->common.Cookie); +- buffer = (BYTE*)malloc(header.header.frag_length); ++ buffer = Stream_New(NULL, header.header.frag_length); + + if (!buffer) + return -1; + +- CopyMemory(buffer, ((BYTE*)&header), 20); /* RTS Header (20 bytes) */ +- rts_destination_command_write(&buffer[20], FDServer); /* Destination (8 bytes)*/ +- rts_cookie_command_write(&buffer[28], +- SuccessorChannelCookie); /* SuccessorChannelCookie (20 bytes) */ +- rts_version_command_write(&buffer[48]); /* Version (8 bytes) */ +- status = rpc_channel_write(&inChannel->common, buffer, header.header.frag_length); +- free(buffer); +- return (status > 0) ? 1 : -1; ++ if (!rts_write_pdu_header(buffer, &header)) /* RTS Header (20 bytes) */ ++ goto fail; ++ if (!rts_destination_command_write(buffer, FDServer)) /* Destination (8 bytes)*/ ++ goto fail; ++ if (!rts_cookie_command_write(buffer, ++ SuccessorChannelCookie)) /* SuccessorChannelCookie (20 bytes) */ ++ goto fail; ++ if (!rts_version_command_write(buffer)) /* Version (8 bytes) */ ++ goto fail; ++ status = rts_send_buffer(&inChannel->common, buffer, header.header.frag_length); ++fail: ++ Stream_Free(buffer, TRUE); ++ return (status) ? 1 : -1; + } + + static int rts_send_OUT_R2_C1_pdu(rdpRpc* rpc) + { +- int status; +- BYTE* buffer; ++ BOOL status = FALSE; ++ wStream* buffer; + rpcconn_rts_hdr_t header = rts_pdu_header_init(); +- RpcOutChannel* nextOutChannel = rpc->VirtualConnection->NonDefaultOutChannel; ++ RpcOutChannel* nextOutChannel; ++ ++ assert(rpc); ++ assert(rpc->VirtualConnection); ++ ++ nextOutChannel = rpc->VirtualConnection->NonDefaultOutChannel; ++ assert(nextOutChannel); + + header.header.frag_length = 24; + header.Flags = RTS_FLAG_PING; + header.NumberOfCommands = 1; ++ + WLog_DBG(TAG, "Sending OUT_R2/C1 RTS PDU"); +- buffer = (BYTE*)malloc(header.header.frag_length); ++ buffer = Stream_New(NULL, header.header.frag_length); + + if (!buffer) + return -1; + +- CopyMemory(buffer, ((BYTE*)&header), 20); /* RTS Header (20 bytes) */ +- rts_empty_command_write(&buffer[20]); /* Empty command (4 bytes) */ +- status = rpc_channel_write(&nextOutChannel->common, buffer, header.header.frag_length); +- free(buffer); +- return (status > 0) ? 1 : -1; ++ if (!rts_write_pdu_header(buffer, &header)) /* RTS Header (20 bytes) */ ++ goto fail; ++ ++ if (!rts_empty_command_write(buffer)) /* Empty command (4 bytes) */ ++ goto fail; ++ status = rts_send_buffer(&nextOutChannel->common, buffer, header.header.frag_length); ++fail: ++ Stream_Free(buffer, TRUE); ++ return (status) ? 1 : -1; + } + +-int rts_send_OUT_R1_A3_pdu(rdpRpc* rpc) ++BOOL rts_send_OUT_R1_A3_pdu(rdpRpc* rpc) + { +- int status; +- BYTE* buffer; ++ BOOL status = FALSE; ++ wStream* buffer; + rpcconn_rts_hdr_t header = rts_pdu_header_init(); + UINT32 ReceiveWindowSize; + BYTE* VirtualConnectionCookie; + BYTE* PredecessorChannelCookie; + BYTE* SuccessorChannelCookie; +- RpcVirtualConnection* connection = rpc->VirtualConnection; +- RpcOutChannel* outChannel = connection->DefaultOutChannel; +- RpcOutChannel* nextOutChannel = connection->NonDefaultOutChannel; ++ RpcVirtualConnection* connection; ++ RpcOutChannel* outChannel; ++ RpcOutChannel* nextOutChannel; ++ ++ assert(rpc); ++ ++ connection = rpc->VirtualConnection; ++ assert(connection); ++ ++ outChannel = connection->DefaultOutChannel; ++ assert(outChannel); ++ ++ nextOutChannel = connection->NonDefaultOutChannel; ++ assert(nextOutChannel); + + header.header.frag_length = 96; + header.Flags = RTS_FLAG_RECYCLE_CHANNEL; + header.NumberOfCommands = 5; ++ + WLog_DBG(TAG, "Sending OUT_R1/A3 RTS PDU"); ++ + VirtualConnectionCookie = (BYTE*)&(connection->Cookie); + PredecessorChannelCookie = (BYTE*)&(outChannel->common.Cookie); + SuccessorChannelCookie = (BYTE*)&(nextOutChannel->common.Cookie); + ReceiveWindowSize = outChannel->ReceiveWindow; +- buffer = (BYTE*)malloc(header.header.frag_length); ++ buffer = Stream_New(NULL, header.header.frag_length); + + if (!buffer) + return -1; + +- CopyMemory(buffer, ((BYTE*)&header), 20); /* RTS Header (20 bytes) */ +- rts_version_command_write(&buffer[20]); /* Version (8 bytes) */ +- rts_cookie_command_write(&buffer[28], +- VirtualConnectionCookie); /* VirtualConnectionCookie (20 bytes) */ +- rts_cookie_command_write(&buffer[48], +- PredecessorChannelCookie); /* PredecessorChannelCookie (20 bytes) */ +- rts_cookie_command_write(&buffer[68], +- SuccessorChannelCookie); /* SuccessorChannelCookie (20 bytes) */ +- rts_receive_window_size_command_write(&buffer[88], +- ReceiveWindowSize); /* ReceiveWindowSize (8 bytes) */ +- status = rpc_channel_write(&nextOutChannel->common, buffer, header.header.frag_length); +- free(buffer); +- return (status > 0) ? 1 : -1; ++ if (!rts_write_pdu_header(buffer, &header)) /* RTS Header (20 bytes) */ ++ goto fail; ++ if (!rts_version_command_write(buffer)) /* Version (8 bytes) */ ++ goto fail; ++ if (!rts_cookie_command_write(buffer, ++ VirtualConnectionCookie)) /* VirtualConnectionCookie (20 bytes) */ ++ goto fail; ++ if (!rts_cookie_command_write( ++ buffer, PredecessorChannelCookie)) /* PredecessorChannelCookie (20 bytes) */ ++ goto fail; ++ if (!rts_cookie_command_write(buffer, ++ SuccessorChannelCookie)) /* SuccessorChannelCookie (20 bytes) */ ++ goto fail; ++ if (!rts_receive_window_size_command_write(buffer, ++ ReceiveWindowSize)) /* ReceiveWindowSize (8 bytes) */ ++ goto fail; ++ ++ status = rts_send_buffer(&nextOutChannel->common, buffer, header.header.frag_length); ++fail: ++ Stream_Free(buffer, TRUE); ++ return status; + } + +-static int rts_recv_OUT_R1_A2_pdu(rdpRpc* rpc, BYTE* buffer, UINT32 length) ++static int rts_recv_OUT_R1_A2_pdu(rdpRpc* rpc, wStream* buffer) + { + int status; +- UINT32 offset; + UINT32 Destination = 0; +- RpcVirtualConnection* connection = rpc->VirtualConnection; ++ RpcVirtualConnection* connection; ++ assert(rpc); ++ assert(buffer); ++ ++ connection = rpc->VirtualConnection; ++ assert(connection); ++ + WLog_DBG(TAG, "Receiving OUT R1/A2 RTS PDU"); +- offset = 24; + +- if (length < offset) +- return -1; ++ status = rts_destination_command_read(rpc, buffer, &Destination); ++ if (status < 0) ++ return status; + +- rts_destination_command_read(rpc, &buffer[offset], length - offset, &Destination); + connection->NonDefaultOutChannel = rpc_out_channel_new(rpc); + + if (!connection->NonDefaultOutChannel) +@@ -859,10 +2036,17 @@ static int rts_recv_OUT_R1_A2_pdu(rdpRpc* rpc, BYTE* buffer, UINT32 length) + return 1; + } + +-static int rts_recv_OUT_R2_A6_pdu(rdpRpc* rpc, BYTE* buffer, UINT32 length) ++static int rts_recv_OUT_R2_A6_pdu(rdpRpc* rpc, wStream* buffer) + { + int status; +- RpcVirtualConnection* connection = rpc->VirtualConnection; ++ RpcVirtualConnection* connection; ++ ++ assert(rpc); ++ assert(buffer); ++ ++ connection = rpc->VirtualConnection; ++ assert(connection); ++ + WLog_DBG(TAG, "Receiving OUT R2/A6 RTS PDU"); + status = rts_send_OUT_R2_C1_pdu(rpc); + +@@ -887,47 +2071,59 @@ static int rts_recv_OUT_R2_A6_pdu(rdpRpc* rpc, BYTE* buffer, UINT32 length) + return 1; + } + +-static int rts_recv_OUT_R2_B3_pdu(rdpRpc* rpc, BYTE* buffer, UINT32 length) ++static int rts_recv_OUT_R2_B3_pdu(rdpRpc* rpc, wStream* buffer) + { +- RpcVirtualConnection* connection = rpc->VirtualConnection; ++ RpcVirtualConnection* connection; ++ ++ assert(rpc); ++ assert(buffer); ++ ++ connection = rpc->VirtualConnection; ++ assert(connection); ++ + WLog_DBG(TAG, "Receiving OUT R2/B3 RTS PDU"); + rpc_out_channel_transition_to_state(connection->DefaultOutChannel, + CLIENT_OUT_CHANNEL_STATE_RECYCLED); + return 1; + } + +-int rts_recv_out_of_sequence_pdu(rdpRpc* rpc, BYTE* buffer, UINT32 length) ++BOOL rts_recv_out_of_sequence_pdu(rdpRpc* rpc, wStream* buffer, const rpcconn_hdr_t* header) + { +- int status = -1; ++ BOOL status = FALSE; + UINT32 SignatureId; +- rpcconn_rts_hdr_t* rts; +- RtsPduSignature signature; ++ size_t length, total; ++ RtsPduSignature signature = { 0 }; + RpcVirtualConnection* connection; + +- if (!rpc || !buffer) +- return -1; ++ assert(rpc); ++ assert(buffer); ++ assert(header); ++ ++ total = Stream_Length(buffer); ++ length = header->common.frag_length; ++ if (total < length) ++ return FALSE; + + connection = rpc->VirtualConnection; + + if (!connection) +- return -1; +- +- rts = (rpcconn_rts_hdr_t*)buffer; ++ return FALSE; + +- if (!rts_extract_pdu_signature(&signature, rts)) +- return -1; ++ if (!rts_extract_pdu_signature(&signature, buffer, header)) ++ return FALSE; + + SignatureId = rts_identify_pdu_signature(&signature, NULL); + +- if (rts_match_pdu_signature(&RTS_PDU_FLOW_CONTROL_ACK_SIGNATURE, rts)) ++ if (rts_match_pdu_signature(&RTS_PDU_FLOW_CONTROL_ACK_SIGNATURE, buffer, header)) + { +- status = rts_recv_flow_control_ack_pdu(rpc, buffer, length); ++ status = rts_recv_flow_control_ack_pdu(rpc, buffer); + } +- else if (rts_match_pdu_signature(&RTS_PDU_FLOW_CONTROL_ACK_WITH_DESTINATION_SIGNATURE, rts)) ++ else if (rts_match_pdu_signature(&RTS_PDU_FLOW_CONTROL_ACK_WITH_DESTINATION_SIGNATURE, buffer, ++ header)) + { +- status = rts_recv_flow_control_ack_with_destination_pdu(rpc, buffer, length); ++ status = rts_recv_flow_control_ack_with_destination_pdu(rpc, buffer); + } +- else if (rts_match_pdu_signature(&RTS_PDU_PING_SIGNATURE, rts)) ++ else if (rts_match_pdu_signature(&RTS_PDU_PING_SIGNATURE, buffer, header)) + { + status = rts_send_ping_pdu(rpc); + } +@@ -935,28 +2131,28 @@ int rts_recv_out_of_sequence_pdu(rdpRpc* rpc, BYTE* buffer, UINT32 length) + { + if (connection->DefaultOutChannel->State == CLIENT_OUT_CHANNEL_STATE_OPENED) + { +- if (rts_match_pdu_signature(&RTS_PDU_OUT_R1_A2_SIGNATURE, rts)) ++ if (rts_match_pdu_signature(&RTS_PDU_OUT_R1_A2_SIGNATURE, buffer, header)) + { +- status = rts_recv_OUT_R1_A2_pdu(rpc, buffer, length); ++ status = rts_recv_OUT_R1_A2_pdu(rpc, buffer); + } + } + else if (connection->DefaultOutChannel->State == CLIENT_OUT_CHANNEL_STATE_OPENED_A6W) + { +- if (rts_match_pdu_signature(&RTS_PDU_OUT_R2_A6_SIGNATURE, rts)) ++ if (rts_match_pdu_signature(&RTS_PDU_OUT_R2_A6_SIGNATURE, buffer, header)) + { +- status = rts_recv_OUT_R2_A6_pdu(rpc, buffer, length); ++ status = rts_recv_OUT_R2_A6_pdu(rpc, buffer); + } + } + else if (connection->DefaultOutChannel->State == CLIENT_OUT_CHANNEL_STATE_OPENED_B3W) + { +- if (rts_match_pdu_signature(&RTS_PDU_OUT_R2_B3_SIGNATURE, rts)) ++ if (rts_match_pdu_signature(&RTS_PDU_OUT_R2_B3_SIGNATURE, buffer, header)) + { +- status = rts_recv_OUT_R2_B3_pdu(rpc, buffer, length); ++ status = rts_recv_OUT_R2_B3_pdu(rpc, buffer); + } + } + } + +- if (status < 0) ++ if (!status) + { + WLog_ERR(TAG, "error parsing RTS PDU with signature id: 0x%08" PRIX32 "", SignatureId); + rts_print_pdu_signature(&signature); +@@ -964,3 +2160,42 @@ int rts_recv_out_of_sequence_pdu(rdpRpc* rpc, BYTE* buffer, UINT32 length) + + return status; + } ++ ++BOOL rts_write_pdu_auth3(wStream* s, const rpcconn_rpc_auth_3_hdr_t* auth) ++{ ++ assert(s); ++ assert(auth); ++ ++ if (!rts_write_common_pdu_header(s, &auth->header)) ++ return FALSE; ++ ++ if (!Stream_EnsureRemainingCapacity(s, 2 * sizeof(UINT16))) ++ return FALSE; ++ ++ Stream_Write_UINT16(s, auth->max_xmit_frag); ++ Stream_Write_UINT16(s, auth->max_recv_frag); ++ ++ return rts_write_auth_verifier(s, &auth->auth_verifier, &auth->header); ++} ++ ++BOOL rts_write_pdu_bind(wStream* s, const rpcconn_bind_hdr_t* bind) ++{ ++ ++ assert(s); ++ assert(bind); ++ ++ if (!rts_write_common_pdu_header(s, &bind->header)) ++ return FALSE; ++ ++ if (!Stream_EnsureRemainingCapacity(s, 8)) ++ return FALSE; ++ ++ Stream_Write_UINT16(s, bind->max_xmit_frag); ++ Stream_Write_UINT16(s, bind->max_recv_frag); ++ Stream_Write_UINT32(s, bind->assoc_group_id); ++ ++ if (!rts_write_context_list(s, &bind->p_context_elem)) ++ return FALSE; ++ ++ return rts_write_auth_verifier(s, &bind->auth_verifier, &bind->header); ++} +diff --git a/libfreerdp/core/gateway/rts.h b/libfreerdp/core/gateway/rts.h +index ccc4cfd..01a66a7 100644 +--- a/libfreerdp/core/gateway/rts.h ++++ b/libfreerdp/core/gateway/rts.h +@@ -24,12 +24,14 @@ + #include "config.h" + #endif + +-#include "rpc.h" ++#include + + #include + #include + #include + ++#include "rpc.h" ++ + #define RTS_FLAG_NONE 0x0000 + #define RTS_FLAG_PING 0x0001 + #define RTS_FLAG_OTHER_CMD 0x0002 +@@ -79,21 +81,28 @@ + + FREERDP_LOCAL void rts_generate_cookie(BYTE* cookie); + +-FREERDP_LOCAL SSIZE_T rts_command_length(UINT32 CommandType, const BYTE* buffer, size_t length); ++FREERDP_LOCAL BOOL rts_write_pdu_auth3(wStream* s, const rpcconn_rpc_auth_3_hdr_t* auth); ++FREERDP_LOCAL BOOL rts_write_pdu_bind(wStream* s, const rpcconn_bind_hdr_t* bind); ++ ++FREERDP_LOCAL BOOL rts_read_pdu_header(wStream* s, rpcconn_hdr_t* header); ++FREERDP_LOCAL void rts_free_pdu_header(rpcconn_hdr_t* header, BOOL allocated); ++ ++FREERDP_LOCAL BOOL rts_read_common_pdu_header(wStream* s, rpcconn_common_hdr_t* header); + +-FREERDP_LOCAL int rts_send_CONN_A1_pdu(rdpRpc* rpc); +-FREERDP_LOCAL int rts_recv_CONN_A3_pdu(rdpRpc* rpc, BYTE* buffer, UINT32 length); ++FREERDP_LOCAL BOOL rts_command_length(UINT32 CommandType, wStream* s, size_t* length); + +-FREERDP_LOCAL int rts_send_CONN_B1_pdu(rdpRpc* rpc); ++FREERDP_LOCAL BOOL rts_send_CONN_A1_pdu(rdpRpc* rpc); ++FREERDP_LOCAL BOOL rts_recv_CONN_A3_pdu(rdpRpc* rpc, wStream* buffer); + +-FREERDP_LOCAL int rts_recv_CONN_C2_pdu(rdpRpc* rpc, BYTE* buffer, UINT32 length); ++FREERDP_LOCAL BOOL rts_send_CONN_B1_pdu(rdpRpc* rpc); + +-FREERDP_LOCAL int rts_send_OUT_R1_A3_pdu(rdpRpc* rpc); ++FREERDP_LOCAL BOOL rts_recv_CONN_C2_pdu(rdpRpc* rpc, wStream* buffer); + +-FREERDP_LOCAL int rts_send_flow_control_ack_pdu(rdpRpc* rpc); ++FREERDP_LOCAL BOOL rts_send_OUT_R1_A3_pdu(rdpRpc* rpc); + +-FREERDP_LOCAL int rts_recv_out_of_sequence_pdu(rdpRpc* rpc, BYTE* buffer, UINT32 length); ++FREERDP_LOCAL BOOL rts_send_flow_control_ack_pdu(rdpRpc* rpc); + +-#include "rts_signature.h" ++FREERDP_LOCAL BOOL rts_recv_out_of_sequence_pdu(rdpRpc* rpc, wStream* buffer, ++ const rpcconn_hdr_t* header); + + #endif /* FREERDP_LIB_CORE_GATEWAY_RTS_H */ +diff --git a/libfreerdp/core/gateway/rts_signature.c b/libfreerdp/core/gateway/rts_signature.c +index 4d605f0..b9319d1 100644 +--- a/libfreerdp/core/gateway/rts_signature.c ++++ b/libfreerdp/core/gateway/rts_signature.c +@@ -17,6 +17,9 @@ + * limitations under the License. + */ + ++#include ++#include ++ + #include + + #include "rts_signature.h" +@@ -276,90 +279,74 @@ static const RTS_PDU_SIGNATURE_ENTRY RTS_PDU_SIGNATURE_TABLE[] = { + { RTS_PDU_PING, TRUE, &RTS_PDU_PING_SIGNATURE, "Ping" }, + { RTS_PDU_FLOW_CONTROL_ACK, TRUE, &RTS_PDU_FLOW_CONTROL_ACK_SIGNATURE, "FlowControlAck" }, + { RTS_PDU_FLOW_CONTROL_ACK_WITH_DESTINATION, TRUE, +- &RTS_PDU_FLOW_CONTROL_ACK_WITH_DESTINATION_SIGNATURE, "FlowControlAckWithDestination" }, +- +- { 0, FALSE, NULL, NULL } ++ &RTS_PDU_FLOW_CONTROL_ACK_WITH_DESTINATION_SIGNATURE, "FlowControlAckWithDestination" } + }; + +-BOOL rts_match_pdu_signature(const RtsPduSignature* signature, const rpcconn_rts_hdr_t* rts) ++BOOL rts_match_pdu_signature(const RtsPduSignature* signature, wStream* src, ++ const rpcconn_hdr_t* header) + { +- UINT16 i; +- int status; +- const BYTE* buffer; +- UINT32 length; +- UINT32 offset; +- UINT32 CommandType; +- UINT32 CommandLength; +- +- if (!signature || !rts) +- return FALSE; ++ RtsPduSignature extracted = { 0 }; + +- if (rts->Flags != signature->Flags) +- return FALSE; ++ assert(signature); ++ assert(src); + +- if (rts->NumberOfCommands != signature->NumberOfCommands) ++ if (!rts_extract_pdu_signature(&extracted, src, header)) + return FALSE; + +- buffer = (const BYTE*)rts; +- offset = RTS_PDU_HEADER_LENGTH; +- length = rts->header.frag_length - offset; +- +- for (i = 0; i < rts->NumberOfCommands; i++) +- { +- CommandType = *((UINT32*)&buffer[offset]); /* CommandType (4 bytes) */ +- offset += 4; +- +- if (CommandType != signature->CommandTypes[i]) +- return FALSE; ++ return memcmp(signature, &extracted, sizeof(extracted)) == 0; ++} + +- status = rts_command_length(CommandType, &buffer[offset], length); ++BOOL rts_extract_pdu_signature(RtsPduSignature* signature, wStream* src, ++ const rpcconn_hdr_t* header) ++{ ++ BOOL rc = FALSE; ++ UINT16 i; ++ wStream tmp; ++ rpcconn_hdr_t rheader = { 0 }; ++ const rpcconn_rts_hdr_t* rts; + +- if (status < 0) +- return FALSE; ++ assert(signature); ++ assert(src); + +- CommandLength = (UINT32)status; +- offset += CommandLength; +- length = rts->header.frag_length - offset; ++ Stream_StaticInit(&tmp, Stream_Pointer(src), Stream_GetRemainingLength(src)); ++ if (!header) ++ { ++ if (!rts_read_pdu_header(&tmp, &rheader)) ++ goto fail; ++ header = &rheader; + } +- +- return TRUE; +-} +- +-BOOL rts_extract_pdu_signature(RtsPduSignature* signature, const rpcconn_rts_hdr_t* rts) +-{ +- int i; +- int status; +- BYTE* buffer; +- UINT32 length; +- UINT32 offset; +- UINT32 CommandType; +- UINT32 CommandLength; +- +- if (!signature || !rts) +- return FALSE; ++ rts = &header->rts; ++ if (rts->header.frag_length < sizeof(rpcconn_rts_hdr_t)) ++ goto fail; + + signature->Flags = rts->Flags; + signature->NumberOfCommands = rts->NumberOfCommands; +- buffer = (BYTE*)rts; +- offset = RTS_PDU_HEADER_LENGTH; +- length = rts->header.frag_length - offset; + + for (i = 0; i < rts->NumberOfCommands; i++) + { +- CommandType = *((UINT32*)&buffer[offset]); /* CommandType (4 bytes) */ +- offset += 4; +- signature->CommandTypes[i] = CommandType; +- status = rts_command_length(CommandType, &buffer[offset], length); ++ UINT32 CommandType; ++ size_t CommandLength; ++ ++ if (Stream_GetRemainingLength(&tmp) < 4) ++ goto fail; ++ ++ Stream_Read_UINT32(&tmp, CommandType); /* CommandType (4 bytes) */ + +- if (status < 0) +- return FALSE; ++ /* We only need this for comparison against known command types */ ++ if (i < ARRAYSIZE(signature->CommandTypes)) ++ signature->CommandTypes[i] = CommandType; + +- CommandLength = (UINT32)status; +- offset += CommandLength; +- length = rts->header.frag_length - offset; ++ if (!rts_command_length(CommandType, &tmp, &CommandLength)) ++ goto fail; ++ if (!Stream_SafeSeek(&tmp, CommandLength)) ++ goto fail; + } + +- return TRUE; ++ rc = TRUE; ++fail: ++ rts_free_pdu_header(&rheader, FALSE); ++ Stream_Free(&tmp, FALSE); ++ return rc; + } + + UINT32 rts_identify_pdu_signature(const RtsPduSignature* signature, +@@ -367,11 +354,15 @@ UINT32 rts_identify_pdu_signature(const RtsPduSignature* signature, + { + size_t i, j; + +- for (i = 0; RTS_PDU_SIGNATURE_TABLE[i].SignatureId != 0; i++) ++ if (entry) ++ *entry = NULL; ++ ++ for (i = 0; i < ARRAYSIZE(RTS_PDU_SIGNATURE_TABLE); i++) + { +- const RtsPduSignature* pSignature = RTS_PDU_SIGNATURE_TABLE[i].Signature; ++ const RTS_PDU_SIGNATURE_ENTRY* current = &RTS_PDU_SIGNATURE_TABLE[i]; ++ const RtsPduSignature* pSignature = current->Signature; + +- if (!RTS_PDU_SIGNATURE_TABLE[i].SignatureClient) ++ if (!current->SignatureClient) + continue; + + if (signature->Flags != pSignature->Flags) +@@ -387,9 +378,9 @@ UINT32 rts_identify_pdu_signature(const RtsPduSignature* signature, + } + + if (entry) +- *entry = &RTS_PDU_SIGNATURE_TABLE[i]; ++ *entry = current; + +- return RTS_PDU_SIGNATURE_TABLE[i].SignatureId; ++ return current->SignatureId; + } + + return 0; +diff --git a/libfreerdp/core/gateway/rts_signature.h b/libfreerdp/core/gateway/rts_signature.h +index 2c43cdc..31f0e81 100644 +--- a/libfreerdp/core/gateway/rts_signature.h ++++ b/libfreerdp/core/gateway/rts_signature.h +@@ -178,10 +178,10 @@ FREERDP_LOCAL extern const RtsPduSignature RTS_PDU_PING_SIGNATURE; + FREERDP_LOCAL extern const RtsPduSignature RTS_PDU_FLOW_CONTROL_ACK_SIGNATURE; + FREERDP_LOCAL extern const RtsPduSignature RTS_PDU_FLOW_CONTROL_ACK_WITH_DESTINATION_SIGNATURE; + +-FREERDP_LOCAL BOOL rts_match_pdu_signature(const RtsPduSignature* signature, +- const rpcconn_rts_hdr_t* rts); +-FREERDP_LOCAL BOOL rts_extract_pdu_signature(RtsPduSignature* signature, +- const rpcconn_rts_hdr_t* rts); ++FREERDP_LOCAL BOOL rts_match_pdu_signature(const RtsPduSignature* signature, wStream* s, ++ const rpcconn_hdr_t* header); ++FREERDP_LOCAL BOOL rts_extract_pdu_signature(RtsPduSignature* signature, wStream* s, ++ const rpcconn_hdr_t* header); + FREERDP_LOCAL UINT32 rts_identify_pdu_signature(const RtsPduSignature* signature, + const RTS_PDU_SIGNATURE_ENTRY** entry); + FREERDP_LOCAL BOOL rts_print_pdu_signature(const RtsPduSignature* signature); +diff --git a/libfreerdp/core/gateway/tsg.c b/libfreerdp/core/gateway/tsg.c +index 3376fb0..ee56a38 100644 +--- a/libfreerdp/core/gateway/tsg.c ++++ b/libfreerdp/core/gateway/tsg.c +@@ -24,7 +24,7 @@ + #include "config.h" + #endif + +-#include ++#include + #include + #include + #include +@@ -221,7 +221,6 @@ struct rdp_tsg + UINT32 TunnelId; + UINT32 ChannelId; + BOOL reauthSequence; +- rdpSettings* settings; + rdpTransport* transport; + UINT64 ReauthTunnelContext; + CONTEXT_HANDLE TunnelContext; +@@ -310,9 +309,9 @@ static BOOL tsg_print(char** buffer, size_t* len, const char* fmt, ...) + static BOOL tsg_packet_header_to_string(char** buffer, size_t* length, + const TSG_PACKET_HEADER* header) + { +- WINPR_ASSERT(buffer); +- WINPR_ASSERT(length); +- WINPR_ASSERT(header); ++ assert(buffer); ++ assert(length); ++ assert(header); + + return tsg_print(buffer, length, + "header { ComponentId=0x%04" PRIx16 ", PacketId=0x%04" PRIx16 " }", +@@ -322,9 +321,9 @@ static BOOL tsg_packet_header_to_string(char** buffer, size_t* length, + static BOOL tsg_type_capability_nap_to_string(char** buffer, size_t* length, + const TSG_CAPABILITY_NAP* cur) + { +- WINPR_ASSERT(buffer); +- WINPR_ASSERT(length); +- WINPR_ASSERT(cur); ++ assert(buffer); ++ assert(length); ++ assert(cur); + + return tsg_print(buffer, length, "%s { capabilities=0x%08" PRIx32 " }", + tsg_packet_id_to_string(TSG_CAPABILITY_TYPE_NAP), cur->capabilities); +@@ -335,9 +334,9 @@ static BOOL tsg_packet_capabilities_to_string(char** buffer, size_t* length, + { + UINT32 x; + +- WINPR_ASSERT(buffer); +- WINPR_ASSERT(length); +- WINPR_ASSERT(caps); ++ assert(buffer); ++ assert(length); ++ assert(caps); + + if (!tsg_print(buffer, length, "capabilities { ")) + return FALSE; +@@ -363,9 +362,9 @@ static BOOL tsg_packet_capabilities_to_string(char** buffer, size_t* length, + static BOOL tsg_packet_versioncaps_to_string(char** buffer, size_t* length, + const TSG_PACKET_VERSIONCAPS* caps) + { +- WINPR_ASSERT(buffer); +- WINPR_ASSERT(length); +- WINPR_ASSERT(caps); ++ assert(buffer); ++ assert(length); ++ assert(caps); + + if (!tsg_print(buffer, length, "versioncaps { ")) + return FALSE; +@@ -391,9 +390,9 @@ static BOOL tsg_packet_versioncaps_to_string(char** buffer, size_t* length, + static BOOL tsg_packet_quarconfigrequest_to_string(char** buffer, size_t* length, + const TSG_PACKET_QUARCONFIGREQUEST* caps) + { +- WINPR_ASSERT(buffer); +- WINPR_ASSERT(length); +- WINPR_ASSERT(caps); ++ assert(buffer); ++ assert(length); ++ assert(caps); + + if (!tsg_print(buffer, length, "quarconfigrequest { ")) + return FALSE; +@@ -414,9 +413,9 @@ static BOOL tsg_packet_quarrequest_to_string(char** buffer, size_t* length, + char* name = NULL; + char* strdata = NULL; + +- WINPR_ASSERT(buffer); +- WINPR_ASSERT(length); +- WINPR_ASSERT(caps); ++ assert(buffer); ++ assert(length); ++ assert(caps); + + if (!tsg_print(buffer, length, "quarrequest { ")) + return FALSE; +@@ -426,7 +425,9 @@ static BOOL tsg_packet_quarrequest_to_string(char** buffer, size_t* length, + + if (caps->nameLength > 0) + { +- if (ConvertFromUnicode(CP_UTF8, 0, caps->machineName, caps->nameLength, &name, 0, NULL, ++ if (caps->nameLength > INT_MAX) ++ return FALSE; ++ if (ConvertFromUnicode(CP_UTF8, 0, caps->machineName, (int)caps->nameLength, &name, 0, NULL, + NULL) < 0) + return FALSE; + } +@@ -454,8 +455,8 @@ static const char* tsg_bool_to_string(BOOL val) + static const char* tsg_redirection_flags_to_string(char* buffer, size_t size, + const TSG_REDIRECTION_FLAGS* flags) + { +- WINPR_ASSERT(buffer || (size == 0)); +- WINPR_ASSERT(flags); ++ assert(buffer || (size == 0)); ++ assert(flags); + + _snprintf(buffer, size, + "enableAllRedirections=%s, disableAllRedirections=%s, driveRedirectionDisabled=%s, " +@@ -479,9 +480,9 @@ static BOOL tsg_packet_response_to_string(char** buffer, size_t* length, + char* strdata = NULL; + char tbuffer[8192] = { 0 }; + +- WINPR_ASSERT(buffer); +- WINPR_ASSERT(length); +- WINPR_ASSERT(caps); ++ assert(buffer); ++ assert(length); ++ assert(caps); + + if (!tsg_print(buffer, length, "response { ")) + return FALSE; +@@ -514,9 +515,9 @@ static BOOL tsg_packet_quarenc_response_to_string(char** buffer, size_t* length, + size_t size = ARRAYSIZE(tbuffer); + char* ptbuffer = tbuffer; + +- WINPR_ASSERT(buffer); +- WINPR_ASSERT(length); +- WINPR_ASSERT(caps); ++ assert(buffer); ++ assert(length); ++ assert(caps); + + if (!tsg_print(buffer, length, "quarenc_response { ")) + return FALSE; +@@ -526,8 +527,10 @@ static BOOL tsg_packet_quarenc_response_to_string(char** buffer, size_t* length, + + if (caps->certChainLen > 0) + { +- if (ConvertFromUnicode(CP_UTF8, 0, caps->certChainData, caps->certChainLen, &strdata, 0, +- NULL, NULL) <= 0) ++ if (caps->certChainLen > INT_MAX) ++ return FALSE; ++ if (ConvertFromUnicode(CP_UTF8, 0, caps->certChainData, (int)caps->certChainLen, &strdata, ++ 0, NULL, NULL) <= 0) + return FALSE; + } + +@@ -549,9 +552,9 @@ static BOOL tsg_packet_quarenc_response_to_string(char** buffer, size_t* length, + static BOOL tsg_packet_message_response_to_string(char** buffer, size_t* length, + const TSG_PACKET_MSG_RESPONSE* caps) + { +- WINPR_ASSERT(buffer); +- WINPR_ASSERT(length); +- WINPR_ASSERT(caps); ++ assert(buffer); ++ assert(length); ++ assert(caps); + + if (!tsg_print(buffer, length, "msg_response { ")) + return FALSE; +@@ -567,9 +570,9 @@ static BOOL tsg_packet_message_response_to_string(char** buffer, size_t* length, + static BOOL tsg_packet_caps_response_to_string(char** buffer, size_t* length, + const TSG_PACKET_CAPS_RESPONSE* caps) + { +- WINPR_ASSERT(buffer); +- WINPR_ASSERT(length); +- WINPR_ASSERT(caps); ++ assert(buffer); ++ assert(length); ++ assert(caps); + + if (!tsg_print(buffer, length, "caps_response { ")) + return FALSE; +@@ -586,9 +589,9 @@ static BOOL tsg_packet_caps_response_to_string(char** buffer, size_t* length, + static BOOL tsg_packet_message_request_to_string(char** buffer, size_t* length, + const TSG_PACKET_MSG_REQUEST* caps) + { +- WINPR_ASSERT(buffer); +- WINPR_ASSERT(length); +- WINPR_ASSERT(caps); ++ assert(buffer); ++ assert(length); ++ assert(caps); + + if (!tsg_print(buffer, length, "caps_message_request { ")) + return FALSE; +@@ -603,9 +606,9 @@ static BOOL tsg_packet_auth_to_string(char** buffer, size_t* length, const TSG_P + { + BOOL rc = FALSE; + char* strdata = NULL; +- WINPR_ASSERT(buffer); +- WINPR_ASSERT(length); +- WINPR_ASSERT(caps); ++ assert(buffer); ++ assert(length); ++ assert(caps); + + if (!tsg_print(buffer, length, "caps_message_request { ")) + return FALSE; +@@ -627,14 +630,14 @@ static BOOL tsg_packet_reauth_to_string(char** buffer, size_t* length, + const TSG_PACKET_REAUTH* caps) + { + BOOL rc = FALSE; +- WINPR_ASSERT(buffer); +- WINPR_ASSERT(length); +- WINPR_ASSERT(caps); ++ assert(buffer); ++ assert(length); ++ assert(caps); + + if (!tsg_print(buffer, length, "caps_message_request { ")) + return FALSE; + +- if (!tsg_print(buffer, length, " tunnelContext=0x%08" PRIx32 ", packetId=%s [0x%08" PRIx32 "]", ++ if (!tsg_print(buffer, length, " tunnelContext=0x%016" PRIx64 ", packetId=%s [0x%08" PRIx32 "]", + caps->tunnelContext, tsg_packet_id_to_string(caps->packetId), caps->packetId)) + return FALSE; + +@@ -793,7 +796,7 @@ static int TsProxySendToServer(handle_t IDL_handle, const byte pRpcMessage[], UI + { + wStream* s; + rdpTsg* tsg; +- int length; ++ size_t length; + const byte* buffer1 = NULL; + const byte* buffer2 = NULL; + const byte* buffer3 = NULL; +@@ -829,7 +832,9 @@ static int TsProxySendToServer(handle_t IDL_handle, const byte pRpcMessage[], UI + totalDataBytes += lengths[2] + 4; + } + +- length = 28 + totalDataBytes; ++ length = 28ull + totalDataBytes; ++ if (length > INT_MAX) ++ return -1; + s = Stream_New(NULL, length); + + if (!s) +@@ -865,7 +870,7 @@ static int TsProxySendToServer(handle_t IDL_handle, const byte pRpcMessage[], UI + if (!rpc_client_write_call(tsg->rpc, s, TsProxySendToServerOpnum)) + return -1; + +- return length; ++ return (int)length; + } + + /** +@@ -1018,12 +1023,20 @@ static BOOL TsProxyCreateTunnelReadResponse(rdpTsg* tsg, RPC_PDU* pdu, + UINT32 SwitchValue; + UINT32 MessageSwitchValue = 0; + UINT32 IsMessagePresent; ++ rdpContext* context; + UINT32 MsgBytes; ++ TSG_PACKET_STRING_MESSAGE packetStringMessage; + PTSG_PACKET_CAPABILITIES tsgCaps = NULL; + PTSG_PACKET_VERSIONCAPS versionCaps = NULL; + PTSG_PACKET_CAPS_RESPONSE packetCapsResponse = NULL; + PTSG_PACKET_QUARENC_RESPONSE packetQuarEncResponse = NULL; + ++ assert(tsg); ++ assert(tsg->rpc); ++ ++ context = tsg->rpc->context; ++ assert(context); ++ + if (!pdu) + return FALSE; + +@@ -1170,8 +1183,8 @@ static BOOL TsProxyCreateTunnelReadResponse(rdpTsg* tsg, RPC_PDU* pdu, + if (Stream_GetRemainingLength(pdu->s) < 16) + goto fail; + +- Stream_Seek_UINT32(pdu->s); /* IsDisplayMandatory (4 bytes) */ +- Stream_Seek_UINT32(pdu->s); /* IsConsent Mandatory (4 bytes) */ ++ Stream_Read_INT32(pdu->s, packetStringMessage.isDisplayMandatory); ++ Stream_Read_INT32(pdu->s, packetStringMessage.isConsentMandatory); + Stream_Read_UINT32(pdu->s, MsgBytes); + Stream_Read_UINT32(pdu->s, Pointer); + +@@ -1351,16 +1364,19 @@ fail: + + static BOOL TsProxyAuthorizeTunnelWriteRequest(rdpTsg* tsg, CONTEXT_HANDLE* tunnelContext) + { +- UINT32 pad; ++ size_t pad; + wStream* s; + size_t count; +- UINT32 offset; ++ size_t offset; + rdpRpc* rpc; + + if (!tsg || !tsg->rpc || !tunnelContext || !tsg->MachineName) + return FALSE; + + count = _wcslen(tsg->MachineName) + 1; ++ if (count > UINT32_MAX) ++ return FALSE; ++ + rpc = tsg->rpc; + WLog_DBG(TAG, "TsProxyAuthorizeTunnelWriteRequest"); + s = Stream_New(NULL, 1024 + count * 2); +@@ -1377,13 +1393,13 @@ static BOOL TsProxyAuthorizeTunnelWriteRequest(rdpTsg* tsg, CONTEXT_HANDLE* tunn + Stream_Write_UINT32(s, 0x00020000); /* PacketQuarRequestPtr (4 bytes) */ + Stream_Write_UINT32(s, 0x00000000); /* Flags (4 bytes) */ + Stream_Write_UINT32(s, 0x00020004); /* MachineNamePtr (4 bytes) */ +- Stream_Write_UINT32(s, count); /* NameLength (4 bytes) */ ++ Stream_Write_UINT32(s, (UINT32)count); /* NameLength (4 bytes) */ + Stream_Write_UINT32(s, 0x00020008); /* DataPtr (4 bytes) */ + Stream_Write_UINT32(s, 0); /* DataLength (4 bytes) */ + /* MachineName */ +- Stream_Write_UINT32(s, count); /* MaxCount (4 bytes) */ ++ Stream_Write_UINT32(s, (UINT32)count); /* MaxCount (4 bytes) */ + Stream_Write_UINT32(s, 0); /* Offset (4 bytes) */ +- Stream_Write_UINT32(s, count); /* ActualCount (4 bytes) */ ++ Stream_Write_UINT32(s, (UINT32)count); /* ActualCount (4 bytes) */ + Stream_Write_UTF16_String(s, tsg->MachineName, count); /* Array */ + /* 4-byte alignment */ + offset = Stream_GetPosition(s); +@@ -1394,7 +1410,7 @@ static BOOL TsProxyAuthorizeTunnelWriteRequest(rdpTsg* tsg, CONTEXT_HANDLE* tunn + return rpc_client_write_call(rpc, s, TsProxyAuthorizeTunnelOpnum); + } + +-static BOOL TsProxyAuthorizeTunnelReadResponse(rdpTsg* tsg, RPC_PDU* pdu) ++static BOOL TsProxyAuthorizeTunnelReadResponse(RPC_PDU* pdu) + { + BOOL rc = FALSE; + UINT32 Pointer; +@@ -1456,25 +1472,24 @@ static BOOL TsProxyAuthorizeTunnelReadResponse(rdpTsg* tsg, RPC_PDU* pdu) + Stream_Seek_UINT32(pdu->s); /* Reserved (4 bytes) */ + Stream_Read_UINT32(pdu->s, Pointer); /* ResponseDataPtr (4 bytes) */ + Stream_Read_UINT32(pdu->s, packetResponse->responseDataLen); /* ResponseDataLength (4 bytes) */ +- Stream_Read_UINT32(pdu->s, packetResponse->redirectionFlags +- .enableAllRedirections); /* EnableAllRedirections (4 bytes) */ +- Stream_Read_UINT32(pdu->s, packetResponse->redirectionFlags +- .disableAllRedirections); /* DisableAllRedirections (4 bytes) */ +- Stream_Read_UINT32(pdu->s, +- packetResponse->redirectionFlags +- .driveRedirectionDisabled); /* DriveRedirectionDisabled (4 bytes) */ +- Stream_Read_UINT32(pdu->s, +- packetResponse->redirectionFlags +- .printerRedirectionDisabled); /* PrinterRedirectionDisabled (4 bytes) */ +- Stream_Read_UINT32(pdu->s, +- packetResponse->redirectionFlags +- .portRedirectionDisabled); /* PortRedirectionDisabled (4 bytes) */ +- Stream_Read_UINT32(pdu->s, packetResponse->redirectionFlags.reserved); /* Reserved (4 bytes) */ +- Stream_Read_UINT32( ++ Stream_Read_INT32(pdu->s, packetResponse->redirectionFlags ++ .enableAllRedirections); /* EnableAllRedirections (4 bytes) */ ++ Stream_Read_INT32(pdu->s, packetResponse->redirectionFlags ++ .disableAllRedirections); /* DisableAllRedirections (4 bytes) */ ++ Stream_Read_INT32(pdu->s, ++ packetResponse->redirectionFlags ++ .driveRedirectionDisabled); /* DriveRedirectionDisabled (4 bytes) */ ++ Stream_Read_INT32(pdu->s, ++ packetResponse->redirectionFlags ++ .printerRedirectionDisabled); /* PrinterRedirectionDisabled (4 bytes) */ ++ Stream_Read_INT32(pdu->s, packetResponse->redirectionFlags ++ .portRedirectionDisabled); /* PortRedirectionDisabled (4 bytes) */ ++ Stream_Read_INT32(pdu->s, packetResponse->redirectionFlags.reserved); /* Reserved (4 bytes) */ ++ Stream_Read_INT32( + pdu->s, packetResponse->redirectionFlags + .clipboardRedirectionDisabled); /* ClipboardRedirectionDisabled (4 bytes) */ +- Stream_Read_UINT32(pdu->s, packetResponse->redirectionFlags +- .pnpRedirectionDisabled); /* PnpRedirectionDisabled (4 bytes) */ ++ Stream_Read_INT32(pdu->s, packetResponse->redirectionFlags ++ .pnpRedirectionDisabled); /* PnpRedirectionDisabled (4 bytes) */ + Stream_Read_UINT32(pdu->s, SizeValue); /* (4 bytes) */ + + if (SizeValue != packetResponse->responseDataLen) +@@ -1574,11 +1589,18 @@ static BOOL TsProxyMakeTunnelCallReadResponse(rdpTsg* tsg, RPC_PDU* pdu) + UINT32 Pointer; + UINT32 SwitchValue; + TSG_PACKET packet; ++ rdpContext* context; + char* messageText = NULL; + TSG_PACKET_MSG_RESPONSE packetMsgResponse = { 0 }; + TSG_PACKET_STRING_MESSAGE packetStringMessage = { 0 }; + TSG_PACKET_REAUTH_MESSAGE packetReauthMessage = { 0 }; + ++ assert(tsg); ++ assert(tsg->rpc); ++ ++ context = tsg->rpc->context; ++ assert(context); ++ + /* This is an asynchronous response */ + + if (!pdu) +@@ -1628,10 +1650,10 @@ static BOOL TsProxyMakeTunnelCallReadResponse(rdpTsg* tsg, RPC_PDU* pdu) + WLog_INFO(TAG, "Consent Message: %s", messageText); + free(messageText); + +- if (tsg->rpc && tsg->rpc->context && tsg->rpc->context->instance) ++ if (context->instance) + { +- rc = IFCALLRESULT(TRUE, tsg->rpc->context->instance->PresentGatewayMessage, +- tsg->rpc->context->instance, SwitchValue, ++ rc = IFCALLRESULT(TRUE, context->instance->PresentGatewayMessage, ++ context->instance, SwitchValue, + packetStringMessage.isDisplayMandatory != 0, + packetStringMessage.isConsentMandatory != 0, + packetStringMessage.msgBytes, packetStringMessage.msgBuffer); +@@ -1649,10 +1671,10 @@ static BOOL TsProxyMakeTunnelCallReadResponse(rdpTsg* tsg, RPC_PDU* pdu) + WLog_INFO(TAG, "Service Message: %s", messageText); + free(messageText); + +- if (tsg->rpc && tsg->rpc->context && tsg->rpc->context->instance) ++ if (context->instance) + { +- rc = IFCALLRESULT(TRUE, tsg->rpc->context->instance->PresentGatewayMessage, +- tsg->rpc->context->instance, SwitchValue, ++ rc = IFCALLRESULT(TRUE, context->instance->PresentGatewayMessage, ++ context->instance, SwitchValue, + packetStringMessage.isDisplayMandatory != 0, + packetStringMessage.isConsentMandatory != 0, + packetStringMessage.msgBytes, packetStringMessage.msgBuffer); +@@ -1704,6 +1726,8 @@ static BOOL TsProxyCreateChannelWriteRequest(rdpTsg* tsg, CONTEXT_HANDLE* tunnel + + rpc = tsg->rpc; + count = _wcslen(tsg->Hostname) + 1; ++ if (count > UINT32_MAX) ++ return FALSE; + s = Stream_New(NULL, 60 + count * 2); + + if (!s) +@@ -1723,15 +1747,15 @@ static BOOL TsProxyCreateChannelWriteRequest(rdpTsg* tsg, CONTEXT_HANDLE* tunnel + Stream_Write_UINT16(s, tsg->Port); /* PortNumber (0xD3D = 3389) (2 bytes) */ + Stream_Write_UINT32(s, 0x00000001); /* NumResourceNames (4 bytes) */ + Stream_Write_UINT32(s, 0x00020004); /* ResourceNamePtr (4 bytes) */ +- Stream_Write_UINT32(s, count); /* MaxCount (4 bytes) */ ++ Stream_Write_UINT32(s, (UINT32)count); /* MaxCount (4 bytes) */ + Stream_Write_UINT32(s, 0); /* Offset (4 bytes) */ +- Stream_Write_UINT32(s, count); /* ActualCount (4 bytes) */ ++ Stream_Write_UINT32(s, (UINT32)count); /* ActualCount (4 bytes) */ + Stream_Write_UTF16_String(s, tsg->Hostname, count); /* Array */ + return rpc_client_write_call(rpc, s, TsProxyCreateChannelOpnum); + } + +-static BOOL TsProxyCreateChannelReadResponse(rdpTsg* tsg, RPC_PDU* pdu, +- CONTEXT_HANDLE* channelContext, UINT32* channelId) ++static BOOL TsProxyCreateChannelReadResponse(RPC_PDU* pdu, CONTEXT_HANDLE* channelContext, ++ UINT32* channelId) + { + BOOL rc = FALSE; + WLog_DBG(TAG, "TsProxyCreateChannelReadResponse"); +@@ -1779,7 +1803,7 @@ static BOOL TsProxyCloseChannelWriteRequest(rdpTsg* tsg, CONTEXT_HANDLE* context + return rpc_client_write_call(rpc, s, TsProxyCloseChannelOpnum); + } + +-static BOOL TsProxyCloseChannelReadResponse(rdpTsg* tsg, RPC_PDU* pdu, CONTEXT_HANDLE* context) ++static BOOL TsProxyCloseChannelReadResponse(RPC_PDU* pdu, CONTEXT_HANDLE* context) + { + BOOL rc = FALSE; + WLog_DBG(TAG, "TsProxyCloseChannelReadResponse"); +@@ -1826,7 +1850,7 @@ static BOOL TsProxyCloseTunnelWriteRequest(rdpTsg* tsg, CONTEXT_HANDLE* context) + return rpc_client_write_call(rpc, s, TsProxyCloseTunnelOpnum); + } + +-static BOOL TsProxyCloseTunnelReadResponse(rdpTsg* tsg, RPC_PDU* pdu, CONTEXT_HANDLE* context) ++static BOOL TsProxyCloseTunnelReadResponse(RPC_PDU* pdu, CONTEXT_HANDLE* context) + { + BOOL rc = FALSE; + WLog_DBG(TAG, "TsProxyCloseTunnelReadResponse"); +@@ -1977,8 +2001,6 @@ BOOL tsg_recv_pdu(rdpTsg* tsg, RPC_PDU* pdu) + return FALSE; + + rpc = tsg->rpc; +- Stream_SealLength(pdu->s); +- Stream_SetPosition(pdu->s, 0); + + if (!(pdu->Flags & RPC_PDU_FLAG_STUB)) + { +@@ -2017,7 +2039,7 @@ BOOL tsg_recv_pdu(rdpTsg* tsg, RPC_PDU* pdu) + CONTEXT_HANDLE* TunnelContext; + TunnelContext = (tsg->reauthSequence) ? &tsg->NewTunnelContext : &tsg->TunnelContext; + +- if (!TsProxyAuthorizeTunnelReadResponse(tsg, pdu)) ++ if (!TsProxyAuthorizeTunnelReadResponse(pdu)) + { + WLog_ERR(TAG, "TsProxyAuthorizeTunnelReadResponse failure"); + return FALSE; +@@ -2066,7 +2088,7 @@ BOOL tsg_recv_pdu(rdpTsg* tsg, RPC_PDU* pdu) + { + CONTEXT_HANDLE ChannelContext; + +- if (!TsProxyCreateChannelReadResponse(tsg, pdu, &ChannelContext, &tsg->ChannelId)) ++ if (!TsProxyCreateChannelReadResponse(pdu, &ChannelContext, &tsg->ChannelId)) + { + WLog_ERR(TAG, "TsProxyCreateChannelReadResponse failure"); + return FALSE; +@@ -2139,7 +2161,7 @@ BOOL tsg_recv_pdu(rdpTsg* tsg, RPC_PDU* pdu) + { + CONTEXT_HANDLE ChannelContext; + +- if (!TsProxyCloseChannelReadResponse(tsg, pdu, &ChannelContext)) ++ if (!TsProxyCloseChannelReadResponse(pdu, &ChannelContext)) + { + WLog_ERR(TAG, "TsProxyCloseChannelReadResponse failure"); + return FALSE; +@@ -2151,7 +2173,7 @@ BOOL tsg_recv_pdu(rdpTsg* tsg, RPC_PDU* pdu) + { + CONTEXT_HANDLE TunnelContext; + +- if (!TsProxyCloseTunnelReadResponse(tsg, pdu, &TunnelContext)) ++ if (!TsProxyCloseTunnelReadResponse(pdu, &TunnelContext)) + { + WLog_ERR(TAG, "TsProxyCloseTunnelReadResponse failure"); + return FALSE; +@@ -2166,7 +2188,7 @@ BOOL tsg_recv_pdu(rdpTsg* tsg, RPC_PDU* pdu) + { + CONTEXT_HANDLE ChannelContext; + +- if (!TsProxyCloseChannelReadResponse(tsg, pdu, &ChannelContext)) ++ if (!TsProxyCloseChannelReadResponse(pdu, &ChannelContext)) + { + WLog_ERR(TAG, "TsProxyCloseChannelReadResponse failure"); + return FALSE; +@@ -2196,7 +2218,7 @@ BOOL tsg_recv_pdu(rdpTsg* tsg, RPC_PDU* pdu) + { + CONTEXT_HANDLE TunnelContext; + +- if (!TsProxyCloseTunnelReadResponse(tsg, pdu, &TunnelContext)) ++ if (!TsProxyCloseTunnelReadResponse(pdu, &TunnelContext)) + { + WLog_ERR(TAG, "TsProxyCloseTunnelReadResponse failure"); + return FALSE; +@@ -2305,10 +2327,25 @@ BOOL tsg_connect(rdpTsg* tsg, const char* hostname, UINT16 port, DWORD timeout) + { + UINT64 looptimeout = timeout * 1000ULL; + DWORD nCount; +- HANDLE events[64]; +- rdpRpc* rpc = tsg->rpc; +- rdpSettings* settings = rpc->settings; +- rdpTransport* transport = rpc->transport; ++ HANDLE events[MAXIMUM_WAIT_OBJECTS] = { 0 }; ++ rdpRpc* rpc; ++ rdpContext* context; ++ rdpSettings* settings; ++ rdpTransport* transport; ++ ++ assert(tsg); ++ ++ rpc = tsg->rpc; ++ assert(rpc); ++ ++ transport = rpc->transport; ++ assert(transport); ++ ++ context = tsg->rpc->context; ++ assert(context); ++ ++ settings = context->settings; ++ + tsg->Port = port; + tsg->transport = transport; + +@@ -2327,7 +2364,7 @@ BOOL tsg_connect(rdpTsg* tsg, const char* hostname, UINT16 port, DWORD timeout) + return FALSE; + } + +- nCount = tsg_get_event_handles(tsg, events, 64); ++ nCount = tsg_get_event_handles(tsg, events, ARRAYSIZE(events)); + + if (nCount == 0) + return FALSE; +@@ -2410,7 +2447,7 @@ BOOL tsg_disconnect(rdpTsg* tsg) + * @return < 0 on error; 0 if not enough data is available (non blocking mode); > 0 bytes to read + */ + +-static int tsg_read(rdpTsg* tsg, BYTE* data, UINT32 length) ++static int tsg_read(rdpTsg* tsg, BYTE* data, size_t length) + { + rdpRpc* rpc; + int status = 0; +@@ -2428,7 +2465,7 @@ static int tsg_read(rdpTsg* tsg, BYTE* data, UINT32 length) + + do + { +- status = rpc_client_receive_pipe_read(rpc->client, data, (size_t)length); ++ status = rpc_client_receive_pipe_read(rpc->client, data, length); + + if (status < 0) + return -1; +@@ -2478,7 +2515,7 @@ static int tsg_write(rdpTsg* tsg, const BYTE* data, UINT32 length) + if (status < 0) + return -1; + +- return length; ++ return (int)length; + } + + rdpTsg* tsg_new(rdpTransport* transport) +@@ -2490,7 +2527,6 @@ rdpTsg* tsg_new(rdpTransport* transport) + return NULL; + + tsg->transport = transport; +- tsg->settings = transport->settings; + tsg->rpc = rpc_new(tsg->transport); + + if (!tsg->rpc) +@@ -2518,7 +2554,10 @@ static int transport_bio_tsg_write(BIO* bio, const char* buf, int num) + int status; + rdpTsg* tsg = (rdpTsg*)BIO_get_data(bio); + BIO_clear_flags(bio, BIO_FLAGS_WRITE); +- status = tsg_write(tsg, (BYTE*)buf, num); ++ ++ if (num < 0) ++ return -1; ++ status = tsg_write(tsg, (const BYTE*)buf, (UINT32)num); + + if (status < 0) + { +@@ -2550,7 +2589,7 @@ static int transport_bio_tsg_read(BIO* bio, char* buf, int size) + } + + BIO_clear_flags(bio, BIO_FLAGS_READ); +- status = tsg_read(tsg, (BYTE*)buf, size); ++ status = tsg_read(tsg, (BYTE*)buf, (size_t)size); + + if (status < 0) + { +@@ -2572,17 +2611,22 @@ static int transport_bio_tsg_read(BIO* bio, char* buf, int size) + + static int transport_bio_tsg_puts(BIO* bio, const char* str) + { ++ WINPR_UNUSED(bio); ++ WINPR_UNUSED(str); + return 1; + } + + static int transport_bio_tsg_gets(BIO* bio, char* str, int size) + { ++ WINPR_UNUSED(bio); ++ WINPR_UNUSED(str); ++ WINPR_UNUSED(size); + return 1; + } + + static long transport_bio_tsg_ctrl(BIO* bio, int cmd, long arg1, void* arg2) + { +- int status = -1; ++ long status = -1; + rdpTsg* tsg = (rdpTsg*)BIO_get_data(bio); + RpcVirtualConnection* connection = tsg->rpc->VirtualConnection; + RpcInChannel* inChannel = connection->DefaultInChannel; +@@ -2611,27 +2655,27 @@ static long transport_bio_tsg_ctrl(BIO* bio, int cmd, long arg1, void* arg2) + + case BIO_C_READ_BLOCKED: + { +- BIO* bio = outChannel->common.bio; +- status = BIO_read_blocked(bio); ++ BIO* cbio = outChannel->common.bio; ++ status = BIO_read_blocked(cbio); + } + break; + + case BIO_C_WRITE_BLOCKED: + { +- BIO* bio = inChannel->common.bio; +- status = BIO_write_blocked(bio); ++ BIO* cbio = inChannel->common.bio; ++ status = BIO_write_blocked(cbio); + } + break; + + case BIO_C_WAIT_READ: + { + int timeout = (int)arg1; +- BIO* bio = outChannel->common.bio; ++ BIO* cbio = outChannel->common.bio; + +- if (BIO_read_blocked(bio)) +- return BIO_wait_read(bio, timeout); +- else if (BIO_write_blocked(bio)) +- return BIO_wait_write(bio, timeout); ++ if (BIO_read_blocked(cbio)) ++ return BIO_wait_read(cbio, timeout); ++ else if (BIO_write_blocked(cbio)) ++ return BIO_wait_write(cbio, timeout); + else + status = 1; + } +@@ -2640,12 +2684,12 @@ static long transport_bio_tsg_ctrl(BIO* bio, int cmd, long arg1, void* arg2) + case BIO_C_WAIT_WRITE: + { + int timeout = (int)arg1; +- BIO* bio = inChannel->common.bio; ++ BIO* cbio = inChannel->common.bio; + +- if (BIO_write_blocked(bio)) +- status = BIO_wait_write(bio, timeout); +- else if (BIO_read_blocked(bio)) +- status = BIO_wait_read(bio, timeout); ++ if (BIO_write_blocked(cbio)) ++ status = BIO_wait_write(cbio, timeout); ++ else if (BIO_read_blocked(cbio)) ++ status = BIO_wait_read(cbio, timeout); + else + status = 1; + } +@@ -2660,6 +2704,7 @@ static long transport_bio_tsg_ctrl(BIO* bio, int cmd, long arg1, void* arg2) + + static int transport_bio_tsg_new(BIO* bio) + { ++ assert(bio); + BIO_set_init(bio, 1); + BIO_set_flags(bio, BIO_FLAGS_SHOULD_RETRY); + return 1; +@@ -2667,6 +2712,8 @@ static int transport_bio_tsg_new(BIO* bio) + + static int transport_bio_tsg_free(BIO* bio) + { ++ assert(bio); ++ WINPR_UNUSED(bio); + return 1; + } + diff --git a/SPECS/freerdp.spec b/SPECS/freerdp.spec index 7bafdb8..f7a97aa 100644 --- a/SPECS/freerdp.spec +++ b/SPECS/freerdp.spec @@ -22,7 +22,7 @@ Name: freerdp Version: 2.2.0 -Release: 2%{?dist} +Release: 4%{?dist} Epoch: 2 Summary: Free implementation of the Remote Desktop Protocol (RDP) License: ASL 2.0 @@ -33,6 +33,13 @@ Source0: https://github.com/FreeRDP/FreeRDP/archive/%{version}/FreeRDP-%{ # https://bugzilla.redhat.com/show_bug.cgi?id=1910029 Patch0: 6741.patch +# CVE-2021-41160 freerdp: improper region checks in all clients allow out of bound write to memory +Patch1: Add-checks-for-bitmap-and-glyph-width-heigth-values.patch + +# CVE-2021-41159 freerdp: improper client input validation for gateway connections allows to overwrite memory +Patch2: Implement-missing-TSG-debug-functions.patch +Patch3: Refactored-RPC-gateway-parser.patch + BuildRequires: gcc BuildRequires: gcc-c++ BuildRequires: alsa-lib-devel @@ -303,6 +310,12 @@ find %{buildroot} -name "*.a" -delete %{_libdir}/pkgconfig/winpr-tools2.pc %changelog +* Wed Nov 10 2021 Ondrej Holy - 2:2.2.0-4 +- Refactored RPC gateway parser (rhbz#2017949) + +* Fri Nov 05 2021 Felipe Borges - 2:2.2.0-3 +- Add checks for bitmap and glyph width and heigth values (rhbz#2017956) + * Wed Apr 28 2021 Ondrej Holy - 2:2.2.0-2 - Fix exit codes for /help and similar options (rhbz#1910029)