diff options
-rw-r--r-- | server/reds.c | 24 | ||||
-rw-r--r-- | server/smartcard.c | 532 | ||||
-rw-r--r-- | server/smartcard.h | 18 |
3 files changed, 574 insertions, 0 deletions
diff --git a/server/reds.c b/server/reds.c index 3edf474f..b4ec6e14 100644 --- a/server/reds.c +++ b/server/reds.c @@ -59,6 +59,9 @@ #ifdef USE_TUNNEL #include "red_tunnel_worker.h" #endif +#ifdef USE_SMARTCARD +#include "smartcard.h" +#endif SpiceCoreInterface *core = NULL; static SpiceKbdInstance *keyboard = NULL; @@ -1251,6 +1254,7 @@ static int read_from_vdi_port(void) VDIReadBuf *dispatch_buf; int total = 0; int n; + if (inside_call) { return 0; } @@ -3432,9 +3436,13 @@ __visible__ void spice_server_char_device_wakeup(SpiceCharDeviceInstance* sin) } #define SUBTYPE_VDAGENT "vdagent" +#define SUBTYPE_SMARTCARD "smartcard" const char *spice_server_char_device_recognized_subtypes_list[] = { SUBTYPE_VDAGENT, +#ifdef USE_SMARTCARD + SUBTYPE_SMARTCARD, +#endif NULL, }; @@ -3460,6 +3468,13 @@ static int spice_server_char_device_add_interface(SpiceServer *s, char_device->st = &vdagent_char_device_state; attach_to_red_agent(char_device); } +#ifdef USE_SMARTCARD + else if (strcmp(char_device->subtype, SUBTYPE_SMARTCARD) == 0) { + if (smartcard_device_connect(char_device) == -1) { + return -1; + } + } +#endif return 0; } @@ -3474,6 +3489,11 @@ static void spice_server_char_device_remove_interface(SpiceBaseInstance *sin) reds_agent_remove(); } } +#ifdef USE_SMARTCARD + else if (strcmp(char_device->subtype, SUBTYPE_SMARTCARD) == 0) { + smartcard_device_disconnect(char_device); + } +#endif } __visible__ int spice_server_add_interface(SpiceServer *s, @@ -3759,6 +3779,10 @@ static void do_spice_init(SpiceCoreInterface *core_interface) } inputs_init(); +#ifdef USE_SMARTCARD + smartcard_channel_init(); +#endif + reds->mouse_mode = SPICE_MOUSE_MODE_SERVER; atexit(reds_exit); } diff --git a/server/smartcard.c b/server/smartcard.c new file mode 100644 index 00000000..7c0a5aaf --- /dev/null +++ b/server/smartcard.c @@ -0,0 +1,532 @@ +#include "server/char_device.h" +#include "server/red_channel.h" +#include "server/smartcard.h" +#include "vscard_common.h" + +#define SMARTCARD_MAX_READERS 10 + +typedef struct SmartCardDeviceState { + SpiceCharDeviceState base; + reader_id_t reader_id; + uint32_t attached; + uint8_t *buf; + uint32_t buf_size; + uint8_t *buf_pos; + uint32_t buf_used; +} SmartCardDeviceState; + +enum { + PIPE_ITEM_TYPE_ERROR=1, + PIPE_ITEM_TYPE_READER_ADD_RESPONSE, + PIPE_ITEM_TYPE_MSG, +}; + +typedef struct ErrorItem { + PipeItem base; + reader_id_t reader_id; + uint32_t error; +} ErrorItem; + +typedef struct ReaderAddResponseItem { + PipeItem base; + uint32_t reader_id; +} ReaderAddResponseItem; + +typedef struct MsgItem { + PipeItem base; + VSCMsgHeader* vheader; +} MsgItem; + +typedef struct SmartCardChannel { + RedChannel base; +} SmartCardChannel; + +static SmartCardChannel *g_smartcard_channel = NULL; + +static struct Readers { + uint32_t num; + SpiceCharDeviceInstance* sin[SMARTCARD_MAX_READERS]; +} g_smartcard_readers = {0, {NULL}}; + +static SpiceCharDeviceInstance* smartcard_readers_get_unattached(); +static SpiceCharDeviceInstance* smartcard_readers_get(reader_id_t reader_id); +static int smartcard_char_device_add_to_readers(SpiceCharDeviceInstance *sin); +static void smartcard_char_device_attach( + SpiceCharDeviceInstance *char_device, SmartCardChannel *smartcard_channel); +static void smartcard_char_device_detach( + SpiceCharDeviceInstance *char_device, SmartCardChannel *smartcard_channel); +static void smartcard_channel_write_to_reader( + SmartCardChannel *smartcard_channel, VSCMsgHeader *vheader); + +static void smartcard_char_device_on_message_from_device( + SmartCardDeviceState *state, VSCMsgHeader *header); +static void smartcard_on_message_from_device( + SmartCardChannel *smartcard_channel, VSCMsgHeader *vheader); +static SmartCardDeviceState* smartcard_device_state_new(); +static void smartcard_device_state_free(SmartCardDeviceState* st); + +void smartcard_char_device_wakeup(SpiceCharDeviceInstance *sin) +{ + SmartCardDeviceState* state = SPICE_CONTAINEROF( + sin->st, SmartCardDeviceState, base); + SpiceCharDeviceInterface *sif = SPICE_CONTAINEROF(sin->base.sif, SpiceCharDeviceInterface, base); + VSCMsgHeader *vheader = (VSCMsgHeader*)state->buf; + int n; + int remaining; + + while ((n = sif->read(sin, state->buf_pos, state->buf_size - state->buf_used)) > 0) { + state->buf_pos += n; + state->buf_used += n; + if (state->buf_used < sizeof(VSCMsgHeader)) { + continue; + } + if (vheader->length > state->buf_size) { + state->buf_size = MAX(state->buf_size*2, vheader->length + sizeof(VSCMsgHeader)); + state->buf = spice_realloc(state->buf, state->buf_size); + ASSERT(state->buf != NULL); + } + if (state->buf_used - sizeof(VSCMsgHeader) < vheader->length) { + continue; + } + smartcard_char_device_on_message_from_device(state, vheader); + remaining = state->buf_used - sizeof(VSCMsgHeader) > vheader->length; + if (remaining > 0) { + memcpy(state->buf, state->buf_pos, remaining); + } + state->buf_pos = state->buf; + state->buf_used = remaining; + } +} + +void smartcard_char_device_on_message_from_device( + SmartCardDeviceState *state, + VSCMsgHeader *vheader) +{ + VSCMsgHeader *sent_header; + + switch (vheader->type) { + case VSC_Init: + return; + break; + case VSC_ReaderAddResponse: + /* The device sends this for vscclient, we send one ourselves, + * a second would be an error. */ + return; + break; + case VSC_Reconnect: + /* Ignore VSC_Reconnect messages, spice channel reconnection does the same. */ + return; + break; + default: + break; + } + ASSERT(state->reader_id != VSCARD_UNDEFINED_READER_ID); + ASSERT(g_smartcard_channel != NULL); + sent_header = spice_memdup(vheader, sizeof(*vheader) + vheader->length); + sent_header->reader_id = state->reader_id; + smartcard_on_message_from_device(g_smartcard_channel, sent_header); +} + +static void smartcard_readers_detach_all(SmartCardChannel *smartcard_channel) +{ + int i; + + for (i = 0 ; i < g_smartcard_readers.num; ++i) { + smartcard_char_device_detach(g_smartcard_readers.sin[i], + smartcard_channel); + } +} + +static int smartcard_char_device_add_to_readers(SpiceCharDeviceInstance *char_device) +{ + SmartCardDeviceState* state = SPICE_CONTAINEROF( + char_device->st, SmartCardDeviceState, base); + + if (g_smartcard_readers.num >= SMARTCARD_MAX_READERS) { + return -1; + } + state->reader_id = g_smartcard_readers.num; + g_smartcard_readers.sin[g_smartcard_readers.num++] = char_device; + return 0; +} + +static SpiceCharDeviceInstance *smartcard_readers_get(reader_id_t reader_id) +{ + ASSERT(reader_id < g_smartcard_readers.num); + return g_smartcard_readers.sin[reader_id]; +} + +static SpiceCharDeviceInstance *smartcard_readers_get_unattached() +{ + int i; + SmartCardDeviceState* state; + + for (i = 0; i < g_smartcard_readers.num; ++i) { + state = SPICE_CONTAINEROF(g_smartcard_readers.sin[i]->st, + SmartCardDeviceState, base); + if (!state->attached) { + return g_smartcard_readers.sin[i]; + } + } + return NULL; +} + +static SmartCardDeviceState* smartcard_device_state_new() +{ + SmartCardDeviceState *st; + + st = spice_new0(SmartCardDeviceState, 1); + st->base.wakeup = smartcard_char_device_wakeup; + st->reader_id = VSCARD_UNDEFINED_READER_ID; + st->attached = FALSE; + st->buf_size = APDUBufSize + sizeof(VSCMsgHeader); + st->buf = spice_malloc(st->buf_size); + st->buf_pos = st->buf; + st->buf_used = 0; + return st; +} + +static void smartcard_device_state_free(SmartCardDeviceState* st) +{ + free(st->buf); + free(st); +} + +void smartcard_device_disconnect(SpiceCharDeviceInstance *char_device) +{ + SmartCardDeviceState *st = SPICE_CONTAINEROF(char_device->st, + SmartCardDeviceState, base); + + smartcard_device_state_free(st); +} + +int smartcard_device_connect(SpiceCharDeviceInstance *char_device) +{ + SmartCardDeviceState *st; + + st = smartcard_device_state_new(); + char_device->st = &st->base; + if (smartcard_char_device_add_to_readers(char_device) == -1) { + smartcard_device_state_free(st); + return -1; + } + return 0; +} + +static void smartcard_char_device_attach( + SpiceCharDeviceInstance *char_device, SmartCardChannel *smartcard_channel) +{ + SmartCardDeviceState *st = SPICE_CONTAINEROF(char_device->st, SmartCardDeviceState, base); + + if (st->attached == TRUE) { + return; + } + st->attached = TRUE; + VSCMsgHeader vheader = {.type = VSC_ReaderAdd, .reader_id=st->reader_id, + .length=0}; + smartcard_channel_write_to_reader(smartcard_channel, &vheader); +} + +static void smartcard_char_device_detach( + SpiceCharDeviceInstance *char_device, SmartCardChannel *smartcard_channel) +{ + SmartCardDeviceState *st = SPICE_CONTAINEROF(char_device->st, SmartCardDeviceState, base); + + if (st->attached == FALSE) { + return; + } + st->attached = FALSE; + VSCMsgHeader vheader = {.type = VSC_ReaderRemove, .reader_id=st->reader_id, + .length=0}; + smartcard_channel_write_to_reader(smartcard_channel, &vheader); +} + +static int smartcard_channel_config_socket(RedChannel *channel) +{ + return TRUE; +} + +static uint8_t *smartcard_channel_alloc_msg_rcv_buf(RedChannel *channel, SpiceDataHeader *msg_header) +{ + //red_printf("allocing %d bytes", msg_header->size); + return spice_malloc(msg_header->size); +} + +static void smartcard_channel_release_msg_rcv_buf(RedChannel *channel, SpiceDataHeader *msg_header, + uint8_t *msg) +{ + red_printf("freeing %d bytes", msg_header->size); + free(msg); +} + +static void smartcard_channel_send_data(RedChannel *channel, PipeItem *item, VSCMsgHeader *vheader) +{ + ASSERT(channel); + ASSERT(vheader); + red_channel_init_send_data(channel, SPICE_MSG_SMARTCARD_DATA, item); + red_channel_add_buf(channel, vheader, sizeof(VSCMsgHeader)); + if (vheader->length > 0) { + red_channel_add_buf(channel, (uint8_t*)(vheader+1), vheader->length); + } + red_channel_begin_send_message(channel); +} + +static void smartcard_channel_send_message(RedChannel *channel, PipeItem *item, + uint32_t reader_id, VSCMsgType type, uint8_t* data, uint32_t len) +{ + VSCMsgHeader mhHeader; + //SpiceMarshaller* m = msg->marshaller(); + + mhHeader.type = type; + mhHeader.length = len; + mhHeader.reader_id = reader_id; + //_marshallers->msg_SpiceMsgData(m, &msgdata); + //spice_marshaller_add(m, (uint8_t*)&mhHeader, sizeof(mhHeader)); + //spice_marshaller_add(m, data, len); + //marshaller_outgoing_write(msg); + + smartcard_channel_send_data(channel, item, &mhHeader); +} + +static void smartcard_channel_send_error( + SmartCardChannel *smartcard_channel, PipeItem *item) +{ + ErrorItem* error_item = (ErrorItem*)item; + VSCMsgError error; + + error.code = error_item->error; + smartcard_channel_send_message(&smartcard_channel->base, item, error_item->reader_id, + VSC_Error, (uint8_t*)&error, sizeof(error)); +} + +static void smartcard_channel_send_reader_add_response( + SmartCardChannel *smartcard_channel, PipeItem *item) +{ + ReaderAddResponseItem* rar_item = (ReaderAddResponseItem*)item; + VSCMsgReaderAddResponse rar; + + smartcard_channel_send_message(&smartcard_channel->base, item, rar_item->reader_id, + VSC_ReaderAddResponse, (uint8_t*)&rar, sizeof(rar)); +} + +static void smartcard_channel_send_msg( + SmartCardChannel *smartcard_channel, PipeItem *item) +{ + MsgItem* msg_item = (MsgItem*)item; + + smartcard_channel_send_data(&smartcard_channel->base, item, msg_item->vheader); +} + +static void smartcard_channel_send_item(RedChannel *channel, PipeItem *item) +{ + SmartCardChannel *smartcard_channel = (SmartCardChannel *)channel; + + red_channel_reset_send_data(channel); + switch (item->type) { + case PIPE_ITEM_TYPE_ERROR: + smartcard_channel_send_error(smartcard_channel, item); + break; + case PIPE_ITEM_TYPE_READER_ADD_RESPONSE: + smartcard_channel_send_reader_add_response(smartcard_channel, item); + break; + case PIPE_ITEM_TYPE_MSG: + smartcard_channel_send_msg(smartcard_channel, item); + } +} + + +static void smartcard_channel_release_pipe_item(RedChannel *channel, PipeItem *item, int item_pushed) +{ + free(item); + if (item->type == PIPE_ITEM_TYPE_MSG) { + free(((MsgItem*)item)->vheader); + } +} + +static void smartcard_channel_disconnect(RedChannel *channel) +{ + smartcard_readers_detach_all((SmartCardChannel*)channel); + red_channel_destroy(channel); + g_smartcard_channel = NULL; +} + +/* this is called from both device input and client input. since the device is + * a usb device, the context is still the main thread (kvm_main_loop, timers) + * so no mutex is required. */ +static void smartcard_channel_pipe_add(SmartCardChannel *channel, PipeItem *item) +{ + red_channel_pipe_add(&channel->base, item); +} + +static void smartcard_push_error(SmartCardChannel* channel, reader_id_t reader_id, VSCErrorCode error) +{ + ErrorItem *error_item = spice_new0(ErrorItem, 1); + + error_item->base.type = PIPE_ITEM_TYPE_ERROR; + error_item->reader_id = reader_id; + error_item->error = error; + smartcard_channel_pipe_add(channel, &error_item->base); +} + +static void smartcard_push_reader_add_response(SmartCardChannel *channel, uint32_t reader_id) +{ + ReaderAddResponseItem *rar_item = spice_new0(ReaderAddResponseItem, 1); + + rar_item->base.type = PIPE_ITEM_TYPE_READER_ADD_RESPONSE; + rar_item->reader_id = reader_id; + smartcard_channel_pipe_add(channel, &rar_item->base); +} + +static void smartcard_push_vscmsg(SmartCardChannel *channel, VSCMsgHeader *vheader) +{ + MsgItem *msg_item = spice_new0(MsgItem, 1); + + msg_item->base.type = PIPE_ITEM_TYPE_MSG; + msg_item->vheader = vheader; + smartcard_channel_pipe_add(channel, &msg_item->base); +} + +void smartcard_on_message_from_device(SmartCardChannel *smartcard_channel, + VSCMsgHeader* vheader) +{ + smartcard_push_vscmsg(smartcard_channel, vheader); +} + +static void smartcard_remove_reader(SmartCardChannel *smartcard_channel, reader_id_t reader_id) +{ + SpiceCharDeviceInstance *char_device = smartcard_readers_get(reader_id); + SmartCardDeviceState *state; + + if (char_device == NULL) { + smartcard_push_error(smartcard_channel, reader_id, + VSC_GENERAL_ERROR); + return; + } + + state = SPICE_CONTAINEROF(char_device->st, SmartCardDeviceState, base); + if (state->attached == FALSE) { + smartcard_push_error(smartcard_channel, reader_id, + VSC_GENERAL_ERROR); + return; + } + smartcard_char_device_detach(char_device, smartcard_channel); +} + +static void smartcard_add_reader(SmartCardChannel *smartcard_channel, uint8_t *name) +{ + // TODO - save name somewhere + SpiceCharDeviceInstance *char_device = + smartcard_readers_get_unattached(); + SmartCardDeviceState *state; + + if (char_device != NULL) { + state = SPICE_CONTAINEROF(char_device->st, SmartCardDeviceState, base); + smartcard_char_device_attach(char_device, smartcard_channel); + smartcard_push_reader_add_response(smartcard_channel, state->reader_id); + } else { + smartcard_push_error(smartcard_channel, VSCARD_UNDEFINED_READER_ID, + VSC_CANNOT_ADD_MORE_READERS); + } +} + +static void smartcard_channel_write_to_reader( + SmartCardChannel *smartcard_channel, VSCMsgHeader *vheader) +{ + SpiceCharDeviceInstance *sin; + SpiceCharDeviceInterface *sif; + uint32_t n; + + ASSERT(vheader->reader_id >= 0 && + vheader->reader_id <= g_smartcard_readers.num); + sin = g_smartcard_readers.sin[vheader->reader_id]; + sif = SPICE_CONTAINEROF(sin->base.sif, SpiceCharDeviceInterface, base); + n = sif->write(sin, (uint8_t*)vheader, + vheader->length + sizeof(VSCMsgHeader)); + // TODO - add ring + ASSERT(n == vheader->length + sizeof(VSCMsgHeader)); +} + +static int smartcard_channel_handle_message(RedChannel *channel, SpiceDataHeader *header, uint8_t *msg) +{ + VSCMsgHeader* vheader = (VSCMsgHeader*)msg; + SmartCardChannel* smartcard_channel = (SmartCardChannel*)channel; + + ASSERT(header->size == vheader->length + sizeof(VSCMsgHeader)); + switch (vheader->type) { + case VSC_ReaderAdd: + smartcard_add_reader(smartcard_channel, msg + sizeof(VSCMsgHeader)); + return TRUE; + break; + case VSC_ReaderRemove: + smartcard_remove_reader(smartcard_channel, vheader->reader_id); + return TRUE; + break; + case VSC_ReaderAddResponse: + /* We shouldn't get this - we only send it */ + return TRUE; + break; + case VSC_Init: + case VSC_Error: + case VSC_ATR: + case VSC_CardRemove: + case VSC_APDU: + break; // passed on to device + default: + printf("ERROR: unexpected message on smartcard channel\n"); + return TRUE; + } + + if (vheader->reader_id >= g_smartcard_readers.num) { + red_printf("ERROR: received message for non existent reader: %d, %d, %d", vheader->reader_id, + vheader->type, vheader->length); + return FALSE; + } + smartcard_channel_write_to_reader(smartcard_channel, vheader); + return TRUE; +} + +static void smartcard_link(Channel *channel, RedsStreamContext *peer, + int migration, int num_common_caps, + uint32_t *common_caps, int num_caps, + uint32_t *caps) +{ + if (g_smartcard_channel) { + red_channel_destroy(&g_smartcard_channel->base); + } + g_smartcard_channel = + (SmartCardChannel *)red_channel_create(sizeof(*g_smartcard_channel), + peer, core, + migration, FALSE /* handle_acks */, + smartcard_channel_config_socket, + smartcard_channel_disconnect, + smartcard_channel_handle_message, + smartcard_channel_alloc_msg_rcv_buf, + smartcard_channel_release_msg_rcv_buf, + smartcard_channel_send_item, + smartcard_channel_release_pipe_item); + if (!g_smartcard_channel) { + return; + } + red_channel_init_outgoing_messages_window(&g_smartcard_channel->base); +} + +static void smartcard_shutdown(Channel *channel) +{ +} + +static void smartcard_migrate(Channel *channel) +{ +} + +void smartcard_channel_init() +{ + Channel *channel; + + channel = spice_new0(Channel, 1); + channel->type = SPICE_CHANNEL_SMARTCARD; + channel->link = smartcard_link; + channel->shutdown = smartcard_shutdown; + channel->migrate = smartcard_migrate; + reds_register_channel(channel); +} + diff --git a/server/smartcard.h b/server/smartcard.h new file mode 100644 index 00000000..790eb878 --- /dev/null +++ b/server/smartcard.h @@ -0,0 +1,18 @@ +#ifndef __SMART_CARD_H__ +#define __SMART_CARD_H__ + +#include "server/spice-experimental.h" + +// Maximal length of APDU +#define APDUBufSize 270 + +/** connect to smartcard interface, used by smartcard channel + * returns -1 if failed, 0 if successfull + */ +int smartcard_device_connect(SpiceCharDeviceInstance *char_device); +void smartcard_device_disconnect(SpiceCharDeviceInstance *char_device); + +void smartcard_channel_init(); + +#endif // __SMART_CARD_H__ + |