libxrdp: Replace mbstowcs/wcstombs calls

These calls are replaced with the newer UTF-16 parsing code
withing the parse module
This commit is contained in:
matt335672 2023-09-20 16:14:55 +01:00
parent 36ea4a3f86
commit 3a5b8936d1
2 changed files with 70 additions and 102 deletions

View File

@ -244,35 +244,11 @@ xrdp_orders_send_window_icon(struct xrdp_orders *self,
static int static int
xrdp_orders_send_as_unicode(struct stream *s, const char *text) xrdp_orders_send_as_unicode(struct stream *s, const char *text)
{ {
int str_chars; unsigned int text_len = strlen(text);
int index; int i32 = utf8_as_utf16_word_count(text, text_len) * 2;
int i32;
int len;
twchar *wdst;
len = g_strlen(text) + 1; out_uint16_le(s, i32);
out_utf8_as_utf16_le(s, text, text_len);
wdst = (twchar *) g_malloc(sizeof(twchar) * len, 1);
if (wdst == 0)
{
return 1;
}
str_chars = g_mbstowcs(wdst, text, len);
if (str_chars > 0)
{
i32 = str_chars * 2;
out_uint16_le(s, i32);
for (index = 0; index < str_chars; index++)
{
i32 = wdst[index];
out_uint16_le(s, i32);
}
}
else
{
out_uint16_le(s, 0);
}
g_free(wdst);
return 0; return 0;
} }
@ -280,21 +256,9 @@ xrdp_orders_send_as_unicode(struct stream *s, const char *text)
static int static int
xrdp_orders_get_unicode_bytes(const char *text) xrdp_orders_get_unicode_bytes(const char *text)
{ {
int num_chars; unsigned int text_len = strlen(text);
/* Add 1 to word size to include length ([MS-RDPERP] 2.2.1.2.1) */
num_chars = g_mbstowcs(0, text, 0); return (utf8_as_utf16_word_count(text, text_len) + 1) * 2;
if (num_chars < 0)
{
/* g_mbstowcs failed, we ignore that text by returning zero bytes */
num_chars = 0;
}
else
{
/* calculate the number of bytes of the resulting null-terminated wide-string */
num_chars = (num_chars + 1) * 2;
}
return num_chars;
} }
/*****************************************************************************/ /*****************************************************************************/

View File

@ -774,51 +774,56 @@ xrdp_sec_encrypt(struct xrdp_sec *self, char *data, int len)
self->encrypt_use_count++; self->encrypt_use_count++;
} }
/***************************************************************************** /*****************************************************************************/
* convert utf-16 encoded string from stream into utf-8 string. /**
* note: src_bytes doesn't include the null-terminator char. * Reads a null-terminated unicode string from a stream where the length
* Copied From: xrdp_sec.c * of the string is known.
*
* Strings are part of one of the following from [MS-RDPBCGR] :-
* - TS_INFO_PACKET (2.2.1.11.1.1)
* - TS_EXTENDED_INFO_PACKET (2.2.1.11.1.1.1)
*
* @param s Stream
* @param src_bytes Size in bytes of the string, EXCLUDING the two-byte
* terminator
* @param dst Destination buffer
* @param dst_len Length of buffer, including terminator space
*
* @return 0 for success, != 0 for a buffer overflow or a missing terminator
*/ */
static int static int
unicode_utf16_in(struct stream *s, int src_bytes, char *dst, int dst_len) ts_info_utf16_in(struct stream *s, int src_bytes, char *dst, int dst_len)
{ {
twchar *src; int rv = 0;
int num_chars;
int i;
int bytes;
LOG_DEVEL(LOG_LEVEL_TRACE, "unicode_utf16_in: uni_len %d, dst_len %d", src_bytes, dst_len); LOG_DEVEL(LOG_LEVEL_TRACE, "ts_info_utf16_in: uni_len %d, dst_len %d", src_bytes, dst_len);
if (src_bytes == 0)
if (!s_check_rem_and_log(s, src_bytes + 2, "ts_info_utf16_in"))
{ {
if (!s_check_rem_and_log(s, 2, "Parsing UTF-16")) rv = 1;
}
else
{
int term;
int num_chars = in_utf16_le_fixed_as_utf8(s, src_bytes / 2,
dst, dst_len);
if (num_chars > dst_len)
{ {
return 1; LOG(LOG_LEVEL_ERROR, "ts_info_utf16_in: output buffer overflow");
rv = 1;
} }
LOG_DEVEL(LOG_LEVEL_TRACE, "unicode_utf16_in: num_chars 0, dst '' (empty string)");
in_uint8s(s, 2); /* null terminator */
return 0;
}
bytes = src_bytes + 2; /* include the null terminator */ // String should be null-terminated. We haven't read the terminator yet
src = g_new0(twchar, bytes); in_uint16_le(s, term);
for (i = 0; i < bytes / 2; ++i) if (term != 0)
{
if (!s_check_rem_and_log(s, 2, "Parsing UTF-16"))
{ {
g_free(src); LOG(LOG_LEVEL_ERROR,
return 1; "ts_info_utf16_in: bad terminator. Expected 0, got %d", term);
rv = 1;
} }
in_uint16_le(s, src[i]);
} }
num_chars = g_wcstombs(dst, src, dst_len);
if (num_chars < 0)
{
g_memset(dst, '\0', dst_len);
}
LOG_DEVEL(LOG_LEVEL_TRACE, "unicode_utf16_in: num_chars %d, dst '%s'", num_chars, dst);
g_free(src);
return 0; return rv;
} }
/*****************************************************************************/ /*****************************************************************************/
@ -998,13 +1003,13 @@ xrdp_sec_process_logon_info(struct xrdp_sec *self, struct stream *s)
return 1; return 1;
} }
if (unicode_utf16_in(s, len_domain, self->rdp_layer->client_info.domain, sizeof(self->rdp_layer->client_info.domain) - 1) != 0) if (ts_info_utf16_in(s, len_domain, self->rdp_layer->client_info.domain, sizeof(self->rdp_layer->client_info.domain)) != 0)
{ {
LOG(LOG_LEVEL_ERROR, "ERROR reading domain"); LOG(LOG_LEVEL_ERROR, "ERROR reading domain");
return 1; return 1;
} }
if (unicode_utf16_in(s, len_user, self->rdp_layer->client_info.username, sizeof(self->rdp_layer->client_info.username) - 1) != 0) if (ts_info_utf16_in(s, len_user, self->rdp_layer->client_info.username, sizeof(self->rdp_layer->client_info.username)) != 0)
{ {
LOG(LOG_LEVEL_ERROR, "ERROR reading user name"); LOG(LOG_LEVEL_ERROR, "ERROR reading user name");
return 1; return 1;
@ -1012,7 +1017,7 @@ xrdp_sec_process_logon_info(struct xrdp_sec *self, struct stream *s)
if (flags & RDP_LOGON_AUTO) if (flags & RDP_LOGON_AUTO)
{ {
if (unicode_utf16_in(s, len_password, self->rdp_layer->client_info.password, sizeof(self->rdp_layer->client_info.password) - 1) != 0) if (ts_info_utf16_in(s, len_password, self->rdp_layer->client_info.password, sizeof(self->rdp_layer->client_info.password)) != 0)
{ {
LOG(LOG_LEVEL_ERROR, "ERROR reading password"); LOG(LOG_LEVEL_ERROR, "ERROR reading password");
return 1; return 1;
@ -1053,13 +1058,13 @@ xrdp_sec_process_logon_info(struct xrdp_sec *self, struct stream *s)
g_strncat(self->rdp_layer->client_info.username, self->rdp_layer->client_info.domain, size - 1 - g_strlen(self->rdp_layer->client_info.domain)); g_strncat(self->rdp_layer->client_info.username, self->rdp_layer->client_info.domain, size - 1 - g_strlen(self->rdp_layer->client_info.domain));
} }
if (unicode_utf16_in(s, len_program, self->rdp_layer->client_info.program, sizeof(self->rdp_layer->client_info.program) - 1) != 0) if (ts_info_utf16_in(s, len_program, self->rdp_layer->client_info.program, sizeof(self->rdp_layer->client_info.program)) != 0)
{ {
LOG(LOG_LEVEL_ERROR, "ERROR reading program"); LOG(LOG_LEVEL_ERROR, "ERROR reading program");
return 1; return 1;
} }
if (unicode_utf16_in(s, len_directory, self->rdp_layer->client_info.directory, sizeof(self->rdp_layer->client_info.directory) - 1) != 0) if (ts_info_utf16_in(s, len_directory, self->rdp_layer->client_info.directory, sizeof(self->rdp_layer->client_info.directory)) != 0)
{ {
LOG(LOG_LEVEL_ERROR, "ERROR reading directory"); LOG(LOG_LEVEL_ERROR, "ERROR reading directory");
return 1; return 1;
@ -1093,7 +1098,7 @@ xrdp_sec_process_logon_info(struct xrdp_sec *self, struct stream *s)
/* TS_EXTENDED_INFO_PACKET required fields */ /* TS_EXTENDED_INFO_PACKET required fields */
in_uint8s(s, 2); /* clientAddressFamily */ in_uint8s(s, 2); /* clientAddressFamily */
in_uint16_le(s, len_ip); in_uint16_le(s, len_ip);
if (unicode_utf16_in(s, len_ip - 2, tmpdata, sizeof(tmpdata) - 1) != 0) if (ts_info_utf16_in(s, len_ip - 2, tmpdata, sizeof(tmpdata)) != 0)
{ {
LOG(LOG_LEVEL_ERROR, "ERROR reading ip"); LOG(LOG_LEVEL_ERROR, "ERROR reading ip");
return 1; return 1;
@ -1103,7 +1108,7 @@ xrdp_sec_process_logon_info(struct xrdp_sec *self, struct stream *s)
return 1; return 1;
} }
in_uint16_le(s, len_dll); in_uint16_le(s, len_dll);
if (unicode_utf16_in(s, len_dll - 2, tmpdata, sizeof(tmpdata) - 1) != 0) if (ts_info_utf16_in(s, len_dll - 2, tmpdata, sizeof(tmpdata)) != 0)
{ {
LOG(LOG_LEVEL_ERROR, "ERROR reading clientDir"); LOG(LOG_LEVEL_ERROR, "ERROR reading clientDir");
return 1; return 1;
@ -1961,7 +1966,7 @@ xrdp_sec_process_mcs_data_CS_CORE(struct xrdp_sec *self, struct stream *s)
2 + 2 + /* desktopWidth + desktopHeight */ \ 2 + 2 + /* desktopWidth + desktopHeight */ \
2 + 2 + /* colorDepth + SASSequence */ \ 2 + 2 + /* colorDepth + SASSequence */ \
4 + /* keyboardLayout */ \ 4 + /* keyboardLayout */ \
4 + 32 + /* clientBuild + clientName */ \ 4 + INFO_CLIENT_NAME_BYTES + /* clientBuild + clientName */ \
4 + 4 + 4 + /* keyboardType + keyboardSubType + keyboardFunctionKey */ \ 4 + 4 + 4 + /* keyboardType + keyboardSubType + keyboardFunctionKey */ \
64 + /* imeFileName */ \ 64 + /* imeFileName */ \
0) 0)
@ -2002,7 +2007,15 @@ xrdp_sec_process_mcs_data_CS_CORE(struct xrdp_sec *self, struct stream *s)
in_uint8s(s, 2); /* SASSequence */ in_uint8s(s, 2); /* SASSequence */
in_uint8s(s, 4); /* keyboardLayout */ in_uint8s(s, 4); /* keyboardLayout */
in_uint8s(s, 4); /* clientBuild */ in_uint8s(s, 4); /* clientBuild */
unicode_utf16_in(s, INFO_CLIENT_NAME_BYTES - 2, clientName, sizeof(clientName) - 1); /* clientName */
/* clientName
*
* This should be null-terminated. Allow for the possibility it
* isn't by ignoring the last two bytes and treating them as a
* terminator anyway */
in_utf16_le_fixed_as_utf8(s, (INFO_CLIENT_NAME_BYTES - 2) / 2,
clientName, sizeof(clientName));
in_uint8s(s, 2); /* See above */
LOG(LOG_LEVEL_INFO, "Connected client computer name: %s", clientName); LOG(LOG_LEVEL_INFO, "Connected client computer name: %s", clientName);
in_uint8s(s, 4); /* keyboardType */ in_uint8s(s, 4); /* keyboardType */
in_uint8s(s, 4); /* keyboardSubType */ in_uint8s(s, 4); /* keyboardSubType */
@ -2643,8 +2656,6 @@ xrdp_sec_in_mcs_data(struct xrdp_sec *self)
{ {
struct stream *s = (struct stream *)NULL; struct stream *s = (struct stream *)NULL;
struct xrdp_client_info *client_info = (struct xrdp_client_info *)NULL; struct xrdp_client_info *client_info = (struct xrdp_client_info *)NULL;
int index = 0;
char c = 0;
client_info = &(self->rdp_layer->client_info); client_info = &(self->rdp_layer->client_info);
s = &(self->client_mcs_data); s = &(self->client_mcs_data);
@ -2657,23 +2668,16 @@ xrdp_sec_in_mcs_data(struct xrdp_sec *self)
in_uint8s(s, 47); /* skip [ITU T.124] ConferenceCreateRequest up to the in_uint8s(s, 47); /* skip [ITU T.124] ConferenceCreateRequest up to the
userData field, and skip [MS-RDPBCGR] TS_UD_CS_CORE userData field, and skip [MS-RDPBCGR] TS_UD_CS_CORE
up to the clientName field */ up to the clientName field */
g_memset(client_info->hostname, 0, 32); if (!s_check_rem_and_log(s, INFO_CLIENT_NAME_BYTES,
c = 1; "Parsing [MS-RDPBCGR] TS_UD_CS_CORE clientName"))
index = 0;
/* TODO: why aren't we using unicode_utf16_in to parse the client name
like we do in xrdp_sec_process_mcs_data_CS_CORE? */
while (index < 16 && c != 0)
{ {
if (!s_check_rem_and_log(s, 2, "Parsing [MS-RDPBCGR] TS_UD_CS_CORE clientName")) return 1;
{
return 1;
}
in_uint8(s, c);
in_uint8s(s, 1);
client_info->hostname[index] = c;
index++;
} }
in_utf16_le_fixed_as_utf8(s, (INFO_CLIENT_NAME_BYTES - 2) / 2,
client_info->hostname,
sizeof(client_info->hostname));
in_uint8s(s, 2); /* Ignored - terminator for full-size clientName */
/* get build */ /* get build */
s->p = s->data; s->p = s->data;
if (!s_check_rem_and_log(s, 43 + 4, "Parsing [MS-RDPBCGR] TS_UD_CS_CORE clientBuild")) if (!s_check_rem_and_log(s, 43 + 4, "Parsing [MS-RDPBCGR] TS_UD_CS_CORE clientBuild"))