[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