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 * Section 14 : Describes the NDR
*/ */
/*
* smartcard redirection support
*/
#if defined(HAVE_CONFIG_H) #if defined(HAVE_CONFIG_H)
#include <config_ac.h> #include <config_ac.h>
#endif #endif
@ -1094,6 +1090,17 @@ scard_send_IsContextValid(IRP *irp, char *context, int context_bytes)
free_stream(s); 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; SMARTCARD *sc;
struct stream *s; struct stream *s;
int bytes; int bytes;
int bytes_groups; // Length of NDR for groups + 2 terminators int bytes_groups = 0; // Length of NDR for groups + 2 terminators
int val; // Referent Id for mszGroups (assume NULL) int val = 0; // Referent Id for mszGroups (assume NULL)
int index; int groups_len = 0; // strlen(groups)
int num_chars;
tui32 ioctl; tui32 ioctl;
twchar w_groups[100];
if ((sc = smartcards[irp->scard_index]) == NULL) if ((sc = smartcards[irp->scard_index]) == NULL)
{ {
@ -1167,18 +1171,18 @@ scard_send_ListReaders(IRP *irp, char *context, int context_bytes,
return; return;
} }
num_chars = 0; if (groups != NULL && *groups != '\0')
bytes_groups = 0;
w_groups[0] = 0;
val = 0;
if (groups != 0)
{ {
if (groups[0] != 0) groups_len = g_strlen(groups);
if (wide)
{ {
num_chars = g_mbstowcs(w_groups, groups, 99); bytes_groups = (utf8_as_utf16_word_count(groups, groups_len) + 2) * 2;
bytes_groups = wide ? (num_chars + 2) * 2 : num_chars + 2;
val = 0x00020004;
} }
else
{
bytes_groups = groups_len + 2;
}
val = 0x00020004;
} }
s_push_layer(s, mcs_hdr, 4); /* bytes, set later */ 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 // mszGroups is also a Uni-dimensional conformant array of bytes
if (bytes_groups > 0) if (bytes_groups > 0)
{ {
align_s(s, 4);
out_uint32_le(s, bytes_groups);
if (wide) if (wide)
{ {
out_uint32_le(s, bytes_groups); out_utf8_as_utf16_le(s, groups, groups_len);
for (index = 0; index < num_chars; index++)
{
out_uint16_le(s, w_groups[index]);
}
out_uint16_le(s, 0); out_uint16_le(s, 0);
out_uint16_le(s, 0); out_uint16_le(s, 0);
} }
else else
{ {
out_uint32_le(s, bytes_groups); out_uint8p(s, groups, groups_len);
for (index = 0; index < num_chars; index++) out_uint8(s, 0);
{ out_uint8(s, 0);
out_uint8(s, w_groups[index]);
}
out_uint16_le(s, 0);
out_uint16_le(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; align_s(s, 4);
unsigned int len = strlen(str);
i32 = (int) (s->p - s->data); if (wide)
while ((i32 % bytes) != 0)
{ {
out_uint8s(s, 1); unsigned int num_chars = utf8_as_utf16_word_count(str, len);
i32 = (int) (s->p - s->data); // 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; tui32 ioctl;
int bytes; int bytes;
unsigned int i; unsigned int i;
int num_chars;
int index;
twchar w_reader_name[100];
if ((sc = smartcards[irp->scard_index]) == NULL) 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); out_uint8a(s, context, context_bytes);
// rgReaderState is a Uni-dimensional conformant array // rgReaderState is a Uni-dimensional conformant array
align_s(s, 4);
out_uint32_le(s, num_readers); out_uint32_le(s, num_readers);
/* insert card reader state */ /* insert card reader state */
@ -1406,43 +1430,11 @@ scard_send_GetStatusChange(IRP *irp, char *context, int context_bytes,
out_uint8s(s, 3); out_uint8s(s, 3);
} }
if (wide) /* insert card reader names */
for (i = 0; i < num_readers; i++)
{ {
/* insert card reader names */ rs = &rsa[i];
for (i = 0; i < num_readers; i++) out_conformant_and_varying_string(s, rs->reader_name, wide);
{
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);
}
} }
s_mark_end(s); s_mark_end(s);
@ -1525,14 +1517,10 @@ scard_send_Connect(IRP *irp, char *context, int context_bytes,
* ?? Conformant Array pointed to by pbContext * ?? Conformant Array pointed to by pbContext
* *
*/ */
SMARTCARD *sc; SMARTCARD *sc;
struct stream *s; struct stream *s;
tui32 ioctl; tui32 ioctl;
int bytes; int bytes;
int num_chars;
int index;
twchar w_reader_name[100];
if ((sc = smartcards[irp->scard_index]) == NULL) 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); out_uint32_le(s, rs->dwPreferredProtocols);
/* insert card reader name */ /* insert card reader name */
num_chars = g_mbstowcs(w_reader_name, rs->reader_name, 99); out_conformant_and_varying_string(s, rs->reader_name, wide);
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);
/* insert context */ /* insert context */
align_s(s, 4);
out_uint32_le(s, context_bytes); out_uint32_le(s, context_bytes);
out_uint8a(s, context, context_bytes); out_uint8a(s, context, context_bytes);
out_uint32_le(s, 0); out_uint32_le(s, 0); // ?
s_mark_end(s); s_mark_end(s);

View File

@ -18,8 +18,6 @@
*/ */
/* /*
* @file sesman/chansrv/smartcard_pcsc.c
*
* smartcard redirection support, PCSC daemon standin * smartcard redirection support, PCSC daemon standin
* this will act like pcsc daemon * this will act like pcsc daemon
* pcsc lib and daemon write struct on unix domain socket for communication * 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; 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 int
scard_function_list_readers_return(void *user_data, scard_function_list_readers_return(void *user_data,
@ -685,19 +721,16 @@ scard_function_list_readers_return(void *user_data,
* 16 Multistring data * 16 Multistring data
*/ */
struct stream *out_s; struct stream *out_s;
int chr;
int readers; int readers;
int rn_index;
int index; int index;
int bytes; int bytes;
int cchReaders; int cchReaders;
int llen; int llen;
int uds_client_id; int uds_client_id;
twchar reader_name[100];
char lreader_name[16][100];
struct pcsc_uds_client *uds_client; struct pcsc_uds_client *uds_client;
struct trans *con; struct trans *con;
struct pcsc_list_readers *pcscListReaders; struct pcsc_list_readers *pcscListReaders;
char *msz_readers = NULL;
LOG_DEVEL(LOG_LEVEL_DEBUG, "scard_function_list_readers_return:"); LOG_DEVEL(LOG_LEVEL_DEBUG, "scard_function_list_readers_return:");
LOG_DEVEL(LOG_LEVEL_DEBUG, " status 0x%8.8x", status); LOG_DEVEL(LOG_LEVEL_DEBUG, " status 0x%8.8x", status);
@ -721,9 +754,6 @@ scard_function_list_readers_return(void *user_data,
return 1; return 1;
} }
con = uds_client->con; 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; readers = 0;
llen = 0; llen = 0;
if (status == 0) if (status == 0)
@ -733,39 +763,23 @@ scard_function_list_readers_return(void *user_data,
// Move to length of multistring in bytes // Move to length of multistring in bytes
in_uint8s(in_s, 12); in_uint8s(in_s, 12);
in_uint32_le(in_s, len); in_uint32_le(in_s, llen);
llen = len;
if (cchReaders > 0) 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); LOG(LOG_LEVEL_ERROR, "scard_function_list_readers_return: "
len -= 2; "Can't allocate %u bytes of memory", u8len);
if (chr == 0) readers = 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++;
}
} }
} else
if (rn_index > 0)
{
if (reader_name[0] != 0)
{ {
g_wcstombs(lreader_name[readers], reader_name, 99); in_utf16_le_fixed_as_utf8(in_s, len / 2, msz_readers, u8len);
g_memset(reader_name, 0, sizeof(reader_name)); readers = count_multistring_elements(msz_readers, u8len);
readers++;
} }
} }
} }
@ -778,10 +792,25 @@ scard_function_list_readers_return(void *user_data,
s_push_layer(out_s, iso_hdr, 8); s_push_layer(out_s, iso_hdr, 8);
out_uint32_le(out_s, llen); out_uint32_le(out_s, llen);
out_uint32_le(out_s, readers); 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 */ out_uint32_le(out_s, status); /* SCARD_S_SUCCESS status */
s_mark_end(out_s); s_mark_end(out_s);
bytes = (int) (out_s->end - out_s->data); bytes = (int) (out_s->end - out_s->data);
@ -1466,14 +1495,12 @@ scard_function_status_return(void *user_data,
* 60 Multistring data * 60 Multistring data
*/ */
struct stream *out_s; struct stream *out_s;
int index;
int bytes; int bytes;
int dwReaderLen; int dwReaderLen;
int dwState; int dwState;
int dwProtocol; int dwProtocol;
int dwAtrLen; int dwAtrLen;
char attr[32]; char attr[32];
twchar reader_name[100];
char lreader_name[100]; char lreader_name[100];
int uds_client_id; int uds_client_id;
struct pcsc_uds_client *uds_client; struct pcsc_uds_client *uds_client;
@ -1519,33 +1546,20 @@ scard_function_status_return(void *user_data,
in_uint32_le(in_s, dwProtocol); in_uint32_le(in_s, dwProtocol);
in_uint8a(in_s, attr, 32); in_uint8a(in_s, attr, 32);
in_uint32_le(in_s, dwAtrLen); in_uint32_le(in_s, dwAtrLen);
if (dwReaderLen > 0)
// Length of multistring and multistring data
if (dwReaderLen <= 0)
{ {
in_uint32_le(in_s, dwReaderLen); lreader_name[0] = '\0';
dwReaderLen /= 2;
} }
else 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 " LOG_DEVEL(LOG_LEVEL_DEBUG, "scard_function_status_return: dwAtrLen %d dwReaderLen %d "
"dwProtocol %d dwState %d name %s", "dwProtocol %d dwState %d name %s",