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

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

 



Hi folks,

I've been working a lot with smartcards for Xspice.

As I've done this, I've come to understand that the Spice client doesn't actually send the physical smartcard data across; instead it sends virtualized smartcard apdus, using PK11 information it gets from libnss.

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?

2. Anyone know what the origin of the VCARD_DIRECT code path was? I use it here. git-blame pins it back to the original libcacard commit; not sure where it came from before then. I was trying to find an alternate consumer of that code to make sure I was aligned with it.

I believe that, with this change, a system that was not otherwise using a smart card could relay that smart card on to a distant Spice server. I'm uncertain what would happen in the case where the smart card was in use by the local system. That's something I'll need to probe yet. I imagine that it won't work, but have no real hard evidence for that :-/.

Cheers,

Jeremy
>From 0a1f57d387e8ea9dd4a20218cab0a5d1ab501dbd 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            |    3 +
 configure                |    4 +-
 libcacard/capcsc.c       |  576 ++++++++++++++++++++++++++++++++++++++++++++++
 libcacard/capcsc.h       |   16 ++
 libcacard/libcacard.syms |    1 +
 libcacard/vcard.c        |    2 +-
 libcacard/vcard.h        |    2 +-
 7 files changed, 600 insertions(+), 4 deletions(-)
 create mode 100644 libcacard/capcsc.c
 create mode 100644 libcacard/capcsc.h

diff --git a/Makefile.objs b/Makefile.objs
index abeb902..560457c 100644
--- a/Makefile.objs
+++ b/Makefile.objs
@@ -30,6 +30,9 @@ libcacard-y += libcacard/vcard_emul_nss.o
 libcacard-y += libcacard/vcard_emul_type.o
 libcacard-y += libcacard/card_7816.o
 libcacard-y += libcacard/vcardt.o
+# Todo make it conditional
+libcacard-y += libcacard/capcsc.o
+libcacard/capcsc.o-cflags := $(NSS_CFLAGS)
 libcacard/vcard_emul_nss.o-cflags := $(NSS_CFLAGS)
 libcacard/vcard_emul_nss.o-libs := $(NSS_LIBS)
 
diff --git a/configure b/configure
index cae588c..c5f9756 100755
--- a/configure
+++ b/configure
@@ -3626,8 +3626,8 @@ if test "$smartcard_nss" != "no"; then
 int main(void) { PK11_FreeSlot(0); return 0; }
 EOF
     # FIXME: do not include $glib_* in here
-    nss_libs="$($pkg_config --libs nss 2>/dev/null) $glib_libs"
-    nss_cflags="$($pkg_config --cflags nss 2>/dev/null) $glib_cflags"
+    nss_libs="$($pkg_config --libs nss libpcsclite 2>/dev/null) $glib_libs"
+    nss_cflags="$($pkg_config --cflags nss libpcsclite 2>/dev/null) $glib_cflags"
     test_cflags="$nss_cflags"
     # The header files in nss < 3.13.3 have a bug which causes them to
     # emit a warning. If we're going to compile QEMU with -Werror, then
diff --git a/libcacard/capcsc.c b/libcacard/capcsc.c
new file mode 100644
index 0000000..fd29510
--- /dev/null
+++ b/libcacard/capcsc.c
@@ -0,0 +1,576 @@
+/*
+ * 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)
+{
+    /* TODO - these aren't safe for reuse by both capcsc and vcard_emul */
+    vreader_init();
+    vevent_queue_init();
+
+    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
-- 
1.7.10.4

>From 2c3aff8c46feef6aee1e1e61374f2a891ebb4b81 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 |   31 +++++++++++++++++++++++--------
 gtk/spice-option.c      |    5 +++++
 gtk/spice-session.c     |   27 +++++++++++++++++++++++++++
 3 files changed, 55 insertions(+), 8 deletions(-)

diff --git a/gtk/smartcard-manager.c b/gtk/smartcard-manager.c
index 9e228e9..a3a6713 100644
--- a/gtk/smartcard-manager.c
+++ b/gtk/smartcard-manager.c
@@ -24,6 +24,7 @@
 
 #ifdef USE_SMARTCARD
 #include <vcard_emul.h>
+#include <capcsc.h>
 #include <vevent.h>
 #include <vreader.h>
 #endif
@@ -416,6 +417,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 +426,8 @@ static gboolean smartcard_manager_init(SmartcardManagerInitArgs *args)
                  "smartcard-certificates", &certificates,
                  NULL);
 
+    g_object_get(G_OBJECT(args->session), "direct-smartcard", &direct_smartcard, NULL);
+
     if ((certificates == NULL) || (g_strv_length(certificates) != 3))
         goto init;
 
@@ -452,14 +456,25 @@ static gboolean smartcard_manager_init(SmartcardManagerInitArgs *args)
         goto end;
 
 init:
-    SPICE_DEBUG("vcard_emul_init");
-    emul_init_status = vcard_emul_init(options);
-    if ((emul_init_status != VCARD_EMUL_OK)
-            && (emul_init_status != VCARD_EMUL_INIT_ALREADY_INITED)) {
-        args->err = g_error_new(SPICE_CLIENT_ERROR,
-                                SPICE_CLIENT_ERROR_FAILED,
-                                "Failed to initialize smartcard");
-        goto end;
+    if (direct_smartcard) {
+        if (capcsc_init()) {
+            args->err = g_error_new(SPICE_CLIENT_ERROR,
+                                    SPICE_CLIENT_ERROR_FAILED,
+                                    "Failed to initialize direct smartcard");
+            goto end;
+        }
+    }
+
+    else {
+        SPICE_DEBUG("vcard_emul_init");
+        emul_init_status = vcard_emul_init(options);
+        if ((emul_init_status != VCARD_EMUL_OK)
+                && (emul_init_status != VCARD_EMUL_INIT_ALREADY_INITED)) {
+            args->err = g_error_new(SPICE_CLIENT_ERROR,
+                                    SPICE_CLIENT_ERROR_FAILED,
+                                    "Failed to initialize smartcard");
+            goto end;
+        }
     }
 
     retval = TRUE;
diff --git a/gtk/spice-option.c b/gtk/spice-option.c
index 958e03c..fc6dbd4 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,8 @@ 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);
     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
-- 
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]