All of lore.kernel.org
 help / color / mirror / Atom feed
* [Qemu-devel] [PATCH v3 0/7] Add support for passthru cards to libcacard
@ 2015-03-13 19:45 Jeremy White
  2015-03-13 19:45 ` [Qemu-devel] [PATCH v3 1/7] Bug fix: delete the reader entry after queueing an event, not before Jeremy White
                   ` (8 more replies)
  0 siblings, 9 replies; 20+ messages in thread
From: Jeremy White @ 2015-03-13 19:45 UTC (permalink / raw)
  To: qemu-devel

This is a resend of my outstanding patches, gathered together
as one series.  I'm hoping to agitate for this whole set to be considered.

Changes since v2:
  * Include outstanding patches not strictly related to passthru mode
  * Include a fix for read binary transactions
  * Mitigate some performance issues arising from libpccslite's
    heavy handed thread safety code

Jeremy White (7):
  Bug fix: delete the reader entry after queueing an event, not before.
  Retrieve the correct TD byte when checking an ATR.
  Add a configure check for libpcsclite, and an option to enable or
    disable it.
  Add error checking to vcard_emul_options.
  Add a VCARD_DIRECT implemention to the libcacard smartcard support.
  Enable support for passthru (e.g. direct to pcsc) smart cards     in
    the emul_options entry point in libcacard.
  Remove the (broken) passthru option.

 Makefile.objs               |    5 +
 configure                   |   38 ++++
 hw/usb/ccid-card-passthru.c |    2 +-
 libcacard/capcsc.c          |  498 +++++++++++++++++++++++++++++++++++++++++++
 libcacard/capcsc.h          |   18 ++
 libcacard/card_7816.c       |    2 +-
 libcacard/card_7816.h       |    3 +
 libcacard/libcacard.syms    |    2 +
 libcacard/vcard.c           |    2 +-
 libcacard/vcard.h           |    2 +-
 libcacard/vcard_emul_nss.c  |   78 +++++--
 libcacard/vcard_emul_type.c |    3 +-
 libcacard/vreader.c         |    2 +-
 libcacard/vscclient.c       |   16 +-
 14 files changed, 640 insertions(+), 31 deletions(-)
 create mode 100644 libcacard/capcsc.c
 create mode 100644 libcacard/capcsc.h

-- 
1.7.10.4

^ permalink raw reply	[flat|nested] 20+ messages in thread

* [Qemu-devel] [PATCH v3 1/7] Bug fix: delete the reader entry after queueing an event, not before.
  2015-03-13 19:45 [Qemu-devel] [PATCH v3 0/7] Add support for passthru cards to libcacard Jeremy White
@ 2015-03-13 19:45 ` Jeremy White
  2015-03-25 15:12   ` Marc-André Lureau
  2015-03-13 19:45 ` [Qemu-devel] [PATCH v3 2/7] Retrieve the correct TD byte when checking an ATR Jeremy White
                   ` (7 subsequent siblings)
  8 siblings, 1 reply; 20+ messages in thread
From: Jeremy White @ 2015-03-13 19:45 UTC (permalink / raw)
  To: qemu-devel

As far as I can tell, the vreader_remove_reader function is not presently in
use anywhere; I have an upcoming patch set that uses it.

Signed-off-by: Jeremy White <jwhite@codeweavers.com>
---
 libcacard/vreader.c |    2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/libcacard/vreader.c b/libcacard/vreader.c
index 0315dd8..8b10d6a 100644
--- a/libcacard/vreader.c
+++ b/libcacard/vreader.c
@@ -529,8 +529,8 @@ vreader_remove_reader(VReader *reader)
     }
     vreader_dequeue(vreader_list, current_entry);
     vreader_list_unlock();
-    vreader_list_entry_delete(current_entry);
     vevent_queue_vevent(vevent_new(VEVENT_READER_REMOVE, reader, NULL));
+    vreader_list_entry_delete(current_entry);
     return VREADER_OK;
 }
 
-- 
1.7.10.4

^ permalink raw reply related	[flat|nested] 20+ messages in thread

* [Qemu-devel] [PATCH v3 2/7] Retrieve the correct TD byte when checking an ATR.
  2015-03-13 19:45 [Qemu-devel] [PATCH v3 0/7] Add support for passthru cards to libcacard Jeremy White
  2015-03-13 19:45 ` [Qemu-devel] [PATCH v3 1/7] Bug fix: delete the reader entry after queueing an event, not before Jeremy White
@ 2015-03-13 19:45 ` Jeremy White
  2015-03-25 15:12   ` Marc-André Lureau
  2015-03-13 19:45 ` [Qemu-devel] [PATCH v3 3/7] Add a configure check for libpcsclite, and an option to enable or disable it Jeremy White
                   ` (6 subsequent siblings)
  8 siblings, 1 reply; 20+ messages in thread
From: Jeremy White @ 2015-03-13 19:45 UTC (permalink / raw)
  To: qemu-devel

A physical smartcard with an ATR of
  3B 95 95 40 FF AE 01 0E 00 00
was parsed incorrectly.

The '40' should have been the second TD; instead
the FF is used, incorrectly.

Signed-off-by: Jeremy White <jwhite@codeweavers.com>
---
 hw/usb/ccid-card-passthru.c |    2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/hw/usb/ccid-card-passthru.c b/hw/usb/ccid-card-passthru.c
index 10f1d30..2ae3b81 100644
--- a/hw/usb/ccid-card-passthru.c
+++ b/hw/usb/ccid-card-passthru.c
@@ -168,8 +168,8 @@ static int check_atr(PassthruState *card, uint8_t *data, int len)
             opt_bytes++;
         }
         if (td & 0x8) {
-            opt_bytes++;
             td = data[opt_bytes + 2] >> 4;
+            opt_bytes++;
         }
     }
     if (len < 2 + historical_length + opt_bytes) {
-- 
1.7.10.4

^ permalink raw reply related	[flat|nested] 20+ messages in thread

* [Qemu-devel] [PATCH v3 3/7] Add a configure check for libpcsclite, and an option to enable or disable it.
  2015-03-13 19:45 [Qemu-devel] [PATCH v3 0/7] Add support for passthru cards to libcacard Jeremy White
  2015-03-13 19:45 ` [Qemu-devel] [PATCH v3 1/7] Bug fix: delete the reader entry after queueing an event, not before Jeremy White
  2015-03-13 19:45 ` [Qemu-devel] [PATCH v3 2/7] Retrieve the correct TD byte when checking an ATR Jeremy White
@ 2015-03-13 19:45 ` Jeremy White
  2015-03-25 15:13   ` Marc-André Lureau
  2015-03-13 19:45 ` [Qemu-devel] [PATCH v3 4/7] Add error checking to vcard_emul_options Jeremy White
                   ` (5 subsequent siblings)
  8 siblings, 1 reply; 20+ messages in thread
From: Jeremy White @ 2015-03-13 19:45 UTC (permalink / raw)
  To: qemu-devel


Signed-off-by: Jeremy White <jwhite@codeweavers.com>
---
 configure |   38 ++++++++++++++++++++++++++++++++++++++
 1 file changed, 38 insertions(+)

diff --git a/configure b/configure
index b858756..f22dbae 100755
--- a/configure
+++ b/configure
@@ -307,6 +307,7 @@ trace_file="trace"
 spice=""
 rbd=""
 smartcard_nss=""
+smartcard_pcsc=""
 libusb=""
 usb_redir=""
 opengl=""
@@ -1043,6 +1044,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"
@@ -1373,6 +1378,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
@@ -3723,6 +3730,30 @@ EOF
     fi
 fi
 
+# check for pcsclite for smartcard passthru support
+# TODO - Add support for Winscard
+if test "$smartcard_pcsc" != "no"; then
+  cat > $TMPC << EOF
+#include <winscard.h>
+int main(void) { SCardEstablishContext(0, 0, 0, 0); return 0; }
+EOF
+    pcsc_libs="$($pkg_config --libs libpcsclite 2>/dev/null)"
+    pcsc_cflags="$($pkg_config --cflags libpcsclite 2>/dev/null)"
+    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
@@ -4396,6 +4427,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 "OpenGL support    $opengl"
@@ -4756,6 +4788,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
-- 
1.7.10.4

^ permalink raw reply related	[flat|nested] 20+ messages in thread

* [Qemu-devel] [PATCH v3 4/7] Add error checking to vcard_emul_options.
  2015-03-13 19:45 [Qemu-devel] [PATCH v3 0/7] Add support for passthru cards to libcacard Jeremy White
                   ` (2 preceding siblings ...)
  2015-03-13 19:45 ` [Qemu-devel] [PATCH v3 3/7] Add a configure check for libpcsclite, and an option to enable or disable it Jeremy White
@ 2015-03-13 19:45 ` Jeremy White
  2015-03-25 15:13   ` Marc-André Lureau
  2015-03-13 19:45 ` [Qemu-devel] [PATCH v3 5/7] Add a VCARD_DIRECT implemention to the libcacard smartcard support Jeremy White
                   ` (4 subsequent siblings)
  8 siblings, 1 reply; 20+ messages in thread
From: Jeremy White @ 2015-03-13 19:45 UTC (permalink / raw)
  To: qemu-devel

Also add an alias of 'nssemul' for the default card
of hw=yes hw_type=cac, and an alias of 'passthru' for
the new card type of hw=yes hw_type=passthru.

This allows the spice-gtk client to take a more human
friendly set of arguments and relay them through to this code.

Signed-off-by: Jeremy White <jwhite@codeweavers.com>
---
 libcacard/vcard_emul_nss.c |   49 ++++++++++++++++++++++++++++++++++----------
 1 file changed, 38 insertions(+), 11 deletions(-)

diff --git a/libcacard/vcard_emul_nss.c b/libcacard/vcard_emul_nss.c
index 950edee..49f0bdf 100644
--- a/libcacard/vcard_emul_nss.c
+++ b/libcacard/vcard_emul_nss.c
@@ -1113,17 +1113,13 @@ static VCardEmulOptions options;
  * and its length in "token_length". "token" will not be nul-terminated.
  * After calling the macro, "args" will be advanced to the beginning of
  * the next token.
- * This macro may call continue or break.
  */
 #define NEXT_TOKEN(token) \
             (token) = args; \
             args = strpbrk(args, ",)"); \
-            if (*args == 0) { \
-                break; \
-            } \
-            if (*args == ')') { \
-                args++; \
-                continue; \
+            if (*args == 0 || *args == ')') { \
+                fprintf(stderr, "Error: invalid soft specification.\n"); \
+                return NULL; \
             } \
             (token##_length) = args - (token); \
             args = strip(args+1);
@@ -1141,6 +1137,7 @@ vcard_emul_options(const char *args)
     do {
         args = strip(args); /* strip off the leading spaces */
         if (*args == ',') {
+            args++;
             continue;
         }
         /* soft=(slot_name,virt_name,emul_type,emul_flags,cert_1, (no eol)
@@ -1159,7 +1156,8 @@ vcard_emul_options(const char *args)
 
             args = strip(args + 5);
             if (*args != '(') {
-                continue;
+                fprintf(stderr, "Error: invalid soft specification.\n");
+                return NULL;
             }
             args = strip(args+1);
 
@@ -1170,11 +1168,17 @@ vcard_emul_options(const char *args)
             memcpy(type_str, type_params, type_params_length);
             type_str[type_params_length] = '\0';
             type = vcard_emul_type_from_string(type_str);
+            if (type == VCARD_EMUL_NONE) {
+                fprintf(stderr, "Error: invalid smartcard type '%s'.\n",
+                        type_str);
+                return NULL;
+            }
 
             NEXT_TOKEN(type_params)
 
             if (*args == 0) {
-                break;
+                fprintf(stderr, "Error: missing cert specification.\n");
+                return NULL;
             }
 
             if (opts->vreader_count >= reader_count) {
@@ -1214,6 +1218,11 @@ vcard_emul_options(const char *args)
         } else if (strncmp(args, "hw_type=", 8) == 0) {
             args = strip(args+8);
             opts->hw_card_type = vcard_emul_type_from_string(args);
+            if (opts->hw_card_type == VCARD_EMUL_NONE) {
+                fprintf(stderr, "Error: invalid smartcard type '%s'.\n",
+                        args);
+                return NULL;
+            }
             args = find_blank(args);
         /* hw_params= */
         } else if (strncmp(args, "hw_params=", 10) == 0) {
@@ -1227,7 +1236,8 @@ vcard_emul_options(const char *args)
             const char *db;
             args = strip(args+3);
             if (*args != '"') {
-                continue;
+                fprintf(stderr, "Error: you must quote the file path.\n");
+                return NULL;
             }
             args++;
             db = args;
@@ -1236,8 +1246,21 @@ vcard_emul_options(const char *args)
             if (*args != 0) {
                 args++;
             }
+        } else if (strncmp(args, "nssemul", 7) == 0) {
+            opts->hw_card_type = VCARD_EMUL_CAC;
+            opts->use_hw = PR_TRUE;
+            args = find_blank(args + 7);
+        /* nssemul */
+#if defined(CONFIG_SMARTCARD_PCSC)
+        } else if (strncmp(args, "passthru", 8) == 0) {
+            opts->hw_card_type = VCARD_EMUL_PASSTHRU;
+            opts->use_hw = PR_TRUE;
+            args = find_blank(args + 8);
+        /* passthru */
+#endif
         } else {
-            args = find_blank(args);
+            fprintf(stderr, "Error: Unknown smartcard specification.\n");
+            return NULL;
         }
     } while (*args != 0);
 
@@ -1253,6 +1276,10 @@ vcard_emul_usage(void)
 " use_hw=[yes|no]                 (default yes)\n"
 " hw_type={card_type_to_emulate}  (default CAC)\n"
 " hw_param={param_for_card}       (default \"\")\n"
+" nssemul                         (alias for use_hw=yes, hw_type=CAC)\n"
+#if defined(CONFIG_SMARTCARD_PCSC)
+" passthru                        (alias for use_hw=yes, hw_type=PASSTHRU)\n"
+#endif
 " soft=({slot_name},{vreader_name},{card_type_to_emulate},{params_for_card},\n"
 "       {cert1},{cert2},{cert3}    (default none)\n"
 "\n"
-- 
1.7.10.4

^ permalink raw reply related	[flat|nested] 20+ messages in thread

* [Qemu-devel] [PATCH v3 5/7] Add a VCARD_DIRECT implemention to the libcacard smartcard support.
  2015-03-13 19:45 [Qemu-devel] [PATCH v3 0/7] Add support for passthru cards to libcacard Jeremy White
                   ` (3 preceding siblings ...)
  2015-03-13 19:45 ` [Qemu-devel] [PATCH v3 4/7] Add error checking to vcard_emul_options Jeremy White
@ 2015-03-13 19:45 ` Jeremy White
  2015-03-25 15:13   ` Marc-André Lureau
  2015-03-13 19:45 ` [Qemu-devel] [PATCH v3 6/7] Enable support for passthru (e.g. direct to pcsc) smart cards in the emul_options entry point in libcacard Jeremy White
                   ` (3 subsequent siblings)
  8 siblings, 1 reply; 20+ messages in thread
From: Jeremy White @ 2015-03-13 19:45 UTC (permalink / raw)
  To: qemu-devel

This uses libpcsclite to provide direct communication with a smartcard.

Signed-off-by: Jeremy White <jwhite@codeweavers.com>
---
 Makefile.objs            |    5 +
 libcacard/capcsc.c       |  498 ++++++++++++++++++++++++++++++++++++++++++++++
 libcacard/capcsc.h       |   18 ++
 libcacard/card_7816.c    |    2 +-
 libcacard/card_7816.h    |    3 +
 libcacard/libcacard.syms |    2 +
 6 files changed, 527 insertions(+), 1 deletion(-)
 create mode 100644 libcacard/capcsc.c
 create mode 100644 libcacard/capcsc.h

diff --git a/Makefile.objs b/Makefile.objs
index 28999d3..cc31b29 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/libcacard/capcsc.c b/libcacard/capcsc.c
new file mode 100644
index 0000000..a1030df
--- /dev/null
+++ b/libcacard/capcsc.c
@@ -0,0 +1,498 @@
+/*
+ * 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.
+ */
+
+#include <glib.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 {
+    PCSCContext *context;
+    int index;
+    char *name;
+    DWORD protocol;
+    DWORD state;
+    SCARDHANDLE card;
+    BYTE atr[MAX_ATR_SIZE];
+    DWORD atrlen;
+    int card_connected;
+    unsigned long request_count;
+} SCardReader;
+
+typedef struct _PCSCContext {
+    SCARDCONTEXT context;
+    SCardReader readers[CAPCSC_MAX_READERS];
+    int reader_count;
+    int readers_changed;
+    GThread *thread;
+    CompatGMutex lock;
+} PCSCContext;
+
+
+static void delete_reader(PCSCContext *pc, int i)
+{
+    SCardReader *r = &pc->readers[i];
+    g_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;
+
+    g_mutex_lock(&r->context->lock);
+    delete_reader(r->context, r->index);
+    g_mutex_unlock(&r->context->lock);
+}
+
+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;
+    }
+
+    r = &pc->readers[pc->reader_count];
+    memset(r, 0, sizeof(*r));
+    r->index = pc->reader_count++;
+    r->context = pc;
+    r->name = g_strdup(name);
+
+    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];
+
+    g_mutex_lock(&pc->lock);
+
+    for (i = 0; i < CAPCSC_MAX_READERS; i++) {
+        matches[i] = 0;
+    }
+
+    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:
+    i = pc->reader_count - 1;
+    g_mutex_unlock(&pc->lock);
+
+    for (; i >= 0; i--) {
+        if (!matches[i]) {
+            VReader *reader = vreader_get_reader_by_name(pc->readers[i].name);
+            if (reader) {
+                vreader_free(reader);
+                vreader_remove_reader(reader);
+            }
+        }
+    }
+
+
+    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) {
+        g_free(*states);
+    }
+
+    *reader_count = pc->reader_count;
+
+    (*reader_count)++;
+    *states = g_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;
+    r->request_count = 0;
+
+    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);
+    BYTE outbuf[4096];
+    DWORD outlen = sizeof(outbuf);
+    LONG rc;
+
+    rc = send_receive(r, apdu->a_data, apdu->a_len, outbuf, &outlen);
+    if (rc || outlen < 2) {
+        ret = VCARD_FAIL;
+    } else {
+        *response = vcard_response_new_data(outbuf, outlen - 2);
+        if (*response == NULL) {
+            return VCARD_FAIL;
+        }
+        vcard_response_set_status_bytes(*response, outbuf[outlen - 2],
+                                                   outbuf[outlen - 1]);
+    }
+
+    return ret;
+}
+
+static VCardStatus reset_cb(VCard *card, int channel)
+{
+    SCardReader *r = (SCardReader *) vcard_get_private(card);
+    LONG rc;
+
+    /* vreader_power_on is a bit too free with it's resets.
+       And a reconnect is expensive; as much as 10-20 seconds.
+       Hence, we discard any initial reconnect request. */
+    if (r->request_count++ == 0) {
+        return VCARD_DONE;
+    }
+
+    rc = SCardReconnect(r->card, SCARD_SHARE_SHARED,
+                        SCARD_PROTOCOL_T0 | SCARD_PROTOCOL_T1,
+                        SCARD_RESET_CARD, &r->protocol);
+    if (rc != SCARD_S_SUCCESS) {
+        fprintf(stderr, "Failed to reconnect to a card reader: %s (0x%lX)\n",
+            pcsc_stringify_error(rc), rc);
+        return VCARD_FAIL;
+    }
+    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);
+    }
+}
+
+static void delete_card_cb(VCardEmul *ve)
+{
+    fprintf(stderr, "TODO, got a delete_card_cb\n");
+}
+
+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) {
+        vcard_delete_applet(applet);
+        vreader_free(reader);
+        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);
+    vreader_free(reader);
+}
+
+static void remove_card(SCardReader *r)
+{
+    LONG rc;
+    memset(r->atr, 0, sizeof(r->atr));
+    r->atrlen = 0;
+
+    rc = SCardDisconnect(r->card, SCARD_LEAVE_CARD);
+    if (rc != SCARD_S_SUCCESS) {
+        fprintf(stderr, "Non fatal info:"
+                        "failed to disconnect card reader: %s (0x%lX)\n",
+            pcsc_stringify_error(rc), rc);
+    }
+    r->card_connected = 0;
+
+    VReader *reader = vreader_get_reader_by_name(r->name);
+    if (!reader) {
+        return;
+    }
+
+    vreader_insert_card(reader, NULL);
+    vreader_free(reader);
+}
+
+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 & ~SCARD_STATE_CHANGED;
+}
+
+/*
+ * This thread looks for card and reader insertions and puts events on the
+ * event queue.
+ */
+static gpointer event_thread(gpointer arg)
+{
+    PCSCContext *pc = (PCSCContext *) arg;
+    DWORD reader_count = 0;
+    SCARD_READERSTATE *reader_states = NULL;
+    LONG rc;
+
+    scan_for_readers(pc);
+
+    do {
+        int i;
+        DWORD timeout = INFINITE;
+
+        g_mutex_lock(&pc->lock);
+        if (pc->readers_changed) {
+            prepare_reader_states(pc, &reader_states, &reader_count);
+            timeout = 0;
+        } else if (reader_count > 1) {
+            timeout = 0;
+        }
+
+        pc->readers_changed = 0;
+        g_mutex_unlock(&pc->lock);
+
+        rc = SCardGetStatusChange(pc->context, timeout, 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;
+        }
+
+        if (rc != SCARD_S_SUCCESS && rc != SCARD_E_TIMEOUT) {
+            fprintf(stderr, "Unexpected SCardGetStatusChange ret %lx(%s)\n",
+                            rc, pcsc_stringify_error(rc));
+            continue;
+        }
+
+        g_mutex_lock(&pc->lock);
+        for (i = 0; i < reader_count; i++) {
+            if (reader_states[i].dwEventState & SCARD_STATE_CHANGED) {
+                process_reader_change(&pc->readers[i], &reader_states[i]);
+                pc->readers_changed++;
+            }
+
+        }
+        g_mutex_unlock(&pc->lock);
+
+        /* libpcsclite is only thread safe at a high level.  If we constantly
+           hold long calls into SCardGetStatusChange, we'll starve any running
+           clients.  So, if we have an active session, and nothing has changed
+           on our front, we just idle.  */
+        if (!pc->readers_changed && reader_count > 1) {
+            usleep(CAPCSC_POLL_TIME * 1000);
+        }
+
+
+    } while (1);
+
+    return NULL;
+}
+
+/*
+ * We poll the PC/SC interface, looking for device changes
+ */
+static int new_event_thread(PCSCContext *pc)
+{
+    pc->thread = g_thread_new("capcsc_event_thread", event_thread, pc);
+    return pc->thread == NULL;
+}
+
+
+static PCSCContext context;
+
+int capcsc_init(void)
+{
+    g_mutex_init(&context.lock);
+
+    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..bb59a4e
--- /dev/null
+++ b/libcacard/capcsc.h
@@ -0,0 +1,18 @@
+/*
+ * 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
+
+#define CAPCSC_APPLET               "CAPCSC APPLET"
+
+int capcsc_init(void);
+
+
+#endif
diff --git a/libcacard/card_7816.c b/libcacard/card_7816.c
index 814fa16..d84d26b 100644
--- a/libcacard/card_7816.c
+++ b/libcacard/card_7816.c
@@ -31,7 +31,7 @@ vcard_response_set_status(VCardResponse *response, vcard_7816_status_t status)
 /*
  * set the status bytes in a response buffer
  */
-static void
+void
 vcard_response_set_status_bytes(VCardResponse *response,
                                unsigned char sw1, unsigned char sw2)
 {
diff --git a/libcacard/card_7816.h b/libcacard/card_7816.h
index 4a01993..c6b3a6b 100644
--- a/libcacard/card_7816.h
+++ b/libcacard/card_7816.h
@@ -31,6 +31,9 @@ VCardResponse *vcard_make_response(vcard_7816_status_t status);
 /* create a raw response (status has already been encoded */
 VCardResponse *vcard_response_new_data(unsigned char *buf, int len);
 
+void
+vcard_response_set_status_bytes(VCardResponse *response,
+                               unsigned char sw1, unsigned char sw2);
 
 
 
diff --git a/libcacard/libcacard.syms b/libcacard/libcacard.syms
index 1697515..e17158f 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
@@ -41,6 +42,7 @@ vcard_response_new
 vcard_response_new_bytes
 vcard_response_new_data
 vcard_response_new_status_bytes
+vcard_response_set_status_bytes
 vcard_select_applet
 vcard_set_applet_private
 vcard_set_atr_func
-- 
1.7.10.4

^ permalink raw reply related	[flat|nested] 20+ messages in thread

* [Qemu-devel] [PATCH v3 6/7] Enable support for passthru (e.g. direct to pcsc) smart cards in the emul_options entry point in libcacard.
  2015-03-13 19:45 [Qemu-devel] [PATCH v3 0/7] Add support for passthru cards to libcacard Jeremy White
                   ` (4 preceding siblings ...)
  2015-03-13 19:45 ` [Qemu-devel] [PATCH v3 5/7] Add a VCARD_DIRECT implemention to the libcacard smartcard support Jeremy White
@ 2015-03-13 19:45 ` Jeremy White
  2015-03-25 15:14   ` Marc-André Lureau
  2015-03-13 19:45 ` [Qemu-devel] [PATCH v3 7/7] Remove the (broken) passthru option Jeremy White
                   ` (2 subsequent siblings)
  8 siblings, 1 reply; 20+ messages in thread
From: Jeremy White @ 2015-03-13 19:45 UTC (permalink / raw)
  To: qemu-devel


Signed-off-by: Jeremy White <jwhite@codeweavers.com>
---
 libcacard/vcard.c           |    2 +-
 libcacard/vcard.h           |    2 +-
 libcacard/vcard_emul_nss.c  |   29 ++++++++++++++++++++++++++++-
 libcacard/vcard_emul_type.c |    3 ++-
 4 files changed, 32 insertions(+), 4 deletions(-)

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 49f0bdf..9b7d54b 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,23 @@ 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);
@@ -1297,5 +1318,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;
      }
-- 
1.7.10.4

^ permalink raw reply related	[flat|nested] 20+ messages in thread

* [Qemu-devel] [PATCH v3 7/7] Remove the (broken) passthru option.
  2015-03-13 19:45 [Qemu-devel] [PATCH v3 0/7] Add support for passthru cards to libcacard Jeremy White
                   ` (5 preceding siblings ...)
  2015-03-13 19:45 ` [Qemu-devel] [PATCH v3 6/7] Enable support for passthru (e.g. direct to pcsc) smart cards in the emul_options entry point in libcacard Jeremy White
@ 2015-03-13 19:45 ` Jeremy White
  2015-03-25 15:14   ` Marc-André Lureau
  2015-03-13 20:11 ` [Qemu-devel] [PATCH v3 0/7] Add support for passthru cards to libcacard Patchew Tool
  2015-03-16 16:22 ` Marc-André Lureau
  8 siblings, 1 reply; 20+ messages in thread
From: Jeremy White @ 2015-03-13 19:45 UTC (permalink / raw)
  To: qemu-devel

That option can be achieved using -e "use_hw=yes hw_type=passthru"

Signed-off-by: Jeremy White <jwhite@codeweavers.com>
---
 libcacard/vscclient.c |   16 +++-------------
 1 file changed, 3 insertions(+), 13 deletions(-)

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

^ permalink raw reply related	[flat|nested] 20+ messages in thread

* Re: [Qemu-devel] [PATCH v3 0/7] Add support for passthru cards to libcacard
  2015-03-13 19:45 [Qemu-devel] [PATCH v3 0/7] Add support for passthru cards to libcacard Jeremy White
                   ` (6 preceding siblings ...)
  2015-03-13 19:45 ` [Qemu-devel] [PATCH v3 7/7] Remove the (broken) passthru option Jeremy White
@ 2015-03-13 20:11 ` Patchew Tool
  2015-03-16 16:22 ` Marc-André Lureau
  8 siblings, 0 replies; 20+ messages in thread
From: Patchew Tool @ 2015-03-13 20:11 UTC (permalink / raw)
  To: qemu-devel


This series passed Patchew automatic testing, but there are some warnings.

Find the log fragments below, or open the following URL to see the full log:

http://qemu.patchew.org/testing/log/<1426275924-1904-1-git-send-email-jwhite@codeweavers.com>

----------8<---------

total: 0 errors, 0 warnings, 74 lines checked

0003-Add-a-configure-check-for-libpcsclite--and-an-option-to-enable-or-disable-it-.patch has no obvious style problems and is ready for submission.

Checking 0004-Add-error-checking-to-vcard_emul_options-.patch
total: 0 errors, 0 warnings, 106 lines checked

0004-Add-error-checking-to-vcard_emul_options-.patch has no obvious style problems and is ready for submission.

Checking 0005-Add-a-VCARD_DIRECT-implemention-to-the-libcacard-smartcard-support-.patch
command failed with exit code 0
$@
WARNING: line over 80 characters
#472: FILE: libcacard/capcsc.c:436:
+        if ((rc == SCARD_S_SUCCESS && (reader_states[reader_count - 1].dwEventState & SCARD_STATE_CHANGED))

total: 0 errors, 1 warnings, 557 lines checked

0005-Add-a-VCARD_DIRECT-implemention-to-the-libcacard-smartcard-support-.patch has style problems, please review.  If any of these errors
are false positives report them to the maintainer, see
CHECKPATCH in MAINTAINERS.

Checking 0006-Enable-support-for-passthru--e-g--direct-to-pcsc--smart-cards-in-the-emul_options-entry-point-in-libcacard-.patch
total: 0 errors, 0 warnings, 82 lines checked

0006-Enable-support-for-passthru--e-g--direct-to-pcsc--smart-cards-in-the-emul_options-entry-point-in-libcacard-.patch has no obvious style problems and is ready for submission.

Checking 0007-Remove-the--broken--passthru-option-.patch
total: 0 errors, 0 warnings, 34 lines checked

0007-Remove-the--broken--passthru-option-.patch has no obvious style problems and is ready for submission.

^ permalink raw reply	[flat|nested] 20+ messages in thread

* Re: [Qemu-devel] [PATCH v3 0/7] Add support for passthru cards to libcacard
  2015-03-13 19:45 [Qemu-devel] [PATCH v3 0/7] Add support for passthru cards to libcacard Jeremy White
                   ` (7 preceding siblings ...)
  2015-03-13 20:11 ` [Qemu-devel] [PATCH v3 0/7] Add support for passthru cards to libcacard Patchew Tool
@ 2015-03-16 16:22 ` Marc-André Lureau
  2015-03-16 16:52   ` Jeremy White
  8 siblings, 1 reply; 20+ messages in thread
From: Marc-André Lureau @ 2015-03-16 16:22 UTC (permalink / raw)
  To: Jeremy White; +Cc: QEMU

Hi Jeremy

I have checked no regression for spice, but not the new pcsc backend.
Do you have a spice-gtk branch or are you using vscclient only?

I would feel better note polluting more the exported symbols from
libcacard. For ex, vcard_init() and cac_card_init() are already quite
useless, since they are called from vcard_emul_init() Would you be
able to init PCSC there too, when it's selected? (there are too many
init or exported functions already)

Why did you export vcard_response_set_status_bytes()?

^ permalink raw reply	[flat|nested] 20+ messages in thread

* Re: [Qemu-devel] [PATCH v3 0/7] Add support for passthru cards to libcacard
  2015-03-16 16:22 ` Marc-André Lureau
@ 2015-03-16 16:52   ` Jeremy White
  2015-03-16 17:19     ` Paolo Bonzini
  0 siblings, 1 reply; 20+ messages in thread
From: Jeremy White @ 2015-03-16 16:52 UTC (permalink / raw)
  To: Marc-André Lureau; +Cc: QEMU

Hey Marc-André,

Thanks for the review.

On 03/16/2015 11:22 AM, Marc-André Lureau wrote:
> Hi Jeremy
> 
> I have checked no regression for spice, but not the new pcsc backend.
> Do you have a spice-gtk branch or are you using vscclient only?

I'm using spice-gtk with this patch:
  http://lists.freedesktop.org/archives/spice-devel/2015-January/018674.html
and then invoking it with --spice-smartcard[=passthru]

> 
> I would feel better note polluting more the exported symbols from
> libcacard. For ex, vcard_init() and cac_card_init() are already quite
> useless, since they are called from vcard_emul_init() Would you be
> able to init PCSC there too, when it's selected? (there are too many
> init or exported functions already)
> 
> Why did you export vcard_response_set_status_bytes()?
> 

It seemed as though the convention was that all non static functions
went into the .syms file; I may have misunderstood.

capcsc_init certainly does not need to be exported; it is only intended
to be used from vcard_emul_init().  I thought I was simply following
existing convention.

For vcard_response_set_status_bytes, I needed that function in capcsc.c.
 Again, I followed what I saw as the convention.  However, that one is
perhaps arguably more useful to export; certainly if you're going to
export the other vcard_response_xxx functions, this would be a useful
one to add to the mix.

Cheers,

Jeremy

^ permalink raw reply	[flat|nested] 20+ messages in thread

* Re: [Qemu-devel] [PATCH v3 0/7] Add support for passthru cards to libcacard
  2015-03-16 16:52   ` Jeremy White
@ 2015-03-16 17:19     ` Paolo Bonzini
  2015-03-25 15:15       ` Marc-André Lureau
  0 siblings, 1 reply; 20+ messages in thread
From: Paolo Bonzini @ 2015-03-16 17:19 UTC (permalink / raw)
  To: Jeremy White, Marc-André Lureau; +Cc: QEMU



On 16/03/2015 17:52, Jeremy White wrote:
> It seemed as though the convention was that all non static functions
> went into the .syms file; I may have misunderstood.

I suspect that the .syms file was introduced as a way to hide utility
functions from libqemuutil.a.  Now that libqemuutil is not used anymore
in libcacard, we should either remove the .syms file or shave its
contents.  This is of course not falling on you.

Paolo

> capcsc_init certainly does not need to be exported; it is only intended
> to be used from vcard_emul_init().  I thought I was simply following
> existing convention.

^ permalink raw reply	[flat|nested] 20+ messages in thread

* Re: [Qemu-devel] [PATCH v3 1/7] Bug fix: delete the reader entry after queueing an event, not before.
  2015-03-13 19:45 ` [Qemu-devel] [PATCH v3 1/7] Bug fix: delete the reader entry after queueing an event, not before Jeremy White
@ 2015-03-25 15:12   ` Marc-André Lureau
  0 siblings, 0 replies; 20+ messages in thread
From: Marc-André Lureau @ 2015-03-25 15:12 UTC (permalink / raw)
  To: Jeremy White; +Cc: QEMU

ACK.

Reviewed-by: Marc-André Lureau <marcandre.lureau@redhat.com>

On Fri, Mar 13, 2015 at 8:45 PM, Jeremy White <jwhite@codeweavers.com> wrote:
> As far as I can tell, the vreader_remove_reader function is not presently in
> use anywhere; I have an upcoming patch set that uses it.
>
> Signed-off-by: Jeremy White <jwhite@codeweavers.com>
> ---
>  libcacard/vreader.c |    2 +-
>  1 file changed, 1 insertion(+), 1 deletion(-)
>
> diff --git a/libcacard/vreader.c b/libcacard/vreader.c
> index 0315dd8..8b10d6a 100644
> --- a/libcacard/vreader.c
> +++ b/libcacard/vreader.c
> @@ -529,8 +529,8 @@ vreader_remove_reader(VReader *reader)
>      }
>      vreader_dequeue(vreader_list, current_entry);
>      vreader_list_unlock();
> -    vreader_list_entry_delete(current_entry);
>      vevent_queue_vevent(vevent_new(VEVENT_READER_REMOVE, reader, NULL));
> +    vreader_list_entry_delete(current_entry);
>      return VREADER_OK;
>  }
>
> --
> 1.7.10.4
>
>



-- 
Marc-André Lureau

^ permalink raw reply	[flat|nested] 20+ messages in thread

* Re: [Qemu-devel] [PATCH v3 2/7] Retrieve the correct TD byte when checking an ATR.
  2015-03-13 19:45 ` [Qemu-devel] [PATCH v3 2/7] Retrieve the correct TD byte when checking an ATR Jeremy White
@ 2015-03-25 15:12   ` Marc-André Lureau
  0 siblings, 0 replies; 20+ messages in thread
From: Marc-André Lureau @ 2015-03-25 15:12 UTC (permalink / raw)
  To: Jeremy White; +Cc: QEMU

ACK.

Reviewed-by: Marc-André Lureau <marcandre.lureau@redhat.com>

On Fri, Mar 13, 2015 at 8:45 PM, Jeremy White <jwhite@codeweavers.com> wrote:
> A physical smartcard with an ATR of
>   3B 95 95 40 FF AE 01 0E 00 00
> was parsed incorrectly.
>
> The '40' should have been the second TD; instead
> the FF is used, incorrectly.
>
> Signed-off-by: Jeremy White <jwhite@codeweavers.com>
> ---
>  hw/usb/ccid-card-passthru.c |    2 +-
>  1 file changed, 1 insertion(+), 1 deletion(-)
>
> diff --git a/hw/usb/ccid-card-passthru.c b/hw/usb/ccid-card-passthru.c
> index 10f1d30..2ae3b81 100644
> --- a/hw/usb/ccid-card-passthru.c
> +++ b/hw/usb/ccid-card-passthru.c
> @@ -168,8 +168,8 @@ static int check_atr(PassthruState *card, uint8_t *data, int len)
>              opt_bytes++;
>          }
>          if (td & 0x8) {
> -            opt_bytes++;
>              td = data[opt_bytes + 2] >> 4;
> +            opt_bytes++;
>          }
>      }
>      if (len < 2 + historical_length + opt_bytes) {
> --
> 1.7.10.4
>
>



-- 
Marc-André Lureau

^ permalink raw reply	[flat|nested] 20+ messages in thread

* Re: [Qemu-devel] [PATCH v3 3/7] Add a configure check for libpcsclite, and an option to enable or disable it.
  2015-03-13 19:45 ` [Qemu-devel] [PATCH v3 3/7] Add a configure check for libpcsclite, and an option to enable or disable it Jeremy White
@ 2015-03-25 15:13   ` Marc-André Lureau
  0 siblings, 0 replies; 20+ messages in thread
From: Marc-André Lureau @ 2015-03-25 15:13 UTC (permalink / raw)
  To: Jeremy White; +Cc: QEMU

ACK.

Reviewed-by: Marc-André Lureau <marcandre.lureau@redhat.com>

On Fri, Mar 13, 2015 at 8:45 PM, Jeremy White <jwhite@codeweavers.com> wrote:
>
> Signed-off-by: Jeremy White <jwhite@codeweavers.com>
> ---
>  configure |   38 ++++++++++++++++++++++++++++++++++++++
>  1 file changed, 38 insertions(+)
>
> diff --git a/configure b/configure
> index b858756..f22dbae 100755
> --- a/configure
> +++ b/configure
> @@ -307,6 +307,7 @@ trace_file="trace"
>  spice=""
>  rbd=""
>  smartcard_nss=""
> +smartcard_pcsc=""
>  libusb=""
>  usb_redir=""
>  opengl=""
> @@ -1043,6 +1044,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"
> @@ -1373,6 +1378,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
> @@ -3723,6 +3730,30 @@ EOF
>      fi
>  fi
>
> +# check for pcsclite for smartcard passthru support
> +# TODO - Add support for Winscard
> +if test "$smartcard_pcsc" != "no"; then
> +  cat > $TMPC << EOF
> +#include <winscard.h>
> +int main(void) { SCardEstablishContext(0, 0, 0, 0); return 0; }
> +EOF
> +    pcsc_libs="$($pkg_config --libs libpcsclite 2>/dev/null)"
> +    pcsc_cflags="$($pkg_config --cflags libpcsclite 2>/dev/null)"
> +    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
> @@ -4396,6 +4427,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 "OpenGL support    $opengl"
> @@ -4756,6 +4788,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
> --
> 1.7.10.4
>
>



-- 
Marc-André Lureau

^ permalink raw reply	[flat|nested] 20+ messages in thread

* Re: [Qemu-devel] [PATCH v3 4/7] Add error checking to vcard_emul_options.
  2015-03-13 19:45 ` [Qemu-devel] [PATCH v3 4/7] Add error checking to vcard_emul_options Jeremy White
@ 2015-03-25 15:13   ` Marc-André Lureau
  0 siblings, 0 replies; 20+ messages in thread
From: Marc-André Lureau @ 2015-03-25 15:13 UTC (permalink / raw)
  To: Jeremy White; +Cc: QEMU

ACK.

Reviewed-by: Marc-André Lureau <marcandre.lureau@redhat.com>

On Fri, Mar 13, 2015 at 8:45 PM, Jeremy White <jwhite@codeweavers.com> wrote:
> Also add an alias of 'nssemul' for the default card
> of hw=yes hw_type=cac, and an alias of 'passthru' for
> the new card type of hw=yes hw_type=passthru.
>
> This allows the spice-gtk client to take a more human
> friendly set of arguments and relay them through to this code.
>
> Signed-off-by: Jeremy White <jwhite@codeweavers.com>
> ---
>  libcacard/vcard_emul_nss.c |   49 ++++++++++++++++++++++++++++++++++----------
>  1 file changed, 38 insertions(+), 11 deletions(-)
>
> diff --git a/libcacard/vcard_emul_nss.c b/libcacard/vcard_emul_nss.c
> index 950edee..49f0bdf 100644
> --- a/libcacard/vcard_emul_nss.c
> +++ b/libcacard/vcard_emul_nss.c
> @@ -1113,17 +1113,13 @@ static VCardEmulOptions options;
>   * and its length in "token_length". "token" will not be nul-terminated.
>   * After calling the macro, "args" will be advanced to the beginning of
>   * the next token.
> - * This macro may call continue or break.
>   */
>  #define NEXT_TOKEN(token) \
>              (token) = args; \
>              args = strpbrk(args, ",)"); \
> -            if (*args == 0) { \
> -                break; \
> -            } \
> -            if (*args == ')') { \
> -                args++; \
> -                continue; \
> +            if (*args == 0 || *args == ')') { \
> +                fprintf(stderr, "Error: invalid soft specification.\n"); \
> +                return NULL; \
>              } \
>              (token##_length) = args - (token); \
>              args = strip(args+1);
> @@ -1141,6 +1137,7 @@ vcard_emul_options(const char *args)
>      do {
>          args = strip(args); /* strip off the leading spaces */
>          if (*args == ',') {
> +            args++;
>              continue;
>          }
>          /* soft=(slot_name,virt_name,emul_type,emul_flags,cert_1, (no eol)
> @@ -1159,7 +1156,8 @@ vcard_emul_options(const char *args)
>
>              args = strip(args + 5);
>              if (*args != '(') {
> -                continue;
> +                fprintf(stderr, "Error: invalid soft specification.\n");
> +                return NULL;
>              }
>              args = strip(args+1);
>
> @@ -1170,11 +1168,17 @@ vcard_emul_options(const char *args)
>              memcpy(type_str, type_params, type_params_length);
>              type_str[type_params_length] = '\0';
>              type = vcard_emul_type_from_string(type_str);
> +            if (type == VCARD_EMUL_NONE) {
> +                fprintf(stderr, "Error: invalid smartcard type '%s'.\n",
> +                        type_str);
> +                return NULL;
> +            }
>
>              NEXT_TOKEN(type_params)
>
>              if (*args == 0) {
> -                break;
> +                fprintf(stderr, "Error: missing cert specification.\n");
> +                return NULL;
>              }
>
>              if (opts->vreader_count >= reader_count) {
> @@ -1214,6 +1218,11 @@ vcard_emul_options(const char *args)
>          } else if (strncmp(args, "hw_type=", 8) == 0) {
>              args = strip(args+8);
>              opts->hw_card_type = vcard_emul_type_from_string(args);
> +            if (opts->hw_card_type == VCARD_EMUL_NONE) {
> +                fprintf(stderr, "Error: invalid smartcard type '%s'.\n",
> +                        args);
> +                return NULL;
> +            }
>              args = find_blank(args);
>          /* hw_params= */
>          } else if (strncmp(args, "hw_params=", 10) == 0) {
> @@ -1227,7 +1236,8 @@ vcard_emul_options(const char *args)
>              const char *db;
>              args = strip(args+3);
>              if (*args != '"') {
> -                continue;
> +                fprintf(stderr, "Error: you must quote the file path.\n");
> +                return NULL;
>              }
>              args++;
>              db = args;
> @@ -1236,8 +1246,21 @@ vcard_emul_options(const char *args)
>              if (*args != 0) {
>                  args++;
>              }
> +        } else if (strncmp(args, "nssemul", 7) == 0) {
> +            opts->hw_card_type = VCARD_EMUL_CAC;
> +            opts->use_hw = PR_TRUE;
> +            args = find_blank(args + 7);
> +        /* nssemul */
> +#if defined(CONFIG_SMARTCARD_PCSC)
> +        } else if (strncmp(args, "passthru", 8) == 0) {
> +            opts->hw_card_type = VCARD_EMUL_PASSTHRU;
> +            opts->use_hw = PR_TRUE;
> +            args = find_blank(args + 8);
> +        /* passthru */
> +#endif
>          } else {
> -            args = find_blank(args);
> +            fprintf(stderr, "Error: Unknown smartcard specification.\n");
> +            return NULL;
>          }
>      } while (*args != 0);
>
> @@ -1253,6 +1276,10 @@ vcard_emul_usage(void)
>  " use_hw=[yes|no]                 (default yes)\n"
>  " hw_type={card_type_to_emulate}  (default CAC)\n"
>  " hw_param={param_for_card}       (default \"\")\n"
> +" nssemul                         (alias for use_hw=yes, hw_type=CAC)\n"
> +#if defined(CONFIG_SMARTCARD_PCSC)
> +" passthru                        (alias for use_hw=yes, hw_type=PASSTHRU)\n"
> +#endif
>  " soft=({slot_name},{vreader_name},{card_type_to_emulate},{params_for_card},\n"
>  "       {cert1},{cert2},{cert3}    (default none)\n"
>  "\n"
> --
> 1.7.10.4
>
>



-- 
Marc-André Lureau

^ permalink raw reply	[flat|nested] 20+ messages in thread

* Re: [Qemu-devel] [PATCH v3 5/7] Add a VCARD_DIRECT implemention to the libcacard smartcard support.
  2015-03-13 19:45 ` [Qemu-devel] [PATCH v3 5/7] Add a VCARD_DIRECT implemention to the libcacard smartcard support Jeremy White
@ 2015-03-25 15:13   ` Marc-André Lureau
  0 siblings, 0 replies; 20+ messages in thread
From: Marc-André Lureau @ 2015-03-25 15:13 UTC (permalink / raw)
  To: Jeremy White; +Cc: QEMU

ACK.

Reviewed-by: Marc-André Lureau <marcandre.lureau@redhat.com>

(the extra symbols should be shaved in a future libcacard update)

On Fri, Mar 13, 2015 at 8:45 PM, Jeremy White <jwhite@codeweavers.com> wrote:
> This uses libpcsclite to provide direct communication with a smartcard.
>
> Signed-off-by: Jeremy White <jwhite@codeweavers.com>
> ---
>  Makefile.objs            |    5 +
>  libcacard/capcsc.c       |  498 ++++++++++++++++++++++++++++++++++++++++++++++
>  libcacard/capcsc.h       |   18 ++
>  libcacard/card_7816.c    |    2 +-
>  libcacard/card_7816.h    |    3 +
>  libcacard/libcacard.syms |    2 +
>  6 files changed, 527 insertions(+), 1 deletion(-)
>  create mode 100644 libcacard/capcsc.c
>  create mode 100644 libcacard/capcsc.h
>
> diff --git a/Makefile.objs b/Makefile.objs
> index 28999d3..cc31b29 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/libcacard/capcsc.c b/libcacard/capcsc.c
> new file mode 100644
> index 0000000..a1030df
> --- /dev/null
> +++ b/libcacard/capcsc.c
> @@ -0,0 +1,498 @@
> +/*
> + * 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.
> + */
> +
> +#include <glib.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 {
> +    PCSCContext *context;
> +    int index;
> +    char *name;
> +    DWORD protocol;
> +    DWORD state;
> +    SCARDHANDLE card;
> +    BYTE atr[MAX_ATR_SIZE];
> +    DWORD atrlen;
> +    int card_connected;
> +    unsigned long request_count;
> +} SCardReader;
> +
> +typedef struct _PCSCContext {
> +    SCARDCONTEXT context;
> +    SCardReader readers[CAPCSC_MAX_READERS];
> +    int reader_count;
> +    int readers_changed;
> +    GThread *thread;
> +    CompatGMutex lock;
> +} PCSCContext;
> +
> +
> +static void delete_reader(PCSCContext *pc, int i)
> +{
> +    SCardReader *r = &pc->readers[i];
> +    g_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;
> +
> +    g_mutex_lock(&r->context->lock);
> +    delete_reader(r->context, r->index);
> +    g_mutex_unlock(&r->context->lock);
> +}
> +
> +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;
> +    }
> +
> +    r = &pc->readers[pc->reader_count];
> +    memset(r, 0, sizeof(*r));
> +    r->index = pc->reader_count++;
> +    r->context = pc;
> +    r->name = g_strdup(name);
> +
> +    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];
> +
> +    g_mutex_lock(&pc->lock);
> +
> +    for (i = 0; i < CAPCSC_MAX_READERS; i++) {
> +        matches[i] = 0;
> +    }
> +
> +    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:
> +    i = pc->reader_count - 1;
> +    g_mutex_unlock(&pc->lock);
> +
> +    for (; i >= 0; i--) {
> +        if (!matches[i]) {
> +            VReader *reader = vreader_get_reader_by_name(pc->readers[i].name);
> +            if (reader) {
> +                vreader_free(reader);
> +                vreader_remove_reader(reader);
> +            }
> +        }
> +    }
> +
> +
> +    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) {
> +        g_free(*states);
> +    }
> +
> +    *reader_count = pc->reader_count;
> +
> +    (*reader_count)++;
> +    *states = g_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;
> +    r->request_count = 0;
> +
> +    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);
> +    BYTE outbuf[4096];
> +    DWORD outlen = sizeof(outbuf);
> +    LONG rc;
> +
> +    rc = send_receive(r, apdu->a_data, apdu->a_len, outbuf, &outlen);
> +    if (rc || outlen < 2) {
> +        ret = VCARD_FAIL;
> +    } else {
> +        *response = vcard_response_new_data(outbuf, outlen - 2);
> +        if (*response == NULL) {
> +            return VCARD_FAIL;
> +        }
> +        vcard_response_set_status_bytes(*response, outbuf[outlen - 2],
> +                                                   outbuf[outlen - 1]);
> +    }
> +
> +    return ret;
> +}
> +
> +static VCardStatus reset_cb(VCard *card, int channel)
> +{
> +    SCardReader *r = (SCardReader *) vcard_get_private(card);
> +    LONG rc;
> +
> +    /* vreader_power_on is a bit too free with it's resets.
> +       And a reconnect is expensive; as much as 10-20 seconds.
> +       Hence, we discard any initial reconnect request. */
> +    if (r->request_count++ == 0) {
> +        return VCARD_DONE;
> +    }
> +
> +    rc = SCardReconnect(r->card, SCARD_SHARE_SHARED,
> +                        SCARD_PROTOCOL_T0 | SCARD_PROTOCOL_T1,
> +                        SCARD_RESET_CARD, &r->protocol);
> +    if (rc != SCARD_S_SUCCESS) {
> +        fprintf(stderr, "Failed to reconnect to a card reader: %s (0x%lX)\n",
> +            pcsc_stringify_error(rc), rc);
> +        return VCARD_FAIL;
> +    }
> +    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);
> +    }
> +}
> +
> +static void delete_card_cb(VCardEmul *ve)
> +{
> +    fprintf(stderr, "TODO, got a delete_card_cb\n");
> +}
> +
> +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) {
> +        vcard_delete_applet(applet);
> +        vreader_free(reader);
> +        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);
> +    vreader_free(reader);
> +}
> +
> +static void remove_card(SCardReader *r)
> +{
> +    LONG rc;
> +    memset(r->atr, 0, sizeof(r->atr));
> +    r->atrlen = 0;
> +
> +    rc = SCardDisconnect(r->card, SCARD_LEAVE_CARD);
> +    if (rc != SCARD_S_SUCCESS) {
> +        fprintf(stderr, "Non fatal info:"
> +                        "failed to disconnect card reader: %s (0x%lX)\n",
> +            pcsc_stringify_error(rc), rc);
> +    }
> +    r->card_connected = 0;
> +
> +    VReader *reader = vreader_get_reader_by_name(r->name);
> +    if (!reader) {
> +        return;
> +    }
> +
> +    vreader_insert_card(reader, NULL);
> +    vreader_free(reader);
> +}
> +
> +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 & ~SCARD_STATE_CHANGED;
> +}
> +
> +/*
> + * This thread looks for card and reader insertions and puts events on the
> + * event queue.
> + */
> +static gpointer event_thread(gpointer arg)
> +{
> +    PCSCContext *pc = (PCSCContext *) arg;
> +    DWORD reader_count = 0;
> +    SCARD_READERSTATE *reader_states = NULL;
> +    LONG rc;
> +
> +    scan_for_readers(pc);
> +
> +    do {
> +        int i;
> +        DWORD timeout = INFINITE;
> +
> +        g_mutex_lock(&pc->lock);
> +        if (pc->readers_changed) {
> +            prepare_reader_states(pc, &reader_states, &reader_count);
> +            timeout = 0;
> +        } else if (reader_count > 1) {
> +            timeout = 0;
> +        }
> +
> +        pc->readers_changed = 0;
> +        g_mutex_unlock(&pc->lock);
> +
> +        rc = SCardGetStatusChange(pc->context, timeout, 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;
> +        }
> +
> +        if (rc != SCARD_S_SUCCESS && rc != SCARD_E_TIMEOUT) {
> +            fprintf(stderr, "Unexpected SCardGetStatusChange ret %lx(%s)\n",
> +                            rc, pcsc_stringify_error(rc));
> +            continue;
> +        }
> +
> +        g_mutex_lock(&pc->lock);
> +        for (i = 0; i < reader_count; i++) {
> +            if (reader_states[i].dwEventState & SCARD_STATE_CHANGED) {
> +                process_reader_change(&pc->readers[i], &reader_states[i]);
> +                pc->readers_changed++;
> +            }
> +
> +        }
> +        g_mutex_unlock(&pc->lock);
> +
> +        /* libpcsclite is only thread safe at a high level.  If we constantly
> +           hold long calls into SCardGetStatusChange, we'll starve any running
> +           clients.  So, if we have an active session, and nothing has changed
> +           on our front, we just idle.  */
> +        if (!pc->readers_changed && reader_count > 1) {
> +            usleep(CAPCSC_POLL_TIME * 1000);
> +        }
> +
> +
> +    } while (1);
> +
> +    return NULL;
> +}
> +
> +/*
> + * We poll the PC/SC interface, looking for device changes
> + */
> +static int new_event_thread(PCSCContext *pc)
> +{
> +    pc->thread = g_thread_new("capcsc_event_thread", event_thread, pc);
> +    return pc->thread == NULL;
> +}
> +
> +
> +static PCSCContext context;
> +
> +int capcsc_init(void)
> +{
> +    g_mutex_init(&context.lock);
> +
> +    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..bb59a4e
> --- /dev/null
> +++ b/libcacard/capcsc.h
> @@ -0,0 +1,18 @@
> +/*
> + * 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
> +
> +#define CAPCSC_APPLET               "CAPCSC APPLET"
> +
> +int capcsc_init(void);
> +
> +
> +#endif
> diff --git a/libcacard/card_7816.c b/libcacard/card_7816.c
> index 814fa16..d84d26b 100644
> --- a/libcacard/card_7816.c
> +++ b/libcacard/card_7816.c
> @@ -31,7 +31,7 @@ vcard_response_set_status(VCardResponse *response, vcard_7816_status_t status)
>  /*
>   * set the status bytes in a response buffer
>   */
> -static void
> +void
>  vcard_response_set_status_bytes(VCardResponse *response,
>                                 unsigned char sw1, unsigned char sw2)
>  {
> diff --git a/libcacard/card_7816.h b/libcacard/card_7816.h
> index 4a01993..c6b3a6b 100644
> --- a/libcacard/card_7816.h
> +++ b/libcacard/card_7816.h
> @@ -31,6 +31,9 @@ VCardResponse *vcard_make_response(vcard_7816_status_t status);
>  /* create a raw response (status has already been encoded */
>  VCardResponse *vcard_response_new_data(unsigned char *buf, int len);
>
> +void
> +vcard_response_set_status_bytes(VCardResponse *response,
> +                               unsigned char sw1, unsigned char sw2);
>
>
>
> diff --git a/libcacard/libcacard.syms b/libcacard/libcacard.syms
> index 1697515..e17158f 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
> @@ -41,6 +42,7 @@ vcard_response_new
>  vcard_response_new_bytes
>  vcard_response_new_data
>  vcard_response_new_status_bytes
> +vcard_response_set_status_bytes
>  vcard_select_applet
>  vcard_set_applet_private
>  vcard_set_atr_func
> --
> 1.7.10.4
>
>



-- 
Marc-André Lureau

^ permalink raw reply	[flat|nested] 20+ messages in thread

* Re: [Qemu-devel] [PATCH v3 6/7] Enable support for passthru (e.g. direct to pcsc) smart cards in the emul_options entry point in libcacard.
  2015-03-13 19:45 ` [Qemu-devel] [PATCH v3 6/7] Enable support for passthru (e.g. direct to pcsc) smart cards in the emul_options entry point in libcacard Jeremy White
@ 2015-03-25 15:14   ` Marc-André Lureau
  0 siblings, 0 replies; 20+ messages in thread
From: Marc-André Lureau @ 2015-03-25 15:14 UTC (permalink / raw)
  To: Jeremy White; +Cc: QEMU

ACK.

Reviewed-by: Marc-André Lureau <marcandre.lureau@redhat.com>

On Fri, Mar 13, 2015 at 8:45 PM, Jeremy White <jwhite@codeweavers.com> wrote:
>
> Signed-off-by: Jeremy White <jwhite@codeweavers.com>
> ---
>  libcacard/vcard.c           |    2 +-
>  libcacard/vcard.h           |    2 +-
>  libcacard/vcard_emul_nss.c  |   29 ++++++++++++++++++++++++++++-
>  libcacard/vcard_emul_type.c |    3 ++-
>  4 files changed, 32 insertions(+), 4 deletions(-)
>
> 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 49f0bdf..9b7d54b 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,23 @@ 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);
> @@ -1297,5 +1318,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;
>       }
> --
> 1.7.10.4
>
>



-- 
Marc-André Lureau

^ permalink raw reply	[flat|nested] 20+ messages in thread

* Re: [Qemu-devel] [PATCH v3 7/7] Remove the (broken) passthru option.
  2015-03-13 19:45 ` [Qemu-devel] [PATCH v3 7/7] Remove the (broken) passthru option Jeremy White
@ 2015-03-25 15:14   ` Marc-André Lureau
  0 siblings, 0 replies; 20+ messages in thread
From: Marc-André Lureau @ 2015-03-25 15:14 UTC (permalink / raw)
  To: Jeremy White; +Cc: QEMU

ACK.

Reviewed-by: Marc-André Lureau <marcandre.lureau@redhat.com>

On Fri, Mar 13, 2015 at 8:45 PM, Jeremy White <jwhite@codeweavers.com> wrote:
> That option can be achieved using -e "use_hw=yes hw_type=passthru"
>
> Signed-off-by: Jeremy White <jwhite@codeweavers.com>
> ---
>  libcacard/vscclient.c |   16 +++-------------
>  1 file changed, 3 insertions(+), 13 deletions(-)
>
> 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
>
>



-- 
Marc-André Lureau

^ permalink raw reply	[flat|nested] 20+ messages in thread

* Re: [Qemu-devel] [PATCH v3 0/7] Add support for passthru cards to libcacard
  2015-03-16 17:19     ` Paolo Bonzini
@ 2015-03-25 15:15       ` Marc-André Lureau
  0 siblings, 0 replies; 20+ messages in thread
From: Marc-André Lureau @ 2015-03-25 15:15 UTC (permalink / raw)
  To: Paolo Bonzini; +Cc: Jeremy White, QEMU

Hi

On Mon, Mar 16, 2015 at 6:19 PM, Paolo Bonzini <pbonzini@redhat.com> wrote:
> On 16/03/2015 17:52, Jeremy White wrote:
>> It seemed as though the convention was that all non static functions
>> went into the .syms file; I may have misunderstood.
>
> I suspect that the .syms file was introduced as a way to hide utility
> functions from libqemuutil.a.  Now that libqemuutil is not used anymore
> in libcacard, we should either remove the .syms file or shave its
> contents.  This is of course not falling on you.


Ok, let's try to get this series first, and we can look at cleaning
the library symbols after.

-- 
Marc-André Lureau

^ permalink raw reply	[flat|nested] 20+ messages in thread

end of thread, other threads:[~2015-03-25 15:15 UTC | newest]

Thread overview: 20+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2015-03-13 19:45 [Qemu-devel] [PATCH v3 0/7] Add support for passthru cards to libcacard Jeremy White
2015-03-13 19:45 ` [Qemu-devel] [PATCH v3 1/7] Bug fix: delete the reader entry after queueing an event, not before Jeremy White
2015-03-25 15:12   ` Marc-André Lureau
2015-03-13 19:45 ` [Qemu-devel] [PATCH v3 2/7] Retrieve the correct TD byte when checking an ATR Jeremy White
2015-03-25 15:12   ` Marc-André Lureau
2015-03-13 19:45 ` [Qemu-devel] [PATCH v3 3/7] Add a configure check for libpcsclite, and an option to enable or disable it Jeremy White
2015-03-25 15:13   ` Marc-André Lureau
2015-03-13 19:45 ` [Qemu-devel] [PATCH v3 4/7] Add error checking to vcard_emul_options Jeremy White
2015-03-25 15:13   ` Marc-André Lureau
2015-03-13 19:45 ` [Qemu-devel] [PATCH v3 5/7] Add a VCARD_DIRECT implemention to the libcacard smartcard support Jeremy White
2015-03-25 15:13   ` Marc-André Lureau
2015-03-13 19:45 ` [Qemu-devel] [PATCH v3 6/7] Enable support for passthru (e.g. direct to pcsc) smart cards in the emul_options entry point in libcacard Jeremy White
2015-03-25 15:14   ` Marc-André Lureau
2015-03-13 19:45 ` [Qemu-devel] [PATCH v3 7/7] Remove the (broken) passthru option Jeremy White
2015-03-25 15:14   ` Marc-André Lureau
2015-03-13 20:11 ` [Qemu-devel] [PATCH v3 0/7] Add support for passthru cards to libcacard Patchew Tool
2015-03-16 16:22 ` Marc-André Lureau
2015-03-16 16:52   ` Jeremy White
2015-03-16 17:19     ` Paolo Bonzini
2015-03-25 15:15       ` Marc-André Lureau

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.