fb9c175b11
Co-authored-by: matt335672 <30179339+matt335672@users.noreply.github.com>
2076 lines
65 KiB
C
2076 lines
65 KiB
C
/**
|
|
* 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 <config_ac.h>
|
|
#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
|