Merge pull request #11340 from akallabeth/rpc-gateway

[core,gateway] add rts parser checks
This commit is contained in:
akallabeth
2025-03-13 12:41:32 +01:00
committed by GitHub
7 changed files with 152 additions and 61 deletions

View File

@@ -42,6 +42,10 @@
#define TAG CHANNELS_TAG("rdpear.client")
#ifndef MAX_KEYTAB_NAME_LEN
#define MAX_KEYTAB_NAME_LEN 1100 /* Defined in MIT krb5.h */
#endif
/* defined in libkrb5 */
krb5_error_code encode_krb5_authenticator(const krb5_authenticator* rep, krb5_data** code_out);
krb5_error_code encode_krb5_ap_rep(const krb5_ap_rep* rep, krb5_data** code_out);
@@ -515,15 +519,33 @@ static BOOL rdpear_kerb_CreateApReqAuthenticator(RDPEAR_PLUGIN* rdpear, NdrConte
for (int i = 0; i < client.length; i++)
{
client.data[i].data = KERB_RPC_UNICODESTR_to_charptr(&req.ClientName->Names[i]);
if (!client.data[i].data)
krb5_data* cur = &client.data[i];
cur->data = KERB_RPC_UNICODESTR_to_charptr(&req.ClientName->Names[i]);
if (!cur->data)
goto out;
client.data[i].length = (unsigned int)strnlen(client.data[i].data, UINT32_MAX);
const size_t len = strnlen(cur->data, MAX_KEYTAB_NAME_LEN + 1);
if (len > MAX_KEYTAB_NAME_LEN)
{
WLog_ERR(TAG,
"Invalid ClientName length %" PRIuz
", limited to %u characters. ClientName: (%s)",
MAX_KEYTAB_NAME_LEN, len, cur->data);
goto out;
}
cur->length = (unsigned int)len;
}
client.realm.data = KERB_RPC_UNICODESTR_to_charptr(req.ClientRealm);
if (!client.realm.data)
goto out;
client.realm.length = (unsigned int)strnlen(client.realm.data, UINT32_MAX);
const size_t len = strnlen(client.realm.data, MAX_KEYTAB_NAME_LEN + 1);
if (len > MAX_KEYTAB_NAME_LEN)
{
WLog_ERR(TAG, "Invalid realm length %" PRIuz ", limited to %u characters. Realm: (%s)",
MAX_KEYTAB_NAME_LEN, len, client.realm.data);
goto out;
}
client.realm.length = (unsigned int)len;
authent.client = &client;
krb5_checksum checksum;

View File

@@ -624,7 +624,8 @@ static BOOL freerdp_dsp_decode_opus(FREERDP_DSP_CONTEXT* WINPR_RESTRICT context,
if (!Stream_EnsureRemainingCapacity(context->common.buffer, max_size))
return FALSE;
frames = opus_decode(context->opus_decoder, src, size, Stream_Pointer(out), OPUS_MAX_FRAMES, 0);
frames = opus_decode(context->opus_decoder, src, WINPR_ASSERTING_INT_CAST(opus_int32, size),
Stream_Pointer(out), OPUS_MAX_FRAMES, 0);
if (frames < 0)
return FALSE;
@@ -645,10 +646,11 @@ static BOOL freerdp_dsp_encode_opus(FREERDP_DSP_CONTEXT* WINPR_RESTRICT context,
if (!Stream_EnsureRemainingCapacity(context->common.buffer, max_size))
return FALSE;
const int src_frames = size / sizeof(opus_int16) / context->common.format.nChannels;
const size_t src_frames = size / sizeof(opus_int16) / context->common.format.nChannels;
const opus_int16* src_data = (const opus_int16*)src;
const int frames =
opus_encode(context->opus_encoder, src_data, src_frames, Stream_Pointer(out), max_size);
const int frames = opus_encode(
context->opus_encoder, src_data, WINPR_ASSERTING_INT_CAST(opus_int32, src_frames),
Stream_Pointer(out), WINPR_ASSERTING_INT_CAST(opus_int32, max_size));
if (frames < 0)
return FALSE;
return Stream_SafeSeek(out, frames * context->common.format.nChannels * sizeof(int16_t));

View File

@@ -204,7 +204,7 @@ void region16_clear(REGION16* region)
}
WINPR_ATTR_MALLOC(freeRegion, 1)
static INLINE REGION16_DATA* allocateRegion(size_t nbItems)
static REGION16_DATA* allocateRegion(size_t nbItems)
{
REGION16_DATA* data = calloc(1, sizeof(REGION16_DATA));
if (!data)

View File

@@ -591,12 +591,6 @@ static BOOL rts_write_syntax_id(wStream* s, const p_syntax_id_t* syntax_id)
return TRUE;
}
static 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;
}
static void rts_context_elem_free(p_cont_elem_t* ptr)
{
if (!ptr)
@@ -605,6 +599,13 @@ static void rts_context_elem_free(p_cont_elem_t* ptr)
free(ptr);
}
WINPR_ATTR_MALLOC(rts_context_elem_free, 1)
static 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;
}
static BOOL rts_read_context_elem(wStream* s, p_cont_elem_t* element, BOOL silent)
{
WINPR_ASSERT(s);
@@ -1240,21 +1241,32 @@ static BOOL rts_write_pdu_header(wStream* s, const rpcconn_rts_hdr_t* header)
return TRUE;
}
static BOOL rts_receive_window_size_command_read(WINPR_ATTR_UNUSED rdpRpc* rpc, wStream* buffer,
UINT64* ReceiveWindowSize)
/* [MS-RPCH] 2.2.3.5.1 ReceiveWindowSize */
static BOOL rts_receive_window_size_command_read(rdpRpc* rpc, wStream* buffer,
UINT32* ReceiveWindowSize)
{
WINPR_ASSERT(rpc);
WINPR_ASSERT(buffer);
if (!Stream_CheckAndLogRequiredLength(TAG, buffer, 8))
return FALSE;
const UINT64 val = Stream_Get_UINT64(buffer);
const uint32_t CommandType = Stream_Get_UINT32(buffer);
if (CommandType != RTS_CMD_RECEIVE_WINDOW_SIZE)
{
WLog_Print(rpc->log, WLOG_ERROR,
"[MS-RPCH] 2.2.3.5.1 ReceiveWindowSize::CommandType must be 0x08" PRIx32 ", got "
"0x%08" PRIx32,
RTS_CMD_RECEIVE_WINDOW_SIZE, CommandType);
return FALSE;
}
const UINT32 val = Stream_Get_UINT32(buffer);
if (ReceiveWindowSize)
*ReceiveWindowSize = val; /* ReceiveWindowSize (8 bytes) */
*ReceiveWindowSize = val; /* ReceiveWindowSize (4 bytes) */
return TRUE;
}
/* [MS-RPCH] 2.2.3.5.1 ReceiveWindowSize */
static BOOL rts_receive_window_size_command_write(wStream* s, UINT32 ReceiveWindowSize)
{
WINPR_ASSERT(s);
@@ -1268,6 +1280,7 @@ static BOOL rts_receive_window_size_command_write(wStream* s, UINT32 ReceiveWind
return TRUE;
}
/* [MS-RPCH] 2.2.3.5.2 FlowControlAck */
static int rts_flow_control_ack_command_read(rdpRpc* rpc, wStream* buffer, UINT32* BytesReceived,
UINT32* AvailableWindow, BYTE* ChannelCookie)
{
@@ -1310,6 +1323,7 @@ static int rts_flow_control_ack_command_read(rdpRpc* rpc, wStream* buffer, UINT3
return 24;
}
/* [MS-RPCH] 2.2.3.5.2 FlowControlAck */
static BOOL rts_flow_control_ack_command_write(wStream* s, UINT32 BytesReceived,
UINT32 AvailableWindow, BYTE* ChannelCookie)
{
@@ -1326,8 +1340,9 @@ static BOOL rts_flow_control_ack_command_write(wStream* s, UINT32 BytesReceived,
return TRUE;
}
/* [MS-RPCH] 2.2.3.5.3 ConnectionTimeout */
static BOOL rts_connection_timeout_command_read(WINPR_ATTR_UNUSED rdpRpc* rpc, wStream* buffer,
UINT64* ConnectionTimeout)
UINT32* ConnectionTimeout)
{
WINPR_ASSERT(rpc);
WINPR_ASSERT(buffer);
@@ -1335,9 +1350,18 @@ static BOOL rts_connection_timeout_command_read(WINPR_ATTR_UNUSED rdpRpc* rpc, w
if (!Stream_CheckAndLogRequiredLength(TAG, buffer, 8))
return FALSE;
UINT64 val = Stream_Get_UINT64(buffer);
const uint32_t CommandType = Stream_Get_UINT32(buffer);
if (CommandType != RTS_CMD_CONNECTION_TIMEOUT)
{
WLog_Print(rpc->log, WLOG_ERROR,
"[MS-RPCH] 2.2.3.5.3 ConnectionTimeout::CommandType must be 0x08" PRIx32 ", got "
"0x%08" PRIx32,
RTS_CMD_CONNECTION_TIMEOUT, CommandType);
return FALSE;
}
const UINT32 val = Stream_Get_UINT32(buffer);
if (ConnectionTimeout)
*ConnectionTimeout = val; /* ConnectionTimeout (8 bytes) */
*ConnectionTimeout = val; /* ConnectionTimeout (4 bytes) */
return TRUE;
}
@@ -1385,19 +1409,38 @@ static BOOL rts_client_keepalive_command_write(wStream* s, UINT32 ClientKeepaliv
return TRUE;
}
static BOOL rts_version_command_read(WINPR_ATTR_UNUSED rdpRpc* rpc, wStream* buffer)
/* [MS-RPCH] 2.2.3.5.7 Version */
static BOOL rts_version_command_read(rdpRpc* rpc, wStream* buffer, uint32_t* pversion)
{
WINPR_ASSERT(rpc);
WINPR_ASSERT(buffer);
if (!Stream_SafeSeek(buffer, 8))
if (!Stream_EnsureRemainingCapacity(buffer, 8))
return FALSE;
/* command (4 bytes) */
/* Version (4 bytes) */
const uint32_t CommandType = Stream_Get_UINT32(buffer); /* CommandType (4 bytes) */
if (CommandType != RTS_CMD_VERSION)
{
WLog_Print(rpc->log, WLOG_ERROR,
"[MS-RPCH] 2.2.3.5.7 Version::CommandType must be 0x08" PRIx32 ", got "
"0x%08" PRIx32,
RTS_CMD_VERSION, CommandType);
return FALSE;
}
const uint32_t version = Stream_Get_UINT32(buffer); /* Version (4 bytes) */
if (version != 1)
{
WLog_Print(rpc->log, WLOG_WARN,
"[MS-RPCH] 2.2.3.5.7 Version::Version should be 0x00000001, got 0x%08" PRIx32,
version);
}
if (pversion)
*pversion = version;
return TRUE;
}
/* [MS-RPCH] 2.2.3.5.7 Version */
static BOOL rts_version_command_write(wStream* buffer)
{
WINPR_ASSERT(buffer);
@@ -1603,24 +1646,21 @@ fail:
BOOL rts_recv_CONN_A3_pdu(rdpRpc* rpc, wStream* buffer)
{
BOOL rc = 0;
UINT64 ConnectionTimeout = 0;
UINT32 ConnectionTimeout = 0;
if (!Stream_SafeSeek(buffer, 20))
return FALSE;
rc = rts_connection_timeout_command_read(rpc, buffer, &ConnectionTimeout);
if (!rc || (ConnectionTimeout > UINT32_MAX))
return rc;
if (!rts_connection_timeout_command_read(rpc, buffer, &ConnectionTimeout))
return FALSE;
WLog_DBG(TAG, "Receiving CONN/A3 RTS PDU: ConnectionTimeout: %" PRIu64 "", ConnectionTimeout);
WLog_DBG(TAG, "Receiving CONN/A3 RTS PDU: ConnectionTimeout: %" PRIu32 "", ConnectionTimeout);
WINPR_ASSERT(rpc);
WINPR_ASSERT(rpc->VirtualConnection);
WINPR_ASSERT(rpc->VirtualConnection->DefaultInChannel);
rpc->VirtualConnection->DefaultInChannel->PingOriginator.ConnectionTimeout =
(UINT32)ConnectionTimeout;
rpc->VirtualConnection->DefaultInChannel->PingOriginator.ConnectionTimeout = ConnectionTimeout;
return TRUE;
}
@@ -1682,32 +1722,32 @@ fail:
return status;
}
/* CONN/C Sequence */
/* [MS-RPCH] 2.2.4.9 CONN/C2 RTS PDU */
BOOL rts_recv_CONN_C2_pdu(rdpRpc* rpc, wStream* buffer)
{
BOOL rc = FALSE;
UINT64 ReceiveWindowSize = 0;
UINT64 ConnectionTimeout = 0;
UINT32 ReceiveWindowSize = 0;
UINT32 ConnectionTimeout = 0;
WINPR_ASSERT(rpc);
WINPR_ASSERT(buffer);
if (!Stream_SafeSeek(buffer, 20))
return FALSE;
rpcconn_hdr_t header = { 0 };
if (!rts_read_pdu_header(buffer, &header))
goto fail;
rc = rts_version_command_read(rpc, buffer);
if (!rc)
return rc;
rc = rts_receive_window_size_command_read(rpc, buffer, &ReceiveWindowSize);
if (!rc || (ReceiveWindowSize > UINT32_MAX))
return rc;
rc = rts_connection_timeout_command_read(rpc, buffer, &ConnectionTimeout);
if (!rc || (ConnectionTimeout > UINT32_MAX))
return rc;
if (!rts_version_command_read(rpc, buffer, NULL))
goto fail;
if (!rts_receive_window_size_command_read(rpc, buffer, &ReceiveWindowSize))
goto fail;
if (!rts_connection_timeout_command_read(rpc, buffer, &ConnectionTimeout))
goto fail;
WLog_DBG(TAG,
"Receiving CONN/C2 RTS PDU: ConnectionTimeout: %" PRIu64 " ReceiveWindowSize: %" PRIu64
"Receiving CONN/C2 RTS PDU: ConnectionTimeout: %" PRIu32 " ReceiveWindowSize: %" PRIu32
"",
ConnectionTimeout, ReceiveWindowSize);
@@ -1715,10 +1755,14 @@ BOOL rts_recv_CONN_C2_pdu(rdpRpc* rpc, wStream* buffer)
WINPR_ASSERT(rpc->VirtualConnection);
WINPR_ASSERT(rpc->VirtualConnection->DefaultInChannel);
rpc->VirtualConnection->DefaultInChannel->PingOriginator.ConnectionTimeout =
(UINT32)ConnectionTimeout;
rpc->VirtualConnection->DefaultInChannel->PeerReceiveWindow = (UINT32)ReceiveWindowSize;
return TRUE;
rpc->VirtualConnection->DefaultInChannel->PingOriginator.ConnectionTimeout = ConnectionTimeout;
rpc->VirtualConnection->DefaultInChannel->PeerReceiveWindow = ReceiveWindowSize;
rc = TRUE;
fail:
rts_free_pdu_header(&header, FALSE);
return rc;
}
/* Out-of-Sequence PDUs */

View File

@@ -1451,11 +1451,11 @@ static BOOL rdp_write_logon_info_v2(wStream* s, logon_info* info)
*/
Stream_Write_UINT32(s, logonInfoV2Size);
Stream_Write_UINT32(s, info->sessionId);
domainLen = strnlen(info->domain, UINT32_MAX);
domainLen = strnlen(info->domain, 256); /* lmcons.h UNLEN */
if (domainLen >= UINT32_MAX / sizeof(WCHAR))
return FALSE;
Stream_Write_UINT32(s, (UINT32)(domainLen + 1) * sizeof(WCHAR));
usernameLen = strnlen(info->username, UINT32_MAX);
usernameLen = strnlen(info->username, 256); /* lmcons.h UNLEN */
if (usernameLen >= UINT32_MAX / sizeof(WCHAR))
return FALSE;
Stream_Write_UINT32(s, (UINT32)(usernameLen + 1) * sizeof(WCHAR));

View File

@@ -35,6 +35,7 @@
#include <winpr/crt.h>
#include <winpr/assert.h>
#include <winpr/string.h>
#include "registry_reg.h"
@@ -43,6 +44,24 @@
static Reg* instance = NULL;
static size_t regsz_length(const char* key, const void* value, bool unicode)
{
/* https://learn.microsoft.com/en-us/windows/win32/sysinfo/registry-element-size-limits
*
* while not strictly limited to this size larger values should be stored to a
* file.
*/
const size_t limit = 16383;
size_t length = 0;
if (unicode)
length = _wcsnlen((const WCHAR*)value, limit);
else
length = strnlen((const char*)value, limit);
if (length >= limit)
WLog_WARN(TAG, "REG_SZ[%s] truncated to size %" PRIuz, key, length);
return length;
}
static Reg* RegGetInstance(void)
{
if (!instance)
@@ -436,7 +455,8 @@ LONG RegQueryValueExW(HKEY hKey, LPCWSTR lpValueName, LPDWORD lpReserved, LPDWOR
goto end;
case REG_SZ:
{
const size_t length = strnlen(pValue->data.string, UINT32_MAX) * sizeof(WCHAR);
const size_t length =
regsz_length(pValue->name, pValue->data.string, TRUE) * sizeof(WCHAR);
status = ERROR_SUCCESS;
if (lpData != NULL)
@@ -508,7 +528,8 @@ LONG RegQueryValueExA(HKEY hKey, LPCSTR lpValueName, LPDWORD lpReserved, LPDWORD
return reg_read_int(pValue, lpData, lpcbData);
case REG_SZ:
{
const size_t length = strnlen(pValue->data.string, UINT32_MAX);
const size_t length = regsz_length(pValue->name, pValue->data.string, FALSE);
char* pData = (char*)lpData;
if (pData != NULL)

View File

@@ -58,7 +58,9 @@ WINPR_PRAGMA_DIAG_POP
#define TAG "com.winpr.utils.debug"
static const char* support_msg = "Invalid stacktrace buffer! check if platform is supported!";
#define LINE_LENGTH_MAX 2048
static const char support_msg[] = "Invalid stacktrace buffer! check if platform is supported!";
void winpr_backtrace_free(void* buffer)
{
@@ -93,7 +95,7 @@ void* winpr_backtrace(DWORD size)
WLog_FATAL(TAG, "%s", support_msg);
/* return a non NULL buffer to allow the backtrace function family to succeed without failing
*/
return _strdup(support_msg);
return strndup(support_msg, sizeof(support_msg));
#endif
}
@@ -125,7 +127,7 @@ char** winpr_backtrace_symbols(void* buffer, size_t* used)
* 2. The first sizeof(char*) bytes contain the pointer to the string following the pointer.
* 3. The at data + sizeof(char*) contains the actual string
*/
size_t len = strlen(support_msg);
size_t len = strnlen(support_msg, sizeof(support_msg));
char* ppmsg = calloc(sizeof(char*) + len + 1, sizeof(char));
if (!ppmsg)
return NULL;
@@ -158,7 +160,7 @@ void winpr_backtrace_symbols_fd(void* buffer, int fd)
return;
for (size_t i = 0; i < used; i++)
(void)_write(fd, lines[i], (unsigned)strnlen(lines[i], UINT32_MAX));
(void)_write(fd, lines[i], (unsigned)strnlen(lines[i], LINE_LENGTH_MAX));
free((void*)lines);
}
#else