/** * xrdp: A Remote Desktop Protocol server. * * Copyright (C) Jay Sorg 2013 jay.sorg@gmail.com * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * */ /* * smartcard redirection support, PCSC daemon standin * this will act like pcsc daemon * pcsc lib and daemon write struct on unix domain socket for communication * * Currently this file implements some of the PDUs detailed in [MS-RDPESC]. * * The PDUs use DCE IDL structs. These are required to be re-interpreted * in DCE NDR (Netword Data Representation) * * For more information on this subject see DCE publication C706 * "DCE 1.1: Remote Procedure Call" 1997. In particular:- * Section 4.2 : Describes the IDL * Section 14 : Describes the NDR */ #if defined(HAVE_CONFIG_H) #include #endif #define JAY_TODO_CONTEXT 0 #define JAY_TODO_WIDE 1 #define PCSC_STANDIN 1 #include "os_calls.h" #include "string_calls.h" #include "smartcard.h" #include "log.h" #include "irp.h" #include "devredir.h" #include "trans.h" #include "chansrv.h" #include "list.h" #include "smartcard_pcsc.h" #if PCSC_STANDIN extern int g_display_num; /* in chansrv.c */ static int g_autoinc = 0; /* general purpose autoinc */ struct pcsc_card /* item for list of open cards in one context */ { tui32 app_card; /* application card, always 4 bytes */ int card_bytes; /* client card bytes */ char card[16]; /* client card */ }; struct pcsc_context { tui32 app_context; /* application context, always 4 byte */ int context_bytes; /* client context bytes */ char context[16]; /* client context */ struct list *cards; /* these need to be released on close */ }; /*****************************************************************************/ struct pcsc_uds_client { int uds_client_id; /* unique id represents each app */ struct trans *con; /* the connection to the app */ struct list *contexts; /* list of struct pcsc_context */ struct pcsc_context *connect_context; }; static struct list *g_uds_clients = 0; /* struct pcsc_uds_client */ static struct trans *g_lis = 0; static char g_pcsclite_ipc_dir[256] = ""; static char g_pcsclite_ipc_file[256] = ""; /*****************************************************************************/ /* got a new unix domain socket connection */ static struct pcsc_uds_client * create_uds_client(struct trans *con) { struct pcsc_uds_client *uds_client; LOG_DEVEL(LOG_LEVEL_DEBUG, "create_uds_client:"); if (con == 0) { return 0; } uds_client = g_new0(struct pcsc_uds_client, 1); if (uds_client == 0) { return 0; } g_autoinc++; uds_client->uds_client_id = g_autoinc; uds_client->con = con; con->callback_data = uds_client; return uds_client; } /*****************************************************************************/ static struct pcsc_uds_client * get_uds_client_by_id(int uds_client_id) { struct pcsc_uds_client *uds_client; int index; LOG_DEVEL(LOG_LEVEL_DEBUG, "get_uds_client_by_id:"); if (uds_client_id == 0) { LOG(LOG_LEVEL_ERROR, "get_uds_client_by_id: uds_client_id is zero"); return 0; } if (g_uds_clients == 0) { LOG(LOG_LEVEL_ERROR, "get_uds_client_by_id: g_uds_clients is nil"); return 0; } LOG_DEVEL(LOG_LEVEL_DEBUG, " count %d", g_uds_clients->count); for (index = 0; index < g_uds_clients->count; index++) { uds_client = (struct pcsc_uds_client *) list_get_item(g_uds_clients, index); if (uds_client->uds_client_id == uds_client_id) { return uds_client; } } LOG(LOG_LEVEL_ERROR, "get_uds_client_by_id: can't find uds_client_id %d", uds_client_id); return 0; } /*****************************************************************************/ static struct pcsc_context * get_pcsc_context_by_app_context(struct pcsc_uds_client *uds_client, tui32 app_context) { struct pcsc_context *rv; int index; if (uds_client == 0) { return 0; } if (uds_client->contexts == 0) { return 0; } for (index = 0; index < uds_client->contexts->count; index++) { rv = (struct pcsc_context *) list_get_item(uds_client->contexts, index); if (rv->app_context == app_context) { return rv; } } return 0; } /*****************************************************************************/ static struct pcsc_card * get_pcsc_card_by_app_card(struct pcsc_uds_client *uds_client, tui32 app_card, struct pcsc_context **acontext) { struct pcsc_card *lcard; struct pcsc_context *lcontext; int index; int index1; LOG_DEVEL(LOG_LEVEL_DEBUG, "get_pcsc_card_by_app_card: app_card %d", app_card); if (uds_client == 0) { LOG(LOG_LEVEL_ERROR, "get_pcsc_card_by_app_card: uds_client is null"); return 0; } if (uds_client->contexts == 0) { LOG(LOG_LEVEL_ERROR, "get_pcsc_card_by_app_card: uds_client->contexts is null"); return 0; } for (index = 0; index < uds_client->contexts->count; index++) { lcontext = (struct pcsc_context *) list_get_item(uds_client->contexts, index); if (lcontext != 0) { if (lcontext->cards != 0) { for (index1 = 0; index1 < lcontext->cards->count; index1++) { lcard = (struct pcsc_card *) list_get_item(lcontext->cards, index1); if (lcard != 0) { if (lcard->app_card == app_card) { if (acontext != 0) { *acontext = lcontext; } return lcard; } } } } } } LOG(LOG_LEVEL_ERROR, "get_pcsc_card_by_app_card: app_card %d " "not found in uds_client->contexts->cards", app_card); return 0; } /*****************************************************************************/ static int free_uds_client(struct pcsc_uds_client *uds_client) { int i; int j; struct pcsc_context *context; struct pcsc_card *card; LOG_DEVEL(LOG_LEVEL_DEBUG, "free_uds_client:"); if (uds_client == 0) { return 0; } if (uds_client->contexts != 0) { for (i = 0; i < uds_client->contexts->count; i++) { context = (struct pcsc_context *) list_get_item(uds_client->contexts, i); if (context != 0) { if (context->cards != 0) { for (j = 0; j < context->cards->count; j++) { card = (struct pcsc_card *) list_get_item(context->cards, j); if (card != 0) { /* TODO: send free card to client */ g_free(card); } } list_delete(context->cards); } LOG_DEVEL(LOG_LEVEL_DEBUG, " left over context %p", context->context); scard_send_cancel(0, context->context, context->context_bytes); scard_send_release_context(0, context->context, context->context_bytes); g_free(context); } } list_delete(uds_client->contexts); } trans_delete(uds_client->con); g_free(uds_client); return 0; } /*****************************************************************************/ static struct pcsc_context * uds_client_add_context(struct pcsc_uds_client *uds_client, char *context, int context_bytes) { struct pcsc_context *pcscContext; LOG_DEVEL(LOG_LEVEL_DEBUG, "uds_client_add_context:"); pcscContext = (struct pcsc_context *) g_malloc(sizeof(struct pcsc_context), 1); if (pcscContext == 0) { LOG(LOG_LEVEL_ERROR, "uds_client_add_context: failed to allocate memory for pcsc_context"); return 0; } g_autoinc++; pcscContext->app_context = g_autoinc; pcscContext->context_bytes = context_bytes; g_memcpy(pcscContext->context, context, context_bytes); if (uds_client->contexts == 0) { uds_client->contexts = list_create(); if (uds_client->contexts == 0) { LOG(LOG_LEVEL_ERROR, "uds_client_add_context: failed to allocate memory for " "uds_client->contexts"); return 0; } } list_add_item(uds_client->contexts, (tintptr) pcscContext); return pcscContext; } /*****************************************************************************/ static int uds_client_remove_context(struct pcsc_uds_client *uds_client, struct pcsc_context *acontext) { int index; if (uds_client->contexts == 0) { LOG(LOG_LEVEL_ERROR, "uds_client_remove_context: uds_client->contexts is null"); return 1; } index = list_index_of(uds_client->contexts, (tintptr) acontext); if (index < 0) { LOG(LOG_LEVEL_ERROR, "uds_client_remove_context: pcsc_context not found in uds_client->contexts"); return 1; } list_remove_item(uds_client->contexts, index); g_free(acontext); // TODO free cards return 0; } /*****************************************************************************/ static struct pcsc_card * context_add_card(struct pcsc_uds_client *uds_client, struct pcsc_context *acontext, char *card, int card_bytes) { struct pcsc_card *pcscCard; LOG_DEVEL(LOG_LEVEL_DEBUG, "context_add_card: card_bytes %d", card_bytes); pcscCard = (struct pcsc_card *) g_malloc(sizeof(struct pcsc_card), 1); if (pcscCard == 0) { LOG(LOG_LEVEL_ERROR, "context_add_card: failed to allocate memory for pcsc_card"); return 0; } g_autoinc++; pcscCard->app_card = g_autoinc; pcscCard->card_bytes = card_bytes; g_memcpy(pcscCard->card, card, card_bytes); if (acontext->cards == 0) { acontext->cards = list_create(); if (acontext->cards == 0) { LOG(LOG_LEVEL_ERROR, "context_add_card: failed to allocate " "memory for uds_client->contexts->cards"); return 0; } } list_add_item(acontext->cards, (tintptr) pcscCard); LOG_DEVEL(LOG_LEVEL_DEBUG, " new app_card %d", pcscCard->app_card); return pcscCard; } /*****************************************************************************/ int scard_pcsc_get_wait_objs(tbus *objs, int *count, int *timeout) { struct pcsc_uds_client *uds_client; int index; LOG_DEVEL(LOG_LEVEL_DEBUG, "scard_pcsc_get_wait_objs:"); if (g_lis != 0) { trans_get_wait_objs(g_lis, objs, count); } if (g_uds_clients != 0) { for (index = 0; index < g_uds_clients->count; index++) { uds_client = (struct pcsc_uds_client *) list_get_item(g_uds_clients, index); if (uds_client != 0) { trans_get_wait_objs(uds_client->con, objs, count); } } } return 0; } /*****************************************************************************/ int scard_pcsc_check_wait_objs(void) { struct pcsc_uds_client *uds_client; int index; LOG_DEVEL(LOG_LEVEL_DEBUG, "scard_pcsc_check_wait_objs:"); if (g_lis != 0) { if (trans_check_wait_objs(g_lis) != 0) { LOG(LOG_LEVEL_ERROR, "scard_pcsc_check_wait_objs: g_lis trans_check_wait_objs error"); } } if (g_uds_clients != 0) { index = 0; while (index < g_uds_clients->count) { uds_client = (struct pcsc_uds_client *) list_get_item(g_uds_clients, index); if (uds_client != 0) { if (trans_check_wait_objs(uds_client->con) != 0) { free_uds_client(uds_client); list_remove_item(g_uds_clients, index); continue; } } index++; } } return 0; } /*****************************************************************************/ /* returns error */ static int scard_process_establish_context(struct trans *con, struct stream *in_s) { int dwScope; struct pcsc_uds_client *uds_client; void *user_data; LOG_DEVEL(LOG_LEVEL_DEBUG, "scard_process_establish_context:"); uds_client = (struct pcsc_uds_client *) (con->callback_data); in_uint32_le(in_s, dwScope); LOG_DEVEL(LOG_LEVEL_DEBUG, "scard_process_establish_context: dwScope 0x%8.8x", dwScope); user_data = (void *) (tintptr) (uds_client->uds_client_id); scard_send_establish_context(user_data, dwScope); return 0; } /*****************************************************************************/ /* returns error */ int scard_function_establish_context_return(void *user_data, struct stream *in_s, int len, int status) { int bytes; int uds_client_id; int context_bytes; int app_context; char context[16]; struct stream *out_s; struct pcsc_uds_client *uds_client; struct trans *con; struct pcsc_context *lcontext; LOG_DEVEL(LOG_LEVEL_DEBUG, "scard_function_establish_context_return:"); LOG_DEVEL(LOG_LEVEL_DEBUG, " status 0x%8.8x", status); uds_client_id = (int) (tintptr) user_data; uds_client = (struct pcsc_uds_client *) get_uds_client_by_id(uds_client_id); if (uds_client == 0) { LOG(LOG_LEVEL_ERROR, "scard_function_establish_context_return: " "get_uds_client_by_id failed to find uds_client_id %d", uds_client_id); return 1; } con = uds_client->con; lcontext = 0; app_context = 0; g_memset(context, 0, 16); if (status == 0) { in_uint8s(in_s, 28); in_uint32_le(in_s, context_bytes); if (context_bytes > 16) { LOG(LOG_LEVEL_ERROR, "scard_function_establish_context_return: opps " "context_bytes %d", context_bytes); LOG_DEVEL_HEXDUMP(LOG_LEVEL_TRACE, "", in_s->p, context_bytes); return 1; } in_uint8a(in_s, context, context_bytes); lcontext = uds_client_add_context(uds_client, context, context_bytes); app_context = lcontext->app_context; LOG_DEVEL(LOG_LEVEL_DEBUG, "scard_function_establish_context_return: " "app_context %d", app_context); } out_s = trans_get_out_s(con, 8192); if (out_s == NULL) { return 1; } s_push_layer(out_s, iso_hdr, 8); out_uint32_le(out_s, app_context); out_uint32_le(out_s, status); /* SCARD_S_SUCCESS status */ s_mark_end(out_s); bytes = (int) (out_s->end - out_s->data); s_pop_layer(out_s, iso_hdr); out_uint32_le(out_s, bytes - 8); out_uint32_le(out_s, 0x01); /* SCARD_ESTABLISH_CONTEXT 0x01 */ return trans_force_write(con); } /*****************************************************************************/ /* returns error */ static int scard_process_release_context(struct trans *con, struct stream *in_s) { int hContext; struct pcsc_uds_client *uds_client; struct pcsc_context *lcontext; void *user_data; LOG_DEVEL(LOG_LEVEL_DEBUG, "scard_process_release_context:"); uds_client = (struct pcsc_uds_client *) (con->callback_data); in_uint32_le(in_s, hContext); LOG_DEVEL(LOG_LEVEL_DEBUG, "scard_process_release_context: hContext 0x%8.8x", hContext); user_data = (void *) (tintptr) (uds_client->uds_client_id); lcontext = get_pcsc_context_by_app_context(uds_client, hContext); if (lcontext == 0) { LOG(LOG_LEVEL_ERROR, "scard_process_release_context: " "get_pcsc_context_by_app_context failed"); return 1; } scard_send_release_context(user_data, lcontext->context, lcontext->context_bytes); uds_client_remove_context(uds_client, lcontext); return 0; } /*****************************************************************************/ /* returns error */ int scard_function_release_context_return(void *user_data, struct stream *in_s, int len, int status) { int bytes; int uds_client_id; struct stream *out_s; struct pcsc_uds_client *uds_client; struct trans *con; LOG_DEVEL(LOG_LEVEL_DEBUG, "scard_function_release_context_return:"); LOG_DEVEL(LOG_LEVEL_DEBUG, " status 0x%8.8x", status); uds_client_id = (int) (tintptr) user_data; uds_client = (struct pcsc_uds_client *) get_uds_client_by_id(uds_client_id); if (uds_client == 0) { LOG(LOG_LEVEL_ERROR, "scard_function_release_context_return: " "get_uds_client_by_id failed to find uds_client_id %d", uds_client_id); return 1; } con = uds_client->con; out_s = trans_get_out_s(con, 8192); if (out_s == NULL) { return 1; } s_push_layer(out_s, iso_hdr, 8); out_uint32_le(out_s, status); /* SCARD_S_SUCCESS status */ s_mark_end(out_s); bytes = (int) (out_s->end - out_s->data); s_pop_layer(out_s, iso_hdr); out_uint32_le(out_s, bytes - 8); out_uint32_le(out_s, 0x02); /* SCARD_RELEASE_CONTEXT 0x02 */ return trans_force_write(con); } /*****************************************************************************/ struct pcsc_list_readers { int uds_client_id; int cchReaders; }; /*****************************************************************************/ /* returns error */ static int scard_process_list_readers(struct trans *con, struct stream *in_s) { int hContext; unsigned int bytes_groups; int cchReaders; /* * At the time of writing, the groups strings which can be sent * over this interface are all small:- * * "SCard$AllReaders", "SCard$DefaultReaders", "SCard$LocalReaders" and * "SCard$SystemReaders" * * We'll allow a bit extra in case the interface changes */ char groups[256]; struct pcsc_uds_client *uds_client; struct pcsc_context *lcontext; struct pcsc_list_readers *pcscListReaders; LOG_DEVEL(LOG_LEVEL_DEBUG, "scard_process_list_readers:"); uds_client = (struct pcsc_uds_client *) (con->callback_data); in_uint32_le(in_s, hContext); in_uint32_le(in_s, bytes_groups); if (bytes_groups > (sizeof(groups) - 1)) { LOG(LOG_LEVEL_ERROR, "scard_process_list_readers: Unreasonable string length %u", bytes_groups); return 1; } in_uint8a(in_s, groups, bytes_groups); groups[bytes_groups] = '\0'; in_uint32_le(in_s, cchReaders); LOG_DEVEL(LOG_LEVEL_DEBUG, "scard_process_list_readers: hContext 0x%8.8x cchReaders %d", hContext, cchReaders); lcontext = get_pcsc_context_by_app_context(uds_client, hContext); if (lcontext == 0) { LOG(LOG_LEVEL_ERROR, "scard_process_list_readers: " "get_pcsc_context_by_app_context failed"); return 1; } pcscListReaders = g_new0(struct pcsc_list_readers, 1); pcscListReaders->uds_client_id = uds_client->uds_client_id; pcscListReaders->cchReaders = cchReaders; scard_send_list_readers(pcscListReaders, lcontext->context, lcontext->context_bytes, groups, cchReaders, 1); 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 */ static 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, struct stream *in_s, int len, int status) { /* see [MS-RDPESC] 2.2.3.4 * * IDL:- * * typedef struct _longAndMultiString_Return { * long ReturnCode; * [range(0,65536)] unsigned long cBytes; * [unique] [size_is(cBytes)] byte *msz; * } ListReaderGroups_Return, ListReaders_Return; * * Type summary:- * * ReturnCode 32-bit word * CBytes Unsigned 32-bit word * msz Embedded full pointer to conformant array of bytes * * NDR:- * * Offset Decription * 0 ReturnCode * 4 cBytes * 8 msz pointer Referent Identifier * 12 length of multistring in bytes * 16 Multistring data */ struct stream *out_s; int readers; int index; int bytes; int cchReaders; int llen; int uds_client_id; 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); pcscListReaders = (struct pcsc_list_readers *) user_data; if (pcscListReaders == 0) { LOG(LOG_LEVEL_ERROR, "scard_function_list_readers_return: " "pcscListReaders is nil"); return 1; } uds_client_id = pcscListReaders->uds_client_id; cchReaders = pcscListReaders->cchReaders; g_free(pcscListReaders); uds_client = (struct pcsc_uds_client *) get_uds_client_by_id(uds_client_id); if (uds_client == 0) { LOG(LOG_LEVEL_ERROR, "scard_function_list_readers_return: " "get_uds_client_by_id failed, could not find id %d", uds_client_id); return 1; } con = uds_client->con; readers = 0; llen = 0; if (status == 0) { // Skip [C706] PDU Header in_uint8s(in_s, 16); // Move to length of multistring in bytes in_uint8s(in_s, 12); in_uint32_le(in_s, llen); if (cchReaders > 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) { LOG(LOG_LEVEL_ERROR, "scard_function_list_readers_return: " "Can't allocate %u bytes of memory", u8len); readers = 0; } else { in_utf16_le_fixed_as_utf8(in_s, len / 2, msz_readers, u8len); readers = count_multistring_elements(msz_readers, u8len); } } } out_s = trans_get_out_s(con, 8192); if (out_s == NULL) { return 1; } 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++) { 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); s_pop_layer(out_s, iso_hdr); out_uint32_le(out_s, bytes - 8); out_uint32_le(out_s, 0x03); /* SCARD_LIST_READERS 0x03 */ return trans_force_write(con); } /*****************************************************************************/ /* returns error */ static int scard_process_connect(struct trans *con, struct stream *in_s) { int hContext; READER_STATE rs; struct pcsc_uds_client *uds_client; void *user_data; struct pcsc_context *lcontext; LOG_DEVEL(LOG_LEVEL_DEBUG, "scard_process_connect:"); uds_client = (struct pcsc_uds_client *) (con->callback_data); g_memset(&rs, 0, sizeof(rs)); in_uint32_le(in_s, hContext); in_uint8a(in_s, rs.reader_name, 100); in_uint32_le(in_s, rs.dwShareMode); in_uint32_le(in_s, rs.dwPreferredProtocols); LOG_DEVEL(LOG_LEVEL_DEBUG, "scard_process_connect: rs.reader_name %s dwShareMode 0x%8.8x " "dwPreferredProtocols 0x%8.8x", rs.reader_name, rs.dwShareMode, rs.dwPreferredProtocols); user_data = (void *) (tintptr) (uds_client->uds_client_id); lcontext = get_pcsc_context_by_app_context(uds_client, hContext); if (lcontext == 0) { LOG(LOG_LEVEL_ERROR, "scard_process_connect: " "get_pcsc_context_by_app_context failed"); return 1; } uds_client->connect_context = lcontext; scard_send_connect(user_data, lcontext->context, lcontext->context_bytes, 1, &rs); return 0; } /*****************************************************************************/ int scard_function_connect_return(void *user_data, struct stream *in_s, int len, int status) { int dwActiveProtocol; int hCard; int bytes; int uds_client_id; struct stream *out_s; struct pcsc_uds_client *uds_client; struct trans *con; char *card; int card_bytes; struct pcsc_card *lcard; LOG_DEVEL(LOG_LEVEL_DEBUG, "scard_function_connect_return:"); LOG_DEVEL(LOG_LEVEL_DEBUG, " status 0x%8.8x", status); uds_client_id = (int) (tintptr) user_data; uds_client = (struct pcsc_uds_client *) get_uds_client_by_id(uds_client_id); if (uds_client == 0) { LOG(LOG_LEVEL_ERROR, "scard_function_connect_return: " "get_uds_client_by_id failed to find uds_client_id %d", uds_client_id); return 1; } con = uds_client->con; dwActiveProtocol = 0; hCard = 0; if (status == 0) { in_uint8s(in_s, 36); in_uint32_le(in_s, dwActiveProtocol); if (len > 40) { in_uint32_le(in_s, card_bytes); in_uint8p(in_s, card, card_bytes); lcard = context_add_card(uds_client, uds_client->connect_context, card, card_bytes); hCard = lcard->app_card; LOG_DEVEL(LOG_LEVEL_DEBUG, " hCard %d dwActiveProtocol %d", hCard, dwActiveProtocol); } else { status = 0x8010000F; /* SCARD_E_PROTO_MISMATCH */ } } out_s = trans_get_out_s(con, 8192); if (out_s == NULL) { return 1; } s_push_layer(out_s, iso_hdr, 8); out_uint32_le(out_s, hCard); out_uint32_le(out_s, dwActiveProtocol); out_uint32_le(out_s, status); /* SCARD_S_SUCCESS status */ s_mark_end(out_s); bytes = (int) (out_s->end - out_s->data); s_pop_layer(out_s, iso_hdr); out_uint32_le(out_s, bytes - 8); out_uint32_le(out_s, 0x04); /* SCARD_CONNECT 0x04 */ return trans_force_write(con); } /*****************************************************************************/ /* returns error */ static int scard_process_disconnect(struct trans *con, struct stream *in_s) { int hCard; int dwDisposition; struct pcsc_uds_client *uds_client; void *user_data; struct pcsc_context *lcontext; struct pcsc_card *lcard; LOG_DEVEL(LOG_LEVEL_DEBUG, "scard_process_disconnect:"); uds_client = (struct pcsc_uds_client *) (con->callback_data); in_uint32_le(in_s, hCard); in_uint32_le(in_s, dwDisposition); user_data = (void *) (tintptr) (uds_client->uds_client_id); lcard = get_pcsc_card_by_app_card(uds_client, hCard, &lcontext); if ((lcontext == 0) || (lcard == 0)) { LOG(LOG_LEVEL_ERROR, "scard_process_disconnect: " "get_pcsc_card_by_app_card failed"); return 1; } scard_send_disconnect(user_data, lcontext->context, lcontext->context_bytes, lcard->card, lcard->card_bytes, dwDisposition); return 0; } /*****************************************************************************/ int scard_function_disconnect_return(void *user_data, struct stream *in_s, int len, int status) { int bytes; int uds_client_id; struct stream *out_s; struct pcsc_uds_client *uds_client; struct trans *con; LOG_DEVEL(LOG_LEVEL_DEBUG, "scard_function_disconnect_return:"); LOG_DEVEL(LOG_LEVEL_DEBUG, " status 0x%8.8x", status); uds_client_id = (int) (tintptr) user_data; uds_client = (struct pcsc_uds_client *) get_uds_client_by_id(uds_client_id); if (uds_client == 0) { LOG(LOG_LEVEL_ERROR, "scard_function_disconnect_return: " "get_uds_client_by_id failed to find uds_client_id %d", uds_client_id); return 1; } con = uds_client->con; out_s = trans_get_out_s(con, 8192); if (out_s == NULL) { return 1; } s_push_layer(out_s, iso_hdr, 8); out_uint32_le(out_s, status); /* SCARD_S_SUCCESS status */ s_mark_end(out_s); bytes = (int) (out_s->end - out_s->data); s_pop_layer(out_s, iso_hdr); out_uint32_le(out_s, bytes - 8); out_uint32_le(out_s, 0x06); /* SCARD_DISCONNECT 0x06 */ return trans_force_write(con); } /*****************************************************************************/ /* returns error */ static int scard_process_begin_transaction(struct trans *con, struct stream *in_s) { int hCard; struct pcsc_uds_client *uds_client; void *user_data; struct pcsc_card *lcard; struct pcsc_context *lcontext; LOG_DEVEL(LOG_LEVEL_DEBUG, "scard_process_begin_transaction:"); uds_client = (struct pcsc_uds_client *) (con->callback_data); in_uint32_le(in_s, hCard); LOG_DEVEL(LOG_LEVEL_DEBUG, "scard_process_begin_transaction: hCard 0x%8.8x", hCard); user_data = (void *) (tintptr) (uds_client->uds_client_id); lcard = get_pcsc_card_by_app_card(uds_client, hCard, &lcontext); if ((lcard == 0) || (lcontext == 0)) { LOG(LOG_LEVEL_ERROR, "scard_process_begin_transaction: " "get_pcsc_card_by_app_card failed"); return 1; } scard_send_begin_transaction(user_data, lcontext->context, lcontext->context_bytes, lcard->card, lcard->card_bytes); return 0; } /*****************************************************************************/ /* returns error */ int scard_function_begin_transaction_return(void *user_data, struct stream *in_s, int len, int status) { struct stream *out_s; int bytes; int uds_client_id; struct pcsc_uds_client *uds_client; struct trans *con; LOG_DEVEL(LOG_LEVEL_DEBUG, "scard_function_begin_transaction_return:"); LOG_DEVEL(LOG_LEVEL_DEBUG, " status 0x%8.8x", status); uds_client_id = (int) (tintptr) user_data; uds_client = (struct pcsc_uds_client *) get_uds_client_by_id(uds_client_id); if (uds_client == 0) { LOG(LOG_LEVEL_ERROR, "scard_function_begin_transaction_return: " "get_uds_client_by_id failed to find uds_client_id %d", uds_client_id); return 1; } con = uds_client->con; out_s = trans_get_out_s(con, 8192); if (out_s == NULL) { return 1; } s_push_layer(out_s, iso_hdr, 8); out_uint32_le(out_s, status); /* SCARD_S_SUCCESS status */ s_mark_end(out_s); bytes = (int) (out_s->end - out_s->data); s_pop_layer(out_s, iso_hdr); out_uint32_le(out_s, bytes - 8); out_uint32_le(out_s, 0x07); /* SCARD_BEGIN_TRANSACTION 0x07 */ return trans_force_write(con); } /*****************************************************************************/ /* returns error */ static int scard_process_end_transaction(struct trans *con, struct stream *in_s) { int hCard; int dwDisposition; struct pcsc_uds_client *uds_client; void *user_data; struct pcsc_card *lcard; struct pcsc_context *lcontext; LOG_DEVEL(LOG_LEVEL_DEBUG, "scard_process_end_transaction:"); uds_client = (struct pcsc_uds_client *) (con->callback_data); in_uint32_le(in_s, hCard); in_uint32_le(in_s, dwDisposition); LOG_DEVEL(LOG_LEVEL_DEBUG, "scard_process_end_transaction: hCard 0x%8.8x", hCard); user_data = (void *) (tintptr) (uds_client->uds_client_id); lcard = get_pcsc_card_by_app_card(uds_client, hCard, &lcontext); if ((lcard == 0) || (lcontext == 0)) { LOG(LOG_LEVEL_ERROR, "scard_process_end_transaction: " "get_pcsc_card_by_app_card failed"); return 1; } scard_send_end_transaction(user_data, lcontext->context, lcontext->context_bytes, lcard->card, lcard->card_bytes, dwDisposition); return 0; } /*****************************************************************************/ /* returns error */ int scard_function_end_transaction_return(void *user_data, struct stream *in_s, int len, int status) { struct stream *out_s; int bytes; int uds_client_id; struct pcsc_uds_client *uds_client; struct trans *con; LOG_DEVEL(LOG_LEVEL_DEBUG, "scard_function_end_transaction_return:"); LOG_DEVEL(LOG_LEVEL_DEBUG, " status 0x%8.8x", status); uds_client_id = (int) (tintptr) user_data; uds_client = (struct pcsc_uds_client *) get_uds_client_by_id(uds_client_id); if (uds_client == 0) { LOG(LOG_LEVEL_ERROR, "scard_function_end_transaction_return: " "get_uds_client_by_id failed to find uds_client_id %d", uds_client_id); return 1; } con = uds_client->con; out_s = trans_get_out_s(con, 8192); if (out_s == NULL) { return 1; } s_push_layer(out_s, iso_hdr, 8); out_uint32_le(out_s, status); /* SCARD_S_SUCCESS status */ s_mark_end(out_s); bytes = (int) (out_s->end - out_s->data); s_pop_layer(out_s, iso_hdr); out_uint32_le(out_s, bytes - 8); out_uint32_le(out_s, 0x08); /* SCARD_END_TRANSACTION 0x08 */ return trans_force_write(con); } /*****************************************************************************/ /* returns error */ int scard_function_get_attrib_return(void *user_data, struct stream *in_s, int len, int status) { return 0; } /*****************************************************************************/ struct pcsc_transmit { int uds_client_id; struct xrdp_scard_io_request recv_ior; int cbRecvLength; }; /*****************************************************************************/ /* returns error */ static int scard_process_transmit(struct trans *con, struct stream *in_s) { int hCard; int recv_bytes; int send_bytes; char *send_data; struct xrdp_scard_io_request send_ior; struct xrdp_scard_io_request recv_ior; struct pcsc_uds_client *uds_client; struct pcsc_card *lcard; struct pcsc_context *lcontext; struct pcsc_transmit *pcscTransmit; LOG_DEVEL(LOG_LEVEL_DEBUG, "scard_process_transmit:"); uds_client = (struct pcsc_uds_client *) (con->callback_data); LOG_DEVEL(LOG_LEVEL_DEBUG, "scard_process_transmit:"); in_uint32_le(in_s, hCard); in_uint32_le(in_s, send_ior.dwProtocol); in_uint32_le(in_s, send_ior.cbPciLength); in_uint32_le(in_s, send_ior.extra_bytes); in_uint8p(in_s, send_ior.extra_data, send_ior.extra_bytes); in_uint32_le(in_s, send_bytes); in_uint8p(in_s, send_data, send_bytes); in_uint32_le(in_s, recv_ior.dwProtocol); in_uint32_le(in_s, recv_ior.cbPciLength); in_uint32_le(in_s, recv_ior.extra_bytes); in_uint8p(in_s, recv_ior.extra_data, recv_ior.extra_bytes); in_uint32_le(in_s, recv_bytes); LOG_DEVEL(LOG_LEVEL_DEBUG, "scard_process_transmit: send dwProtocol %d cbPciLength %d " "recv dwProtocol %d cbPciLength %d send_bytes %d ", send_ior.dwProtocol, send_ior.cbPciLength, recv_ior.dwProtocol, recv_ior.cbPciLength, send_bytes); LOG_DEVEL(LOG_LEVEL_DEBUG, "scard_process_transmit: recv_bytes %d", recv_bytes); lcard = get_pcsc_card_by_app_card(uds_client, hCard, &lcontext); if ((lcard == 0) || (lcontext == 0)) { LOG(LOG_LEVEL_ERROR, "scard_process_transmit: " "get_pcsc_card_by_app_card failed"); return 1; } pcscTransmit = (struct pcsc_transmit *) g_malloc(sizeof(struct pcsc_transmit), 1); pcscTransmit->uds_client_id = uds_client->uds_client_id; pcscTransmit->recv_ior = recv_ior; pcscTransmit->cbRecvLength = recv_bytes; scard_send_transmit(pcscTransmit, lcontext->context, lcontext->context_bytes, lcard->card, lcard->card_bytes, send_data, send_bytes, recv_bytes, &send_ior, &recv_ior); return 0; } /*****************************************************************************/ /* returns error */ int scard_function_transmit_return(void *user_data, struct stream *in_s, int len, int status) { struct stream *out_s; int bytes; int val; int cbRecvLength; char *recvBuf; struct xrdp_scard_io_request recv_ior; struct pcsc_uds_client *uds_client; struct trans *con; struct pcsc_transmit *pcscTransmit; LOG_DEVEL(LOG_LEVEL_DEBUG, "scard_function_transmit_return:"); LOG_DEVEL(LOG_LEVEL_DEBUG, " status 0x%8.8x", status); pcscTransmit = (struct pcsc_transmit *) user_data; recv_ior = pcscTransmit->recv_ior; uds_client = (struct pcsc_uds_client *) get_uds_client_by_id(pcscTransmit->uds_client_id); g_free(pcscTransmit); if (uds_client == 0) { LOG(LOG_LEVEL_ERROR, "scard_function_transmit_return: " "get_uds_client_by_id failed"); return 1; } con = uds_client->con; cbRecvLength = 0; recvBuf = 0; if (status == 0) { in_uint8s(in_s, 20); in_uint32_le(in_s, val); if (val != 0) { /* pioRecvPci */ in_uint8s(in_s, 8); in_uint32_le(in_s, recv_ior.dwProtocol); in_uint32_le(in_s, recv_ior.cbPciLength); recv_ior.cbPciLength += 8; in_uint32_le(in_s, recv_ior.extra_bytes); if (recv_ior.extra_bytes > 0) { in_uint8p(in_s, recv_ior.extra_data, recv_ior.extra_bytes); } } in_uint8s(in_s, 4); in_uint32_le(in_s, val); if (val != 0) { in_uint32_le(in_s, cbRecvLength); in_uint8p(in_s, recvBuf, cbRecvLength); } } LOG_DEVEL(LOG_LEVEL_DEBUG, "scard_function_transmit_return: cbRecvLength %d", cbRecvLength); out_s = trans_get_out_s(con, 8192); if (out_s == NULL) { return 1; } s_push_layer(out_s, iso_hdr, 8); out_uint32_le(out_s, recv_ior.dwProtocol); out_uint32_le(out_s, recv_ior.cbPciLength); out_uint32_le(out_s, recv_ior.extra_bytes); out_uint8a(out_s, recv_ior.extra_data, recv_ior.extra_bytes); out_uint32_le(out_s, cbRecvLength); out_uint8a(out_s, recvBuf, cbRecvLength); out_uint32_le(out_s, status); /* SCARD_S_SUCCESS status */ s_mark_end(out_s); bytes = (int) (out_s->end - out_s->data); s_pop_layer(out_s, iso_hdr); out_uint32_le(out_s, bytes - 8); out_uint32_le(out_s, 0x09); /* SCARD_TRANSMIT 0x09 */ return trans_force_write(con); } /*****************************************************************************/ /* returns error */ static int scard_process_control(struct trans *con, struct stream *in_s) { int hCard; int send_bytes; int recv_bytes; int control_code; char *send_data; struct pcsc_uds_client *uds_client; void *user_data; struct pcsc_context *lcontext; struct pcsc_card *lcard; LOG_DEVEL(LOG_LEVEL_DEBUG, "scard_process_control:"); uds_client = (struct pcsc_uds_client *) (con->callback_data); LOG_DEVEL(LOG_LEVEL_DEBUG, "scard_process_control:"); in_uint32_le(in_s, hCard); in_uint32_le(in_s, control_code); in_uint32_le(in_s, send_bytes); in_uint8p(in_s, send_data, send_bytes); in_uint32_le(in_s, recv_bytes); user_data = (void *) (tintptr) (uds_client->uds_client_id); lcard = get_pcsc_card_by_app_card(uds_client, hCard, &lcontext); if ((lcard == 0) || (lcontext == 0)) { LOG(LOG_LEVEL_ERROR, "scard_process_control: " "get_pcsc_card_by_app_card failed"); return 1; } scard_send_control(user_data, lcontext->context, lcontext->context_bytes, lcard->card, lcard->card_bytes, send_data, send_bytes, recv_bytes, control_code); return 0; } /*****************************************************************************/ /* returns error */ int scard_function_control_return(void *user_data, struct stream *in_s, int len, int status) { struct stream *out_s; int bytes; int cbRecvLength; char *recvBuf; int uds_client_id; struct pcsc_uds_client *uds_client; struct trans *con; LOG_DEVEL(LOG_LEVEL_DEBUG, "scard_function_control_return:"); LOG_DEVEL(LOG_LEVEL_DEBUG, " status 0x%8.8x", status); uds_client_id = (int) (tintptr) user_data; uds_client = (struct pcsc_uds_client *) get_uds_client_by_id(uds_client_id); if (uds_client == 0) { LOG(LOG_LEVEL_ERROR, "scard_function_control_return: " "get_uds_client_by_id failed to find uds_client_id %d", uds_client_id); return 1; } con = uds_client->con; cbRecvLength = 0; recvBuf = 0; if (status == 0) { in_uint8s(in_s, 28); in_uint32_le(in_s, cbRecvLength); in_uint8p(in_s, recvBuf, cbRecvLength); } LOG_DEVEL(LOG_LEVEL_DEBUG, "scard_function_control_return: cbRecvLength %d", cbRecvLength); out_s = trans_get_out_s(con, 8192); if (out_s == NULL) { return 1; } s_push_layer(out_s, iso_hdr, 8); out_uint32_le(out_s, cbRecvLength); out_uint8a(out_s, recvBuf, cbRecvLength); out_uint32_le(out_s, status); /* SCARD_S_SUCCESS status */ s_mark_end(out_s); bytes = (int) (out_s->end - out_s->data); s_pop_layer(out_s, iso_hdr); out_uint32_le(out_s, bytes - 8); out_uint32_le(out_s, 0x0A); /* SCARD_CONTROL 0x0A */ return trans_force_write(con); } /*****************************************************************************/ struct pcsc_status { int uds_client_id; int cchReaderLen; }; /*****************************************************************************/ /* returns error */ static int scard_process_status(struct trans *con, struct stream *in_s) { int hCard; int cchReaderLen; int cbAtrLen; struct pcsc_uds_client *uds_client; struct pcsc_card *lcard; struct pcsc_context *lcontext; struct pcsc_status *pcscStatus; LOG_DEVEL(LOG_LEVEL_DEBUG, "scard_process_status:"); uds_client = (struct pcsc_uds_client *) (con->callback_data); in_uint32_le(in_s, hCard); in_uint32_le(in_s, cchReaderLen); in_uint32_le(in_s, cbAtrLen); LOG_DEVEL(LOG_LEVEL_DEBUG, "scard_process_status: hCard 0x%8.8x cchReaderLen %d " "cbAtrLen %d", hCard, cchReaderLen, cbAtrLen); lcard = get_pcsc_card_by_app_card(uds_client, hCard, &lcontext); if ((lcard == 0) || (lcontext == 0)) { LOG(LOG_LEVEL_ERROR, "scard_process_status: " "get_pcsc_card_by_app_card failed"); return 1; } pcscStatus = (struct pcsc_status *) g_malloc(sizeof(struct pcsc_status), 1); pcscStatus->uds_client_id = uds_client->uds_client_id; pcscStatus->cchReaderLen = cchReaderLen; scard_send_status(pcscStatus, 1, lcontext->context, lcontext->context_bytes, lcard->card, lcard->card_bytes, cchReaderLen, cbAtrLen); return 0; } #define MS_SCARD_UNKNOWN 0 #define MS_SCARD_ABSENT 1 #define MS_SCARD_PRESENT 2 #define MS_SCARD_SWALLOWED 3 #define MS_SCARD_POWERED 4 #define MS_SCARD_NEGOTIABLE 5 #define MS_SCARD_SPECIFIC 6 #define PC_SCARD_UNKNOWN 0x0001 /**< Unknown state */ #define PC_SCARD_ABSENT 0x0002 /**< Card is absent */ #define PC_SCARD_PRESENT 0x0004 /**< Card is present */ #define PC_SCARD_SWALLOWED 0x0008 /**< Card not powered */ #define PC_SCARD_POWERED 0x0010 /**< Card is powered */ #define PC_SCARD_NEGOTIABLE 0x0020 /**< Ready for PTS */ #define PC_SCARD_SPECIFIC 0x0040 /**< PTS has been set */ static int g_ms2pc[] = { PC_SCARD_UNKNOWN, PC_SCARD_ABSENT, PC_SCARD_PRESENT, PC_SCARD_SWALLOWED, PC_SCARD_POWERED, PC_SCARD_NEGOTIABLE, PC_SCARD_SPECIFIC }; /*****************************************************************************/ /* returns error */ int scard_function_status_return(void *user_data, struct stream *in_s, int len, int status) { /* see [MS-RDPESC] 2.2.3.10 * * IDL:- * * typedef struct _Status_Return { * long ReturnCode; * unsigned long cBytes; * [unique] [size_is(cBytes)] byte *mszReaderNames; * unsigned long dwState; * unsigned long dwProtocol; * byte pbAtr[32]; * [range(0,32)] unsigned long cbAtrLen; * } Status_Return; * * NDR:- * * Offset Decription * 0 ReturnCode * 4 cBytes * 8 Referent Identifier for mszReaderNames; * 12 dwState * 16 dwProtocol * 20 pbAtr * 52 cbAtrLen * 56 length of multistring in bytes (same as cBytes) * 60 Multistring data */ struct stream *out_s; int bytes; int dwReaderLen; int dwState; int dwProtocol; int dwAtrLen; char attr[32]; char lreader_name[100]; int uds_client_id; struct pcsc_uds_client *uds_client; struct trans *con; struct pcsc_status *pcscStatus; LOG_DEVEL(LOG_LEVEL_DEBUG, "scard_function_status_return:"); LOG_DEVEL(LOG_LEVEL_DEBUG, " status 0x%8.8x", status); pcscStatus = (struct pcsc_status *) user_data; if (pcscStatus == 0) { LOG(LOG_LEVEL_ERROR, "scard_function_status_return: pcscStatus is nil"); return 1; } uds_client_id = pcscStatus->uds_client_id; uds_client = (struct pcsc_uds_client *) get_uds_client_by_id(uds_client_id); if (uds_client == 0) { LOG(LOG_LEVEL_ERROR, "scard_function_status_return: " "get_uds_client_by_id failed to find uds_client_id %d", uds_client_id); g_free(pcscStatus); return 1; } g_free(pcscStatus); con = uds_client->con; dwReaderLen = 0; dwState = 0; dwProtocol = 0; dwAtrLen = 0; lreader_name[0] = 0; if (status == 0) { in_uint8s(in_s, 16); // Skip [C706] PDU Header in_uint8s(in_s, 4); // ReturnCode in_uint32_le(in_s, dwReaderLen); in_uint8s(in_s, 4); // Referent Identifier in_uint32_le(in_s, dwState); dwState = g_ms2pc[dwState % 6]; in_uint32_le(in_s, dwProtocol); in_uint8a(in_s, attr, 32); in_uint32_le(in_s, dwAtrLen); // Length of multistring and multistring data if (dwReaderLen <= 0) { lreader_name[0] = '\0'; } else { 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)); } } LOG_DEVEL(LOG_LEVEL_DEBUG, "scard_function_status_return: dwAtrLen %d dwReaderLen %d " "dwProtocol %d dwState %d name %s", dwAtrLen, dwReaderLen, dwProtocol, dwState, lreader_name); out_s = trans_get_out_s(con, 8192); if (out_s == NULL) { return 1; } s_push_layer(out_s, iso_hdr, 8); dwReaderLen = g_strlen(lreader_name); out_uint32_le(out_s, dwReaderLen); out_uint8a(out_s, lreader_name, dwReaderLen); out_uint32_le(out_s, dwState); out_uint32_le(out_s, dwProtocol); out_uint32_le(out_s, dwAtrLen); out_uint8a(out_s, attr, dwAtrLen); out_uint32_le(out_s, status); /* SCARD_S_SUCCESS status */ s_mark_end(out_s); bytes = (int) (out_s->end - out_s->data); s_pop_layer(out_s, iso_hdr); out_uint32_le(out_s, bytes - 8); out_uint32_le(out_s, 0x0B); /* SCARD_STATUS 0x0B */ return trans_force_write(con); } /*****************************************************************************/ /* returns error */ static int scard_process_get_status_change(struct trans *con, struct stream *in_s) { int index; int hContext; int dwTimeout; int cReaders; READER_STATE *rsa; struct pcsc_uds_client *uds_client; void *user_data; struct pcsc_context *lcontext; LOG_DEVEL(LOG_LEVEL_DEBUG, "scard_process_get_status_change:"); uds_client = (struct pcsc_uds_client *) (con->callback_data); in_uint32_le(in_s, hContext); in_uint32_le(in_s, dwTimeout); in_uint32_le(in_s, cReaders); if ((cReaders < 0) || (cReaders > 16)) { LOG(LOG_LEVEL_ERROR, "scard_process_get_status_change: bad cReaders %d", cReaders); return 1; } rsa = (READER_STATE *) g_malloc(sizeof(READER_STATE) * cReaders, 1); for (index = 0; index < cReaders; index++) { in_uint8a(in_s, rsa[index].reader_name, 100); LOG_DEVEL(LOG_LEVEL_DEBUG, "scard_process_get_status_change: reader_name %s", rsa[index].reader_name); in_uint32_le(in_s, rsa[index].current_state); LOG_DEVEL(LOG_LEVEL_DEBUG, "scard_process_get_status_change: current_state %d", rsa[index].current_state); in_uint32_le(in_s, rsa[index].event_state); LOG_DEVEL(LOG_LEVEL_DEBUG, "scard_process_get_status_change: event_state %d", rsa[index].event_state); in_uint32_le(in_s, rsa[index].atr_len); LOG_DEVEL(LOG_LEVEL_DEBUG, "scard_process_get_status_change: atr_len %d", rsa[index].atr_len); in_uint8a(in_s, rsa[index].atr, 36); } LOG_DEVEL(LOG_LEVEL_DEBUG, "scard_process_get_status_change: hContext 0x%8.8x dwTimeout " "%d cReaders %d", hContext, dwTimeout, cReaders); user_data = (void *) (tintptr) (uds_client->uds_client_id); lcontext = get_pcsc_context_by_app_context(uds_client, hContext); if (lcontext == 0) { LOG(LOG_LEVEL_ERROR, "scard_process_get_status_change: " "get_pcsc_context_by_app_context failed"); g_free(rsa); return 1; } scard_send_get_status_change(user_data, lcontext->context, lcontext->context_bytes, 1, dwTimeout, cReaders, rsa); g_free(rsa); return 0; } /*****************************************************************************/ int scard_function_get_status_change_return(void *user_data, struct stream *in_s, int len, int status) { int bytes; int index; int cReaders; tui32 current_state; tui32 event_state; tui32 atr_len; /* number of bytes in atr[] */ tui8 atr[36]; struct stream *out_s; int uds_client_id; struct pcsc_uds_client *uds_client; struct trans *con; LOG_DEVEL(LOG_LEVEL_DEBUG, "scard_function_get_status_change_return:"); LOG_DEVEL(LOG_LEVEL_DEBUG, " status 0x%8.8x", status); uds_client_id = (int) (tintptr) user_data; uds_client = (struct pcsc_uds_client *) get_uds_client_by_id(uds_client_id); if (uds_client == 0) { LOG(LOG_LEVEL_ERROR, "scard_function_get_status_change_return: " "get_uds_client_by_id failed to find uds_client_id %d", uds_client_id); return 1; } con = uds_client->con; out_s = trans_get_out_s(con, 8192); if (out_s == NULL) { return 1; } s_push_layer(out_s, iso_hdr, 8); if (status != 0) { out_uint32_le(out_s, 0); /* cReaders */ out_uint32_le(out_s, status); /* SCARD_S_SUCCESS status */ } else { in_uint8s(in_s, 28); in_uint32_le(in_s, cReaders); LOG_DEVEL(LOG_LEVEL_DEBUG, " cReaders %d", cReaders); out_uint32_le(out_s, cReaders); if (cReaders > 0) { for (index = 0; index < cReaders; index++) { in_uint32_le(in_s, current_state); out_uint32_le(out_s, current_state); in_uint32_le(in_s, event_state); out_uint32_le(out_s, event_state); in_uint32_le(in_s, atr_len); out_uint32_le(out_s, atr_len); in_uint8a(in_s, atr, 36); out_uint8a(out_s, atr, 36); } } out_uint32_le(out_s, status); /* SCARD_S_SUCCESS status */ } s_mark_end(out_s); bytes = (int) (out_s->end - out_s->data); s_pop_layer(out_s, iso_hdr); out_uint32_le(out_s, bytes - 8); out_uint32_le(out_s, 0x0C); /* SCARD_ESTABLISH_CONTEXT 0x0C */ return trans_force_write(con); } /*****************************************************************************/ /* returns error */ static int scard_process_cancel(struct trans *con, struct stream *in_s) { int hContext; struct pcsc_uds_client *uds_client; void *user_data; struct pcsc_context *lcontext; LOG_DEVEL(LOG_LEVEL_DEBUG, "scard_process_cancel:"); uds_client = (struct pcsc_uds_client *) (con->callback_data); in_uint32_le(in_s, hContext); LOG_DEVEL(LOG_LEVEL_DEBUG, "scard_process_cancel: hContext 0x%8.8x", hContext); user_data = (void *) (tintptr) (uds_client->uds_client_id); lcontext = get_pcsc_context_by_app_context(uds_client, hContext); if (lcontext == 0) { LOG(LOG_LEVEL_ERROR, "scard_process_cancel: " "get_pcsc_context_by_app_context failed"); return 1; } scard_send_cancel(user_data, lcontext->context, lcontext->context_bytes); return 0; } /*****************************************************************************/ /* returns error */ int scard_function_cancel_return(void *user_data, struct stream *in_s, int len, int status) { int bytes; int uds_client_id; struct stream *out_s; struct pcsc_uds_client *uds_client; struct trans *con; LOG_DEVEL(LOG_LEVEL_DEBUG, "scard_function_cancel_return:"); LOG_DEVEL(LOG_LEVEL_DEBUG, " status 0x%8.8x", status); uds_client_id = (int) (tintptr) user_data; uds_client = (struct pcsc_uds_client *) get_uds_client_by_id(uds_client_id); if (uds_client == 0) { LOG(LOG_LEVEL_ERROR, "scard_function_cancel_return: " "get_uds_client_by_id failed to find uds_client_id %d", uds_client_id); return 1; } con = uds_client->con; out_s = trans_get_out_s(con, 8192); if (out_s == NULL) { return 1; } s_push_layer(out_s, iso_hdr, 8); out_uint32_le(out_s, status); /* SCARD_S_SUCCESS status */ s_mark_end(out_s); bytes = (int) (out_s->end - out_s->data); s_pop_layer(out_s, iso_hdr); out_uint32_le(out_s, bytes - 8); out_uint32_le(out_s, 0x0D); /* SCARD_CANCEL 0x0D */ return trans_force_write(con); } /*****************************************************************************/ /* returns error */ int scard_function_is_context_valid_return(void *user_data, struct stream *in_s, int len, int status) { return 0; } /*****************************************************************************/ /* returns error */ int scard_function_reconnect_return(void *user_data, struct stream *in_s, int len, int status) { return 0; } /*****************************************************************************/ /* returns error */ static int scard_process_msg(struct trans *con, struct stream *in_s, int command) { int rv; LOG_DEVEL(LOG_LEVEL_DEBUG, "scard_process_msg: command 0x%4.4x", command); rv = 0; switch (command) { case 0x01: /* SCARD_ESTABLISH_CONTEXT */ LOG_DEVEL(LOG_LEVEL_INFO, "scard_process_msg: SCARD_ESTABLISH_CONTEXT"); rv = scard_process_establish_context(con, in_s); break; case 0x02: /* SCARD_RELEASE_CONTEXT */ LOG_DEVEL(LOG_LEVEL_INFO, "scard_process_msg: SCARD_RELEASE_CONTEXT"); rv = scard_process_release_context(con, in_s); break; case 0x03: /* SCARD_LIST_READERS */ /* This is only called from xrdp_pcsc.c */ LOG_DEVEL(LOG_LEVEL_INFO, "scard_process_msg: SCARD_LIST_READERS"); rv = scard_process_list_readers(con, in_s); break; case 0x04: /* SCARD_CONNECT */ LOG_DEVEL(LOG_LEVEL_INFO, "scard_process_msg: SCARD_CONNECT"); rv = scard_process_connect(con, in_s); break; case 0x05: /* SCARD_RECONNECT */ LOG_DEVEL(LOG_LEVEL_INFO, "scard_process_msg: SCARD_RECONNECT"); break; case 0x06: /* SCARD_DISCONNECT */ LOG_DEVEL(LOG_LEVEL_INFO, "scard_process_msg: SCARD_DISCONNECT"); rv = scard_process_disconnect(con, in_s); break; case 0x07: /* SCARD_BEGIN_TRANSACTION */ LOG_DEVEL(LOG_LEVEL_INFO, "scard_process_msg: SCARD_BEGIN_TRANSACTION"); rv = scard_process_begin_transaction(con, in_s); break; case 0x08: /* SCARD_END_TRANSACTION */ LOG_DEVEL(LOG_LEVEL_INFO, "scard_process_msg: SCARD_END_TRANSACTION"); rv = scard_process_end_transaction(con, in_s); break; case 0x09: /* SCARD_TRANSMIT */ LOG_DEVEL(LOG_LEVEL_INFO, "scard_process_msg: SCARD_TRANSMIT"); rv = scard_process_transmit(con, in_s); break; case 0x0A: /* SCARD_CONTROL */ LOG_DEVEL(LOG_LEVEL_INFO, "scard_process_msg: SCARD_CONTROL"); rv = scard_process_control(con, in_s); break; case 0x0B: /* SCARD_STATUS */ LOG_DEVEL(LOG_LEVEL_INFO, "scard_process_msg: SCARD_STATUS"); rv = scard_process_status(con, in_s); break; case 0x0C: /* SCARD_GET_STATUS_CHANGE */ LOG_DEVEL(LOG_LEVEL_INFO, "scard_process_msg: SCARD_GET_STATUS_CHANGE"); rv = scard_process_get_status_change(con, in_s); break; case 0x0D: /* SCARD_CANCEL */ LOG_DEVEL(LOG_LEVEL_INFO, "scard_process_msg: SCARD_CANCEL"); rv = scard_process_cancel(con, in_s); break; case 0x0E: /* SCARD_CANCEL_TRANSACTION */ LOG_DEVEL(LOG_LEVEL_INFO, "scard_process_msg: SCARD_CANCEL_TRANSACTION"); break; case 0x0F: /* SCARD_GET_ATTRIB */ LOG_DEVEL(LOG_LEVEL_INFO, "scard_process_msg: SCARD_GET_ATTRIB"); break; case 0x10: /* SCARD_SET_ATTRIB */ LOG_DEVEL(LOG_LEVEL_INFO, "scard_process_msg: SCARD_SET_ATTRIB"); break; default: LOG_DEVEL(LOG_LEVEL_WARNING, "scard_process_msg: unknown mtype 0x%4.4x", command); rv = 1; break; } return rv; } /*****************************************************************************/ /* returns error */ static int my_pcsc_trans_data_in(struct trans *trans) { struct stream *s; int size; int command; int error; LOG_DEVEL(LOG_LEVEL_DEBUG, "my_pcsc_trans_data_in:"); if (trans == 0) { return 0; } s = trans_get_in_s(trans); in_uint32_le(s, size); in_uint32_le(s, command); LOG_DEVEL(LOG_LEVEL_DEBUG, "my_pcsc_trans_data_in: size %d command %d", size, command); error = trans_force_read(trans, size); if (error == 0) { error = scard_process_msg(trans, s, command); } return error; } /*****************************************************************************/ /* got a new connection from libpcsclite */ static int my_pcsc_trans_conn_in(struct trans *trans, struct trans *new_trans) { struct pcsc_uds_client *uds_client; LOG_DEVEL(LOG_LEVEL_DEBUG, "my_pcsc_trans_conn_in:"); if (trans == 0) { return 1; } if (trans != g_lis) { return 1; } if (new_trans == 0) { return 1; } uds_client = create_uds_client(new_trans); if (uds_client == 0) { return 1; } uds_client->con->trans_data_in = my_pcsc_trans_data_in; uds_client->con->header_size = 8; if (g_uds_clients == 0) { g_uds_clients = list_create(); } list_add_item(g_uds_clients, (tbus)uds_client); return 0; } /*****************************************************************************/ int scard_pcsc_init(void) { char *home; int disp; int error; LOG_DEVEL(LOG_LEVEL_DEBUG, "scard_pcsc_init:"); if (g_lis == 0) { g_lis = trans_create(2, 8192, 8192); // TODO: See #2501. Use needs a way to move PCSCLITE_CSOCK_NAME // to a location not under $HOME. home = g_getenv("HOME"); disp = g_display_num; g_snprintf(g_pcsclite_ipc_dir, 255, "%s/.pcsc%d", home, disp); if (g_directory_exist(g_pcsclite_ipc_dir)) { if (!g_remove_dir(g_pcsclite_ipc_dir)) { LOG_DEVEL(LOG_LEVEL_WARNING, "scard_pcsc_init: g_remove_dir failed"); } } if (!g_directory_exist(g_pcsclite_ipc_dir)) { if (!g_create_dir(g_pcsclite_ipc_dir)) { if (!g_directory_exist(g_pcsclite_ipc_dir)) { LOG_DEVEL(LOG_LEVEL_WARNING, "scard_pcsc_init: g_create_dir failed"); } } } /* Only the current user should be able to access the remote * smartcard */ g_chmod_hex(g_pcsclite_ipc_dir, 0x700); g_snprintf(g_pcsclite_ipc_file, 255, "%s/pcscd.comm", g_pcsclite_ipc_dir); g_lis->trans_conn_in = my_pcsc_trans_conn_in; error = trans_listen(g_lis, g_pcsclite_ipc_file); if (error != 0) { LOG(LOG_LEVEL_ERROR, "scard_pcsc_init: trans_listen failed for port %s", g_pcsclite_ipc_file); return 1; } } return 0; } /*****************************************************************************/ int scard_pcsc_deinit(void) { LOG_DEVEL(LOG_LEVEL_DEBUG, "scard_pcsc_deinit:"); if (g_lis != 0) { trans_delete(g_lis); g_lis = 0; } if (g_pcsclite_ipc_dir[0] != 0) { g_file_delete(g_pcsclite_ipc_file); if (!g_remove_dir(g_pcsclite_ipc_dir)) { LOG_DEVEL(LOG_LEVEL_WARNING, "scard_pcsc_deinit: g_remove_dir failed"); } g_pcsclite_ipc_dir[0] = 0; } return 0; } #else int scard_pcsc_get_wait_objs(tbus *objs, int *count, int *timeout) { return 0; } int scard_pcsc_check_wait_objs(void) { return 0; } int scard_pcsc_init(void) { return 0; } int scard_pcsc_deinit(void) { return 0; } #endif