[Spice-devel] [xf86-video-qxl v5] Enable smartcard support for XSpice.
Uri Lublin
uril at redhat.com
Mon Dec 15 00:58:14 PST 2014
Hi Jeremy,
If you're sending v6, please consider the comments below.
Most likely can be added later as separate patches.
On 12/11/2014 11:04 PM, Jeremy White wrote:
> This is done by creating a Unix domain socket to which smartcard
> messages are transferred, using the vscard protocol.
>
> A further system library, spiceccid, is used to provide an interface into
> pcsc-lite, specifically the pcsc-lite daemon, so that regular Unix applications
> can access the passed through smartcard information.
>
> Signed-off-by: Jeremy White<jwhite at codeweavers.com>
> ---
> configure.ac | 21 ++
> examples/spiceqxl.xorg.conf.example | 3 +
> src/Makefile.am | 3 +-
> src/qxl.h | 2 +
> src/qxl_driver.c | 14 +-
> src/spiceccid/Makefile.am | 29 ++
> src/spiceccid/spice.pcsc.conf.template | 7 +
> src/spiceccid/spiceccid.c | 455 ++++++++++++++++++++++++++++++++
> src/spiceqxl_smartcard.c | 193 ++++++++++++++
> src/spiceqxl_smartcard.h | 31 +++
> 10 files changed, 756 insertions(+), 2 deletions(-)
> create mode 100644 src/spiceccid/Makefile.am
> create mode 100644 src/spiceccid/spice.pcsc.conf.template
> create mode 100644 src/spiceccid/spiceccid.c
> create mode 100644 src/spiceqxl_smartcard.c
> create mode 100644 src/spiceqxl_smartcard.h
>
> diff --git a/configure.ac b/configure.ac
> index 14e0597..d9da852 100644
> --- a/configure.ac
> +++ b/configure.ac
> @@ -137,8 +137,27 @@ if test "x$enable_xspice" = "xyes"; then
> else
> enable_xspice=no
> fi
> +
> +AC_ARG_ENABLE([ccid],
> + [AS_HELP_STRING([--enable-ccid],
> + [Build the spiceccid SmartCard driver (default is no)])],
> + [enable_ccid=$enableval],
> + [enable_ccid=no])
> +AC_ARG_WITH(ccid-module-dir,
> + [AS_HELP_STRING([--with-ccid-module-dir=DIR ],
> + [Specify the install path for spiceccid driver (default is $libdir/pcsc/drivers/serial)])],
> + [ cciddir="$withval" ],
> + [ cciddir="$libdir/pcsc/drivers/serial" ])
> +AC_SUBST(cciddir)
ccid requires xspice (rephrase error message at will):
+if test "x$enable_ccid" != "xno" && test "x$enable_xspice" = "xno"; then
+ AC_MSG_ERROR([Building with ccid requires xspice, but xspice is not
enabled])
+fi
> +if test "x$enable_ccid" != "xno"; then
> + PKG_CHECK_MODULES(LIBPCSCLITE, [libpcsclite])
> + PKG_CHECK_MODULES(LIBCACARD, [libcacard])
> +fi
> +
> +
> AM_CONDITIONAL(BUILD_XSPICE, test "x$enable_xspice" = "xyes")
> AM_CONDITIONAL(BUILD_QXL, test "x$enable_qxl" = "xyes")
> +AM_CONDITIONAL(BUILD_SPICECCID, test "x$enable_ccid" = "xyes")
>
> AC_ARG_ENABLE([udev],
> AS_HELP_STRING([--disable-udev], [Disable libudev support [default=auto]]),
> @@ -168,6 +187,7 @@ fi
> AC_CONFIG_FILES([
> Makefile
> src/Makefile
> + src/spiceccid/Makefile
> src/uxa/Makefile
> scripts/Makefile
> examples/Makefile
> @@ -187,4 +207,5 @@ echo "
> KMS: ${DRM_MODE}
> Build qxl: ${enable_qxl}
> Build xspice: ${enable_xspice}
> + Build spiceccid: ${enable_ccid}
> "
> diff --git a/examples/spiceqxl.xorg.conf.example b/examples/spiceqxl.xorg.conf.example
> index 597a5bd..d15f7f2 100644
> --- a/examples/spiceqxl.xorg.conf.example
> +++ b/examples/spiceqxl.xorg.conf.example
> @@ -143,6 +143,9 @@ Section "Device"
> # to the client. Default is no mixing.
> #Option "SpicePlaybackFIFODir" "/tmp/"
>
> + # A unix domain name for a unix domain socket
> + # to communicate with a spiceccid smartcard driver
> + #Option "SpiceSmartCardFile" "/tmp/spice.pcsc.comm"
> EndSection
>
> Section "InputDevice"
> diff --git a/src/Makefile.am b/src/Makefile.am
> index bf50ae1..6c72bbd 100644
> --- a/src/Makefile.am
> +++ b/src/Makefile.am
> @@ -25,7 +25,7 @@
> # _ladir passes a dummy rpath to libtool so the thing will actually link
> # TODO: -nostdlib/-Bstatic/-lgcc platform magic, not installing the .a, etc.
>
> -SUBDIRS=uxa
> +SUBDIRS=uxa spiceccid
>
> AM_CFLAGS = $(SPICE_PROTOCOL_CFLAGS) $(XORG_CFLAGS) $(PCIACCESS_CFLAGS) $(CWARNFLAGS) $(DRM_CFLAGS) @LIBUDEV_CFLAGS@
>
> @@ -96,6 +96,7 @@ spiceqxl_drv_la_SOURCES = \
> spiceqxl_uinput.c \
> spiceqxl_uinput.h \
> spiceqxl_audio.c \
> + spiceqxl_smartcard.c \
> spiceqxl_audio.h \
> spiceqxl_inputs.c \
> spiceqxl_inputs.h \
> diff --git a/src/qxl.h b/src/qxl.h
> index 603faca..54995cf 100644
> --- a/src/qxl.h
> +++ b/src/qxl.h
> @@ -157,6 +157,7 @@ enum {
> OPTION_FRAME_BUFFER_SIZE,
> OPTION_SURFACE_BUFFER_SIZE,
> OPTION_COMMAND_BUFFER_SIZE,
> + OPTION_SPICE_SMARTCARD_FILE,
> #endif
> OPTION_COUNT,
> };
> @@ -352,6 +353,7 @@ struct _qxl_screen_t
>
> char playback_fifo_dir[PATH_MAX];
> void *playback_opaque;
> + char smartcard_file[PATH_MAX];
> #endif /* XSPICE */
>
> uint32_t deferred_fps;
> diff --git a/src/qxl_driver.c b/src/qxl_driver.c
> index 165f468..9ad8921 100644
> --- a/src/qxl_driver.c
> +++ b/src/qxl_driver.c
> @@ -55,6 +55,7 @@
> #include "spiceqxl_io_port.h"
> #include "spiceqxl_spice_server.h"
> #include "spiceqxl_audio.h"
> +#include "spiceqxl_smartcard.h"
> #include "spiceqxl_vdagent.h"
> #endif /* XSPICE */
>
> @@ -152,8 +153,10 @@ const OptionInfoRec DefaultOptions[] =
> "SurfaceBufferSize", OPTV_INTEGER, {DEFAULT_SURFACE_BUFFER_SIZE}, FALSE},
> { OPTION_COMMAND_BUFFER_SIZE,
> "CommandBufferSize", OPTV_INTEGER, {DEFAULT_COMMAND_BUFFER_SIZE}, FALSE},
> + { OPTION_SPICE_SMARTCARD_FILE,
> + "SpiceSmartcardFile", OPTV_STRING, {0}, FALSE},
> #endif
> -
> +
> { -1, NULL, OPTV_NONE, {0}, FALSE }
> };
>
> @@ -659,6 +662,7 @@ spiceqxl_screen_init (ScrnInfoPtr pScrn, qxl_screen_t *qxl)
> }
> qxl_add_spice_display_interface (qxl);
> qxl_add_spice_playback_interface (qxl);
> + qxl_add_spice_smartcard_interface (qxl);
> spiceqxl_vdagent_init (qxl);
> }
> else
> @@ -1034,6 +1038,7 @@ qxl_pre_init (ScrnInfoPtr pScrn, int flags)
> unsigned int max_x, max_y;
> #ifdef XSPICE
> const char *playback_fifo_dir;
> + const char *smartcard_file;
> #endif
>
> /* In X server 1.7.5, Xorg -configure will cause this
> @@ -1089,6 +1094,13 @@ qxl_pre_init (ScrnInfoPtr pScrn, int flags)
> else
> qxl->playback_fifo_dir[0] = '\0';
>
> + smartcard_file = get_str_option(qxl->options, OPTION_SPICE_SMARTCARD_FILE,
> + "XSPICE_SMARTCARD_FILE");
> + if (smartcard_file)
> + strncpy(qxl->smartcard_file, smartcard_file, sizeof(qxl->smartcard_file));
> + else
> + qxl->smartcard_file[0] = '\0';
> +
> qxl->surface0_size =
> get_int_option (qxl->options, OPTION_FRAME_BUFFER_SIZE, "QXL_FRAME_BUFFER_SIZE") << 20L;
> qxl->vram_size =
> diff --git a/src/spiceccid/Makefile.am b/src/spiceccid/Makefile.am
> new file mode 100644
> index 0000000..437e992
> --- /dev/null
> +++ b/src/spiceccid/Makefile.am
> @@ -0,0 +1,29 @@
> +# Copyright 2014 Jeremy White for CodeWeavers, Inc.
> +#
> +# Permission is hereby granted, free of charge, to any person obtaining a
> +# copy of this software and associated documentation files (the "Software"),
> +# to deal in the Software without restriction, including without limitation
> +# on the rights to use, copy, modify, merge, publish, distribute, sub
> +# license, and/or sell copies of the Software, and to permit persons to whom
> +# the Software is furnished to do so, subject to the following conditions:
> +#
> +# The above copyright notice and this permission notice (including the next
> +# paragraph) shall be included in all copies or substantial portions of the
> +# Software.
> +#
> +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
> +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
> +# FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL
> +# THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
> +# IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
> +# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
> +
> +
> +AM_CFLAGS = $(LIBPCSCLITE_CFLAGS)
> +
> +if BUILD_SPICECCID
> +libspiceccid_la_LTLIBRARIES = libspiceccid.la
> +libspiceccid_la_LDFLAGS = $(LIBPCSCLITE_LDFLAGS)
> +libspiceccid_la_SOURCES = spiceccid.c
> +libspiceccid_ladir = @cciddir@/
> +endif
> diff --git a/src/spiceccid/spice.pcsc.conf.template b/src/spiceccid/spice.pcsc.conf.template
> new file mode 100644
> index 0000000..345cdf5
> --- /dev/null
> +++ b/src/spiceccid/spice.pcsc.conf.template
> @@ -0,0 +1,7 @@
> +# Spice CCID Reader
> +# This configuration file is the format required by the pcscd deamon for
> +# serial devices; so the qxl driver looks like a serial device to pcscd.
> +FRIENDLYNAME "Spice ccid"
> +DEVICENAME /tmp/spice.pcsc.comm
> +LIBPATH /usr/lib/pcsc/drivers/serial/libspiceccid.so
> +CHANNELID 1
> diff --git a/src/spiceccid/spiceccid.c b/src/spiceccid/spiceccid.c
> new file mode 100644
> index 0000000..ef9c931
> --- /dev/null
> +++ b/src/spiceccid/spiceccid.c
> @@ -0,0 +1,455 @@
> +/*
> + * Copyright (C) 2014 CodeWeavers, Inc.
> + *
> + * Permission is hereby granted, free of charge, to any person obtaining a
> + * copy of this software and associated documentation files (the "Software"),
> + * to deal in the Software without restriction, including without limitation
> + * the rights to use, copy, modify, merge, publish, distribute, sublicense,
> + * and/or sell copies of the Software, and to permit persons to whom the
> + * Software is furnished to do so, subject to the following conditions:
> + *
> + * The above copyright notice and this permission notice (including the next
> + * paragraph) shall be included in all copies or substantial portions of the
> + * Software.
> + *
> + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
> + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
> + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
> + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
> + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
> + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
> + * SOFTWARE.
> + *
> + * Authors:
> + * Jeremy White<jwhite at codeweavers.com>
> + */
> +
> +/*----------------------------------------------------------------------------
> + Chip/Smart Card Interface Devices driver for Spice
> +
> + This driver is built to interface to pcsc-lite as a serial smartcard
> + device.
> + It translates the IFD (Interface device) ABI into the Spice protocol.
> +----------------------------------------------------------------------------*/
> +
> +#include <stdio.h>
> +#include <string.h>
> +#include <stdlib.h>
> +#include <sys/types.h>
> +#include <sys/stat.h>
> +#include <fcntl.h>
> +#include <unistd.h>
> +#include <pthread.h>
> +#include <errno.h>
> +#include <sys/socket.h>
> +#include <sys/un.h>
> +
> +#include "cacard/vscard_common.h"
> +#include "ifdhandler.h"
> +#include <arpa/inet.h>
> +
> +typedef struct apdu_list {
> + void *data;
> + int len;
> + struct apdu_list *next;
> +} apdu_t;
> +
> +#define MAX_LUNS 2
> +typedef struct smartcard_ccid {
> + int fd;
> + int lun;
> + pthread_t tid;
> + int state;
> + char atr[36];
> + int atr_len;
> + pthread_mutex_t apdu_lock;
> + apdu_t *apdu_list;
> +} smartcard_ccid_t;
> +
> +#define STATE_OPEN 1
> +#define STATE_READER_ADDED 2
> +#define STATE_READER_REMOVED 4
> +
> +#if ! defined(MIN)
> +#define MIN(x, y) (((x) < (y)) ? (x) : (y))
> +#endif
> +
> +
> +smartcard_ccid_t luns[MAX_LUNS] = { { -1 }, { -1 } };
> +
> +RESPONSECODE IFDHCloseChannel(DWORD Lun);
> +
> +static void push_apdu(smartcard_ccid_t *ccid, void *data, int len)
> +{
> + apdu_t *a = malloc(sizeof(*a));
> + apdu_t **p;
> +
> + a->data = malloc(len);
> + a->len = len;
> + a->next = NULL;
> + memcpy(a->data, data, len);
> +
> + pthread_mutex_lock(&ccid->apdu_lock);
> + for (p = &ccid->apdu_list; *p; p = &(*p)->next)
> + ;
> + *p = a;
> +
> + pthread_mutex_unlock(&ccid->apdu_lock);
> +}
> +
> +static apdu_t * pop_apdu(smartcard_ccid_t *ccid)
> +{
> + apdu_t *p;
> + pthread_mutex_lock(&ccid->apdu_lock);
> + p = ccid->apdu_list;
> + if (ccid->apdu_list)
> + ccid->apdu_list = p->next;
> + pthread_mutex_unlock(&ccid->apdu_lock);
> + return p;
> +}
> +
> +static void free_apdu(apdu_t *a)
> +{
> + free(a->data);
> + free(a);
> +}
> +
> +static void send_reply(smartcard_ccid_t *ccid, uint32_t code)
> +{
> + uint32_t reply[4];
> +
> + reply[0] = htonl(VSC_Error); // type
> + reply[1] = htonl(ccid->lun); // reader id
> + reply[2] = htonl(sizeof(uint32_t)); // length
> + reply[3] = htonl(code); // Error code
> +
> + if (write(ccid->fd, (char *) reply, sizeof(reply)) != sizeof(reply)) {
> + fprintf(stderr, "Error: lun %d fd %d write failed; errno %d\n", ccid->lun, ccid->fd, errno);
> + IFDHCloseChannel(ccid->lun);
> + }
> +}
> +
> +static int send_tx_buffer(smartcard_ccid_t *ccid, void *data, int len)
> +{
> + uint32_t *reply, *p;
> + int write_len = sizeof(*reply) * 3 + len;
> +
> + reply = malloc(write_len);
> + p = reply;
> +
> + *p++ = htonl(VSC_APDU); // type
> + *p++ = htonl(ccid->lun); // reader id
> + *p++ = htonl(len);
> + memcpy(p, data, len);
> +
> + if (write(ccid->fd, (char *) reply, write_len) != write_len) {
> + fprintf(stderr, "Error: lun %d fd %d write failed; errno %d\n", ccid->lun, ccid->fd, errno);
> + IFDHCloseChannel(ccid->lun);
> + free(reply);
> + return 0;
> + }
> + free(reply);
> + return 1;
> +}
> +
> +static void process_reader_add(smartcard_ccid_t *ccid, VSCMsgHeader *h, char *data)
> +{
> + ccid->state |= STATE_READER_ADDED;
ccid->state &= ~STATE_READER_REMOVED;
> +
> + pthread_mutex_init(&ccid->apdu_lock, NULL);
> + ccid->apdu_list = NULL;
> +
> + send_reply(ccid, VSC_SUCCESS);
> +}
> +
> +static void process_reader_remove(smartcard_ccid_t *ccid, VSCMsgHeader *h)
> +{
> + apdu_t *p;
> +
> + ccid->state |= STATE_READER_REMOVED;
> + ccid->state &= ~STATE_READER_ADDED;
> +
> + while (p = pop_apdu(ccid))
> + free_apdu(p);
> +
> + pthread_mutex_destroy(&ccid->apdu_lock);
> +
> + send_reply(ccid, VSC_SUCCESS);
> +}
> +
> +static void process_atr(smartcard_ccid_t *ccid, VSCMsgHeader *h, char *data)
> +{
> + ccid->atr_len = MIN(h->length, sizeof(ccid->atr));
> +
> + memset(ccid->atr, 0, sizeof(ccid->atr));
> + memcpy(ccid->atr, data, ccid->atr_len);
> +
> + send_reply(ccid, VSC_SUCCESS);
> +}
> +
> +static void process_apdu(smartcard_ccid_t *ccid, VSCMsgHeader *h, char *data)
> +{
> + push_apdu(ccid, data, h->length);
> +}
> +
> +static void process_card_remove(smartcard_ccid_t *ccid, VSCMsgHeader *h)
> +{
> + ccid->atr_len = 0;
> + memset(ccid->atr, 0, sizeof(ccid->atr));
> + send_reply(ccid, VSC_SUCCESS);
Trying to understand the flow:
Is there a way (API) to notify pcsc-lite ?
Does it get to know about this in the next IFDHTransmitToICC, where
it tries to send as if nothing happened and receive an error code ?
> +}
> +
> +static int process_message(smartcard_ccid_t *ccid, char *buf, int len)
> +{
> + VSCMsgHeader h;
> + uint32_t *p = (uint32_t *) buf;
> +
> + h.type = ntohl(*p++);
> + h.reader_id = ntohl(*p++);
> + h.length = ntohl(*p++);
> +
> + if (len < sizeof(h) || len < sizeof(h) + h.length)
> + return 0;
> +
> + switch (h.type) {
> + case VSC_ReaderAdd:
> + process_reader_add(ccid, &h, h.length > 0 ? buf + sizeof(h) : NULL);
> + break;
> +
> + case VSC_ReaderRemove:
> + process_reader_remove(ccid, &h);
> + break;
> +
> + case VSC_ATR:
> + process_atr(ccid, &h, h.length > 0 ? buf + sizeof(h) : NULL);
> + break;
> +
> + case VSC_CardRemove:
> + process_card_remove(ccid, &h);
> + break;
> +
> + case VSC_APDU:
> + process_apdu(ccid, &h, h.length > 0 ? buf + sizeof(h) : NULL);
> + break;
> +
> + default:
> + fprintf(stderr, "spiceccid %s: unknown smartcard message %d / %d\n", __FUNCTION__, h.type, sizeof(h) + h.length);
> +
> + }
> +
> + return(MIN(len, h.length + sizeof(h)));
Nitpick: Note that for sure len >= sizeof(h) + h.length as it's checked
at the beginning of the function,
So it should be safe to return h.length + sizeof(h) (as long as that
check stays in the function).
Possibly the compiler optimization takes care of this.
> +}
> +
> +static void * lun_thread(void *arg)
> +{
> + char buf[8096];
> + int pos = 0;
> + smartcard_ccid_t *ccid = (smartcard_ccid_t *) arg;
> + int rc;
> +
> + while (1) {
> + rc = read(ccid->fd, buf + pos, sizeof(buf) - pos);
> + if (rc == -1)
> + if (errno == EINTR)
> + continue;
> + else
> + break;
> +
> + if (rc == 0)
> + break;
> +
> + pos += rc;
> +
> + do {
> + rc = process_message(ccid, buf, pos);
> + pos -= rc;
> + } while (rc > 0);
> + }
> +
> + return NULL;
> +}
> +
> +
> +static void send_init(smartcard_ccid_t *ccid)
> +{
> + uint32_t msg[6];
> +
> + msg[0] = htonl(VSC_Init); // type
> + msg[1] = htonl(ccid->lun); // reader id
> + msg[2] = htonl(sizeof(uint32_t) * 3); // length
> + msg[3] = htonl(VSCARD_MAGIC); // VSCD
> + msg[4] = htonl(VSCARD_VERSION); // VSCD
> + msg[5] = 0; // capabilities
> +
> + if (write(ccid->fd, (char *) msg, sizeof(msg)) != sizeof(msg)) {
> + fprintf(stderr, "Error: lun %d fd %d write failed; errno %d\n", ccid->lun, ccid->fd, errno);
> + IFDHCloseChannel(ccid->lun);
> + }
> +}
> +
> +/*----------------------------------------------------------------------------
> + IFDHCreateChannelByName
> + The pcsc daemon should invoke this function passing in the path name
> + configured in reader.conf.
> +*/
> +RESPONSECODE IFDHCreateChannelByName(DWORD Lun, LPSTR DeviceName)
> +{
> + int i;
> + struct sockaddr_un addr;
> +
> + for (i = 0; i < MAX_LUNS; i++)
> + if (luns[i].fd != -1 && luns[i].lun == Lun)
> + return IFD_COMMUNICATION_ERROR;
> +
> + for (i = 0; i < MAX_LUNS; i++)
> + if (luns[i].fd == -1)
> + break;
> +
> + if (i >= MAX_LUNS)
> + return IFD_COMMUNICATION_ERROR;
> +
> + luns[i].fd = socket(AF_UNIX, SOCK_STREAM, 0);
> + if (luns[i].fd < 0)
> + return IFD_NO_SUCH_DEVICE;
> +
> + memset(&addr, 0, sizeof(addr));
> + addr.sun_family = AF_UNIX;
> + strncpy(addr.sun_path, DeviceName, sizeof(addr.sun_path) - 1);
> + if (connect(luns[i].fd, (struct sockaddr *) &addr, sizeof(addr))) {
> + close(luns[i].fd);
> + return IFD_COMMUNICATION_ERROR;
> + }
> +
> + if (pthread_create(&luns[i].tid, NULL, &lun_thread, &luns[i])) {
> + close(luns[i].fd);
> + return IFD_COMMUNICATION_ERROR;
> + }
> +
> + luns[i].lun = Lun;
> + luns[i].state = STATE_OPEN;
> +
> + return IFD_SUCCESS;
> +}
> +
> +RESPONSECODE IFDHCreateChannel(DWORD Lun, DWORD Channel)
> +{
> + fprintf(stderr, "spiceccid %s unsupported: Lun %ld, Channel %ld\n", __FUNCTION__, Lun, Channel);
> + return IFD_ERROR_NOT_SUPPORTED;
> +}
> +
> +RESPONSECODE IFDHCloseChannel(DWORD Lun)
> +{
> + int i;
> +
> + for (i = 0; i < MAX_LUNS; i++) {
> + if (luns[i].fd != -1 && luns[i].lun == Lun) {
> + pthread_cancel(luns[i].tid);
> + close(luns[i].fd);
> + luns[i].fd = -1;
> + luns[i].lun = 0;
> + luns[i].atr_len = 0;
luns[i].state &= ~STATE_READER_OPEN;
> + break;
> + }
> + }
> +
> + if (i == MAX_LUNS)
> + return IFD_NO_SUCH_DEVICE;
> +
> + return IFD_SUCCESS;
> +}
> +
> +RESPONSECODE IFDHGetCapabilities(DWORD Lun, DWORD Tag, PDWORD Length, PUCHAR Value)
> +{
> + fprintf(stderr, "spiceccid %s unsupported: Lun %ld, Tag %ld, Length %ld, Value %p\n", __FUNCTION__, Lun, Tag, *Length, Value);
> + /* TODO - explore supporting TAG_IFD_POLLING_THREAD */
> + return IFD_ERROR_NOT_SUPPORTED;
> +}
> +
> +RESPONSECODE IFDHSetCapabilities(DWORD Lun, DWORD Tag, DWORD Length, PUCHAR Value)
> +{
> + return IFD_ERROR_NOT_SUPPORTED;
> +}
> +
> +RESPONSECODE IFDHPowerICC(DWORD Lun, DWORD Action, PUCHAR Atr, PDWORD AtrLength)
> +{
> + int i;
> +
> + for (i = 0; i < MAX_LUNS; i++)
> + if (luns[i].fd != -1 && luns[i].lun == Lun)
> + if (Action == IFD_POWER_UP) {
> + if (*AtrLength >= luns[i].atr_len) {
> + memcpy(Atr, luns[i].atr, luns[i].atr_len);
> + *AtrLength = luns[i].atr_len;
> + }
> + send_init(&luns[i]);
> + return IFD_SUCCESS;
> + }
> +
> + return IFD_ERROR_NOT_SUPPORTED;
> +}
> +
> +#define TX_MAX_SLEEP 5000
> +#define TX_SLEEP_INTERVAL 1000
> +RESPONSECODE IFDHTransmitToICC(DWORD Lun, SCARD_IO_HEADER SendPci,
> + PUCHAR TxBuffer, DWORD TxLength, PUCHAR RxBuffer, PDWORD
> + RxLength, PSCARD_IO_HEADER RecvPci)
> +{
> + apdu_t *p;
> + int i, j;
> +
> + for (i = 0; i < MAX_LUNS; i++)
> + if (luns[i].fd != -1 && luns[i].lun == Lun) {
> + while (p = pop_apdu(&luns[i]))
> + free_apdu(p);
> +
> + if (send_tx_buffer(&luns[i], TxBuffer, TxLength)) {
> + for (j = 0; j < TX_MAX_SLEEP; j++)
> + if (p = pop_apdu(&luns[i]))
> + break;
> + else
> + usleep(TX_SLEEP_INTERVAL);
> +
> + if (p) {
> + memcpy(RxBuffer, p->data, MIN(p->len, *RxLength));
> + *RxLength = MIN(p->len, *RxLength);
> + free_apdu(p);
> + return IFD_SUCCESS;
> + }
> +
> + return IFD_RESPONSE_TIMEOUT;
> + }
> + }
> + return IFD_NO_SUCH_DEVICE;
> +}
> +
> +RESPONSECODE IFDHICCPresence(DWORD Lun)
> +{
> + int i;
> +
> + for (i = 0; i < MAX_LUNS; i++)
> + if (luns[i].fd != -1 && luns[i].lun == Lun) {
> + if (luns[i].atr_len > 0 && luns[i].state & STATE_READER_ADDED)
> + return IFD_SUCCESS;
> +
> + return IFD_ICC_NOT_PRESENT;
> + }
> +
> + return IFD_NO_SUCH_DEVICE;
> +}
> +
> +RESPONSECODE IFDHSetProtocolParameters(DWORD Lun, DWORD Protocol, UCHAR Flags,
> + UCHAR PTS1, UCHAR PTS2, UCHAR PTS3)
> +{
> + if (Protocol == SCARD_PROTOCOL_T1)
> + return IFD_SUCCESS;
> +
> + return IFD_NOT_SUPPORTED;
> +}
> +
> +RESPONSECODE IFDHControl(DWORD Lun, DWORD dwControlCode, PUCHAR
> + TxBuffer, DWORD TxLength, PUCHAR RxBuffer, DWORD RxLength,
> + LPDWORD pdwBytesReturned)
> +{
> + fprintf(stderr, "spiceccid %s unsupported: Lun %ld\n", __FUNCTION__, Lun);
> + return IFD_ERROR_NOT_SUPPORTED;
> +}
> diff --git a/src/spiceqxl_smartcard.c b/src/spiceqxl_smartcard.c
> new file mode 100644
> index 0000000..f2ec2e2
> --- /dev/null
> +++ b/src/spiceqxl_smartcard.c
> @@ -0,0 +1,193 @@
> +/*
> + * Copyright 2014 Jeremy White for CodeWeavers Inc.
> + *
> + * Permission is hereby granted, free of charge, to any person obtaining a
> + * copy of this software and associated documentation files (the "Software"),
> + * to deal in the Software without restriction, including without limitation
> + * on the rights to use, copy, modify, merge, publish, distribute, sub
> + * license, and/or sell copies of the Software, and to permit persons to whom
> + * the Software is furnished to do so, subject to the following conditions:
> + *
> + * The above copyright notice and this permission notice (including the next
> + * paragraph) shall be included in all copies or substantial portions of the
> + * Software.
> + *
> + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
> + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
> + * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL
> + * THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
> + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
> + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
> + */
> +
> +#ifdef HAVE_CONFIG_H
> +#include "config.h"
> +#endif
> +
> +#include <errno.h>
> +#include <sys/types.h>
> +#include <sys/stat.h>
> +#include <fcntl.h>
> +#include <unistd.h>
> +#include <sys/socket.h>
> +#include <sys/un.h>
> +
> +
> +#include "spiceqxl_smartcard.h"
> +
> +typedef struct XSpiceSmartcardCharDeviceInstance {
> + SpiceCharDeviceInstance base;
> + qxl_screen_t *qxl;
> + int listen_fd;
> + int fd;
> + SpiceWatch *listen_watch;
> + SpiceWatch *watch;
> +} XSpiceSmartcardCharDeviceInstance;
> +
> +XSpiceSmartcardCharDeviceInstance smartcard_sin = {
> + .base = {
> + .subtype = "smartcard"
> + }
> +};
> +
> +static int smartcard_write(SpiceCharDeviceInstance *sin, const uint8_t *buf, int len)
> +{
> + int written;
> +
> + if (smartcard_sin.fd == -1)
> + return 0;
> +
> + written = write(smartcard_sin.fd, buf, len);
> + if (written != len)
> + ErrorF("%s: ERROR: short write to smartcard socket - TODO buffering\n", __FUNCTION__);
> +
> + return written;
> +}
> +
> +static int smartcard_read(SpiceCharDeviceInstance *sin, uint8_t *buf, int len)
> +{
> + int rc;
> +
> + if (smartcard_sin.fd == -1)
> + return 0;
> +
Maybe retry immediately for EINTR:
do {
rc = read()
} while (! ((rc == -1) && (errno == EINTR)))
and remove "|| errno == EINTR" below.
Thanks,
Uri
> + rc = read(smartcard_sin.fd, buf, len);
> + if (rc <= 0) {
> + if (errno == EAGAIN || errno == EWOULDBLOCK || errno == EINTR) {
> + return 0;
> + }
> + ErrorF("smartcard socket died: %s\n", strerror(errno));
> +
> + smartcard_sin.qxl->core->watch_remove(smartcard_sin.watch);
> + close(smartcard_sin.fd);
> + smartcard_sin.fd = -1;
> + smartcard_sin.watch = NULL;
> + }
> +
> + return rc;
> +}
> +
> +static void on_read_available(int fd, int event, void *opaque)
> +{
> + spice_server_char_device_wakeup(&smartcard_sin.base);
> +}
> +
> +static void on_accept_available(int fd, int event, void *opaque)
> +{
> + qxl_screen_t *qxl = (qxl_screen_t *) opaque;
> + int flags;
> + int client_fd;
> +
> + client_fd = accept(fd, NULL, NULL);
> + if (client_fd < 0)
> + return;
> +
> + if (smartcard_sin.fd != -1) {
> + ErrorF("smartcard error: a new connection came in while an old one was active.\n");
> + close(client_fd);
> + return;
> + }
> +
> + flags = fcntl(client_fd, F_GETFL, 0);
> + if (flags < 0)
> + flags = 0;
> + flags |= O_NONBLOCK;
> + fcntl(client_fd, F_SETFL, flags);
> +
> + smartcard_sin.fd = client_fd;
> + smartcard_sin.watch = qxl->core->watch_add(smartcard_sin.fd, SPICE_WATCH_EVENT_READ, on_read_available, qxl);
> +
> +}
> +
> +
> +#if SPICE_SERVER_VERSION >= 0x000c02
> +static void smartcard_event(SpiceCharDeviceInstance *sin, uint8_t event)
> +{
> + ErrorF("%s: unimplemented; event is %d\n", __FUNCTION__, event);
> +}
> +#endif
> +
> +static void smartcard_state(SpiceCharDeviceInstance *sin, int connected)
> +{
> + ErrorF("%s: unimplemented; connected is %d\n", __FUNCTION__, connected);
> +}
> +
> +static SpiceCharDeviceInterface smartcard_interface = {
> + .base.type = SPICE_INTERFACE_CHAR_DEVICE,
> + .base.description = "Xspice virtual channel char device",
> + .base.major_version = SPICE_INTERFACE_CHAR_DEVICE_MAJOR,
> + .base.minor_version = SPICE_INTERFACE_CHAR_DEVICE_MINOR,
> + .state = smartcard_state,
> + .write = smartcard_write,
> + .read = smartcard_read,
> +#if SPICE_SERVER_VERSION >= 0x000c02
> + .event = smartcard_event,
> +#endif
> +};
> +
> +int
> +qxl_add_spice_smartcard_interface (qxl_screen_t *qxl)
> +{
> + int rc;
> + struct sockaddr_un addr;
> +
> + if (qxl->smartcard_file[0] == 0) {
> + xf86DrvMsg(qxl->pScrn->scrnIndex, X_INFO, "smartcard: no file given, smartcard is disabled\n");
> + return 0;
> + }
> +
> + smartcard_sin.fd = -1;
> + smartcard_sin.listen_fd = socket(AF_UNIX, SOCK_STREAM, 0);
> + if (smartcard_sin.listen_fd < 0) {
> + ErrorF("smartcard: unable to open socket: %s\n", strerror(errno));
> + return errno;
> + }
> +
> + memset(&addr, 0, sizeof(addr));
> + addr.sun_family = AF_UNIX;
> + strncpy(addr.sun_path, qxl->smartcard_file, sizeof(addr.sun_path) - 1);
> + unlink(qxl->smartcard_file);
> +
> + if (bind(smartcard_sin.listen_fd, (struct sockaddr *) &addr, sizeof(addr))) {
> + ErrorF("smartcard: unable to bind to unix domain %s: %s\n", qxl->smartcard_file, strerror(errno));
> + close(smartcard_sin.listen_fd);
> + return errno;
> + }
> +
> + if (listen(smartcard_sin.listen_fd, 1)) {
> + ErrorF("smartcard: unable to listen to unix domain %s: %s\n", qxl->smartcard_file, strerror(errno));
> + close(smartcard_sin.listen_fd);
> + return errno;
> + }
> +
> + smartcard_sin.listen_watch = qxl->core->watch_add(smartcard_sin.listen_fd, SPICE_WATCH_EVENT_READ, on_accept_available, qxl);
> +
> + smartcard_sin.base.base.sif = &smartcard_interface.base;
> + smartcard_sin.qxl = qxl;
> +
> + rc = spice_server_add_interface(qxl->spice_server, &smartcard_sin.base.base);
> + if (rc < 0)
> + return errno;
> +
> + return 0;
> +}
More information about the Spice-devel
mailing list