From 8d39c172e33750fef6345dfe9d552f6b0a88031e Mon Sep 17 00:00:00 2001 From: matt335672 <30179339+matt335672@users.noreply.github.com> Date: Thu, 19 Oct 2023 12:16:58 +0100 Subject: [PATCH] Update smartcard code to use new UTF-8 calls --- sesman/chansrv/smartcard.c | 181 +++++++++++++------------------- sesman/chansrv/smartcard_pcsc.c | 140 +++++++++++++----------- 2 files changed, 151 insertions(+), 170 deletions(-) diff --git a/sesman/chansrv/smartcard.c b/sesman/chansrv/smartcard.c index 6e28bb34ff..03c453b3df 100644 --- a/sesman/chansrv/smartcard.c +++ b/sesman/chansrv/smartcard.c @@ -34,10 +34,6 @@ * Section 14 : Describes the NDR */ -/* - * smartcard redirection support - */ - #if defined(HAVE_CONFIG_H) #include #endif @@ -1094,6 +1090,17 @@ scard_send_IsContextValid(IRP *irp, char *context, int context_bytes) free_stream(s); } +/*****************************************************************************/ +static void +align_s(struct stream *s, unsigned int boundary) +{ + unsigned int over = (unsigned int)(s->p - s->data) % boundary; + if (over != 0) + { + out_uint8s(s, boundary - over); + } +} + /** * *****************************************************************************/ @@ -1144,13 +1151,10 @@ scard_send_ListReaders(IRP *irp, char *context, int context_bytes, SMARTCARD *sc; struct stream *s; int bytes; - int bytes_groups; // Length of NDR for groups + 2 terminators - int val; // Referent Id for mszGroups (assume NULL) - int index; - int num_chars; + int bytes_groups = 0; // Length of NDR for groups + 2 terminators + int val = 0; // Referent Id for mszGroups (assume NULL) + int groups_len = 0; // strlen(groups) tui32 ioctl; - twchar w_groups[100]; - if ((sc = smartcards[irp->scard_index]) == NULL) { @@ -1167,18 +1171,18 @@ scard_send_ListReaders(IRP *irp, char *context, int context_bytes, return; } - num_chars = 0; - bytes_groups = 0; - w_groups[0] = 0; - val = 0; - if (groups != 0) + if (groups != NULL && *groups != '\0') { - if (groups[0] != 0) + groups_len = g_strlen(groups); + if (wide) { - num_chars = g_mbstowcs(w_groups, groups, 99); - bytes_groups = wide ? (num_chars + 2) * 2 : num_chars + 2; - val = 0x00020004; + bytes_groups = (utf8_as_utf16_word_count(groups, groups_len) + 2) * 2; } + else + { + bytes_groups = groups_len + 2; + } + val = 0x00020004; } s_push_layer(s, mcs_hdr, 4); /* bytes, set later */ @@ -1204,25 +1208,19 @@ scard_send_ListReaders(IRP *irp, char *context, int context_bytes, // mszGroups is also a Uni-dimensional conformant array of bytes if (bytes_groups > 0) { + align_s(s, 4); + out_uint32_le(s, bytes_groups); if (wide) { - out_uint32_le(s, bytes_groups); - for (index = 0; index < num_chars; index++) - { - out_uint16_le(s, w_groups[index]); - } + out_utf8_as_utf16_le(s, groups, groups_len); out_uint16_le(s, 0); out_uint16_le(s, 0); } else { - out_uint32_le(s, bytes_groups); - for (index = 0; index < num_chars; index++) - { - out_uint8(s, w_groups[index]); - } - out_uint16_le(s, 0); - out_uint16_le(s, 0); + out_uint8p(s, groups, groups_len); + out_uint8(s, 0); + out_uint8(s, 0); } } @@ -1249,18 +1247,46 @@ scard_send_ListReaders(IRP *irp, char *context, int context_bytes, } /*****************************************************************************/ -static int -align_s(struct stream *s, int bytes) +/** + * Outputs the pointed-to-data for one of these IDL pointer types:- + * [string] const wchar_t* str; (wide != 0) + * [string] const char* str; (wide == 0) + * + * It is assumed that the referent identifier for the string has already + * been sent + * + * @param s Output stream + * @param str UTF-8 string to output + * @param wide Whether to output as a wide string or ASCII + * + * Note that wchar_t on Windows is 16-bit + * TODO: These strings have two terminators. Is this necessary? + */ +static void +out_conformant_and_varying_string(struct stream *s, const char *str, int wide) { - int i32; - - i32 = (int) (s->p - s->data); - while ((i32 % bytes) != 0) + align_s(s, 4); + unsigned int len = strlen(str); + if (wide) { - out_uint8s(s, 1); - i32 = (int) (s->p - s->data); + unsigned int num_chars = utf8_as_utf16_word_count(str, len); + // Max number, offset and actual count + out_uint32_le(s, num_chars + 2); + out_uint32_le(s, 0); + out_uint32_le(s, num_chars + 2); + out_utf8_as_utf16_le(s, str, len); + out_uint16_le(s, 0); // Terminate string + out_uint16_le(s, 0); // ? + } + else + { + out_uint32_le(s, len + 2); + out_uint32_le(s, 0); + out_uint32_le(s, len + 2); + out_uint8p(s, str, len); + out_uint8(s, 0); + out_uint8(s, 0); } - return 0; } /** @@ -1347,9 +1373,6 @@ scard_send_GetStatusChange(IRP *irp, char *context, int context_bytes, tui32 ioctl; int bytes; unsigned int i; - int num_chars; - int index; - twchar w_reader_name[100]; if ((sc = smartcards[irp->scard_index]) == NULL) { @@ -1386,6 +1409,7 @@ scard_send_GetStatusChange(IRP *irp, char *context, int context_bytes, out_uint8a(s, context, context_bytes); // rgReaderState is a Uni-dimensional conformant array + align_s(s, 4); out_uint32_le(s, num_readers); /* insert card reader state */ @@ -1406,43 +1430,11 @@ scard_send_GetStatusChange(IRP *irp, char *context, int context_bytes, out_uint8s(s, 3); } - if (wide) - { - /* insert card reader names */ - for (i = 0; i < num_readers; i++) - { - rs = &rsa[i]; - num_chars = g_mbstowcs(w_reader_name, rs->reader_name, 99); - out_uint32_le(s, num_chars + 2); - out_uint32_le(s, 0); - out_uint32_le(s, num_chars + 2); - for (index = 0; index < num_chars; index++) - { - out_uint16_le(s, w_reader_name[index]); - } - out_uint16_le(s, 0); - out_uint16_le(s, 0); - align_s(s, 4); - } - } - else + /* insert card reader names */ + for (i = 0; i < num_readers; i++) { - /* insert card reader names */ - for (i = 0; i < num_readers; i++) - { - rs = &rsa[i]; - num_chars = g_mbstowcs(w_reader_name, rs->reader_name, 99); - out_uint32_le(s, num_chars + 2); - out_uint32_le(s, 0); - out_uint32_le(s, num_chars + 2); - for (index = 0; index < num_chars; index++) - { - out_uint8(s, w_reader_name[index]); - } - out_uint8(s, 0); - out_uint8(s, 0); - align_s(s, 4); - } + rs = &rsa[i]; + out_conformant_and_varying_string(s, rs->reader_name, wide); } s_mark_end(s); @@ -1525,14 +1517,10 @@ scard_send_Connect(IRP *irp, char *context, int context_bytes, * ?? Conformant Array pointed to by pbContext * */ - SMARTCARD *sc; struct stream *s; tui32 ioctl; int bytes; - int num_chars; - int index; - twchar w_reader_name[100]; if ((sc = smartcards[irp->scard_index]) == NULL) { @@ -1564,34 +1552,13 @@ scard_send_Connect(IRP *irp, char *context, int context_bytes, out_uint32_le(s, rs->dwPreferredProtocols); /* insert card reader name */ - num_chars = g_mbstowcs(w_reader_name, rs->reader_name, 99); - out_uint32_le(s, num_chars + 2); - out_uint32_le(s, 0x00000000); - out_uint32_le(s, num_chars + 2); - if (wide) - { - for (index = 0; index < num_chars; index++) - { - out_uint16_le(s, w_reader_name[index]); - } - out_uint16_le(s, 0); - out_uint16_le(s, 0); - } - else - { - for (index = 0; index < num_chars; index++) - { - out_uint8(s, w_reader_name[index]); - } - out_uint8(s, 0); - out_uint8(s, 0); - } - align_s(s, 4); + out_conformant_and_varying_string(s, rs->reader_name, wide); /* insert context */ + align_s(s, 4); out_uint32_le(s, context_bytes); out_uint8a(s, context, context_bytes); - out_uint32_le(s, 0); + out_uint32_le(s, 0); // ? s_mark_end(s); diff --git a/sesman/chansrv/smartcard_pcsc.c b/sesman/chansrv/smartcard_pcsc.c index e6f950db33..cfce6dbb98 100644 --- a/sesman/chansrv/smartcard_pcsc.c +++ b/sesman/chansrv/smartcard_pcsc.c @@ -18,8 +18,6 @@ */ /* - * @file sesman/chansrv/smartcard_pcsc.c - * * smartcard redirection support, PCSC daemon standin * this will act like pcsc daemon * pcsc lib and daemon write struct on unix domain socket for communication @@ -653,6 +651,44 @@ scard_process_list_readers(struct trans *con, struct stream *in_s) return 0; } +/*****************************************************************************/ +/** + * Counts the number of non-NULL strings in a multistring + * + * [MS-RDPESC] A multistring is "A series of null-terminated character + * strings terminated by a final null character stored in a contiguous + * block of memory." + * + * The string is guaranteed to have at least the returned number of NULL + * characters in it + */ +unsigned int +count_multistring_elements(const char *str, unsigned int len) +{ + unsigned int rv = 0; + + if (str != NULL) + { + while (len > 0) + { + // Look for a terminator + const char *p = (const char *)memchr(str, '\0', len); + if (!p || p == str) + { + // No terminator, or an empty string encountered */ + break; + } + + ++rv; + ++p; // Skip terminator + len -= (p - str); + str = p; + } + } + + return rv; +} + /*****************************************************************************/ int scard_function_list_readers_return(void *user_data, @@ -685,19 +721,16 @@ scard_function_list_readers_return(void *user_data, * 16 Multistring data */ struct stream *out_s; - int chr; int readers; - int rn_index; int index; int bytes; int cchReaders; int llen; int uds_client_id; - twchar reader_name[100]; - char lreader_name[16][100]; struct pcsc_uds_client *uds_client; struct trans *con; struct pcsc_list_readers *pcscListReaders; + char *msz_readers = NULL; LOG_DEVEL(LOG_LEVEL_DEBUG, "scard_function_list_readers_return:"); LOG_DEVEL(LOG_LEVEL_DEBUG, " status 0x%8.8x", status); @@ -721,9 +754,6 @@ scard_function_list_readers_return(void *user_data, return 1; } con = uds_client->con; - g_memset(reader_name, 0, sizeof(reader_name)); - g_memset(lreader_name, 0, sizeof(lreader_name)); - rn_index = 0; readers = 0; llen = 0; if (status == 0) @@ -733,39 +763,23 @@ scard_function_list_readers_return(void *user_data, // Move to length of multistring in bytes in_uint8s(in_s, 12); - in_uint32_le(in_s, len); - llen = len; + in_uint32_le(in_s, llen); if (cchReaders > 0) { - while (len > 0) + // Convert the wide multistring to a UTF-8 multistring + unsigned int u8len; + u8len = in_utf16_le_fixed_as_utf8_length(in_s, len / 2); + msz_readers = (char *)malloc(u8len); + if (msz_readers == NULL) { - in_uint16_le(in_s, chr); - len -= 2; - if (chr == 0) - { - if (reader_name[0] != 0) - { - g_wcstombs(lreader_name[readers], reader_name, 99); - g_memset(reader_name, 0, sizeof(reader_name)); - readers++; - } - reader_name[0] = 0; - rn_index = 0; - } - else - { - reader_name[rn_index] = chr; - rn_index++; - } + LOG(LOG_LEVEL_ERROR, "scard_function_list_readers_return: " + "Can't allocate %u bytes of memory", u8len); + readers = 0; } - } - if (rn_index > 0) - { - if (reader_name[0] != 0) + else { - g_wcstombs(lreader_name[readers], reader_name, 99); - g_memset(reader_name, 0, sizeof(reader_name)); - readers++; + in_utf16_le_fixed_as_utf8(in_s, len / 2, msz_readers, u8len); + readers = count_multistring_elements(msz_readers, u8len); } } } @@ -778,10 +792,25 @@ scard_function_list_readers_return(void *user_data, s_push_layer(out_s, iso_hdr, 8); out_uint32_le(out_s, llen); out_uint32_le(out_s, readers); - for (index = 0; index < readers; index++) { - out_uint8a(out_s, lreader_name[index], 100); + const char *p = msz_readers; + for (index = 0; index < readers; index++) + { + unsigned int slen = strlen(p); + if (slen < 100) + { + out_uint8a(out_s, p, slen); + out_uint8s(out_s, 100 - slen); + } + else + { + out_uint8a(out_s, p, 99); + out_uint8s(out_s, 1); + } + p += (slen + 1); + } } + free(msz_readers); out_uint32_le(out_s, status); /* SCARD_S_SUCCESS status */ s_mark_end(out_s); bytes = (int) (out_s->end - out_s->data); @@ -1466,14 +1495,12 @@ scard_function_status_return(void *user_data, * 60 Multistring data */ struct stream *out_s; - int index; int bytes; int dwReaderLen; int dwState; int dwProtocol; int dwAtrLen; char attr[32]; - twchar reader_name[100]; char lreader_name[100]; int uds_client_id; struct pcsc_uds_client *uds_client; @@ -1519,33 +1546,20 @@ scard_function_status_return(void *user_data, in_uint32_le(in_s, dwProtocol); in_uint8a(in_s, attr, 32); in_uint32_le(in_s, dwAtrLen); - if (dwReaderLen > 0) + + // Length of multistring and multistring data + if (dwReaderLen <= 0) { - in_uint32_le(in_s, dwReaderLen); - dwReaderLen /= 2; + lreader_name[0] = '\0'; } else { - dwReaderLen = 1; - } - if (dwReaderLen < 1) - { - LOG(LOG_LEVEL_ERROR, "scard_function_status_return: dwReaderLen < 1"); - dwReaderLen = 1; - } - if (dwReaderLen > 99) - { - LOG_DEVEL(LOG_LEVEL_WARNING, "scard_function_status_return: dwReaderLen too big " - "0x%8.8x", dwReaderLen); - dwReaderLen = 99; - } - g_memset(reader_name, 0, sizeof(reader_name)); - g_memset(lreader_name, 0, sizeof(lreader_name)); - for (index = 0; index < dwReaderLen - 1; index++) - { - in_uint16_le(in_s, reader_name[index]); + in_uint8s(in_s, 4); // Skip length of msz in bytes + + // TODO: why are we just returning the first name of the card? + in_utf16_le_terminated_as_utf8(in_s, lreader_name, + sizeof(lreader_name)); } - g_wcstombs(lreader_name, reader_name, 99); } LOG_DEVEL(LOG_LEVEL_DEBUG, "scard_function_status_return: dwAtrLen %d dwReaderLen %d " "dwProtocol %d dwState %d name %s",