Update smartcard code to use new UTF-8 calls

This commit is contained in:
matt335672 2023-10-19 12:16:58 +01:00
parent 1b286a0469
commit d722ffe357
2 changed files with 151 additions and 170 deletions

View File

@ -34,10 +34,6 @@
* Section 14 : Describes the NDR
*/
/*
* smartcard redirection support
*/
#if defined(HAVE_CONFIG_H)
#include <config_ac.h>
#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++)
{
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);
}
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);

View File

@ -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;
LOG(LOG_LEVEL_ERROR, "scard_function_list_readers_return: "
"Can't allocate %u bytes of memory", u8len);
readers = 0;
}
else
{
reader_name[rn_index] = chr;
rn_index++;
}
}
}
if (rn_index > 0)
{
if (reader_name[0] != 0)
{
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);
{
const char *p = msz_readers;
for (index = 0; index < readers; index++)
{
out_uint8a(out_s, lreader_name[index], 100);
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;
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));
}
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]);
}
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",