Re: RFC - Direct smart card support in libcacard/spice-gtk

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



After some discussion on irc, I decided to explore expanding libcacard to
support sending the apdus directly to the card, using the PC/SC (aka
pcsclite) library.

I've attached a proof of concept set of patches - one for the client, and
the substantial one for qemu/libcacard.

The basic approach is to add a parallel to the vcard_emul_nss.c stack, where
we add and remove readers and cards in response to pcsclite events.

Note that I don't consider the code submission quality; it has quite a few
rough edges.  It does work for me in some limited test cases, though, and it
I think largely illustrates my proposed path.

I am hoping to ask:

   1.  Does this basic approach seem reasonable?

I think VCardEmulType VCARD_EMUL_PASSTHRU was supposed to be used for
this case, although the current code doesn't make it straightforward
to add that, as it initializes nss in vcard_emul_init. It should be
possible to change that though.

It probably doesn't make much sense to push this in libcacard if you
don't reuse any of the cacard framework.

Hi,

I've spun up v2 of this discussion patch set. I struggled for a while; I made an attempt to rename the vcard_emul_init entry point, and in the end, I felt that it was a pointless ABI change, just to satisfy my need to feel that the name made sense. (I'm bothered that I call an xxx_emul_xxx function to use a *non* emulated device).

Once I got over that, the change actually became fairly atomic, and seemed to connect up reasonably well.

Attached is a v2 that seems like a good approach to me. Again, I may be being myopic. I did remove the -p (passthru) option from vscclient; you can achieve that result with '-e "use_hw=yes hw_type=passthru"', so it seemed redundant to me.

Feedback appreciated. If it doesn't seem offensive, I will likely polish it a bit and submit it properly.

Cheers,

Jeremy
>From 0c1af9a01de00604556f58ca3cbf7d9ca0a30aea Mon Sep 17 00:00:00 2001
From: Jeremy White <jwhite@xxxxxxxxxxxxxxx>
Date: Tue, 23 Dec 2014 13:09:12 -0600
Subject: [PATCH] Implement a proof of concept of a direct smartcard mode.


Signed-off-by: Jeremy White <jwhite@xxxxxxxxxxxxxxx>
---
 gtk/smartcard-manager.c |   15 +++++++++++++++
 gtk/spice-option.c      |    7 +++++++
 gtk/spice-session.c     |   27 +++++++++++++++++++++++++++
 man/spice-client.pod    |    6 +++++-
 4 files changed, 54 insertions(+), 1 deletion(-)

diff --git a/gtk/smartcard-manager.c b/gtk/smartcard-manager.c
index 9e228e9..d2c3740 100644
--- a/gtk/smartcard-manager.c
+++ b/gtk/smartcard-manager.c
@@ -416,6 +416,7 @@ static gboolean smartcard_manager_init(SmartcardManagerInitArgs *args)
     gchar *dbname = NULL;
     GStrv certificates = NULL;
     gboolean retval = FALSE;
+    gboolean direct_smartcard = FALSE;
 
     SPICE_DEBUG("smartcard_manager_init");
     g_return_val_if_fail(SPICE_IS_SESSION(args->session), FALSE);
@@ -424,6 +425,20 @@ static gboolean smartcard_manager_init(SmartcardManagerInitArgs *args)
                  "smartcard-certificates", &certificates,
                  NULL);
 
+    g_object_get(G_OBJECT(args->session), "direct-smartcard", &direct_smartcard, NULL);
+
+    if (direct_smartcard) {
+        emul_args = g_strdup_printf("use_hw=yes hw_type=passthru");
+        options = vcard_emul_options(emul_args);
+        if (options == NULL) {
+            args->err = g_error_new(SPICE_CLIENT_ERROR,
+                                    SPICE_CLIENT_ERROR_FAILED,
+                                    "vcard_emul_options() for direct_smartcard failed!");
+            goto end;
+        }
+        goto init;
+    }
+
     if ((certificates == NULL) || (g_strv_length(certificates) != 3))
         goto init;
 
diff --git a/gtk/spice-option.c b/gtk/spice-option.c
index 958e03c..72a7112 100644
--- a/gtk/spice-option.c
+++ b/gtk/spice-option.c
@@ -35,6 +35,7 @@ static char *smartcard_certificates = NULL;
 static char *usbredir_auto_redirect_filter = NULL;
 static char *usbredir_redirect_on_connect = NULL;
 static gboolean smartcard = FALSE;
+static gboolean direct_smartcard = FALSE;
 static gboolean disable_audio = FALSE;
 static gboolean disable_usbredir = FALSE;
 static gint cache_size = 0;
@@ -175,6 +176,8 @@ GOptionGroup* spice_get_option_group(void)
           N_("Disable audio support"), NULL },
         { "spice-smartcard", '\0', 0, G_OPTION_ARG_NONE, &smartcard,
           N_("Enable smartcard support"), NULL },
+        { "spice-direct-smartcard", '\0', 0, G_OPTION_ARG_NONE, &direct_smartcard,
+          N_("Enable direct PC/SC card passthru support"), NULL },
         { "spice-smartcard-certificates", '\0', 0, G_OPTION_ARG_STRING, &smartcard_certificates,
           N_("Certificates to use for software smartcards (field=values separated by commas)"), N_("<certificates>") },
         { "spice-smartcard-db", '\0', 0, G_OPTION_ARG_STRING, &smartcard_db,
@@ -247,6 +250,10 @@ void spice_set_session_option(SpiceSession *session)
         g_object_set(session, "ca-file", ca_file, NULL);
     if (host_subject)
         g_object_set(session, "cert-subject", host_subject, NULL);
+    if (direct_smartcard) {
+        g_object_set(session, "direct-smartcard", direct_smartcard, NULL);
+        smartcard = TRUE;
+    }
     if (smartcard) {
         g_object_set(session, "enable-smartcard", smartcard, NULL);
         if (smartcard_certificates) {
diff --git a/gtk/spice-session.c b/gtk/spice-session.c
index 7971f3c..e23c20b 100644
--- a/gtk/spice-session.c
+++ b/gtk/spice-session.c
@@ -65,6 +65,9 @@ struct _SpiceSessionPrivate {
     /* whether to enable smartcard event forwarding to the server */
     gboolean          smartcard;
 
+    /* whether to directly go to the smartcard, or to use libnss */
+    gboolean          direct_smartcard;
+
     /* list of certificates to use for the software smartcard reader if
      * enabled. For now, it has to contain exactly 3 certificates for
      * the software reader to be functional
@@ -182,6 +185,7 @@ enum {
     PROP_MIGRATION_STATE,
     PROP_AUDIO,
     PROP_SMARTCARD,
+    PROP_DIRECT_SMARTCARD,
     PROP_SMARTCARD_CERTIFICATES,
     PROP_SMARTCARD_DB,
     PROP_USBREDIR,
@@ -570,6 +574,9 @@ static void spice_session_get_property(GObject    *gobject,
     case PROP_SMARTCARD:
         g_value_set_boolean(value, s->smartcard);
         break;
+    case PROP_DIRECT_SMARTCARD:
+        g_value_set_boolean(value, s->direct_smartcard);
+        break;
     case PROP_SMARTCARD_CERTIFICATES:
         g_value_set_boxed(value, s->smartcard_certificates);
         break;
@@ -696,6 +703,9 @@ static void spice_session_set_property(GObject      *gobject,
     case PROP_SMARTCARD:
         s->smartcard = g_value_get_boolean(value);
         break;
+    case PROP_DIRECT_SMARTCARD:
+        s->direct_smartcard = g_value_get_boolean(value);
+        break;
     case PROP_SMARTCARD_CERTIFICATES:
         g_strfreev(s->smartcard_certificates);
         s->smartcard_certificates = g_value_dup_boxed(value);
@@ -1025,6 +1035,23 @@ static void spice_session_class_init(SpiceSessionClass *klass)
                           G_PARAM_STATIC_STRINGS));
 
     /**
+     * SpiceSession:direct-smartcard:
+     *
+     * If set to TRUE, the smartcard channel will try to directly use the
+     *  Smartcard instead of using libnss and simulating a card
+     *
+     * Since: 0.X
+     **/
+    g_object_class_install_property
+        (gobject_class, PROP_DIRECT_SMARTCARD,
+         g_param_spec_boolean("direct-smartcard",
+                          "Enable direct access to Smartcards using PC/SC",
+                          "Instead of using nss to simulate a card, directly get events with PC/SC",
+                          FALSE,
+                          G_PARAM_READWRITE |
+                          G_PARAM_STATIC_STRINGS));
+
+    /**
      * SpiceSession:enable-audio:
      *
      * If set to TRUE, the audio channels will be enabled for
diff --git a/man/spice-client.pod b/man/spice-client.pod
index 69ea84c..99a59f8 100644
--- a/man/spice-client.pod
+++ b/man/spice-client.pod
@@ -142,7 +142,11 @@ Display Spice-GTK version information
 
 =item --spice-smartcard
 
-Enable smartcard support
+Enable smartcard support, using libnss to access the card, and by simulating a CAC card
+
+=item --spice-direct-smartcard
+
+Use the PC/SC library to provide direct access to the smart card.
 
 =item --spice-smartcard-db=<certificate-db>
 
-- 
1.7.10.4

>From 2689b99f2c93aeed0b02b59c5dd9efb72decc457 Mon Sep 17 00:00:00 2001
From: Jeremy White <jwhite@xxxxxxxxxxxxxxx>
Date: Tue, 23 Dec 2014 13:05:42 -0600
Subject: [PATCH] Proof of concept of a VCARD_DIRECT implementation.


Signed-off-by: Jeremy White <jwhite@xxxxxxxxxxxxxxx>
---
 Makefile.objs               |    5 +
 configure                   |   38 +++
 libcacard/capcsc.c          |  571 +++++++++++++++++++++++++++++++++++++++++++
 libcacard/capcsc.h          |   16 ++
 libcacard/libcacard.syms    |    1 +
 libcacard/vcard.c           |    2 +-
 libcacard/vcard.h           |    2 +-
 libcacard/vcard_emul_nss.c  |   28 ++-
 libcacard/vcard_emul_type.c |    3 +-
 libcacard/vscclient.c       |   16 +-
 10 files changed, 665 insertions(+), 17 deletions(-)
 create mode 100644 libcacard/capcsc.c
 create mode 100644 libcacard/capcsc.h

diff --git a/Makefile.objs b/Makefile.objs
index abeb902..bb9659e 100644
--- a/Makefile.objs
+++ b/Makefile.objs
@@ -32,6 +32,11 @@ libcacard-y += libcacard/card_7816.o
 libcacard-y += libcacard/vcardt.o
 libcacard/vcard_emul_nss.o-cflags := $(NSS_CFLAGS)
 libcacard/vcard_emul_nss.o-libs := $(NSS_LIBS)
+ifeq ($(CONFIG_SMARTCARD_PCSC),y)
+libcacard-y += libcacard/capcsc.o
+libcacard/capcsc.o-cflags := $(PCSC_CFLAGS)
+libcacard/capcsc.o-libs := $(PCSC_LIBS)
+endif
 
 ######################################################################
 # Target independent part of system emulation. The long term path is to
diff --git a/configure b/configure
index cae588c..f1dd05c 100755
--- a/configure
+++ b/configure
@@ -307,6 +307,7 @@ trace_file="trace"
 spice=""
 rbd=""
 smartcard_nss=""
+smartcard_pcsc=""
 libusb=""
 usb_redir=""
 glx=""
@@ -1042,6 +1043,10 @@ for opt do
   ;;
   --enable-smartcard-nss) smartcard_nss="yes"
   ;;
+  --disable-smartcard-pcsc) smartcard_pcsc="no"
+  ;;
+  --enable-smartcard-pcsc) smartcard_pcsc="yes"
+  ;;
   --disable-libusb) libusb="no"
   ;;
   --enable-libusb) libusb="yes"
@@ -1368,6 +1373,8 @@ Advanced options (experts only):
   --enable-libnfs          enable nfs support
   --disable-smartcard-nss  disable smartcard nss support
   --enable-smartcard-nss   enable smartcard nss support
+  --disable-smartcard-pcsc disable smartcard pcsc passthru support
+  --enable-smartcard-pcsc  enable smartcard pcsc passthru support
   --disable-libusb         disable libusb (for usb passthrough)
   --enable-libusb          enable libusb (for usb passthrough)
   --disable-usb-redir      disable usb network redirection support
@@ -3648,6 +3655,30 @@ EOF
     fi
 fi
 
+# check for pcsclite for smartcard passthru support
+if test "$smartcard_pcsc" != "no"; then
+  cat > $TMPC << EOF
+#include <winscard.h>
+int main(void) { SCardEstablishContext(0, 0, 0, 0); return 0; }
+EOF
+    # FIXME: do not include $glib_* in here
+    pcsc_libs="$($pkg_config --libs libpcsclite 2>/dev/null) $glib_libs"
+    pcsc_cflags="$($pkg_config --cflags libpcsclite 2>/dev/null) $glib_cflags"
+    test_cflags="$pcsc_cflags"
+    if test "$werror" = "yes"; then
+        test_cflags="-Werror $test_cflags"
+    fi
+    if test -n "$libtool" &&
+      compile_prog "$test_cflags" "$pcsc_libs"; then
+        smartcard_pcsc="yes"
+    else
+        if test "$smartcard_pcsc" = "yes"; then
+            feature_not_found "pcsc" "Install libpcsclite"
+        fi
+        smartcard_pcsc="no"
+    fi
+fi
+
 # check for libusb
 if test "$libusb" != "no" ; then
     if $pkg_config --atleast-version=1.0.13 libusb-1.0; then
@@ -4318,6 +4349,7 @@ fi
 echo "rbd support       $rbd"
 echo "xfsctl support    $xfs"
 echo "nss used          $smartcard_nss"
+echo "pcsc used         $smartcard_pcsc"
 echo "libusb            $libusb"
 echo "usb net redir     $usb_redir"
 echo "GLX support       $glx"
@@ -4674,6 +4706,12 @@ if test "$smartcard_nss" = "yes" ; then
   echo "NSS_CFLAGS=$nss_cflags" >> $config_host_mak
 fi
 
+if test "$smartcard_pcsc" = "yes" ; then
+  echo "CONFIG_SMARTCARD_PCSC=y" >> $config_host_mak
+  echo "PCSC_LIBS=$pcsc_libs" >> $config_host_mak
+  echo "PCSC_CFLAGS=$pcsc_cflags" >> $config_host_mak
+fi
+
 if test "$libusb" = "yes" ; then
   echo "CONFIG_USB_LIBUSB=y" >> $config_host_mak
 fi
diff --git a/libcacard/capcsc.c b/libcacard/capcsc.c
new file mode 100644
index 0000000..c01f91d
--- /dev/null
+++ b/libcacard/capcsc.c
@@ -0,0 +1,571 @@
+/*
+ * Supply a vreader using the PC/SC interface.
+ *
+ * This work is licensed under the terms of the GNU LGPL, version 2.1 or later.
+ * See the COPYING.LIB file in the top-level directory.
+ */
+
+/* avoid including prototypes.h that causes some qemu type conflicts */
+#define NO_NSPR_10_SUPPORT
+#include <prthread.h>
+
+#include "qemu-common.h"
+
+#include "vcard.h"
+#include "card_7816.h"
+#include "capcsc.h"
+#include "vreader.h"
+#include "vevent.h"
+
+#include <PCSC/wintypes.h>
+#include <PCSC/winscard.h>
+
+
+typedef struct _PCSCContext PCSCContext;
+
+typedef struct _APDUMsg {
+    void *data;
+    int len;
+    pid_t sender;
+    LONG rc;
+    struct _APDUMsg *next;
+} APDUMsg;
+
+typedef struct
+{
+    PCSCContext *context;
+    int index;
+    char *name;
+    DWORD protocol;
+    DWORD state;
+    SCARDHANDLE card;
+    BYTE atr[MAX_ATR_SIZE];
+    DWORD atrlen;
+    int card_connected;
+    CompatGMutex request_lock;
+    APDUMsg *requests;
+    CompatGMutex response_lock;
+    APDUMsg *responses;
+} SCardReader;
+
+typedef struct _PCSCContext
+{
+    SCARDCONTEXT context;
+    SCardReader readers[CAPCSC_MAX_READERS];
+    int reader_count;
+    int readers_changed;
+    PRThread *thread;
+} PCSCContext;
+
+PCSCContext context;
+
+
+static void push_request(SCardReader *r, void *data, int len)
+{
+    APDUMsg *a = malloc(sizeof(*a));
+    APDUMsg **p;
+
+    a->data = malloc(len);
+    a->len = len;
+    a->sender = getpid();
+    a->next = NULL;
+    memcpy(a->data, data, len);
+
+    g_mutex_lock(&r->request_lock);
+    for (p = &r->requests; *p; p = &(*p)->next)
+        ;
+    *p = a;
+
+    g_mutex_unlock(&r->request_lock);
+}
+
+static APDUMsg * get_request(SCardReader *r)
+{
+    APDUMsg *p;
+    g_mutex_lock(&r->request_lock);
+    p = r->requests;
+    if (r->requests)
+        r->requests = p->next;
+    g_mutex_unlock(&r->request_lock);
+    return p;
+}
+
+static void push_response(SCardReader *r, void *data, int len, LONG rc, pid_t sender)
+{
+    APDUMsg *a = malloc(sizeof(*a));
+    APDUMsg **p;
+
+    a->data = malloc(len);
+    a->len = len;
+    a->sender = sender;
+    a->next = NULL;
+    a->rc = rc;
+    memcpy(a->data, data, len);
+
+    g_mutex_lock(&r->response_lock);
+    for (p = &r->responses; *p; p = &(*p)->next)
+        ;
+    *p = a;
+
+    g_mutex_unlock(&r->response_lock);
+}
+
+static APDUMsg * get_response(SCardReader *r)
+{
+    APDUMsg **p;
+    APDUMsg *ret = NULL;
+    g_mutex_lock(&r->response_lock);
+    for (p = &r->responses; *p && (*p)->sender != getpid(); p = &((*p)->next))
+        ;
+    if (*p)
+    {
+        ret = *p;
+        *p = ret->next;
+    }
+    g_mutex_unlock(&r->response_lock);
+    return ret;
+}
+
+static void free_msg(APDUMsg *m)
+{
+    free(m->data);
+    free(m);
+}
+
+
+static void delete_reader(PCSCContext *pc, int i)
+{
+    SCardReader *r = &pc->readers[i];
+    g_mutex_clear(&r->request_lock);
+    g_mutex_clear(&r->response_lock);
+    free(r->name);
+    r->name = NULL;
+
+    if (i < (pc->reader_count - 1))
+    {
+        int rem = pc->reader_count - i - 1;
+        memmove(&pc->readers[i], &pc->readers[i + 1], sizeof(SCardReader) * rem);
+    }
+
+    pc->reader_count--;
+}
+
+static void delete_reader_cb(VReaderEmul *ve)
+{
+    SCardReader *r = (SCardReader *) ve;
+
+    /* TODO - lock? */
+    delete_reader(r->context, r->index);
+}
+
+static int new_reader(PCSCContext *pc, const char *name, DWORD state)
+{
+    SCardReader *r;
+    VReader *vreader;
+
+    if (pc->reader_count >= CAPCSC_MAX_READERS - 1)
+        return 1;
+
+        /* TODO - get it's state */
+
+    r = &pc->readers[pc->reader_count];
+    memset(r, 0, sizeof(*r));
+    r->index = pc->reader_count++;
+    r->context = pc;
+    r->name = strdup(name);
+    g_mutex_init(&r->request_lock);
+    g_mutex_init(&r->response_lock);
+
+    vreader = vreader_new(name, (VReaderEmul *) r, delete_reader_cb);
+    vreader_add_reader(vreader);
+    vreader_free(vreader);
+
+    return 0;
+}
+
+static int find_reader(PCSCContext *pc, const char *name)
+{
+    int i;
+    for (i = 0; i < pc->reader_count; i++)
+        if (strcmp(pc->readers[i].name, name) == 0)
+            return i;
+
+    return -1;
+}
+
+static int scan_for_readers(PCSCContext *pc)
+{
+    LONG rc;
+
+    int i;
+    char buf[8192];
+    DWORD buflen = sizeof(buf);
+
+    char *p;
+    int matches[CAPCSC_MAX_READERS];
+
+    for (i = 0; i < CAPCSC_MAX_READERS; i++)
+        matches[i] = 0;
+
+    /* TODO lock mutex */
+
+    pc->readers_changed = 1;
+    memset(buf, 0, sizeof(buf));
+    rc = SCardListReaders(pc->context, NULL, buf, &buflen);
+    if (rc == SCARD_E_NO_READERS_AVAILABLE)
+    {
+        rc = 0;
+        goto exit;
+    }
+
+    if (rc != SCARD_S_SUCCESS)
+    {
+        fprintf(stderr, "SCardListReaders failed: %s (0x%lX)\n",
+            pcsc_stringify_error(rc), rc);
+        goto exit;
+    }
+
+    for (p = buf; p && p < buf + sizeof(buf); p += (strlen(p) + 1))
+    {
+        if (strlen(p) > 0)
+        {
+            i = find_reader(pc, p);
+            if (i >= 0)
+                matches[i]++;
+            else
+            {
+                if (! new_reader(pc, p, SCARD_STATE_UNAWARE))
+                    matches[pc->reader_count - 1]++;
+            }
+        }
+    }
+
+    rc = 0;
+
+exit:
+    for (i = pc->reader_count - 1; i >= 0; i--)
+        if (! matches[i])
+        {
+            VReader *reader = vreader_get_reader_by_name(pc->readers[i].name);
+            if (reader)
+                vreader_remove_reader(reader);
+        }
+
+    /* TODO unlock mutex? */
+
+    return rc;
+}
+
+static int init_pcsc(PCSCContext *pc)
+{
+    LONG rc;
+
+    memset(pc, 0, sizeof(*pc));
+
+    rc = SCardEstablishContext(SCARD_SCOPE_SYSTEM, NULL, NULL, &pc->context);
+    if (rc != SCARD_S_SUCCESS)
+    {
+        fprintf(stderr, "SCardEstablishContext: Cannot Connect to Resource Manager %lX\n", rc);
+        return rc;
+    }
+
+    return 0;
+}
+
+
+static void prepare_reader_states(PCSCContext *pc, SCARD_READERSTATE **states, DWORD *reader_count)
+{
+    SCARD_READERSTATE *state;
+    int i;
+
+    if (*states)
+        free(*states);
+
+    *reader_count = pc->reader_count;
+
+    (*reader_count)++;
+    *states = malloc((*reader_count) * sizeof(**states));
+    memset(*states, 0, sizeof((*reader_count) * sizeof(**states)));
+
+    for (i = 0, state = *states; i < pc->reader_count; i++, state++)
+    {
+        state->szReader = pc->readers[i].name;
+        state->dwCurrentState = pc->readers[i].state;
+    }
+
+    /* Leave a space to be notified of new readers */
+    state->szReader = "\\\\?PnP?\\Notification";
+    state->dwCurrentState = SCARD_STATE_UNAWARE;
+}
+
+static int connect_card(SCardReader *r)
+{
+    LONG rc;
+
+    r->protocol = -1;
+    rc = SCardConnect(r->context->context, r->name, SCARD_SHARE_SHARED,
+                        SCARD_PROTOCOL_T0 | SCARD_PROTOCOL_T1,
+                        &r->card, &r->protocol);
+    if (rc != SCARD_S_SUCCESS)
+    {
+        fprintf(stderr, "Failed to connect to a card reader: %s (0x%lX)\n",
+            pcsc_stringify_error(rc), rc);
+        return rc;
+    }
+
+    r->card_connected = 1;
+
+    return 0;
+}
+
+static LONG send_receive(SCardReader *r, BYTE *transmit, DWORD transmit_len,
+                 BYTE *receive, DWORD *receive_len)
+{
+    const SCARD_IO_REQUEST *send_header;
+    SCARD_IO_REQUEST receive_header;
+    LONG rc;
+
+    if (! r->card_connected)
+    {
+        rc = connect_card(r);
+        if (rc)
+            return rc;
+    }
+
+    if (r->protocol == SCARD_PROTOCOL_T0)
+        send_header = SCARD_PCI_T0;
+    else if (r->protocol == SCARD_PROTOCOL_T1)
+        send_header = SCARD_PCI_T1;
+    else
+    {
+        fprintf(stderr, "Unknown protocol %lX\n", r->protocol);
+        return 1;
+    }
+
+    rc = SCardTransmit(r->card, send_header, transmit, transmit_len,
+                        &receive_header, receive, receive_len);
+    if (rc != SCARD_S_SUCCESS)
+    {
+        fprintf(stderr, "Failed to transmit %ld bytes: %s (0x%lX)\n",
+            transmit_len, pcsc_stringify_error(rc), rc);
+        return rc;
+    }
+
+    return 0;
+}
+
+
+static VCardStatus apdu_cb(VCard *card, VCardAPDU *apdu, VCardResponse **response)
+{
+    VCardStatus ret = VCARD_DONE;
+    SCardReader *r = (SCardReader *) vcard_get_private(card);
+    APDUMsg *resp;
+
+
+    push_request(r, apdu->a_data, apdu->a_len);
+    while (1)
+    {
+        resp = get_response(r);
+        if (resp)
+            break;
+
+        usleep(1);
+    }
+
+    if (resp->rc || resp->len < 2)
+        ret = VCARD_FAIL;
+    else
+        *response = vcard_response_new_bytes(card, resp->data, resp->len - 2, apdu->a_Le,
+                                ((BYTE *)resp->data)[resp->len - 2],
+                                ((BYTE *)resp->data)[resp->len - 1]);
+
+    free_msg(resp);
+
+    return ret;
+}
+
+static VCardStatus reset_cb(VCard *card, int channel)
+{
+/* TODO - scardreconnect */
+    return VCARD_DONE;
+}
+
+static void get_atr_cb (VCard *card, unsigned char *atr, int *atr_len)
+{
+    SCardReader *r = (SCardReader *) vcard_get_private(card);
+    *atr_len = r->atrlen;
+    if (atr)
+        memcpy(atr, r->atr, r->atrlen);
+
+{
+    int i;
+    fprintf(stderr, "  ATR: ");
+    for (i = 0; i < *atr_len; i++)
+        fprintf(stderr, "%02x ", r->atr[i]);
+    fprintf(stderr, "\n");
+}
+}
+
+static void delete_card_cb (VCardEmul *ve)
+{
+}
+
+static void insert_card(SCardReader *r, SCARD_READERSTATE *s)
+{
+    memcpy(r->atr, s->rgbAtr, MIN(sizeof(r->atr), sizeof(s->rgbAtr)));
+    r->atrlen = s->cbAtr;
+
+    VReader *reader = vreader_get_reader_by_name(r->name);
+    if (! reader)
+        return;
+
+    if (connect_card(r))
+        return;
+
+    VCardApplet * applet = vcard_new_applet(apdu_cb, reset_cb, (const unsigned char *) CAPCSC_APPLET, strlen(CAPCSC_APPLET));
+    if (! applet)
+        return;
+
+    VCard * card =  vcard_new((VCardEmul *) r, delete_card_cb);
+    if (! card)
+    {
+        /* TODO free applet */
+        return;
+    }
+
+    vcard_set_type(card, VCARD_DIRECT);
+    vcard_set_atr_func(card, get_atr_cb);
+    vcard_add_applet(card, applet);
+
+    vreader_insert_card(reader, card);
+}
+
+static void remove_card(SCardReader *r)
+{
+    memset(r->atr, 0, sizeof(r->atr));
+    r->atrlen = 0;
+    r->card_connected = 0;
+    /* TODO - disconnect?  */
+
+    VReader *reader = vreader_get_reader_by_name(r->name);
+    if (! reader)
+        return;
+
+    vreader_insert_card(reader, NULL);
+}
+
+static void process_reader_change(SCardReader *r, SCARD_READERSTATE *s)
+{
+    if (s->dwEventState & SCARD_STATE_PRESENT)
+        insert_card(r, s);
+    else if (s->dwEventState & SCARD_STATE_EMPTY)
+        remove_card(r);
+    else
+        fprintf(stderr, "Unexpected card state change from %lx to %lx:\n", r->state, s->dwEventState);
+    r->state = s->dwEventState;
+}
+
+static void process_requests(SCardReader *r)
+{
+    BYTE outbuf[4096];
+    DWORD outlen = sizeof(outbuf);
+    LONG rc;
+    APDUMsg *request = get_request(r);
+
+    if (request)
+    {
+        rc = send_receive(r, request->data, request->len, outbuf, &outlen);
+        push_response(r, outbuf, outlen, rc, request->sender);
+        free_msg(request);
+    }
+}
+
+/*
+ * This thread looks for card and reader insertions and puts events on the
+ * event queue.  PCSC is also not thread safe, so we relay requests for
+ * the smart card stack through here as well.
+ */
+static void event_thread(void *arg)
+{
+    PCSCContext *pc = (PCSCContext *) arg;
+    DWORD reader_count = 0;
+    SCARD_READERSTATE *reader_states = NULL;
+    LONG rc;
+
+    scan_for_readers(pc);
+
+    do {
+        int i;
+
+        /* TODO lock a mutex */
+
+        if (pc->readers_changed)
+            prepare_reader_states(pc, &reader_states, &reader_count);
+
+        /* TODO unlock the mutex */
+
+        rc = SCardGetStatusChange(pc->context,
+                reader_count > 1 ? CAPCSC_POLL_TIME : INFINITE,
+                reader_states, reader_count);
+
+        /* If we have a new reader, or an unknown reader, rescan and go back and do it again */
+        if ( (rc == SCARD_S_SUCCESS && (reader_states[reader_count - 1].dwEventState & SCARD_STATE_CHANGED)) ||
+             rc == SCARD_E_UNKNOWN_READER )
+        {
+            scan_for_readers(pc);
+            continue;
+        }
+
+        pc->readers_changed = 0;
+
+        if (rc == SCARD_E_TIMEOUT)
+        {
+            /* TODO - get an EINTR to speed this up? */
+            for (i = 0; i < reader_count - 1; i++)
+                process_requests(&pc->readers[i]);
+
+            continue;
+        }
+
+        if (rc != SCARD_S_SUCCESS)
+        {
+            fprintf(stderr, "Unexpected SCardGetStatusChange ret %lx(%s)\n", rc, pcsc_stringify_error(rc));
+            continue;
+        }
+
+        for (i = 0; i < reader_count; i++)
+        {
+            if (reader_states[i].dwEventState & SCARD_STATE_CHANGED)
+            {
+                /* TODO - apply thought here..., not just blindly indicate change */
+                process_reader_change(&pc->readers[i], &reader_states[i]);
+                pc->readers_changed++;
+            }
+
+        }
+
+    } while (1);
+}
+
+/*
+ * We poll the PC/SC interface, looking for device changes
+ */
+static int new_event_thread(PCSCContext *pc)
+{
+    pc->thread = PR_CreateThread(PR_SYSTEM_THREAD, event_thread,
+                     pc, PR_PRIORITY_NORMAL, PR_GLOBAL_THREAD,
+                     PR_UNJOINABLE_THREAD, 0);
+    return pc->thread == NULL;
+}
+
+
+int capcsc_init(void)
+{
+    if (init_pcsc(&context))
+        return -1;
+
+    if (new_event_thread(&context))
+        return -1;
+
+    return 0;
+}
diff --git a/libcacard/capcsc.h b/libcacard/capcsc.h
new file mode 100644
index 0000000..4e292cf
--- /dev/null
+++ b/libcacard/capcsc.h
@@ -0,0 +1,16 @@
+/*
+ * This work is licensed under the terms of the GNU LGPL, version 2.1 or later.
+ * See the COPYING.LIB file in the top-level directory.
+ */
+#ifndef CAPCSC_H
+#define CAPCSC_H 1
+
+#define CAPCSC_POLL_TIME            50      /* ms  - Time we will poll for card change when a reader is connected */
+#define CAPCSC_MAX_READERS          16      /* Technically, -1, as we leave a space to find new readers */
+
+#define CAPCSC_APPLET               "CAPCSC APPLET"
+
+int capcsc_init(void);
+
+
+#endif
diff --git a/libcacard/libcacard.syms b/libcacard/libcacard.syms
index 1697515..0e44dc0 100644
--- a/libcacard/libcacard.syms
+++ b/libcacard/libcacard.syms
@@ -1,5 +1,6 @@
 cac_card_init
 cac_is_cac_card
+capcsc_init
 vcard_add_applet
 vcard_apdu_delete
 vcard_apdu_new
diff --git a/libcacard/vcard.c b/libcacard/vcard.c
index d140a8e..4a1d91e 100644
--- a/libcacard/vcard.c
+++ b/libcacard/vcard.c
@@ -95,7 +95,7 @@ vcard_reset(VCard *card, VCardPower power)
 VCardApplet *
 vcard_new_applet(VCardProcessAPDU applet_process_function,
                  VCardResetApplet applet_reset_function,
-                 unsigned char *aid, int aid_len)
+                 const unsigned char *aid, int aid_len)
 {
     VCardApplet *applet;
 
diff --git a/libcacard/vcard.h b/libcacard/vcard.h
index 47dc703..c16b944 100644
--- a/libcacard/vcard.h
+++ b/libcacard/vcard.h
@@ -30,7 +30,7 @@ void vcard_reset(VCard *card, VCardPower power);
  */
 VCardApplet *vcard_new_applet(VCardProcessAPDU applet_process_function,
                               VCardResetApplet applet_reset_function,
-                              unsigned char *aid, int aid_len);
+                              const unsigned char *aid, int aid_len);
 
 /*
  * destructor for a VCardApplet
diff --git a/libcacard/vcard_emul_nss.c b/libcacard/vcard_emul_nss.c
index 950edee..18e6871 100644
--- a/libcacard/vcard_emul_nss.c
+++ b/libcacard/vcard_emul_nss.c
@@ -25,6 +25,7 @@
 #include <prthread.h>
 #include <secerr.h>
 
+#include "config-host.h"
 #include "qemu-common.h"
 
 #include "vcard.h"
@@ -34,6 +35,9 @@
 #include "vevent.h"
 
 #include "libcacard/vcardt_internal.h"
+#if defined(CONFIG_SMARTCARD_PCSC)
+#include "capcsc.h"
+#endif
 
 
 typedef enum {
@@ -892,6 +896,22 @@ vcard_emul_init(const VCardEmulOptions *options)
         options = &default_options;
     }
 
+#if defined(CONFIG_SMARTCARD_PCSC)
+    if (options->use_hw && options->hw_card_type == VCARD_EMUL_PASSTHRU) {
+        if (options->vreader_count > 0) {
+            fprintf(stderr, "Error: you cannot use a soft card and a passthru card simultaneously.\n");
+            return VCARD_EMUL_FAIL;
+        }
+
+        if (capcsc_init()) {
+            fprintf(stderr, "Error initializing PCSC interface.\n");
+            return VCARD_EMUL_FAIL;
+        }
+
+        return VCARD_EMUL_OK;
+    }
+#endif
+
     /* first initialize NSS */
     if (options->nss_db) {
         rv = NSS_Init(options->nss_db);
@@ -1270,5 +1290,11 @@ vcard_emul_usage(void)
 "hw_type, and parameters of hw_param.\n"
 "\n"
 "If more one or more soft= parameters are specified, these readers will be\n"
-"presented to the guest\n");
+"presented to the guest\n"
+#if defined(CONFIG_SMARTCARD_PCSC)
+"\n"
+"If a hw_type of PASSTHRU is given, a connection will be made to the hardware\n"
+"using libpcscslite.  Note that in that case, no soft cards are permitted.\n"
+#endif
+);
 }
diff --git a/libcacard/vcard_emul_type.c b/libcacard/vcard_emul_type.c
index 59a1458..e8f6a4c 100644
--- a/libcacard/vcard_emul_type.c
+++ b/libcacard/vcard_emul_type.c
@@ -9,6 +9,7 @@
  */
 
 #include <strings.h>
+#include "config-host.h"
 #include "vcardt.h"
 #include "vcard_emul_type.h"
 #include "cac.h"
@@ -48,7 +49,7 @@ VCardEmulType vcard_emul_type_from_string(const char *type_string)
      if (strcasecmp(type_string, "CAC") == 0) {
         return VCARD_EMUL_CAC;
      }
-#ifdef USE_PASSTHRU
+#ifdef CONFIG_SMARTCARD_PCSC
      if (strcasecmp(type_string, "PASSTHRU") == 0) {
         return VCARD_EMUL_PASSTHRU;
      }
diff --git a/libcacard/vscclient.c b/libcacard/vscclient.c
index fa6041d..8573f50 100644
--- a/libcacard/vscclient.c
+++ b/libcacard/vscclient.c
@@ -41,14 +41,8 @@ print_byte_array(
 
 static void
 print_usage(void) {
-    printf("vscclient [-c <certname> .. -e <emul_args> -d <level>%s] "
-            "<host> <port>\n",
-#ifdef USE_PASSTHRU
-    " -p");
-    printf(" -p use passthrough mode\n");
-#else
-   "");
-#endif
+    printf("vscclient [-c <certname> .. -e <emul_args> -d <level>] "
+            "<host> <port>\n");
     vcard_emul_usage();
 }
 
@@ -673,7 +667,7 @@ main(
     }
 #endif
 
-    while ((c = getopt(argc, argv, "c:e:pd:")) != -1) {
+    while ((c = getopt(argc, argv, "c:e:d:")) != -1) {
         switch (c) {
         case 'c':
             if (cert_count >= MAX_CERTS) {
@@ -685,10 +679,6 @@ main(
         case 'e':
             emul_args = optarg;
             break;
-        case 'p':
-            print_usage();
-            exit(4);
-            break;
         case 'd':
             verbose = get_id_from_string(optarg, 1);
             break;
-- 
1.7.10.4

_______________________________________________
Spice-devel mailing list
Spice-devel@xxxxxxxxxxxxxxxxxxxxx
http://lists.freedesktop.org/mailman/listinfo/spice-devel

[Index of Archives]     [Linux ARM Kernel]     [Linux ARM]     [Linux Omap]     [Fedora ARM]     [IETF Annouce]     [Security]     [Bugtraq]     [Linux]     [Linux OMAP]     [Linux MIPS]     [ECOS]     [Asterisk Internet PBX]     [Linux API]     [Monitors]