All of lore.kernel.org
 help / color / mirror / Atom feed
* [Qemu-devel] [PATCH 0/7] usb-ccid (v15)
@ 2011-01-11  8:42 Alon Levy
  2011-01-11  8:42 ` [Qemu-devel] [PATCH 1/7] usb-ccid: add CCID bus Alon Levy
                   ` (8 more replies)
  0 siblings, 9 replies; 23+ messages in thread
From: Alon Levy @ 2011-01-11  8:42 UTC (permalink / raw)
  To: qemu-devel

This patchset adds three new devices, usb-ccid, ccid-card-passthru and
ccid-card-emulated, providing a CCID bus, a simple passthru protocol
implementing card requiring a client, and a standalone emulated card.

It also introduces a new directory libcaccard with CAC card emulation,
CAC is a type of ISO 7816 smart card.

Tree for pull: git://anongit.freedesktop.org/~alon/qemu usb_ccid.v15

v14-v15 changes:
 * add patch with --enable-smartcard and --disable-smartcard and only
  disable ccid-card-emulated if nss not found.
 * add patch with description strings
 * s/libcaccard/libcacard/ in docs/ccid.txt

v13-v14 changes:
 - support device_del/device_add on ccid-card-* and usb-ccid
 * usb-ccid:
  * lose card reference when card device deleted
  * check slot number and deny adding a slot if one is already added.
 * ccid-card-*: use qdev_simple_unplug_cb in both emulated and passthru ccid cards,
   the exitfn already takes care of triggering card removal in the usb dev.
 * libcacard:
  * remove double include of config-host.mak
  * add replay of card events to libcacard to support second and more emulation
  * don't initialize more then once (doesn't support it right now, so one
   thread, NSS thread, is left when device_del is done)
  * add VCARD_EMUL_INIT_ALREADY_INITED
 * ccid-card-emulated:
  * take correct mutexes on signaling to fix deadlocks on device_del
  * allow card insertion/removal event without proper reader insertion event

v12-v13 changes:
 * libcacard:
  * fix Makefile clean to remove vscclient
  * fix double include of config-host in Makefile
 * usb-ccid: remove attach/detach logic, usb is always attached. Guest
  doesn't care if there is a reader attached with no card anyway.
 * ccid-card-passthru: don't close chr_dev on removal, makes it possible
  to use device_del/device_add to create remove/insertion for debugging.

v11-v12 changes:
 * fix out of tree build

v10-v11 changes:
 * fix last patch that removed one of the doc files.
 * updated flow table in docs/ccid.txt

v8-v10 changes:
 * usb-ccid:
  * add slot for future use (Gerd)
  * ifdef ENABLE_MIGRATION for migration support on account of usb
   migration not being ready in general. (Gerd)
 * verbosified commit messages. (Gerd)
 * put libcacard docs in libcacard commit. (Gerd)

v8-v9 changes:
 * Blue Swirl comments:
  * white space fixes
  * enabled by default, disabled only if missing nss
  * forgotten fix from v8 (don't build libcacard.so)
 * added a note about device being little endian
 * library renamed from libcaccard to libcacard
 * squashed both of libcacard patches, they touched different files anyway.

v7-v8 changes:
 * Blue Swirl comments:
  * usb-ccid: deannonymize some structs
  * usb-ccid: coding style change - answer_t and bulk_in_t fixed
  * usb-ccid: handle endianess conversion between guest and host
 * usb-ccid: s/ccid_bulk_in_copy_out/ccid_bulk_in_copy_to_guest/
 * ccid-card-emulated: fix segfault if backend not specified
 * ccid-card-emulated: let last reader inserted win
 * libcaccard: remove double vscard_common.h

v6->v7 changes:
 * external libcaccard became internal directory libcaccard
  * statically link object files into qemu
  * produce libcaccard.so for usage by external projects
  * applied coding style to new code (please check me)
  - did not use the qemu options parsing for libcaccard, since
   it seems to draw large amounts of qemu code (monitor for instance).

v5->v6 changes:
 * really remove static debug (I apologize for claiming to have done so before)

v4->v5 changes:
 * rebased to latest
 * remove static debug in card devices
 * fix --enable-smartcard to link
 * stall instead of assert when exceeding BULK_OUT_DATA_SIZE
 * make ccid_reserve_recv_buf for too large len discard message, not exit
 * make ccid_reserve_recv_buf return void*
 * fix typo
 * remove commented code in VMState

v3->v4:
 * remove ccid field in CCIDBus
 * remove static debug in bus
 * add back docs

v2->v3:
 * split into bus (usb-ccid.c, uses ccid.h) and card (ccid-card-passthru.c).
 * removed documentation (being revised).

v1->v2:
 * all QSIMPLEQ turned into fixed sized rings
 * all allocated buffers turned into fixed size buffers
 * added migration support
 * added a message to tell client qemu has migrated to ip:port
  * for lack of monitor commands ip:port are 0:0, which causes the updated
   vscclient to connect to one port higher on the same host. will add monitor
   commands in a separate patch. tested with current setup.

Alon Levy (6):
  usb-ccid: add CCID bus
  ccid: add passthru card device
  ccid: add ccid-card-emulated device (v2)
  ccid: add docs
  ccid: configure: add --enable/disable and nss only disable
  ccid: add qdev description strings

Robert Relyea (1):
  libcacard: initial commit after coding style fixes

 Makefile                    |    6 +-
 Makefile.objs               |    7 +
 Makefile.target             |    2 +
 configure                   |   48 ++
 docs/ccid.txt               |  135 +++++
 docs/libcacard.txt          |  483 +++++++++++++++
 hw/ccid-card-emulated.c     |  535 +++++++++++++++++
 hw/ccid-card-passthru.c     |  273 +++++++++
 hw/ccid.h                   |   35 ++
 hw/usb-ccid.c               | 1356 +++++++++++++++++++++++++++++++++++++++++++
 libcacard/Makefile          |   14 +
 libcacard/cac.c             |  411 +++++++++++++
 libcacard/cac.h             |   20 +
 libcacard/card_7816.c       |  780 +++++++++++++++++++++++++
 libcacard/card_7816.h       |   60 ++
 libcacard/card_7816t.h      |  163 ++++++
 libcacard/config.h          |   81 +++
 libcacard/event.c           |  112 ++++
 libcacard/eventt.h          |   28 +
 libcacard/link_test.c       |   20 +
 libcacard/mutex.h           |   59 ++
 libcacard/passthru.c        |  612 +++++++++++++++++++
 libcacard/passthru.h        |   50 ++
 libcacard/vcard.c           |  350 +++++++++++
 libcacard/vcard.h           |   85 +++
 libcacard/vcard_emul.h      |   62 ++
 libcacard/vcard_emul_nss.c  | 1171 +++++++++++++++++++++++++++++++++++++
 libcacard/vcard_emul_type.c |   60 ++
 libcacard/vcard_emul_type.h |   29 +
 libcacard/vcardt.h          |   66 +++
 libcacard/vevent.h          |   26 +
 libcacard/vreader.c         |  526 +++++++++++++++++
 libcacard/vreader.h         |   54 ++
 libcacard/vreadert.h        |   23 +
 libcacard/vscard_common.h   |  130 ++++
 libcacard/vscclient.c       |  710 ++++++++++++++++++++++
 36 files changed, 8580 insertions(+), 2 deletions(-)
 create mode 100644 docs/ccid.txt
 create mode 100644 docs/libcacard.txt
 create mode 100644 hw/ccid-card-emulated.c
 create mode 100644 hw/ccid-card-passthru.c
 create mode 100644 hw/ccid.h
 create mode 100644 hw/usb-ccid.c
 create mode 100644 libcacard/Makefile
 create mode 100644 libcacard/cac.c
 create mode 100644 libcacard/cac.h
 create mode 100644 libcacard/card_7816.c
 create mode 100644 libcacard/card_7816.h
 create mode 100644 libcacard/card_7816t.h
 create mode 100644 libcacard/config.h
 create mode 100644 libcacard/event.c
 create mode 100644 libcacard/eventt.h
 create mode 100644 libcacard/link_test.c
 create mode 100644 libcacard/mutex.h
 create mode 100644 libcacard/passthru.c
 create mode 100644 libcacard/passthru.h
 create mode 100644 libcacard/vcard.c
 create mode 100644 libcacard/vcard.h
 create mode 100644 libcacard/vcard_emul.h
 create mode 100644 libcacard/vcard_emul_nss.c
 create mode 100644 libcacard/vcard_emul_type.c
 create mode 100644 libcacard/vcard_emul_type.h
 create mode 100644 libcacard/vcardt.h
 create mode 100644 libcacard/vevent.h
 create mode 100644 libcacard/vreader.c
 create mode 100644 libcacard/vreader.h
 create mode 100644 libcacard/vreadert.h
 create mode 100644 libcacard/vscard_common.h
 create mode 100644 libcacard/vscclient.c

-- 
1.7.3.4

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

* [Qemu-devel] [PATCH 1/7] usb-ccid: add CCID bus
  2011-01-11  8:42 [Qemu-devel] [PATCH 0/7] usb-ccid (v15) Alon Levy
@ 2011-01-11  8:42 ` Alon Levy
  2011-01-11  8:42 ` [Qemu-devel] [PATCH 2/7] ccid: add passthru card device Alon Levy
                   ` (7 subsequent siblings)
  8 siblings, 0 replies; 23+ messages in thread
From: Alon Levy @ 2011-01-11  8:42 UTC (permalink / raw)
  To: qemu-devel

A CCID device is a smart card reader. It is a USB device, defined at [1].
This patch introduces the usb-ccid device that is a ccid bus. Next patches will
introduce two card types to use it, a passthru card and an emulated card.

 [1] http://www.usb.org/developers/devclass_docs/DWG_Smart-Card_CCID_Rev110.

Signed-off-by: Alon Levy <alevy@redhat.com>
---
 Makefile.objs |    1 +
 configure     |    6 +
 hw/ccid.h     |   35 ++
 hw/usb-ccid.c | 1355 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 4 files changed, 1397 insertions(+), 0 deletions(-)
 create mode 100644 hw/ccid.h
 create mode 100644 hw/usb-ccid.c

diff --git a/Makefile.objs b/Makefile.objs
index d6b3d60..7da4771 100644
--- a/Makefile.objs
+++ b/Makefile.objs
@@ -197,6 +197,7 @@ hw-obj-$(CONFIG_FDC) += fdc.o
 hw-obj-$(CONFIG_ACPI) += acpi.o acpi_piix4.o
 hw-obj-$(CONFIG_APM) += pm_smbus.o apm.o
 hw-obj-$(CONFIG_DMA) += dma.o
+hw-obj-$(CONFIG_SMARTCARD) += usb-ccid.o
 
 # PPC devices
 hw-obj-$(CONFIG_OPENPIC) += openpic.o
diff --git a/configure b/configure
index 831a741..839980c 100755
--- a/configure
+++ b/configure
@@ -334,6 +334,7 @@ trace_backend="nop"
 trace_file="trace"
 spice=""
 rbd=""
+smartcard="yes"
 
 # OS specific
 if check_define __linux__ ; then
@@ -2441,6 +2442,7 @@ echo "Trace output file $trace_file-<pid>"
 echo "spice support     $spice"
 echo "rbd support       $rbd"
 echo "xfsctl support    $xfs"
+echo "smartcard support $smartcard"
 
 if test $sdl_too_old = "yes"; then
 echo "-> Your SDL version is too old - please upgrade to have SDL support"
@@ -2710,6 +2712,10 @@ if test "$spice" = "yes" ; then
   echo "CONFIG_SPICE=y" >> $config_host_mak
 fi
 
+if test "$smartcard" = "yes" ; then
+  echo "CONFIG_SMARTCARD=y" >> $config_host_mak
+fi
+
 # XXX: suppress that
 if [ "$bsd" = "yes" ] ; then
   echo "CONFIG_BSD=y" >> $config_host_mak
diff --git a/hw/ccid.h b/hw/ccid.h
new file mode 100644
index 0000000..af59070
--- /dev/null
+++ b/hw/ccid.h
@@ -0,0 +1,35 @@
+#ifndef __CCID_H__
+#define __CCID_H__
+
+#include "qdev.h"
+
+typedef struct CCIDCardState CCIDCardState;
+typedef struct CCIDCardInfo CCIDCardInfo;
+
+struct CCIDCardState {
+    DeviceState qdev;
+    uint32_t    slot; // For future use with multiple slot reader.
+};
+
+struct CCIDCardInfo {
+    DeviceInfo qdev;
+    void (*print)(Monitor *mon, CCIDCardState *card, int indent);
+    const uint8_t *(*get_atr)(CCIDCardState *card, uint32_t *len);
+    void (*apdu_from_guest)(CCIDCardState *card, const uint8_t *apdu, uint32_t len);
+    int (*exitfn)(CCIDCardState *card);
+    int (*initfn)(CCIDCardState *card);
+};
+
+void ccid_card_send_apdu_to_guest(CCIDCardState *card, uint8_t* apdu, uint32_t len);
+void ccid_card_card_removed(CCIDCardState *card);
+void ccid_card_card_inserted(CCIDCardState *card);
+void ccid_card_card_error(CCIDCardState *card, uint64_t error);
+void ccid_card_qdev_register(CCIDCardInfo *card);
+
+/* support guest visible insertion/removal of ccid devices based on actual
+ * devices connected/removed. Called by card implementation (passthru, local) */
+int ccid_card_ccid_attach(CCIDCardState *card);
+void ccid_card_ccid_detach(CCIDCardState *card);
+
+#endif // __CCID_H__
+
diff --git a/hw/usb-ccid.c b/hw/usb-ccid.c
new file mode 100644
index 0000000..58f69a6
--- /dev/null
+++ b/hw/usb-ccid.c
@@ -0,0 +1,1355 @@
+/*
+ * CCID Device emulation
+ *
+ * Based on usb-serial.c:
+ * Copyright (c) 2006 CodeSourcery.
+ * Copyright (c) 2008 Samuel Thibault <samuel.thibault@ens-lyon.org>
+ * Written by Paul Brook, reused for FTDI by Samuel Thibault,
+ * Reused for CCID by Alon Levy.
+ * Contributed to by Robert Relyea
+ * Copyright (c) 2010 Red Hat.
+ *
+ * This code is licenced under the LGPL.
+ */
+
+/* References:
+ *
+ * CCID Specification Revision 1.1 April 22nd 2005
+ *  "Universal Serial Bus, Device Class: Smart Card"
+ *  Specification for Integrated Circuit(s) Cards Interface Devices
+ *
+ * Endianess note: from the spec (1.3)
+ *  "Fields that are larger than a byte are stored in little endian
+ *
+ * KNOWN BUGS
+ * 1. remove/insert can sometimes result in removed state instead of inserted.
+ * This is a result of the following:
+ *  symptom: dmesg shows ERMOTEIO (-121), pcscd shows -99. This happens
+ *  when we send a too short packet, seen in uhci-usb.c, resulting from
+ *  a urb requesting SPD and us returning a smaller packet.
+ *  Not sure which messages trigger this.
+ *
+ * Migration note:
+ *
+ * All the VMStateDescription's are left here for future use, but
+ * not enabled right now since there is no support for USB migration.
+ *
+ * To enable define ENABLE_MIGRATION
+ */
+
+#include "qemu-common.h"
+#include "qemu-error.h"
+#include "usb.h"
+#include "monitor.h"
+
+#include "hw/ccid.h"
+
+//#define DEBUG_CCID
+
+#define DPRINTF(s, lvl, fmt, ...) \
+do { if (lvl <= s->debug) { printf("usb-ccid: " fmt , ## __VA_ARGS__); } } while (0)
+
+#define CCID_DEV_NAME "usb-ccid"
+
+/* The two options for variable sized buffers:
+ * make them constant size, for large enough constant,
+ * or handle the migration complexity - VMState doesn't handle this case.
+ * sizes are expected never to be exceeded, unless guest misbehaves. */
+#define BULK_OUT_DATA_SIZE 65536
+#define PENDING_ANSWERS_NUM 128
+
+#define BULK_IN_BUF_SIZE 384
+#define BULK_IN_PENDING_NUM 8
+
+#define InterfaceOutClass    ((USB_DIR_OUT|USB_TYPE_CLASS|USB_RECIP_INTERFACE)<<8)
+#define InterfaceInClass     ((USB_DIR_IN |USB_TYPE_CLASS|USB_RECIP_INTERFACE)<<8)
+
+#define CCID_CONTROL_ABORT                  0x1
+#define CCID_CONTROL_GET_CLOCK_FREQUENCIES  0x2
+#define CCID_CONTROL_GET_DATA_RATES         0x3
+
+#define CCID_PRODUCT_DESCRIPTION        "QEMU USB CCID"
+#define CCID_VENDOR_DESCRIPTION         "QEMU " QEMU_VERSION
+#define CCID_INTERFACE_NAME             "CCID Interface"
+#define CCID_SERIAL_NUMBER_STRING       "1"
+/* Using Gemplus Vendor and Product id
+  Effect on various drivers:
+  * usbccid.sys (winxp, others untested) is a class driver so it doesn't care.
+  * linux has a number of class drivers, but openct filters based on
+    vendor/product (/etc/openct.conf under fedora), hence Gemplus.
+ */
+#define CCID_VENDOR_ID                  0x08e6
+#define CCID_PRODUCT_ID                 0x4433
+#define CCID_DEVICE_VERSION             0x0000
+
+/* BULK_OUT messages from PC to Reader
+   Defined in CCID Rev 1.1 6.1 (page 26)
+ */
+#define CCID_MESSAGE_TYPE_PC_to_RDR_IccPowerOn              0x62
+#define CCID_MESSAGE_TYPE_PC_to_RDR_IccPowerOff             0x63
+#define CCID_MESSAGE_TYPE_PC_to_RDR_GetSlotStatus           0x65
+#define CCID_MESSAGE_TYPE_PC_to_RDR_XfrBlock                0x6f
+#define CCID_MESSAGE_TYPE_PC_to_RDR_GetParameters           0x6c
+#define CCID_MESSAGE_TYPE_PC_to_RDR_ResetParameters         0x6d
+#define CCID_MESSAGE_TYPE_PC_to_RDR_SetParameters           0x61
+#define CCID_MESSAGE_TYPE_PC_to_RDR_Escape                  0x6b
+#define CCID_MESSAGE_TYPE_PC_to_RDR_IccClock                0x6e
+#define CCID_MESSAGE_TYPE_PC_to_RDR_T0APDU                  0x6a
+#define CCID_MESSAGE_TYPE_PC_to_RDR_Secure                  0x69
+#define CCID_MESSAGE_TYPE_PC_to_RDR_Mechanical              0x71
+#define CCID_MESSAGE_TYPE_PC_to_RDR_Abort                   0x72
+#define CCID_MESSAGE_TYPE_PC_to_RDR_SetDataRateAndClockFrequency 0x73
+
+/* BULK_IN messages from Reader to PC
+   Defined in CCID Rev 1.1 6.2 (page 48)
+ */
+#define CCID_MESSAGE_TYPE_RDR_to_PC_DataBlock               0x80
+#define CCID_MESSAGE_TYPE_RDR_to_PC_SlotStatus              0x81
+#define CCID_MESSAGE_TYPE_RDR_to_PC_Parameters              0x82
+#define CCID_MESSAGE_TYPE_RDR_to_PC_Escape                  0x83
+#define CCID_MESSAGE_TYPE_RDR_to_PC_DataRateAndClockFrequency 0x84
+
+/* INTERRUPT_IN messages from Reader to PC
+   Defined in CCID Rev 1.1 6.3 (page 56)
+ */
+#define CCID_MESSAGE_TYPE_RDR_to_PC_NotifySlotChange        0x50
+#define CCID_MESSAGE_TYPE_RDR_to_PC_HardwareError           0x51
+
+/* Endpoints for CCID - addresses are up to us to decide.
+   To support slot insertion and removal we must have an interrupt in ep
+   in addition we need a bulk in and bulk out ep
+   5.2, page 20
+ */
+#define CCID_INT_IN_EP       1
+#define CCID_BULK_IN_EP      2
+#define CCID_BULK_OUT_EP     3
+
+/* bmSlotICCState masks */
+#define SLOT_0_STATE_MASK    1
+#define SLOT_0_CHANGED_MASK  2
+
+/* Status codes that go in bStatus (see 6.2.6) */
+enum {
+    ICC_STATUS_PRESENT_ACTIVE = 0,
+    ICC_STATUS_PRESENT_INACTIVE,
+    ICC_STATUS_NOT_PRESENT
+};
+
+enum {
+    COMMAND_STATUS_NO_ERROR = 0,
+    COMMAND_STATUS_FAILED,
+    COMMAND_STATUS_TIME_EXTENSION_REQUIRED
+};
+
+/* Error codes that go in bError (see 6.2.6)
+ */
+enum {
+    ERROR_CMD_NOT_SUPPORTED = 0,
+    ERROR_CMD_ABORTED       = -1,
+    ERROR_ICC_MUTE          = -2,
+    ERROR_XFR_PARITY_ERROR  = -3,
+    ERROR_XFR_OVERRUN       = -4,
+    ERROR_HW_ERROR          = -5,
+};
+
+/* 6.2.6 RDR_to_PC_SlotStatus definitions */
+enum {
+    CLOCK_STATUS_RUNNING = 0,
+    /* 0 - Clock Running, 1 - Clock stopped in State L, 2 - H,
+       3 - unkonwn state. rest are RFU
+     */
+};
+
+typedef struct __attribute__ ((__packed__)) CCID_Header {
+    uint8_t     bMessageType;
+    uint32_t    dwLength;
+    uint8_t     bSlot;
+    uint8_t     bSeq;
+} CCID_Header;
+
+typedef struct __attribute__ ((__packed__)) CCID_BULK_IN {
+    CCID_Header hdr;
+    uint8_t     bStatus;        /* Only used in BULK_IN */
+    uint8_t     bError;         /* Only used in BULK_IN */
+} CCID_BULK_IN;
+
+typedef struct __attribute__ ((__packed__)) CCID_SlotStatus {
+    CCID_BULK_IN b;
+    uint8_t     bClockStatus;
+} CCID_SlotStatus;
+
+typedef struct __attribute__ ((__packed__)) CCID_Parameter {
+    CCID_BULK_IN b;
+    uint8_t     bProtocolNum;
+    uint8_t     abProtocolDataStructure[0];
+} CCID_Parameter;
+
+typedef struct __attribute__ ((__packed__)) CCID_DataBlock {
+    CCID_BULK_IN b;
+    uint8_t      bChainParameter;
+    uint8_t      abData[0];
+} CCID_DataBlock;
+
+/* 6.1.4 PC_to_RDR_XfrBlock */
+typedef struct __attribute__ ((__packed__)) CCID_XferBlock {
+    CCID_Header  hdr;
+    uint8_t      bBWI; /* Block Waiting Timeout */
+    uint16_t     wLevelParameter; /* XXX currently unused */
+    uint8_t      abData[0];
+} CCID_XferBlock;
+
+typedef struct __attribute__ ((__packed__)) CCID_IccPowerOn {
+    CCID_Header hdr;
+    uint8_t     bPowerSelect;
+    uint16_t    abRFU;
+} CCID_IccPowerOn;
+
+typedef struct __attribute__ ((__packed__)) CCID_IccPowerOff {
+    CCID_Header hdr;
+    uint16_t    abRFU;
+} CCID_IccPowerOff;
+
+typedef struct __attribute__ ((__packed__)) CCID_SetParameter {
+    CCID_Header hdr;
+    uint8_t     bProtocolNum;
+    uint8_t    abProtocolDataStructure[0];
+} CCID_SetParameter;
+
+typedef struct CCID_Notify_Slot_Change {
+    uint8_t     bMessageType; /* CCID_MESSAGE_TYPE_RDR_to_PC_NotifySlotChange */
+    uint8_t     bmSlotICCState;
+} CCID_Notify_Slot_Change;
+
+/* used for DataBlock response to XferBlock */
+typedef struct Answer {
+    uint8_t slot;
+    uint8_t seq;
+} Answer;
+
+/* pending BULK_IN messages */
+typedef struct BulkIn {
+    uint8_t  data[BULK_IN_BUF_SIZE];
+    uint32_t len;
+    uint32_t pos;
+} BulkIn;
+
+enum {
+    MIGRATION_NONE,
+    MIGRATION_MIGRATED,
+};
+
+typedef struct CCIDBus CCIDBus;
+typedef struct USBCCIDState USBCCIDState;
+
+#define MAX_PROTOCOL_SIZE   7
+
+/**
+ * powered - defaults to true, changed by PowerOn/PowerOff messages
+ */
+struct USBCCIDState {
+    USBDevice dev;
+    CCIDBus *bus;
+    CCIDCardState *card;
+    CCIDCardInfo *cardinfo; /* caching the info pointer */
+    uint8_t  debug;
+    BulkIn bulk_in_pending[BULK_IN_PENDING_NUM]; /* circular */
+    uint32_t bulk_in_pending_start;
+    uint32_t bulk_in_pending_end; /* first free */
+    uint32_t bulk_in_pending_num;
+    BulkIn *current_bulk_in;
+    uint8_t  bulk_out_data[BULK_OUT_DATA_SIZE];
+    uint32_t bulk_out_pos;
+    uint8_t  bmSlotICCState;
+    uint8_t  powered;
+    uint8_t  notify_slot_change;
+    uint64_t last_answer_error;
+    Answer pending_answers[PENDING_ANSWERS_NUM];
+    uint32_t pending_answers_start;
+    uint32_t pending_answers_end;
+    uint32_t pending_answers_num;
+    uint8_t  bError;
+    uint8_t  bmCommandStatus;
+    uint8_t  bProtocolNum;
+    uint8_t  abProtocolDataStructure[MAX_PROTOCOL_SIZE];
+    uint32_t ulProtocolDataStructureSize;
+    uint32_t state_vmstate;
+    uint8_t  migration_state;
+    uint32_t migration_target_ip;
+    uint16_t migration_target_port;
+};
+
+/* Slot specific variables. We emulate a single slot card reader.
+ */
+
+
+/* CCID Spec chapter 4: CCID uses a standard device descriptor per Chapter 9,
+ * "USB Device Framework", section 9.6.1, in the Universal Serial Bus
+ * Specification.
+ *
+ * This device implemented based on the spec and with an Athena Smart Card
+ * Reader as reference:
+ *   0dc3:1004 Athena Smartcard Solutions, Inc.
+ */
+
+static const uint8_t qemu_ccid_dev_descriptor[] = {
+        0x12,       /*  u8 bLength; */
+        USB_DT_DEVICE, /*  u8 bDescriptorType; Device */
+        0x10, 0x01, /*  u16 bcdUSB; v1.1 */
+
+        0x00,       /*  u8  bDeviceClass; */
+        0x00,       /*  u8  bDeviceSubClass; */
+        0x00,       /*  u8  bDeviceProtocol; [ low/full speeds only ] */
+        0x40,       /*  u8  bMaxPacketSize0; 8 Bytes (valid: 8,16,32,64) */
+
+        /* Vendor and product id are arbitrary.  */
+                    /*  u16 idVendor  */
+        CCID_VENDOR_ID & 0xff, CCID_VENDOR_ID >> 8,
+                    /*  u16 idProduct */
+        CCID_PRODUCT_ID & 0xff, CCID_PRODUCT_ID >> 8,
+                    /*  u16 bcdDevice */
+        CCID_DEVICE_VERSION & 0xff, CCID_DEVICE_VERSION >> 8,
+        0x01,       /*  u8  iManufacturer; */
+        0x02,       /*  u8  iProduct; */
+        0x03,       /*  u8  iSerialNumber; */
+        0x01,       /*  u8  bNumConfigurations; */
+};
+
+static const uint8_t qemu_ccid_config_descriptor[] = {
+
+        /* one configuration */
+        0x09,       /*  u8  bLength; */
+        USB_DT_CONFIG, /*  u8  bDescriptorType; Configuration */
+        0x5d, 0x00, /*  u16 wTotalLength; 9+9+54+7+7+7 */
+        0x01,       /*  u8  bNumInterfaces; (1) */
+        0x01,       /*  u8  bConfigurationValue; */
+        0x00,       /*  u8  iConfiguration; */
+        0xe0,       /*  u8  bmAttributes;
+                                 Bit 7: must be set,
+                                     6: Self-powered,
+                                     5: Remote wakeup,
+                                     4..0: resvd */
+        100/2,      /*  u8  MaxPower; 50 == 100mA */
+
+        /* one interface */
+        0x09,       /*  u8  if_bLength; */
+        USB_DT_INTERFACE, /*  u8  if_bDescriptorType; Interface */
+        0x00,       /*  u8  if_bInterfaceNumber; */
+        0x00,       /*  u8  if_bAlternateSetting; */
+        0x03,       /*  u8  if_bNumEndpoints; */
+        0x0b,       /*  u8  if_bInterfaceClass; Smart Card Device Class */
+        0x00,       /*  u8  if_bInterfaceSubClass; Subclass code */
+        0x00,       /*  u8  if_bInterfaceProtocol; Protocol code */
+        0x04,       /*  u8  if_iInterface; Index of string descriptor */
+
+        /* Smart Card Device Class Descriptor */
+        0x36,       /*  u8  bLength; */
+        0x21,       /*  u8  bDescriptorType; Functional */
+        0x10, 0x01, /*  u16 bcdCCID; CCID Specification Release Number. */
+        0x00,       /*  u8  bMaxSlotIndex; The index of the highest available
+                        slot on this device. All slots are consecutive starting
+                        at 00h. */
+        0x07,       /*  u8  bVoltageSupport; 01h - 5.0v, 02h - 3.0, 03 - 1.8 */
+
+        0x03, 0x00, /*  u32 dwProtocols; RRRR PPPP. RRRR = 0000h.*/
+        0x00, 0x00, /*  PPPP: 0001h = Protocol T=0, 0002h = Protocol T=1 */
+                    /*  u32 dwDefaultClock; in kHZ (0x0fa0 is 4 MHz) */
+        0xa0, 0x0f, 0x00, 0x00,
+                    /*  u32 dwMaximumClock; */
+        0x00, 0x00, 0x01, 0x00,
+        0x00,       /*  u8 bNumClockSupported; 0 means just the default and max. */
+                    /*  u32 dwDataRate ;bps. 9600 == 00002580h */
+        0x80, 0x25, 0x00, 0x00,
+                    /*  u32 dwMaxDataRate ; 11520 bps == 0001C200h */
+        0x00, 0xC2, 0x01, 0x00,
+        0x00,       /*  u8  bNumDataRatesSupported; 00 means all rates between
+                     *      default and max */
+                    /*  u32 dwMaxIFSD; maximum IFSD supported by CCID for protocol
+                     *      T=1 (Maximum seen from various cards) */
+        0xfe, 0x00, 0x00, 0x00,
+                    /*  u32 dwSyncProtocols; 1 - 2-wire, 2 - 3-wire, 4 - I2C */
+        0x00, 0x00, 0x00, 0x00,
+                    /*  u32 dwMechanical;  0 - no special characteristics. */
+        0x00, 0x00, 0x00, 0x00,
+                    /*  u32 dwFeatures;
+                     *  0 - No special characteristics
+                     *  + 2 Automatic parameter configuration based on ATR data
+                     *  + 4 Automatic activation of ICC on inserting
+                     *  + 8 Automatic ICC voltage selection
+                     *  + 10 Automatic ICC clock frequency change
+                     *  + 20 Automatic baud rate change
+                     *  + 40 Automatic parameters negotiation made by the CCID
+                     *  + 80 automatic PPS made by the CCID
+                     *  100 CCID can set ICC in clock stop mode
+                     *  200 NAD value other then 00 accepted (T=1 protocol)
+                     *  + 400 Automatic IFSD exchange as first exchange (T=1)
+                     *  One of the following only:
+                     *  + 10000 TPDU level exchanges with CCID
+                     *  20000 Short APDU level exchange with CCID
+                     *  40000 Short and Extended APDU level exchange with CCID
+                     *
+                     *  + 100000 USB Wake up signaling supported on card insertion
+                     *  and removal. Must set bit 5 in bmAttributes in Configuration
+                     *  descriptor if 100000 is set.*/
+        0xfe, 0x04, 0x11, 0x00,
+                    /*  u32 dwMaxCCIDMessageLength; For extended APDU in [261 + 10
+                     *  , 65544 + 10]. Otherwise the minimum is wMaxPacketSize of
+                     *  the Bulk-OUT endpoint */
+        0x12, 0x00, 0x01, 0x00,
+        0xFF,       /*  u8  bClassGetResponse; Significant only for CCID that
+                     *  offers an APDU level for exchanges. Indicates the default
+                     *  class value used by the CCID when it sends a Get Response
+                     *  command to perform the transportation of an APDU by T=0
+                     *  protocol
+                     *  FFh indicates that the CCID echos the class of the APDU.
+                     */
+        0xFF,       /*  u8  bClassEnvelope; EAPDU only. Envelope command for T=0 */
+        0x00, 0x00, /*  u16 wLcdLayout; XXYY Number of lines (XX) and chars per
+                     *  line for LCD display used for PIN entry. 0000 - no LCD */
+        0x01,       /*  u8  bPINSupport; 01h PIN Verification,
+                     *                   02h PIN Modification */
+        0x01,       /*  u8  bMaxCCIDBusySlots; */
+
+        /* Interrupt-IN endpoint */
+        0x07,       /*  u8  ep_bLength; */
+                    /*  u8  ep_bDescriptorType; Endpoint */
+        USB_DT_ENDPOINT,
+                    /*  u8  ep_bEndpointAddress; IN Endpoint 1 */
+        0x80 | CCID_INT_IN_EP,
+        0x03,       /*  u8  ep_bmAttributes; Interrupt */
+        0x40, 0x00, /*  u16 ep_wMaxPacketSize; */
+        0xff,       /*  u8  ep_bInterval; */
+
+        /* Bulk-In endpoint */
+        0x07,       /*  u8  ep_bLength; */
+                    /*  u8  ep_bDescriptorType; Endpoint */
+        USB_DT_ENDPOINT,
+                    /*  u8  ep_bEndpointAddress; IN Endpoint 2 */
+        0x80 | CCID_BULK_IN_EP,
+        0x02,       /*  u8  ep_bmAttributes; Bulk */
+        0x40, 0x00, /*  u16 ep_wMaxPacketSize; */
+        0x00,       /*  u8  ep_bInterval; */
+
+        /* Bulk-Out endpoint */
+        0x07,       /*  u8  ep_bLength; */
+                    /*  u8  ep_bDescriptorType; Endpoint */
+        USB_DT_ENDPOINT,
+                    /*  u8  ep_bEndpointAddress; OUT Endpoint 3 */
+        CCID_BULK_OUT_EP,
+        0x02,       /*  u8  ep_bmAttributes; Bulk */
+        0x40, 0x00, /*  u16 ep_wMaxPacketSize; */
+        0x00,       /*  u8  ep_bInterval; */
+
+};
+
+static bool ccid_has_pending_answers(USBCCIDState *s)
+{
+    return s->pending_answers_num > 0;
+}
+
+static void ccid_clear_pending_answers(USBCCIDState *s)
+{
+    s->pending_answers_num = 0;
+    s->pending_answers_start = 0;
+    s->pending_answers_end = 0;
+}
+
+static void ccid_print_pending_answers(USBCCIDState *s)
+{
+#ifdef DEBUG_CCID
+    Answer *answer;
+    int i, count;
+
+    printf("usb-ccid: pending answers:");
+    if (!ccid_has_pending_answers(s)) {
+        printf(" empty\n");
+        return;
+    }
+    for (i = s->pending_answers_start, count=s->pending_answers_num ;
+         count > 0; count--, i++) {
+        answer = &s->pending_answers[i % PENDING_ANSWERS_NUM];
+        if (count == 1) {
+            printf("%d:%d\n", answer->slot, answer->seq);
+        } else {
+            printf("%d:%d,", answer->slot, answer->seq);
+        }
+    }
+#endif
+}
+
+static void ccid_add_pending_answer(USBCCIDState *s, CCID_Header *hdr)
+{
+    Answer* answer;
+
+    assert(s->pending_answers_num++ < PENDING_ANSWERS_NUM);
+    answer = &s->pending_answers[(s->pending_answers_end++) % PENDING_ANSWERS_NUM];
+    answer->slot = hdr->bSlot;
+    answer->seq = hdr->bSeq;
+    ccid_print_pending_answers(s);
+}
+
+static void ccid_remove_pending_answer(USBCCIDState *s,
+    uint8_t *slot, uint8_t *seq)
+{
+    Answer *answer;
+
+    assert(s->pending_answers_num-- > 0);
+    answer = &s->pending_answers[(s->pending_answers_start++) % PENDING_ANSWERS_NUM];
+    *slot = answer->slot;
+    *seq = answer->seq;
+    ccid_print_pending_answers(s);
+}
+
+static void ccid_bulk_in_clear(USBCCIDState *s)
+{
+    s->bulk_in_pending_start = 0;
+    s->bulk_in_pending_end = 0;
+    s->bulk_in_pending_num = 0;
+}
+
+static void ccid_bulk_in_release(USBCCIDState *s)
+{
+    assert(s->current_bulk_in != NULL);
+    s->current_bulk_in->pos = 0;
+    s->current_bulk_in = NULL;
+}
+
+static void ccid_bulk_in_get(USBCCIDState *s)
+{
+    if (s->current_bulk_in != NULL || s->bulk_in_pending_num == 0) {
+        return;
+    }
+    assert(s->bulk_in_pending_num > 0);
+    s->bulk_in_pending_num--;
+    s->current_bulk_in = &s->bulk_in_pending[
+        (s->bulk_in_pending_start++) % BULK_IN_PENDING_NUM];
+}
+
+static void* ccid_reserve_recv_buf(USBCCIDState* s, uint16_t len)
+{
+    BulkIn* bulk_in;
+
+    DPRINTF(s, 4, "%s: QUEUE: reserve %d bytes\n", __func__, len);
+
+    /* look for an existing element */
+    if (len > BULK_IN_BUF_SIZE) {
+        printf("usb-ccid.c: %s: len larger then max (%d>%d). discarding message.\n",
+            __func__, len, BULK_IN_BUF_SIZE);
+        return NULL;
+    }
+    if (s->bulk_in_pending_num >= BULK_IN_PENDING_NUM) {
+        printf("usb-ccid.c: %s: No free bulk_in buffers. discarding message.\n",
+                __func__);
+        return NULL;
+    }
+    bulk_in = &s->bulk_in_pending[(s->bulk_in_pending_end++) % BULK_IN_PENDING_NUM];
+    s->bulk_in_pending_num++;
+    bulk_in->len = len;
+    return bulk_in->data;
+}
+
+static void ccid_reset(USBCCIDState *s)
+{
+    ccid_bulk_in_clear(s);
+    ccid_clear_pending_answers(s);
+}
+
+static void ccid_detach(USBCCIDState *s)
+{
+    ccid_reset(s);
+}
+
+static void ccid_handle_reset(USBDevice *dev)
+{
+    USBCCIDState *s = DO_UPCAST(USBCCIDState, dev, dev);
+
+    DPRINTF(s, 1, "Reset\n");
+
+    ccid_reset(s);
+}
+
+static int ccid_handle_control(USBDevice *dev, int request, int value,
+                                  int index, int length, uint8_t *data)
+{
+    USBCCIDState *s = DO_UPCAST(USBCCIDState, dev, dev);
+    int ret = 0;
+
+    DPRINTF(s, 1, "got control %x, value %x\n",request, value);
+    switch (request) {
+    case DeviceRequest | USB_REQ_GET_STATUS:
+        data[0] = (0 << USB_DEVICE_SELF_POWERED) |
+            (dev->remote_wakeup << USB_DEVICE_REMOTE_WAKEUP);
+        data[1] = 0x00;
+        ret = 2;
+        break;
+    case DeviceOutRequest | USB_REQ_CLEAR_FEATURE:
+        if (value == USB_DEVICE_REMOTE_WAKEUP) {
+            dev->remote_wakeup = 0;
+        } else {
+            goto fail;
+        }
+        ret = 0;
+        break;
+    case DeviceOutRequest | USB_REQ_SET_FEATURE:
+        if (value == USB_DEVICE_REMOTE_WAKEUP) {
+            dev->remote_wakeup = 1;
+        } else {
+            goto fail;
+        }
+        ret = 0;
+        break;
+    case DeviceOutRequest | USB_REQ_SET_ADDRESS:
+        dev->addr = value;
+        ret = 0;
+        break;
+    case DeviceRequest | USB_REQ_GET_DESCRIPTOR:
+        switch(value >> 8) {
+        case USB_DT_DEVICE:
+            memcpy(data, qemu_ccid_dev_descriptor,
+                   sizeof(qemu_ccid_dev_descriptor));
+            ret = sizeof(qemu_ccid_dev_descriptor);
+            break;
+        case USB_DT_CONFIG:
+            memcpy(data, qemu_ccid_config_descriptor,
+                   sizeof(qemu_ccid_config_descriptor));
+            ret = sizeof(qemu_ccid_config_descriptor);
+            break;
+        case USB_DT_STRING:
+            switch(value & 0xff) {
+            case 0:
+                /* language ids */
+                data[0] = 4;
+                data[1] = 3;
+                data[2] = 0x09;
+                data[3] = 0x04;
+                ret = 4;
+                break;
+            case 1:
+                /* vendor description */
+                ret = set_usb_string(data, CCID_VENDOR_DESCRIPTION);
+                break;
+            case 2:
+                /* product description */
+                ret = set_usb_string(data, CCID_PRODUCT_DESCRIPTION);
+                break;
+            case 3:
+                /* serial number */
+                ret = set_usb_string(data, CCID_SERIAL_NUMBER_STRING);
+                break;
+            case 4:
+                /* interface name */
+                ret = set_usb_string(data, CCID_INTERFACE_NAME);
+                break;
+            default:
+                goto fail;
+            }
+            break;
+        default:
+            goto fail;
+        }
+        break;
+    case DeviceRequest | USB_REQ_GET_CONFIGURATION:
+        data[0] = 1;
+        ret = 1;
+        break;
+    case DeviceOutRequest | USB_REQ_SET_CONFIGURATION:
+        /* Only one configuration - we just ignore the request */
+        ret = 0;
+        break;
+    case DeviceRequest | USB_REQ_GET_INTERFACE:
+        data[0] = 0;
+        ret = 1;
+        break;
+    case InterfaceOutRequest | USB_REQ_SET_INTERFACE:
+        ret = 0;
+        break;
+    case EndpointOutRequest | USB_REQ_CLEAR_FEATURE:
+        ret = 0;
+        break;
+
+        /* Class specific requests.  */
+    case InterfaceOutClass | CCID_CONTROL_ABORT:
+        DPRINTF(s, 1, "ccid_control abort UNIMPLEMENTED\n");
+        ret = USB_RET_STALL;
+        break;
+    case InterfaceInClass | CCID_CONTROL_GET_CLOCK_FREQUENCIES:
+        DPRINTF(s, 1, "ccid_control get clock frequencies UNIMPLEMENTED\n");
+        ret = USB_RET_STALL;
+        break;
+    case InterfaceInClass | CCID_CONTROL_GET_DATA_RATES:
+        DPRINTF(s, 1, "ccid_control get data rates UNIMPLEMENTED\n");
+        ret = USB_RET_STALL;
+        break;
+    default:
+    fail:
+        DPRINTF(s, 1, "got unsupported/bogus control %x, value %x\n", request, value);
+        ret = USB_RET_STALL;
+        break;
+    }
+    return ret;
+}
+
+static bool ccid_card_inserted(USBCCIDState *s)
+{
+    return s->bmSlotICCState & SLOT_0_STATE_MASK;
+}
+
+static uint8_t ccid_card_status(USBCCIDState *s)
+{
+    return ccid_card_inserted(s)
+            ? (s->powered ?
+                ICC_STATUS_PRESENT_ACTIVE
+              : ICC_STATUS_PRESENT_INACTIVE
+              )
+            : ICC_STATUS_NOT_PRESENT;
+}
+
+static uint8_t ccid_calc_status(USBCCIDState *s)
+{
+    /* page 55, 6.2.6, calculation of bStatus from bmICCStatus and
+       bmCommandStatus
+     */
+    uint8_t ret = ccid_card_status(s) | (s->bmCommandStatus << 6);
+    DPRINTF(s, 4, "status = %d\n", ret);
+    return ret;
+}
+
+static void ccid_reset_error_status(USBCCIDState* s)
+{
+    s->bError = ERROR_CMD_NOT_SUPPORTED;
+    s->bmCommandStatus = COMMAND_STATUS_NO_ERROR;
+}
+
+static void ccid_write_slot_status(USBCCIDState* s, CCID_Header* recv)
+{
+    CCID_SlotStatus *h = ccid_reserve_recv_buf(s, sizeof(CCID_SlotStatus));
+    if (h == NULL) {
+        return;
+    }
+    h->b.hdr.bMessageType = CCID_MESSAGE_TYPE_RDR_to_PC_SlotStatus;
+    h->b.hdr.dwLength = 0;
+    h->b.hdr.bSlot = recv->bSlot;
+    h->b.hdr.bSeq = recv->bSeq;
+    h->b.bStatus = ccid_calc_status(s);
+    h->b.bError = s->bError;
+    h->bClockStatus = CLOCK_STATUS_RUNNING;
+    ccid_reset_error_status(s);
+}
+
+static void ccid_write_parameters(USBCCIDState* s, CCID_Header* recv)
+{
+    CCID_Parameter *h;
+    uint32_t len = s->ulProtocolDataStructureSize;
+
+    h = ccid_reserve_recv_buf(s, sizeof(CCID_Parameter) + len);
+    if (h == NULL) {
+        return;
+    }
+    h->b.hdr.bMessageType = CCID_MESSAGE_TYPE_RDR_to_PC_Parameters;
+    h->b.hdr.dwLength = 0;
+    h->b.hdr.bSlot = recv->bSlot;
+    h->b.hdr.bSeq = recv->bSeq;
+    h->b.bStatus = ccid_calc_status(s);
+    h->b.bError = s->bError;
+    h->bProtocolNum = s->bProtocolNum;
+    memcpy(h->abProtocolDataStructure, s->abProtocolDataStructure, len);
+    ccid_reset_error_status(s);
+}
+
+static void ccid_write_data_block(
+    USBCCIDState* s, uint8_t slot, uint8_t seq,
+    const uint8_t* data, uint32_t len)
+{
+    CCID_DataBlock *p = ccid_reserve_recv_buf(s, sizeof(*p) + len);
+
+    if (p == NULL) {
+        return;
+    }
+    p->b.hdr.bMessageType = CCID_MESSAGE_TYPE_RDR_to_PC_DataBlock;
+    p->b.hdr.dwLength = cpu_to_le32(len);
+    p->b.hdr.bSlot = slot;
+    p->b.hdr.bSeq = seq;
+    p->b.bStatus = ccid_calc_status(s);
+    p->b.bError = s->bError;
+#ifdef DEBUG_CCID
+    if (p->b.bError) {
+        DPRINTF(s, 4, "error %d", p->b.bError);
+    }
+#endif
+    memcpy(p->abData, data, len);
+    ccid_reset_error_status(s);
+}
+
+static void ccid_write_data_block_answer(USBCCIDState* s,
+    const uint8_t* data, uint32_t len)
+{
+    uint8_t seq;
+    uint8_t slot;
+
+    if (!ccid_has_pending_answers(s)) {
+        abort();
+    }
+    ccid_remove_pending_answer(s, &slot, &seq);
+    ccid_write_data_block(s, slot, seq, data, len);
+}
+
+static void ccid_write_data_block_atr(USBCCIDState* s, CCID_Header* recv)
+{
+    const uint8_t *atr = NULL;
+    uint32_t len = 0;
+
+    if (s->card) {
+        atr = s->cardinfo->get_atr(s->card, &len);
+    }
+    ccid_write_data_block(s, recv->bSlot, recv->bSeq, atr, len);
+}
+
+static void ccid_set_parameters(USBCCIDState *s, CCID_Header *recv)
+{
+    CCID_SetParameter *ph = (CCID_SetParameter *) recv;
+    uint32_t len = 0;
+    if (ph->bProtocolNum == 0) {
+        len = 5;
+    }
+    if (ph->bProtocolNum == 1) {
+        len = 7;
+    }
+    if (len == 0) {
+        s->bmCommandStatus = COMMAND_STATUS_FAILED;
+        s->bError = 7; /* Protocol invalid or not supported */
+        return;
+    }
+    s->bProtocolNum = ph->bProtocolNum;
+    memcpy(s->abProtocolDataStructure, ph->abProtocolDataStructure, len);
+    s->ulProtocolDataStructureSize = len;
+    DPRINTF(s, 1, "%s: using len %d\n", __func__, len);
+}
+
+/* must be 5 bytes for T=0, 7 bytes for T=1
+ * See page 52 */
+static const uint8_t abDefaultProtocolDataStructure[7] =
+    { 0x77, 0x00, 0x00, 0x00, 0x00, 0xfe /*IFSC*/, 0x00 /*NAD*/ };
+
+static void ccid_reset_parameters(USBCCIDState *s)
+{
+   uint32_t len = sizeof(abDefaultProtocolDataStructure);
+
+   s->bProtocolNum = 1; /* T=1 */
+   s->ulProtocolDataStructureSize = len;
+   memcpy(s->abProtocolDataStructure, abDefaultProtocolDataStructure, len);
+}
+
+static void ccid_report_error_failed(USBCCIDState *s, uint8_t error)
+{
+    s->bmCommandStatus = COMMAND_STATUS_FAILED;
+    s->bError = error;
+}
+
+/* NOTE: only a single slot is supported (SLOT_0)
+ */
+static void ccid_on_slot_change(USBCCIDState* s, bool full)
+{
+    /* RDR_to_PC_NotifySlotChange, 6.3.1 page 56
+     */
+    uint8_t current = s->bmSlotICCState;
+    if (full) {
+        s->bmSlotICCState |= SLOT_0_STATE_MASK;
+    } else {
+        s->bmSlotICCState &= ~SLOT_0_STATE_MASK;
+    }
+    if (current != s->bmSlotICCState) {
+        s->bmSlotICCState |= SLOT_0_CHANGED_MASK;
+    }
+    s->notify_slot_change = true;
+}
+
+static void ccid_write_data_block_error(
+    USBCCIDState *s, uint8_t slot, uint8_t seq)
+{
+    ccid_write_data_block(s, slot, seq, NULL, 0);
+}
+
+static void ccid_on_apdu_from_guest(USBCCIDState *s, CCID_XferBlock *recv)
+{
+    uint32_t len;
+
+    if (ccid_card_status(s) != ICC_STATUS_PRESENT_ACTIVE) {
+        DPRINTF(s, 1, "usb-ccid: not sending apdu to client, no card connected\n");
+        ccid_write_data_block_error(s, recv->hdr.bSlot, recv->hdr.bSeq);
+        return;
+    }
+    len = le32_to_cpu(recv->hdr.dwLength);
+    DPRINTF(s, 1, "%s: seq %d, len %d\n", __FUNCTION__,
+                recv->hdr.bSeq, len);
+    ccid_add_pending_answer(s, (CCID_Header*)recv);
+    if (s->card) {
+        s->cardinfo->apdu_from_guest(s->card, recv->abData, len);
+    } else {
+        printf("warning: discarded apdu\n");
+    }
+}
+
+/* handle a single USB_TOKEN_OUT, return value returned to guest.
+ * 0 - all ok
+ * USB_RET_STALL - failed to handle packet */
+static int ccid_handle_bulk_out(USBCCIDState *s, USBPacket *p)
+{
+    CCID_Header* ccid_header;
+
+    if (p->len + s->bulk_out_pos > BULK_OUT_DATA_SIZE) {
+        return USB_RET_STALL;
+    }
+    ccid_header = (CCID_Header*)s->bulk_out_data;
+    memcpy(s->bulk_out_data + s->bulk_out_pos, p->data, p->len);
+    s->bulk_out_pos += p->len;
+    if (p->len == 64) {
+        DPRINTF(s, 4, "usb-ccid: bulk_in: expecting more packets (%d/%d)\n",
+            p->len, ccid_header->dwLength);
+        return 0;
+    }
+    if (s->bulk_out_pos < 10) {
+        DPRINTF(s, 1, "%s: bad USB_TOKEN_OUT length, should be at least 10 bytes\n", __func__);
+    } else {
+        DPRINTF(s, 3, "%s %x\n", __func__, ccid_header->bMessageType);
+        switch (ccid_header->bMessageType) {
+            case CCID_MESSAGE_TYPE_PC_to_RDR_GetSlotStatus:
+                ccid_write_slot_status(s, ccid_header);
+                break;
+            case CCID_MESSAGE_TYPE_PC_to_RDR_IccPowerOn:
+                DPRINTF(s, 1, "PowerOn: %d\n",
+                    ((CCID_IccPowerOn*)(ccid_header))->bPowerSelect);
+                s->powered = true;
+                if (!ccid_card_inserted(s)) {
+                    ccid_report_error_failed(s, ERROR_ICC_MUTE);
+                }
+                /* atr is written regardless of error. */
+                ccid_write_data_block_atr(s, ccid_header);
+                break;
+            case CCID_MESSAGE_TYPE_PC_to_RDR_IccPowerOff:
+                DPRINTF(s, 1, "PowerOff\n");
+                ccid_reset_error_status(s);
+                s->powered = false;
+                ccid_write_slot_status(s, ccid_header);
+                break;
+            case CCID_MESSAGE_TYPE_PC_to_RDR_XfrBlock:
+                ccid_on_apdu_from_guest(s, (CCID_XferBlock*)s->bulk_out_data);
+                break;
+            case CCID_MESSAGE_TYPE_PC_to_RDR_SetParameters:
+                ccid_reset_error_status(s);
+                ccid_set_parameters(s, ccid_header);
+                ccid_write_parameters(s, ccid_header);
+                break;
+            case CCID_MESSAGE_TYPE_PC_to_RDR_ResetParameters:
+                ccid_reset_error_status(s);
+                ccid_reset_parameters(s);
+                ccid_write_parameters(s, ccid_header);
+                break;
+            case CCID_MESSAGE_TYPE_PC_to_RDR_GetParameters:
+                ccid_reset_error_status(s);
+                ccid_write_parameters(s, ccid_header);
+                break;
+            default:
+                DPRINTF(s, 1, "handle_data: ERROR: unhandled message type %Xh\n",
+                    ccid_header->bMessageType);
+                /* the caller is expecting the device to respond, tell it we
+                 * do't support the operation */
+                ccid_report_error_failed(s, ERROR_CMD_NOT_SUPPORTED);
+                ccid_write_slot_status(s, ccid_header);
+                break;
+        }
+    }
+    s->bulk_out_pos = 0;
+    return 0;
+}
+
+static int ccid_bulk_in_copy_to_guest(USBCCIDState *s, uint8_t *data, int len)
+{
+    int ret = 0;
+
+    assert(len>0);
+    ccid_bulk_in_get(s);
+    if (s->current_bulk_in != NULL) {
+        ret = MIN(s->current_bulk_in->len - s->current_bulk_in->pos, len);
+        memcpy(data, s->current_bulk_in->data + s->current_bulk_in->pos, ret);
+        s->current_bulk_in->pos += ret;
+        if (s->current_bulk_in->pos == s->current_bulk_in->len) {
+            ccid_bulk_in_release(s);
+        }
+    } else {
+        ret = USB_RET_NAK; /* return when device has no data - usb 2.0 spec Table 8-4 */
+    }
+    if (ret > 0) {
+        DPRINTF(s, 3, "%s: %d/%d req/act to guest (BULK_IN)\n", __func__, len, ret);
+    }
+    if (ret != USB_RET_NAK && ret < len) {
+        DPRINTF(s, 1, "%s: returning short (EREMOTEIO) %d < %d\n", __func__, ret, len);
+    }
+    return ret;
+}
+
+static int ccid_handle_data(USBDevice *dev, USBPacket *p)
+{
+    USBCCIDState *s = DO_UPCAST(USBCCIDState, dev, dev);
+    int ret = 0;
+    uint8_t *data = p->data;
+    int len = p->len;
+
+    switch (p->pid) {
+    case USB_TOKEN_OUT:
+        ret = ccid_handle_bulk_out(s, p);
+        break;
+
+    case USB_TOKEN_IN:
+        switch (p->devep & 0xf) {
+            case CCID_BULK_IN_EP:
+                if (!len) {
+                    ret = USB_RET_NAK;
+                } else {
+                    ret = ccid_bulk_in_copy_to_guest(s, data, len);
+                }
+                break;
+            case CCID_INT_IN_EP:
+                if (s->notify_slot_change) {
+                    /* page 56, RDR_to_PC_NotifySlotChange */
+                    data[0] = CCID_MESSAGE_TYPE_RDR_to_PC_NotifySlotChange;
+                    data[1] = s->bmSlotICCState;
+                    ret = 2;
+                    s->notify_slot_change = false;
+                    s->bmSlotICCState &= ~SLOT_0_CHANGED_MASK;
+                    DPRINTF(s, 2, "handle_data: int_in: notify_slot_change %X, requested len %d\n",
+                            s->bmSlotICCState, len);
+                }
+                break;
+            default:
+                DPRINTF(s, 1, "Bad endpoint\n");
+                break;
+        }
+        break;
+    default:
+        DPRINTF(s, 1, "Bad token\n");
+        ret = USB_RET_STALL;
+        break;
+    }
+
+    return ret;
+}
+
+static void ccid_handle_destroy(USBDevice *dev)
+{
+    USBCCIDState *s = DO_UPCAST(USBCCIDState, dev, dev);
+
+    ccid_bulk_in_clear(s);
+}
+
+static void ccid_flush_pending_answers(USBCCIDState *s) {
+    while (ccid_has_pending_answers(s)) {
+        ccid_write_data_block_answer(s, NULL, 0);
+    }
+}
+
+static Answer *ccid_peek_next_answer(USBCCIDState *s)
+{
+    return s->pending_answers_num == 0
+        ? NULL
+        : &s->pending_answers[s->pending_answers_start % PENDING_ANSWERS_NUM];
+}
+
+static void ccid_bus_dev_print(Monitor *mon, DeviceState *qdev, int indent)
+{
+    CCIDCardState *card = DO_UPCAST(CCIDCardState, qdev, qdev);
+    CCIDCardInfo *info = DO_UPCAST(CCIDCardInfo, qdev, qdev->info);
+
+    if (info->print) {
+        info->print(mon, card, indent);
+    }
+}
+
+struct CCIDBus {
+    BusState qbus;
+};
+
+static struct BusInfo ccid_bus_info = {
+    .name = "ccid-bus",
+    .size = sizeof(CCIDBus),
+    .print_dev = ccid_bus_dev_print,
+    .props = (Property[]) {
+        DEFINE_PROP_UINT32("slot", struct CCIDCardState, slot, 0),
+        DEFINE_PROP_END_OF_LIST(),
+    }
+};
+
+static CCIDBus *ccid_bus_new(DeviceState *dev)
+{
+    CCIDBus *bus;
+
+    bus = FROM_QBUS(CCIDBus, qbus_create(&ccid_bus_info, dev, NULL));
+    bus->qbus.allow_hotplug = 1;
+
+    return bus;
+}
+
+void ccid_card_send_apdu_to_guest(CCIDCardState *card, uint8_t* apdu, uint32_t len)
+{
+    USBCCIDState *s = DO_UPCAST(USBCCIDState, dev.qdev, card->qdev.parent_bus->parent);
+    Answer *answer;
+
+    if (!ccid_has_pending_answers(s)) {
+        DPRINTF(s, 1, "CCID ERROR: got an APDU without pending answers\n");
+        return;
+    }
+    s->bmCommandStatus = COMMAND_STATUS_NO_ERROR;
+    answer = ccid_peek_next_answer(s);
+    if (answer == NULL) {
+        abort();
+    }
+    DPRINTF(s, 1, "APDU returned to guest %d (answer seq %d, slot %d)\n",
+        len, answer->seq, answer->slot);
+    ccid_write_data_block_answer(s, apdu, len);
+}
+
+void ccid_card_card_removed(CCIDCardState *card)
+{
+    USBCCIDState *s = DO_UPCAST(USBCCIDState, dev.qdev, card->qdev.parent_bus->parent);
+
+    ccid_on_slot_change(s, false);
+    ccid_flush_pending_answers(s);
+    ccid_reset(s);
+}
+
+int ccid_card_ccid_attach(CCIDCardState *card)
+{
+    USBCCIDState *s = DO_UPCAST(USBCCIDState, dev.qdev, card->qdev.parent_bus->parent);
+
+    DPRINTF(s, 1, "CCID Attach\n");
+    if (s->migration_state == MIGRATION_MIGRATED) {
+        s->migration_state = MIGRATION_NONE;
+    }
+    return 0;
+}
+
+void ccid_card_ccid_detach(CCIDCardState *card)
+{
+    USBCCIDState *s = DO_UPCAST(USBCCIDState, dev.qdev, card->qdev.parent_bus->parent);
+
+    DPRINTF(s, 1, "CCID Detach\n");
+    if (ccid_card_inserted(s)) {
+        ccid_on_slot_change(s, false);
+    }
+    ccid_detach(s);
+}
+
+void ccid_card_card_error(CCIDCardState *card, uint64_t error)
+{
+    USBCCIDState *s = DO_UPCAST(USBCCIDState, dev.qdev, card->qdev.parent_bus->parent);
+
+    s->bmCommandStatus = COMMAND_STATUS_FAILED;
+    s->last_answer_error = error;
+    DPRINTF(s, 1, "VSC_Error: %lX\n", s->last_answer_error);
+    /* TODO: these error's should be more verbose and propogated to the guest.
+     * */
+    ccid_write_data_block_answer(s, NULL, 0);
+}
+
+void ccid_card_card_inserted(CCIDCardState *card)
+{
+    USBCCIDState *s = DO_UPCAST(USBCCIDState, dev.qdev, card->qdev.parent_bus->parent);
+
+    s->bmCommandStatus = COMMAND_STATUS_NO_ERROR;
+    ccid_flush_pending_answers(s);
+    ccid_on_slot_change(s, true);
+}
+
+static int ccid_card_exit(DeviceState *qdev)
+{
+    int ret = 0;
+    CCIDCardState *card = DO_UPCAST(CCIDCardState, qdev, qdev);
+    CCIDCardInfo *info = DO_UPCAST(CCIDCardInfo, qdev, qdev->info);
+    USBCCIDState *s = DO_UPCAST(USBCCIDState, dev.qdev, card->qdev.parent_bus->parent);
+
+    if (ccid_card_inserted(s)) {
+        ccid_card_card_removed(card);
+    }
+    if (info->exitfn) {
+        ret = info->exitfn(card);
+    }
+    s->card = NULL;
+    s->cardinfo = NULL;
+    return ret;
+}
+
+static int ccid_card_init(DeviceState *qdev, DeviceInfo *base)
+{
+    CCIDCardState *card = DO_UPCAST(CCIDCardState, qdev, qdev);
+    CCIDCardInfo *info = DO_UPCAST(CCIDCardInfo, qdev, base);
+    USBCCIDState *s = DO_UPCAST(USBCCIDState, dev.qdev, card->qdev.parent_bus->parent);
+    int ret = 0;
+
+    if (card->slot != 0) {
+        fprintf(stderr, "Warning: usb-ccid supports one slot, can't add %d",
+                card->slot);
+        return -1;
+    }
+    if (s->card != NULL) {
+        fprintf(stderr, "Warning: usb-ccid card already full, not adding\n");
+        return -1;
+    }
+    ret = info->initfn ? info->initfn(card) : ret;
+    if (ret == 0) {
+        s->card = card;
+        s->cardinfo = info;
+    }
+    return ret;
+}
+
+void ccid_card_qdev_register(CCIDCardInfo *card)
+{
+    card->qdev.bus_info = &ccid_bus_info;
+    card->qdev.init = ccid_card_init;
+    card->qdev.exit = ccid_card_exit;
+    qdev_register(&card->qdev);
+}
+
+static int ccid_initfn(USBDevice *dev)
+{
+    USBCCIDState *s = DO_UPCAST(USBCCIDState, dev, dev);
+
+    s->bus = ccid_bus_new(&dev->qdev);
+    s->card = NULL;
+    s->cardinfo = NULL;
+    s->migration_state = MIGRATION_NONE;
+    s->migration_target_ip = 0;
+    s->migration_target_port = 0;
+    s->dev.speed = USB_SPEED_FULL;
+    s->notify_slot_change = false;
+    s->powered = true;
+    s->pending_answers_num = 0;
+    s->last_answer_error = 0;
+    s->bulk_in_pending_start = 0;
+    s->bulk_in_pending_end = 0;
+    s->current_bulk_in = NULL;
+    ccid_reset_error_status(s);
+    s->bulk_out_pos = 0;
+    ccid_reset_parameters(s);
+    ccid_reset(s);
+    return 0;
+}
+
+#ifdef ENABLE_MIGRATION
+static int ccid_post_load(void *opaque, int version_id)
+{
+    USBCCIDState *s = opaque;
+
+    // This must be done after usb_device_attach, which sets state to ATTACHED,
+    // while it must be DEFAULT in order to accept packets (like it is after
+    // reset, but reset will reset our addr and call our reset handler which
+    // may change state, and we don't want to do that when migrating).
+    s->dev.state = s->state_vmstate;
+    return 0;
+}
+
+static void ccid_pre_save(void *opaque)
+{
+    USBCCIDState *s = opaque;
+
+    s->state_vmstate = s->dev.state;
+    if (s->dev.attached) {
+        // migrating an open device, ignore reconnection CHR_EVENT to avoid an
+        // erronous detach.
+        s->migration_state = MIGRATION_MIGRATED;
+    }
+}
+
+static VMStateDescription bulk_in_vmstate = {
+    .name = "CCID BulkIn state",
+    .version_id = 1,
+    .minimum_version_id = 1,
+    .fields = (VMStateField []) {
+        VMSTATE_BUFFER(data, BulkIn),
+        VMSTATE_UINT32(len, BulkIn),
+        VMSTATE_UINT32(pos, BulkIn),
+        VMSTATE_END_OF_LIST()
+    }
+};
+
+static VMStateDescription answer_vmstate = {
+    .name = "CCID Answer state",
+    .version_id = 1,
+    .minimum_version_id = 1,
+    .fields = (VMStateField []) {
+        VMSTATE_UINT8(slot, Answer),
+        VMSTATE_UINT8(seq, Answer),
+        VMSTATE_END_OF_LIST()
+    }
+};
+
+static VMStateDescription usb_device_vmstate = {
+    .name = "usb_device",
+    .version_id = 1,
+    .minimum_version_id = 1,
+    .fields = (VMStateField []) {
+        VMSTATE_UINT8(addr, USBDevice),
+        VMSTATE_BUFFER(setup_buf, USBDevice),
+        VMSTATE_BUFFER(data_buf, USBDevice),
+        VMSTATE_END_OF_LIST()
+    }
+};
+
+static VMStateDescription ccid_vmstate = {
+    .name = CCID_DEV_NAME,
+    .version_id = 1,
+    .minimum_version_id = 1,
+    .post_load = ccid_post_load,
+    .pre_save = ccid_pre_save,
+    .fields = (VMStateField []) {
+        VMSTATE_STRUCT(dev, USBCCIDState, 1, usb_device_vmstate, USBDevice),
+        VMSTATE_UINT8(debug, USBCCIDState),
+        VMSTATE_BUFFER(bulk_out_data, USBCCIDState),
+        VMSTATE_UINT32(bulk_out_pos, USBCCIDState),
+        VMSTATE_UINT8(bmSlotICCState, USBCCIDState),
+        VMSTATE_UINT8(powered, USBCCIDState),
+        VMSTATE_UINT8(notify_slot_change, USBCCIDState),
+        VMSTATE_UINT64(last_answer_error, USBCCIDState),
+        VMSTATE_UINT8(bError, USBCCIDState),
+        VMSTATE_UINT8(bmCommandStatus, USBCCIDState),
+        VMSTATE_UINT8(bProtocolNum, USBCCIDState),
+        VMSTATE_BUFFER(abProtocolDataStructure, USBCCIDState),
+        VMSTATE_UINT32(ulProtocolDataStructureSize, USBCCIDState),
+        VMSTATE_STRUCT_ARRAY(bulk_in_pending, USBCCIDState,
+                       BULK_IN_PENDING_NUM, 1, bulk_in_vmstate, BulkIn),
+        VMSTATE_UINT32(bulk_in_pending_start, USBCCIDState),
+        VMSTATE_UINT32(bulk_in_pending_end, USBCCIDState),
+        VMSTATE_STRUCT_ARRAY(pending_answers, USBCCIDState,
+                        PENDING_ANSWERS_NUM, 1, answer_vmstate, Answer),
+        VMSTATE_UINT32(pending_answers_num, USBCCIDState),
+        VMSTATE_UINT8(migration_state, USBCCIDState),
+        VMSTATE_UINT32(state_vmstate, USBCCIDState),
+        VMSTATE_END_OF_LIST()
+    }
+};
+#endif // ENABLE_MIGRATION
+
+static struct USBDeviceInfo ccid_info = {
+    .product_desc   = "QEMU USB CCID",
+    .qdev.name      = CCID_DEV_NAME,
+    .qdev.size      = sizeof(USBCCIDState),
+    .init           = ccid_initfn,
+    .handle_packet  = usb_generic_handle_packet,
+    .handle_reset   = ccid_handle_reset,
+    .handle_control = ccid_handle_control,
+    .handle_data    = ccid_handle_data,
+    .handle_destroy = ccid_handle_destroy,
+    .usbdevice_name = "ccid",
+    .qdev.props     = (Property[]) {
+        DEFINE_PROP_UINT8("debug", USBCCIDState, debug, 0),
+        DEFINE_PROP_END_OF_LIST(),
+    },
+#ifdef ENABLE_MIGRATION
+    .qdev.vmsd      = &ccid_vmstate,
+#endif
+};
+
+
+static void ccid_register_devices(void)
+{
+    usb_qdev_register(&ccid_info);
+}
+device_init(ccid_register_devices)
-- 
1.7.3.4

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

* [Qemu-devel] [PATCH 2/7] ccid: add passthru card device
  2011-01-11  8:42 [Qemu-devel] [PATCH 0/7] usb-ccid (v15) Alon Levy
  2011-01-11  8:42 ` [Qemu-devel] [PATCH 1/7] usb-ccid: add CCID bus Alon Levy
@ 2011-01-11  8:42 ` Alon Levy
  2011-01-25 14:17   ` Anthony Liguori
  2011-01-11  8:42 ` [Qemu-devel] [PATCH 3/7] libcacard: initial commit after coding style fixes Alon Levy
                   ` (6 subsequent siblings)
  8 siblings, 1 reply; 23+ messages in thread
From: Alon Levy @ 2011-01-11  8:42 UTC (permalink / raw)
  To: qemu-devel

The passthru ccid card is a device sitting on the usb-ccid bus and
using a chardevice to communicate with a remote device using the
VSCard protocol defined in libcacard/vscard_common.h

Usage docs available in following patch in docs/ccid.txt

Signed-off-by: Alon Levy <alevy@redhat.com>
---
 Makefile.objs             |    2 +-
 hw/ccid-card-passthru.c   |  273 +++++++++++++++++++++++++++++++++++++++++++++
 libcacard/vscard_common.h |  130 +++++++++++++++++++++
 3 files changed, 404 insertions(+), 1 deletions(-)
 create mode 100644 hw/ccid-card-passthru.c
 create mode 100644 libcacard/vscard_common.h

diff --git a/Makefile.objs b/Makefile.objs
index 7da4771..274db5e 100644
--- a/Makefile.objs
+++ b/Makefile.objs
@@ -197,7 +197,7 @@ hw-obj-$(CONFIG_FDC) += fdc.o
 hw-obj-$(CONFIG_ACPI) += acpi.o acpi_piix4.o
 hw-obj-$(CONFIG_APM) += pm_smbus.o apm.o
 hw-obj-$(CONFIG_DMA) += dma.o
-hw-obj-$(CONFIG_SMARTCARD) += usb-ccid.o
+hw-obj-$(CONFIG_SMARTCARD) += usb-ccid.o ccid-card-passthru.o
 
 # PPC devices
 hw-obj-$(CONFIG_OPENPIC) += openpic.o
diff --git a/hw/ccid-card-passthru.c b/hw/ccid-card-passthru.c
new file mode 100644
index 0000000..6ec4f21
--- /dev/null
+++ b/hw/ccid-card-passthru.c
@@ -0,0 +1,273 @@
+/*
+ * CCID Card Device emulation
+ *
+ * Copyright (c) 2010 Red Hat.
+ * Written by Alon Levy.
+ *
+ * This code is licenced under the LGPL.
+ */
+
+#include "qemu-char.h"
+#include "monitor.h"
+#include "hw/ccid.h"
+#include "libcacard/vscard_common.h"
+
+#define DPRINTF(card, lvl, fmt, ...) \
+do { if (lvl <= card->debug) { printf("ccid-card: " fmt , ## __VA_ARGS__); } } while (0)
+
+/* Passthru card */
+
+
+// TODO: do we still need this?
+uint8_t DEFAULT_ATR[] = {
+/* From some example somewhere
+ 0x3B, 0xB0, 0x18, 0x00, 0xD1, 0x81, 0x05, 0xB1, 0x40, 0x38, 0x1F, 0x03, 0x28
+ */
+
+/* From an Athena smart card */
+ 0x3B, 0xD5, 0x18, 0xFF, 0x80, 0x91, 0xFE, 0x1F, 0xC3, 0x80, 0x73, 0xC8, 0x21, 0x13, 0x08
+
+}; /* maximum size of ATR - from 7816-3 */
+
+
+#define PASSTHRU_DEV_NAME "ccid-card-passthru"
+#define VSCARD_IN_SIZE 65536
+#define MAX_ATR_SIZE        40
+
+typedef struct PassthruState PassthruState;
+
+struct PassthruState {
+    CCIDCardState base;
+    CharDriverState *cs;
+    uint8_t  vscard_in_data[VSCARD_IN_SIZE];
+    uint32_t vscard_in_pos;
+    uint32_t vscard_in_hdr;
+    uint8_t  atr[MAX_ATR_SIZE];
+    uint8_t  atr_length;
+    uint8_t debug;
+};
+
+/* VSCard protocol over chardev
+ * This code should not depend on the card type.
+ * */
+
+static void ccid_card_vscard_send_msg(
+    PassthruState *s, VSCMsgType type, reader_id_t reader_id,
+        const uint8_t* payload, uint32_t length)
+{
+    VSCMsgHeader scr_msg_header;
+
+    scr_msg_header.type = type;
+    scr_msg_header.reader_id = reader_id;
+    scr_msg_header.length = length;
+    qemu_chr_write(s->cs, (uint8_t*)&scr_msg_header, sizeof(VSCMsgHeader));
+    qemu_chr_write(s->cs, payload, length);
+}
+
+static void ccid_card_vscard_send_apdu(
+    PassthruState *s, const uint8_t* apdu, uint32_t length)
+{
+    ccid_card_vscard_send_msg(s, VSC_APDU, VSCARD_MINIMAL_READER_ID, apdu, length);
+}
+
+static void ccid_card_vscard_send_error(
+    PassthruState *s, reader_id_t reader_id, VSCErrorCode code)
+{
+    VSCMsgError msg = {.code=code};
+
+    ccid_card_vscard_send_msg(s, VSC_Error, reader_id, (uint8_t*)&msg, sizeof(msg));
+}
+
+static void ccid_card_vscard_send_init(PassthruState *s)
+{
+    VSCMsgInit msg = {.version=VSCARD_VERSION};
+
+    ccid_card_vscard_send_msg(s, VSC_Init, VSCARD_UNDEFINED_READER_ID,
+                         (uint8_t*)&msg, sizeof(msg));
+}
+
+static int ccid_card_vscard_can_read(void *opaque)
+{
+    return 65535;
+}
+
+static void ccid_card_vscard_handle_message(PassthruState *card,
+    VSCMsgHeader* scr_msg_header)
+{
+    uint8_t *data = (uint8_t*)&scr_msg_header[1];
+
+    switch (scr_msg_header->type) {
+        case VSC_ATR:
+            DPRINTF(card, 1, "VSC_ATR %d\n", scr_msg_header->length);
+            assert(scr_msg_header->length <= MAX_ATR_SIZE);
+            memcpy(card->atr, data, scr_msg_header->length);
+            card->atr_length = scr_msg_header->length;
+            ccid_card_card_inserted(&card->base);
+            break;
+        case VSC_APDU:
+            ccid_card_send_apdu_to_guest(&card->base, data, scr_msg_header->length);
+            break;
+        case VSC_CardRemove:
+            DPRINTF(card, 1, "VSC_CardRemove\n");
+            ccid_card_card_removed(&card->base);
+            break;
+        case VSC_Init:
+            break;
+        case VSC_Error:
+            ccid_card_card_error(&card->base, *(uint64_t*)data);
+            break;
+        case VSC_ReaderAdd:
+            if (ccid_card_ccid_attach(&card->base) < 0) {
+                ccid_card_vscard_send_error(card, VSCARD_UNDEFINED_READER_ID,
+                                          VSC_CANNOT_ADD_MORE_READERS);
+            } else {
+                ccid_card_vscard_send_msg(card, VSC_ReaderAddResponse,
+                                             VSCARD_MINIMAL_READER_ID, NULL, 0);
+            }
+            break;
+        case VSC_ReaderRemove:
+            ccid_card_ccid_detach(&card->base);
+            break;
+        default:
+            printf("usb-ccid: chardev: unexpected message of type %X\n",
+                   scr_msg_header->type);
+            ccid_card_vscard_send_error(card, scr_msg_header->reader_id,
+                VSC_GENERAL_ERROR);
+    }
+}
+
+static void ccid_card_vscard_read(void *opaque, const uint8_t *buf, int size)
+{
+    PassthruState *card = opaque;
+    VSCMsgHeader *hdr;
+
+    assert(card->vscard_in_pos + size <= VSCARD_IN_SIZE);
+    memcpy(card->vscard_in_data + card->vscard_in_pos, buf, size);
+    card->vscard_in_pos += size;
+    hdr = (VSCMsgHeader*)(card->vscard_in_data + card->vscard_in_hdr);
+
+    while ((card->vscard_in_pos - card->vscard_in_hdr >= sizeof(VSCMsgHeader)) &&
+           (card->vscard_in_pos - card->vscard_in_hdr - sizeof(VSCMsgHeader) >=
+           hdr->length)) {
+        ccid_card_vscard_handle_message(card, hdr);
+        card->vscard_in_hdr += hdr->length + sizeof(VSCMsgHeader);
+        hdr = (VSCMsgHeader*)(card->vscard_in_data + card->vscard_in_hdr);
+    }
+    if (card->vscard_in_hdr == card->vscard_in_pos) {
+        card->vscard_in_pos = card->vscard_in_hdr = 0;
+    }
+}
+
+static void ccid_card_vscard_event(void *opaque, int event)
+{
+    PassthruState *card = opaque;
+
+    switch (event) {
+        case CHR_EVENT_BREAK:
+            break;
+        case CHR_EVENT_FOCUS:
+            break;
+        case CHR_EVENT_OPENED:
+            DPRINTF(card, 1, "%s: CHR_EVENT_OPENED\n", __func__);
+            break;
+    }
+}
+
+/* End VSCard handling */
+
+static void passthru_apdu_from_guest(CCIDCardState *base, const uint8_t *apdu, uint32_t len)
+{
+    PassthruState *card = DO_UPCAST(PassthruState, base, base);
+
+    if (!card->cs) {
+        printf("ccid-passthru: no chardev, discarding apdu length %d\n", len);
+        return;
+    }
+    ccid_card_vscard_send_apdu(card, apdu, len);
+}
+
+static const uint8_t* passthru_get_atr(CCIDCardState *base, uint32_t *len)
+{
+    PassthruState *card = DO_UPCAST(PassthruState, base, base);
+
+    *len = card->atr_length;
+    return card->atr;
+}
+
+static int passthru_initfn(CCIDCardState *base)
+{
+    PassthruState *card = DO_UPCAST(PassthruState, base, base);
+
+    card->vscard_in_pos = 0;
+    card->vscard_in_hdr = 0;
+    if (card->cs) {
+        DPRINTF(card, 1, "initing chardev\n");
+        qemu_chr_add_handlers(card->cs,
+            ccid_card_vscard_can_read,
+            ccid_card_vscard_read,
+            ccid_card_vscard_event, card);
+        ccid_card_vscard_send_init(card);
+    }
+    assert(sizeof(DEFAULT_ATR) <= MAX_ATR_SIZE);
+    memcpy(card->atr, DEFAULT_ATR, sizeof(DEFAULT_ATR));
+    card->atr_length = sizeof(DEFAULT_ATR);
+    return 0;
+}
+
+static int passthru_exitfn(CCIDCardState *base)
+{
+    return 0;
+}
+
+static void passthru_pre_save(void *opaque)
+{
+    PassthruState *card = opaque;
+    VSCMsgReconnect reconnect;
+
+    reconnect.ip = 0; // TODO - does the bus keep the target ip? s->migration_target_ip;
+    reconnect.port = 0; // TODO - does the bus keep the target ip? s->migration_target_port;
+    ccid_card_vscard_send_msg(card, VSC_Reconnect, VSCARD_UNDEFINED_READER_ID,
+                         (uint8_t*)&reconnect, sizeof(reconnect));
+}
+
+static VMStateDescription passthru_vmstate = {
+    .name = PASSTHRU_DEV_NAME,
+    .version_id = 1,
+    .minimum_version_id = 1,
+    .pre_save = passthru_pre_save,
+    .fields = (VMStateField []) {
+        VMSTATE_BUFFER(vscard_in_data, PassthruState),
+        VMSTATE_UINT32(vscard_in_pos, PassthruState),
+        VMSTATE_UINT32(vscard_in_hdr, PassthruState),
+        VMSTATE_BUFFER(atr, PassthruState),
+        VMSTATE_UINT8(atr_length, PassthruState),
+        VMSTATE_END_OF_LIST()
+    }
+};
+
+static CCIDCardInfo passthru_card_info = {
+    .qdev.name = PASSTHRU_DEV_NAME,
+    .qdev.size = sizeof(PassthruState),
+    .qdev.vmsd = &passthru_vmstate,
+    .initfn = passthru_initfn,
+    .exitfn = passthru_exitfn,
+    .get_atr = passthru_get_atr,
+    .apdu_from_guest = passthru_apdu_from_guest,
+    .qdev.unplug    = qdev_simple_unplug_cb,
+    .qdev.props     = (Property[]) {
+        DEFINE_PROP_CHR("chardev", PassthruState, cs),
+        DEFINE_PROP_UINT8("debug", PassthruState, debug, 0),
+        DEFINE_PROP_END_OF_LIST(),
+    },
+};
+
+static void ccid_card_passthru_register_devices(void)
+{
+    ccid_card_qdev_register(&passthru_card_info);
+    // TODO: passthru local card (or: just a case of passthru with no chardev
+    //  given and instead some other arguments that would be required for local
+    //  card anyway and can be shared with the emulated local card)
+    // TODO: emulated local card
+}
+
+device_init(ccid_card_passthru_register_devices)
diff --git a/libcacard/vscard_common.h b/libcacard/vscard_common.h
new file mode 100644
index 0000000..9ff1295
--- /dev/null
+++ b/libcacard/vscard_common.h
@@ -0,0 +1,130 @@
+/* Virtual Smart Card protocol definition
+ *
+ * This protocol is between a host implementing a group of virtual smart card
+ * reader, and a client implementing a virtual smart card, or passthrough to
+ * a real card.
+ *
+ * The current implementation passes the raw APDU's from 7816 and additionally
+ * contains messages to setup and teardown readers, handle insertion and
+ * removal of cards, negotiate the protocol and provide for error responses.
+ *
+ * Copyright (c) 2010 Red Hat.
+ *
+ * This code is licensed under the LGPL.
+ */
+
+#ifndef _VSCARD_COMMON_H
+#define _VSCARD_COMMON_H
+
+#include <stdint.h>
+
+#define VERSION_MAJOR_BITS 11
+#define VERSION_MIDDLE_BITS 11
+#define VERSION_MINOR_BITS 10
+
+#define MAKE_VERSION(major, middle, minor) \
+     (  (major  << (VERSION_MINOR_BITS + VERSION_MIDDLE_BITS)) \
+      | (middle <<  VERSION_MINOR_BITS) \
+      | (minor)  )
+
+/** IMPORTANT NOTE on VERSION
+ *
+ * The version below MUST be changed whenever a change in this file is made.
+ *
+ * The last digit, the minor, is for bug fix changes only.
+ *
+ * The middle digit is for backward / forward compatible changes, updates
+ * to the existing messages, addition of fields.
+ *
+ * The major digit is for a breaking change of protocol, presumably
+ * something that cannot be accomodated with the existing protocol.
+ */
+
+#define VSCARD_VERSION MAKE_VERSION(0,0,1)
+
+typedef enum {
+    VSC_Init,
+    VSC_Error,
+    VSC_ReaderAdd,
+    VSC_ReaderAddResponse,
+    VSC_ReaderRemove,
+    VSC_ATR,
+    VSC_CardRemove,
+    VSC_APDU,
+    VSC_Reconnect
+} VSCMsgType;
+
+typedef enum {
+    VSC_GENERAL_ERROR=1,
+    VSC_CANNOT_ADD_MORE_READERS,
+} VSCErrorCode;
+
+typedef uint32_t reader_id_t;
+#define VSCARD_UNDEFINED_READER_ID 0xffffffff
+#define VSCARD_MINIMAL_READER_ID    0
+
+typedef struct VSCMsgHeader {
+    VSCMsgType type;
+    reader_id_t   reader_id;
+    uint32_t   length;
+    uint8_t    data[0];
+} VSCMsgHeader;
+
+/* VSCMsgInit               Client <-> Host
+ * Host replies with allocated reader id in ReaderAddResponse
+ * */
+typedef struct VSCMsgInit {
+    uint32_t   version;
+} VSCMsgInit;
+
+/* VSCMsgError              Client <-> Host
+ * */
+typedef struct VSCMsgError {
+    uint32_t   code;
+} VSCMsgError;
+
+/* VSCMsgReaderAdd          Client -> Host
+ * Host replies with allocated reader id in ReaderAddResponse
+ * name - name of the reader on client side.
+ * */
+typedef struct VSCMsgReaderAdd {
+    uint8_t    name[0];
+} VSCMsgReaderAdd;
+
+/* VSCMsgReaderAddResponse  Host -> Client
+ * Reply to ReaderAdd
+ * */
+typedef struct VSCMsgReaderAddResponse {
+} VSCMsgReaderAddResponse;
+
+/* VSCMsgReaderRemove       Client -> Host
+ * */
+typedef struct VSCMsgReaderRemove {
+} VSCMsgReaderRemove;
+
+/* VSCMsgATR                Client -> Host
+ * Answer to reset. Sent for card insertion or card reset.
+ * */
+typedef struct VSCMsgATR {
+    uint8_t     atr[0];
+} VSCMsgATR;
+
+/* VSCMsgCardRemove         Client -> Host
+ * */
+typedef struct VSCMsgCardRemove {
+} VSCMsgCardRemove;
+
+/* VSCMsgAPDU               Client <-> Host
+ * */
+typedef struct VSCMsgAPDU {
+    uint8_t    data[0];
+} VSCMsgAPDU;
+
+/* VSCMsgReconnect          Host -> Client
+ * */
+typedef struct VSCMsgReconnect {
+    uint32_t   ip;
+    uint16_t   port;
+} VSCMsgReconnect;
+
+#endif
-- 
1.7.3.4

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

* [Qemu-devel] [PATCH 3/7] libcacard: initial commit after coding style fixes
  2011-01-11  8:42 [Qemu-devel] [PATCH 0/7] usb-ccid (v15) Alon Levy
  2011-01-11  8:42 ` [Qemu-devel] [PATCH 1/7] usb-ccid: add CCID bus Alon Levy
  2011-01-11  8:42 ` [Qemu-devel] [PATCH 2/7] ccid: add passthru card device Alon Levy
@ 2011-01-11  8:42 ` Alon Levy
  2011-01-25 14:19   ` Anthony Liguori
  2011-01-11  8:42 ` [Qemu-devel] [PATCH 4/7] ccid: add ccid-card-emulated device (v2) Alon Levy
                   ` (5 subsequent siblings)
  8 siblings, 1 reply; 23+ messages in thread
From: Alon Levy @ 2011-01-11  8:42 UTC (permalink / raw)
  To: qemu-devel

From: Robert Relyea <rrelyea@redhat.com>

libcacard emulates a Common Access Card (CAC) which is a standard
for smartcards. It is used by the emulated ccid card introduced in
a following patch. Docs are available in docs/libcacard.txt

Signed-off-by: Alon Levy <alevy@redhat.com>
---
 Makefile                    |    6 +-
 Makefile.objs               |    5 +
 Makefile.target             |    2 +
 configure                   |   25 +
 docs/libcacard.txt          |  483 ++++++++++++++++++
 libcacard/Makefile          |   14 +
 libcacard/cac.c             |  411 +++++++++++++++
 libcacard/cac.h             |   20 +
 libcacard/card_7816.c       |  780 ++++++++++++++++++++++++++++
 libcacard/card_7816.h       |   60 +++
 libcacard/card_7816t.h      |  163 ++++++
 libcacard/config.h          |   81 +++
 libcacard/event.c           |  112 ++++
 libcacard/eventt.h          |   28 +
 libcacard/link_test.c       |   20 +
 libcacard/mutex.h           |   59 +++
 libcacard/passthru.c        |  612 ++++++++++++++++++++++
 libcacard/passthru.h        |   50 ++
 libcacard/vcard.c           |  350 +++++++++++++
 libcacard/vcard.h           |   85 ++++
 libcacard/vcard_emul.h      |   62 +++
 libcacard/vcard_emul_nss.c  | 1171 +++++++++++++++++++++++++++++++++++++++++++
 libcacard/vcard_emul_type.c |   60 +++
 libcacard/vcard_emul_type.h |   29 ++
 libcacard/vcardt.h          |   66 +++
 libcacard/vevent.h          |   26 +
 libcacard/vreader.c         |  526 +++++++++++++++++++
 libcacard/vreader.h         |   54 ++
 libcacard/vreadert.h        |   23 +
 libcacard/vscclient.c       |  710 ++++++++++++++++++++++++++
 30 files changed, 6091 insertions(+), 2 deletions(-)
 create mode 100644 docs/libcacard.txt
 create mode 100644 libcacard/Makefile
 create mode 100644 libcacard/cac.c
 create mode 100644 libcacard/cac.h
 create mode 100644 libcacard/card_7816.c
 create mode 100644 libcacard/card_7816.h
 create mode 100644 libcacard/card_7816t.h
 create mode 100644 libcacard/config.h
 create mode 100644 libcacard/event.c
 create mode 100644 libcacard/eventt.h
 create mode 100644 libcacard/link_test.c
 create mode 100644 libcacard/mutex.h
 create mode 100644 libcacard/passthru.c
 create mode 100644 libcacard/passthru.h
 create mode 100644 libcacard/vcard.c
 create mode 100644 libcacard/vcard.h
 create mode 100644 libcacard/vcard_emul.h
 create mode 100644 libcacard/vcard_emul_nss.c
 create mode 100644 libcacard/vcard_emul_type.c
 create mode 100644 libcacard/vcard_emul_type.h
 create mode 100644 libcacard/vcardt.h
 create mode 100644 libcacard/vevent.h
 create mode 100644 libcacard/vreader.c
 create mode 100644 libcacard/vreader.h
 create mode 100644 libcacard/vreadert.h
 create mode 100644 libcacard/vscclient.c

diff --git a/Makefile b/Makefile
index 6d601ee..b522d0a 100644
--- a/Makefile
+++ b/Makefile
@@ -173,6 +173,8 @@ check-qlist: check-qlist.o qlist.o qint.o $(CHECK_PROG_DEPS)
 check-qfloat: check-qfloat.o qfloat.o $(CHECK_PROG_DEPS)
 check-qjson: check-qjson.o qfloat.o qint.o qdict.o qstring.o qlist.o qbool.o qjson.o json-streamer.o json-lexer.o json-parser.o $(CHECK_PROG_DEPS)
 
+QEMULIBS=libhw32 libhw64 libuser libdis libdis-user libcacard
+
 clean:
 # avoid old build problems by removing potentially incorrect old files
 	rm -f config.mak op-i386.h opc-i386.h gen-op-i386.h op-arm.h opc-arm.h gen-op-arm.h
@@ -184,7 +186,7 @@ clean:
 	rm -f trace-dtrace.dtrace trace-dtrace.dtrace-timestamp
 	rm -f trace-dtrace.h trace-dtrace.h-timestamp
 	$(MAKE) -C tests clean
-	for d in $(ALL_SUBDIRS) libhw32 libhw64 libuser libdis libdis-user; do \
+	for d in $(ALL_SUBDIRS) $(QEMULIBS); do \
 	if test -d $$d; then $(MAKE) -C $$d $@ || exit 1; fi; \
 	rm -f $$d/qemu-options.def; \
         done
@@ -195,7 +197,7 @@ distclean: clean
 	rm -f roms/seabios/config.mak roms/vgabios/config.mak
 	rm -f qemu-doc.info qemu-doc.aux qemu-doc.cp qemu-doc.dvi qemu-doc.fn qemu-doc.info qemu-doc.ky qemu-doc.log qemu-doc.pdf qemu-doc.pg qemu-doc.toc qemu-doc.tp qemu-doc.vr
 	rm -f qemu-tech.info qemu-tech.aux qemu-tech.cp qemu-tech.dvi qemu-tech.fn qemu-tech.info qemu-tech.ky qemu-tech.log qemu-tech.pdf qemu-tech.pg qemu-tech.toc qemu-tech.tp qemu-tech.vr
-	for d in $(TARGET_DIRS) libhw32 libhw64 libuser libdis libdis-user; do \
+	for d in $(TARGET_DIRS) $(QEMULIBS); do \
 	rm -rf $$d || exit 1 ; \
         done
 
diff --git a/Makefile.objs b/Makefile.objs
index 274db5e..6a0030b 100644
--- a/Makefile.objs
+++ b/Makefile.objs
@@ -315,6 +315,11 @@ user-obj-y += qemu-timer-common.o
 endif
 endif
 
+######################################################################
+# smartcard
+
+libcacard-y = cac.o event.o passthru.o vcard.o vreader.o vcard_emul_nss.o vcard_emul_type.o card_7816.o
+
 vl.o: QEMU_CFLAGS+=$(GPROF_CFLAGS)
 
 vl.o: QEMU_CFLAGS+=$(SDL_CFLAGS)
diff --git a/Makefile.target b/Makefile.target
index a5e217e..419a530 100644
--- a/Makefile.target
+++ b/Makefile.target
@@ -335,6 +335,8 @@ obj-y += $(addprefix $(HWDIR)/, $(hw-obj-y))
 
 endif # CONFIG_SOFTMMU
 
+obj-y += $(addprefix ../libcacard/, $(libcacard-$(CONFIG_SMARTCARD)))
+
 obj-y += $(addprefix ../, $(trace-obj-y))
 obj-$(CONFIG_GDBSTUB_XML) += gdbstub-xml.o
 
diff --git a/configure b/configure
index 839980c..4567057 100755
--- a/configure
+++ b/configure
@@ -2209,6 +2209,19 @@ EOF
   fi
 fi
 
+# check for libcacard for smartcard support
+smartcard_cflags="-I\$(SRC_PATH)/libcacard"
+libcacard_libs=$($pkgconfig --libs nss 2>/dev/null)
+libcacard_cflags=$($pkgconfig --cflags nss)
+# TODO - what's the minimal nss version we support?
+if $pkgconfig --atleast-version=3.12.8 nss; then
+    smartcard="yes"
+    QEMU_CFLAGS="$QEMU_CFLAGS $smartcard_cflags $libcacard_cflags"
+    LIBS="$libcacard_libs $LIBS"
+else
+    smartcard="no"
+fi
+
 ##########################################
 
 ##########################################
@@ -3046,6 +3059,11 @@ fi
 if test "$target_darwin_user" = "yes" ; then
   echo "CONFIG_DARWIN_USER=y" >> $config_target_mak
 fi
+if test "$smartcard" = "yes" ; then
+  echo "subdir-$target: subdir-libcacard" >> $config_host_mak
+  echo "libcacard_libs=$libcacard_libs" >> $config_host_mak
+  echo "libcacard_cflags=$libcacard_cflags" >> $config_host_mak
+fi
 list=""
 if test ! -z "$gdb_xml_files" ; then
   for x in $gdb_xml_files; do
@@ -3266,6 +3284,13 @@ for hwlib in 32 64; do
   echo "QEMU_CFLAGS+=-DTARGET_PHYS_ADDR_BITS=$hwlib" > $d/config.mak
 done
 
+if [ $source_path != $workdir ]; then
+    # out of tree build
+    mkdir -p libcacard
+    rm -f libcacard/Makefile
+    ln -s $source_path/libcacard/Makefile libcacard/Makefile
+fi
+
 d=libuser
 mkdir -p $d
 rm -f $d/Makefile
diff --git a/docs/libcacard.txt b/docs/libcacard.txt
new file mode 100644
index 0000000..5dee6fa
--- /dev/null
+++ b/docs/libcacard.txt
@@ -0,0 +1,483 @@
+This file documents the CAC (Common Access Card) library in the libcacard
+subdirectory.
+
+Virtual Smart Card Emulator
+
+This emulator is designed to provide emulation of actual smart cards to a
+virtual card reader running in a guest virtual machine. The emulated smart
+cards can be representations of real smart cards, where the necessary functions
+such as signing, card removal/insertion, etc. are mapped to real, physical
+cards which are shared with the client machine the emulator is running on, or
+the cards could be pure software constructs.
+
+The emulator is structured to allow multiple replacable or additional pieces,
+so it can be easily modified for future requirements. The primary envisioned
+modifications are:
+
+1) The socket connection to the virtual card reader (presumably a CCID reader,
+but other ISO-7816 compatible readers could be used). The code that handles
+this is in vscclient.c.
+
+2) The virtual card low level emulation. This is currently supplied by using
+NSS. This emulation could be replaced by implementations based on other
+security libraries, including but not limitted to openssl+pkcs#11 library,
+raw pkcs#11, Microsoft CAPI, direct opensc calls, etc. The code that handles
+this is in vcard_emul_nss.c.
+
+3) Emulation for new types of cards. The current implementation emulates the
+original DoD CAC standard with separate pki containers. This emulator lives in
+cac.c. More than one card type emulator could be included. Other cards could
+be emulated as well, including PIV, newer versions of CAC, PKCS #15, etc.
+
+--------------------
+Replacing the Socket Based Virtual Reader Interface.
+
+The current implementation contains a replacable module vscclient.c. The
+current vscclient.c implements a sockets interface to the virtual ccid reader
+on the guest. CCID commands that are pertinent to emulation are passed
+across the socket, and their responses are passed back along that same socket.
+The protocol that vscclient uses is defined in vscard_common.h and connects
+to a qemu ccid usb device. Since this socket runs as a client, vscclient.c
+implements a program with a main entry. It also handles argument parsing for
+the emulator.
+
+An application that wants to use the virtual reader can replace vscclient.c
+with it's own implementation that connects to it's own CCID reader.  The calls
+that the CCID reader can call are:
+
+      VReaderList * vreader_get_reader_list();
+
+  This function returns a list of virtual readers.  These readers may map to
+  physical devices, or simulated devices depending on vcard the back end. Each
+  reader in the list should represent a reader to the virtual machine. Virtual
+  USB address mapping is left to the CCID reader front end. This call can be
+  made any time to get an updated list. The returned list is a copy of the
+  internal list that can be referenced by the caller without locking. This copy
+  must be freed by the caller with vreader_list_delete when it is no longer
+  needed.
+
+      VReaderListEntry *vreader_list_get_first(VReaderList *);
+
+  This function gets the first entry on the reader list. Along with
+  vreader_list_get_next(), vreader_list_get_first() can be used to walk the
+  reader list returned from vreader_get_reader_list(). VReaderListEntries are
+  part of the list themselves and do not need to be freed separately from the
+  list. If there are no entries on the list, it will return NULL.
+
+      VReaderListEntry *vreader_list_get_next(VReaderListEntry *);
+
+  This function gets the next entry in the list. If there are no more entries
+  it will return NULL.
+
+      VReader * vreader_list_get_reader(VReaderListEntry *)
+
+  This function returns the reader stored in the reader List entry. Caller gets
+  a new reference to a reader. The caller must free it's reference when it is
+  finished with vreader_free().
+
+      void vreader_free(VReader *reader);
+
+   This function frees a reference to a reader. Reader's are reference counted
+   and are automatically deleted when the last reference is freed.
+
+      void vreader_list_delete(VReaderList *list);
+
+   This function frees the list, all the elements on the list, and all the
+   reader references held by the list.
+
+      VReaderStatus vreader_power_on(VReader *reader, char *atr, int *len);
+
+  This functions simulates a card power on. Virtual cards do not care about
+  the actual voltage and other physical parameters, but it does care that the
+  card is actually on or off. Cycling the card causes the card to reset. If
+  the caller provides enough space, vreader_power_on will return the ATR of
+  the virtual card. The amount of space provided in atr should be indicated
+  in *len. The function modifies *len to be the actual length of of the
+  returned ATR.
+
+      VReaderStatus vreader_power_off(VReader *reader);
+
+  This function simulates a power off of a virtual card.
+
+      VReaderStatus vreader_xfer_bytes(VReader *reader, unsigne char *send_buf,
+                                       int send_buf_len,
+                                       unsigned char *receive_buf,
+                                       int receive_buf_len);
+
+  This functions send a raw apdu to a card and returns the card's response.
+  The CCID front end should return the response back. Most of the emulation
+  is driven from these APDUs.
+
+      VReaderStatus vreader_card_is_present(VReader *reader);
+
+  This function returns whether or not the reader has a card inserted. The
+  vreader_power_on, vreader_power_off, and vreader_xfer_bytes will return
+  VREADER_NO_CARD.
+
+       const char *vreader_get_name(VReader *reader);
+
+  This function returns the name of the reader. The name comes from the card
+  emulator level and is usually related to the name of the physical reader.
+
+       VReaderID vreader_get_id(VReader *reader);
+
+  This function returns the id of a reader. All readers start out with an id
+  of -1. The application can set the id with vreader_set_id.
+
+       VReaderStatus vreader_get_id(VReader *reader, VReaderID id);
+
+  This function sets the reader id. The application is responsible for making
+  sure that the id is unique for all readers it is actively using.
+
+       VReader *vreader_find_reader_by_id(VReaderID id);
+
+  This function returns the reader which matches the id. If two readers match,
+  only one is returned. The function returns NULL if the id is -1.
+
+       Event *vevent_wait_next_vevent();
+
+  This function blocks waiting for reader and card insertion events. There
+  will be one event for each card insertion, each card removal, each reader
+  insertion and each reader removal. At start up, events are created for all
+  the initial readers found, as well as all the cards that are inserted.
+
+       Event *vevent_get_next_vevent();
+
+  This function returns a pending event if it exists, otherwise it returns
+  NULL. It does not block.
+
+----------------
+Card Type Emulator: Adding a New Virtual Card Type
+
+The ISO 7816 card spec describes 2 types of cards:
+ 1) File system cards, where the smartcard is managed by reading and writing
+data to files in a file system. There is currently only boiler plate
+implemented for file system cards.
+ 2) VM cards, where the card has loadable applets which perform the card
+functions. The current implementation supports VM cards.
+
+In the case of VM cards, the difference between various types of cards is
+really what applets have been installed in that card. This structure is
+mirrored in card type emulators. The 7816 emulator already handles the basic
+ISO 7186 commands. Card type emulators simply need to add the virtual applets
+which emulate the real card applets. Card type emulators have exactly one
+public entry point:
+
+       VCARDStatus xxx_card_init(VCard *card, const char *flags,
+                               const unsigned char *cert[],
+                               int cert_len[],
+                               VCardKey *key[],
+                               int cert_count);
+
+  The parameters for this are:
+  card       - the virtual card structure which will prepresent this card.
+  flags      - option flags that may be specific to this card type.
+  cert       - array of binary certificates.
+  cert_len   - array of lengths of each of the certificates specified in cert.
+  key        - array of opaque key structures representing the private keys on
+               the card.
+  cert_count - number of entries in cert, cert_len, and key arrays.
+
+  Any cert, cert_len, or key with the same index are matching sets. That is
+  cert[0] is cert_len[0] long and has the corresponsing private key of key[0].
+
+The card type emulator is expected to own the VCardKeys, but it should copy
+any raw cert data it wants to save. It can create new applets and add them to
+the card using the following functions:
+
+       VCardApplet *vcard_new_applet(VCardProcessAPDU apdu_func,
+                                     VCardResetApplet reset_func,
+                                     const unsigned char *aid,
+                                     int aid_len);
+
+  This function creates a new applet. Applet structures store the following
+  information:
+     1) the AID of the applet (set by aid and aid_len).
+     2) a function to handle APDUs for this applet. (set by apdu_func, more on
+        this below).
+     3) a function to reset the applet state when the applet is selected.
+        (set by reset_func, more on this below).
+     3) applet private data, a data pointer used by the card type emulator to
+        store any data or state it needs to complete requests. (set by a
+        separate call).
+     4) applet private data free, a function used to free the applet private
+        data when the applet itself is destroyed.
+  The created applet can be added to the card with vcard_add_applet below.
+
+        void vcard_set_applet_private(VCardApplet *applet,
+                                      VCardAppletPrivate *private,
+                                      VCardAppletPrivateFree private_free);
+  This function sets the private data and the corresponding free function.
+  VCardAppletPrivate is an opaque data structure to the rest of the emulator.
+  The card type emulator can define it any way it wants by defining
+  struct VCardAppletPrivateStruct {};. If there is already a private data
+  structure on the applet, the old one is freed before the new one is set up.
+  passing two NULL clear any existing private data.
+
+         VCardStatus vcard_add_applet(VCard *card, VCardApplet *applet);
+
+  Add an applet onto the list of applets attached to the card. Once an applet
+  has been added, it can be selected by it's aid, and then commands will be
+  routed to it VCardProcessAPDU function. This function adopts the applet the
+  passed int applet. Note: 2 applets with the same AID should not be added to
+  the same card. It's permissible to add more than one applet. Multiple applets
+  may have the same VCardPRocessAPDU entry point.
+
+The certs and keys should be attached to private data associated with one or
+more appropriate applets for that card. Control will come to the card type
+emulators once one of its applets are selected through the VCardProcessAPDU
+function it specified when it created the applet.
+
+The signature of VCardResetApplet is:
+        VCardStatus (*VCardResetApplet) (VCard *card, int channel);
+  This function will reset the any internal applet state that needs to be
+  cleared after a select applet call. It should return VCARD_DONE;
+
+The signature of VCardProcessAPDU is:
+        VCardStatus (*VCardProcessAPDU)(VCard *card, VCardAPDU *apdu,
+                                         VCardResponse **response);
+  This function examines the APDU and determines whether it should process
+  the apdu directly, reject the apdu as invalid, or pass the apdu on to
+  the basic 7816 emulator for processing.
+      If the 7816 emulator should process the apdu, then the VCardProcessAPDU
+  should return VCARD_NEXT.
+      If there is an error, then VCardProcessAPDU should return an error
+  response using vcard_make_response and the appropriate 7816 error code
+  (see card_7816t.h) or vcard_make_response with a card type specific error
+  code. It should then return VCARD_DONE.
+      If the apdu can be processed correctly, VCardProcessAPDU should do so,
+  set the response value appropriately for that APDU, and return VCARD_DONE.
+  VCardProcessAPDU should always set the response if it returns VCARD_DONE.
+  It should always either return VCARD_DONE or VCARD_NEXT.
+
+Parsing the APDU --
+
+Prior to processing calling the card type emulator's VCardProcessAPDU function, the emulator has already decoded the APDU header and set several fields:
+
+   apdu->a_data - The raw apdu data bytes.
+   apdu->a_len  - The len of the raw apdu data.
+   apdu->a_body - The start of any post header parameter data.
+   apdu->a_Lc   - The parameter length value.
+   apdu->a_Le   - The expected length of any returned data.
+   apdu->a_cla  - The raw apdu class.
+   apdu->a_channel - The channel (decoded from the class).
+   apdu->a_secure_messaging_type - The decoded secure messagin type
+                                   (from class).
+   apdu->a_type - The decode class type.
+   apdu->a_gen_type - the generic class type (7816, PROPRIETARY, RFU, PTS).
+   apdu->a_ins  - The instruction byte.
+   apdu->a_p1   - Parameter 1.
+   apdu->a_p2   - Parameter 2.
+
+Creating a Response --
+
+The expected result of any APDU call is a response. The card type emulator must
+set *response with an appropriate VCardResponse value if it returns VCARD_DONE.
+Reponses could be as simple as returning a 2 byte status word response, to as
+complex as returning a block of data along with a 2 byte response. Which is
+returned will depend on the semantics of the APDU. The following functions will
+create card responses.
+
+        VCardResponse *vcard_make_response(VCard7816Status status);
+
+    This is the most basic function to get a response. This function will
+    return a response the consists soley one 2 byte status code. If that status
+    code is defined in card_7816t.h, then this function is guarrenteed to
+    return a response with that status. If a cart type specific status code
+    is passed and vcard_make_response fails to allocate the appropriate memory
+    for that response, then vcard_make_response will return a VCardResponse
+    of VCARD7816_STATUS_EXC_ERROR_MEMORY. In any case, this function is
+    guarrenteed to return a valid VCardResponse.
+
+        VCardResponse *vcard_response_new(unsigned char *buf, int len,
+                                          VCard7816Status status);
+
+    This function is similar to vcard_make_response except it includes some
+    returned data with the response. It could also fail to allocate enough
+    memory, in which case it will return NULL.
+
+        VCardResponse *vcard_response_new_status_bytes(unsigned char sw1,
+                                                       unsigned char sw2);
+
+    Sometimes in 7816 the response bytes are treated as two separate bytes with
+    split meanings. This function allows you to create a response based on
+    two separate bytes. This function could fail, in which case it will return
+    NULL.
+
+       VCardResponse *vcard_response_new_bytes(unsigned char *buf, int len,
+                                               unsigned char sw1,
+                                               unsigned char sw2);
+
+    This function is the same as vcard_response_new except you may specify
+    the status as two separate bytes like vcard_response_new_status_bytes.
+
+
+Implementing functionality ---
+
+The following helper functions access information about the current card
+and applet.
+
+        VCARDAppletPrivate *vcard_get_current_applet_private(VCard *card,
+                                                             int channel);
+
+    This function returns any private data set by the card type emulator on
+    the currently selected applet. The card type emulator keeps track of the
+    current applet state in this data structure. Any certs and keys associated
+    with a particular applet is also stored here.
+
+        int vcard_emul_get_login_count(VCard *card);
+
+    This function returns the the number of remaing login attempts for this
+    card. If the card emulator does not know, or the card does not have a
+    way of giving this information, this function returns -1.
+
+
+         VCard7816Status vcard_emul_login(VCard *card, unsigned char *pin,
+                                          int pin_len);
+
+    This function logins into the card and return the standard 7816 status
+    word depending on the success or failure of the call.
+
+         void vcard_emul_delete_key(VCardKey *key);
+
+     This function frees the VCardKey passed in to xxxx_card_init. The card
+     type emulator is responsible for freeing this key when it no longer needs
+     it.
+
+         VCard7816Status vcard_emul_rsa_op(VCard *card, VCardKey *key,
+                                           unsigned char *buffer,
+                                           int buffer_size);
+
+     This function does a raw rsa op on the buffer with the given key.
+
+The sample card type emulator is found in cac.c. It implements the cac specific
+applets.  Only those applets needed by the coolkey pkcs#11 driver on the guest
+have been implemented. To support the full range CAC middleware, a complete CAC
+card according to the CAC specs should be implemented here.
+
+------------------------------
+Virtual Card Emulator
+
+This code accesses both real smart cards and simulated smart cards through
+services provided on the client. The current implementation uses NSS, which
+already knows how to talk to various PKCS #11 modules on the client, and is
+portable to most operating systems. A particular emulator can have only one
+virtual card implementation at a time.
+
+The virtual card emulator consists of a series of virtual card services. In
+addition to the services describe above (services starting with
+vcard_emul_xxxx), the virtual card emulator also provides the following
+functions:
+
+    VCardEmulError vcard_emul_init(cont VCardEmulOptions *options);
+
+  The options structure is built by another function in the virtual card
+  interface where a string of virtual card emulator specific strings are
+  mapped to the options. The actual structure is defined by the virutal card
+  emulator and is used to determine the configuration of soft cards, or to
+  determine which physical cards to present to the guest.
+
+  The vcard_emul_init function will build up sets of readers, create any
+  threads that are needed to watch for changes in the reader state. If readers
+  have cards present in them, they are also initialized.
+
+  Readers are created with the function.
+
+          VReader *vreader_new(VReaderEmul *reader_emul,
+                               VReaderEmulFree reader_emul_free);
+
+      The freeFunc is used to free the VReaderEmul * when the reader is
+      destroyed.  The VReaderEmul structure is an opaque structure to the
+      rest of the code, but defined by the virtual card emulator, which can
+      use it to store any reader specific state.
+
+  Once the reader has been created, it can be added to the front end with the
+  call:
+
+           VReaderStatus vreader_add_reader(VReader *reader);
+
+      This function will automatically generate the appropriate new reader
+      events and add the reader to the list.
+
+  To create a new card, the virtual card emulator will call a similiar
+  function.
+
+           VCard *vcard_new(VCardEmul *card_emul,
+                            VCardEmulFree card_emul_free);
+
+      Like vreader_new, this function takes a virtual card emulator specific
+      structure which it uses to keep track of the card state.
+
+  Once the card is created, it is attached to a card type emulator with the
+  following function:
+
+            VCardStatus vcard_init(VCard *vcard, VCardEmulType type,
+                                   const char *flags,
+                                   unsigned char *const *certs,
+                                   int *cert_len,
+                                   VCardKey *key[],
+                                   int cert_count);
+
+      The vcard is the value returned from vcard_new. The type is the
+      card type emulator that this card should presented to the guest as.
+      The flags are card type emulator specific options. The certs,
+      cert_len, and keys are all arrays of length cert_count. These are the
+      the same of the parameters xxxx_card_init() accepts.
+
+   Finally the card is associated with it's reader by the call:
+
+            VReaderStatus vreader_insert_card(VReader *vreader, VCard *vcard);
+
+      This function, like vreader_add_reader, will take care of any event
+      notification for the card insert.
+
+
+    VCardEmulError vcard_emul_force_card_remove(VReader *vreader);
+
+  Force a card that is present to appear to be removed to the guest, even if
+  that card is a physical card and is present.
+
+
+    VCardEmulError vcard_emul_force_card_insert(VReader *reader);
+
+  Force a card that has been removed by vcard_emul_force_card_remove to be
+  reinserted from the point of view of the guest. This will only work if the
+  card is physically present (which is always true fro a soft card).
+
+     void vcard_emul_get_atr(Vcard *card, unsigned char *atr, int *atr_len);
+
+  Return the virtual ATR for the card. By convention this should be the value
+  VCARD_ATR_PREFIX(size) followed by several ascii bytes related to this
+  particular emulator. For instance the NSS emulator returns
+  {VCARD_ATR_PREFIX(3), 'N', 'S', 'S' }. Do ot return more data then *atr_len;
+
+     void vcard_emul_reset(VCard *card, VCardPower power)
+
+   Set the state of 'card' to the current power level and reset its internal
+   state (logout, etc).
+
+-------------------------------------------------------
+List of files and their function:
+README - This file
+card_7816.c - emulate basic 7816 functionality. Parse APDUs.
+card_7816.h - apdu and response services definitions.
+card_7816t.h - 7816 specific structures, types and definitions.
+event.c - event handling code.
+event.h - event handling services definitions.
+eventt.h - event handling structures and types
+vcard.c - handle common virtual card services like creation, destruction, and
+          applet management.
+vcard.h - common virtual card services function definitions.
+vcardt.h - comon virtual card types
+vreader.c - common virtual reader services.
+vreader.h - common virtual reader services definitions.
+vreadert.h - comon virtual reader types.
+vcard_emul_type.c - manage the card type emulators.
+vcard_emul_type.h - definitions for card type emulators.
+cac.c - card type emulator for CAC cards
+vcard_emul.h - virtual card emulator service definitions.
+vcard_emul_nss.c - virtual card emulator implementation for nss.
+vscclient.c - socket connection to guest qemu usb driver.
+vscard_common.h - common header with the guest qemu usb driver.
+mutex.h - header file for machine independent mutexes.
+link_test.c - static test to make sure all the symbols are properly defined.
diff --git a/libcacard/Makefile b/libcacard/Makefile
new file mode 100644
index 0000000..b146779
--- /dev/null
+++ b/libcacard/Makefile
@@ -0,0 +1,14 @@
+include ../config-host.mak
+include $(SRC_PATH)/Makefile.objs
+include $(SRC_PATH)/rules.mak
+
+$(call set-vpath, $(SRC_PATH):$(SRC_PATH)/libcacard)
+
+vscclient: $(libcacard-y) vscclient.o
+	gcc $(libcacard_libs) -o $@ $^
+
+all: vscclient
+
+clean:
+	rm -f *.o */*.o *.d */*.d *.a */*.a *~ */*~ vscclient
+
diff --git a/libcacard/cac.c b/libcacard/cac.c
new file mode 100644
index 0000000..e51caec
--- /dev/null
+++ b/libcacard/cac.c
@@ -0,0 +1,411 @@
+/*
+ * implement the applets for the CAC card.
+ */
+#include "cac.h"
+#include "vcard.h"
+#include "vcard_emul.h"
+#include "card_7816.h"
+#include <stdlib.h>
+#include <string.h>
+
+#define CAC_GET_PROPERTIES  0x56
+#define CAC_GET_ACR         0x4c
+#define CAC_READ_BUFFER     0x52
+#define CAC_UPDATE_BUFFER   0x58
+#define CAC_SIGN_DECRYPT    0x42
+#define CAC_GET_CERTIFICATE 0x36
+
+/* private data for PKI applets */
+typedef struct CACPKIAppletDataStruct {
+    unsigned char *cert;
+    int cert_len;
+    unsigned char *cert_buffer;
+    int cert_buffer_len;
+    unsigned char *sign_buffer;
+    int sign_buffer_len;
+    VCardKey *key;
+} CACPKIAppletData;
+
+/*
+ * CAC applet private data
+ */
+struct VCardAppletPrivateStruct {
+    union {
+        CACPKIAppletData pki_data;
+        void *reserved;
+    } u;
+};
+
+/*
+ * handle all the APDU's that are common to all CAC applets
+ */
+static VCardStatus
+cac_common_process_apdu(VCard *card, VCardAPDU *apdu, VCardResponse **response)
+{
+    int ef;
+
+    switch (apdu->a_ins) {
+    case VCARD7816_INS_SELECT_FILE:
+        if (apdu->a_p1 != 0x02) {
+            /* let the 7816 code handle applet switches */
+            return VCARD_NEXT;
+        }
+        /* handle file id setting */
+        if (apdu->a_Lc != 2) {
+            *response = vcard_make_response(
+                VCARD7816_STATUS_ERROR_DATA_INVALID);
+            return VCARD_DONE;
+        }
+        /* CAC 1.0 only supports ef = 0 */
+        ef = apdu->a_body[0] | (apdu->a_body[1] << 8);
+        if (ef != 0 ) {
+            *response = vcard_make_response(
+                VCARD7816_STATUS_ERROR_FILE_NOT_FOUND);
+            return VCARD_DONE;
+        }
+        *response = vcard_make_response(VCARD7816_STATUS_SUCCESS);
+        return VCARD_DONE;
+    case VCARD7816_INS_GET_RESPONSE:
+    case VCARD7816_INS_VERIFY:
+        /* let the 7816 code handle these */
+        return VCARD_NEXT;
+    case CAC_GET_PROPERTIES:
+    case CAC_GET_ACR:
+        /* skip these for now, this will probably be needed */
+        *response = vcard_make_response(VCARD7816_STATUS_ERROR_P1_P2_INCORRECT);
+        return VCARD_DONE;
+    }
+    *response = vcard_make_response(
+        VCARD7816_STATUS_ERROR_COMMAND_NOT_SUPPORTED);
+    return VCARD_DONE;
+}
+
+/*
+ *  resest the inter call state between applet selects
+ */
+static VCardStatus
+cac_applet_pki_reset(VCard *card, int channel)
+{
+    VCardAppletPrivate *applet_private = NULL;
+    CACPKIAppletData *pki_applet = NULL;
+    applet_private = vcard_get_current_applet_private(card, channel);
+    ASSERT(applet_private);
+    pki_applet = &(applet_private->u.pki_data);
+
+    pki_applet->cert_buffer = NULL;
+    if (pki_applet->sign_buffer) {
+        free(pki_applet->sign_buffer);
+        pki_applet->sign_buffer = NULL;
+    }
+    pki_applet->cert_buffer_len = 0;
+    pki_applet->sign_buffer_len = 0;
+    return VCARD_DONE;
+}
+
+static VCardStatus
+cac_applet_pki_process_apdu(VCard *card, VCardAPDU *apdu,
+                            VCardResponse **response)
+{
+    CACPKIAppletData *pki_applet = NULL;
+    VCardAppletPrivate *applet_private = NULL;
+    int size, next;
+    unsigned char *sign_buffer;
+    vcard_7816_status_t status;
+
+    applet_private = vcard_get_current_applet_private(card, apdu->a_channel);
+    ASSERT(applet_private);
+    pki_applet = &(applet_private->u.pki_data);
+
+    switch (apdu->a_ins) {
+    case CAC_UPDATE_BUFFER:
+        *response = vcard_make_response(
+            VCARD7816_STATUS_ERROR_CONDITION_NOT_SATISFIED);
+        return VCARD_DONE;
+    case CAC_GET_CERTIFICATE:
+        if ((apdu->a_p2 != 0) || (apdu->a_p1 != 0)) {
+           *response = vcard_make_response(
+                            VCARD7816_STATUS_ERROR_P1_P2_INCORRECT);
+           break;
+        }
+        ASSERT(pki_applet->cert != NULL);
+        size = apdu->a_Le;
+        if (pki_applet->cert_buffer == NULL) {
+            pki_applet->cert_buffer=pki_applet->cert;
+            pki_applet->cert_buffer_len=pki_applet->cert_len;
+        }
+        size = MIN(size, pki_applet->cert_buffer_len);
+        next = MIN(255, pki_applet->cert_buffer_len - size);
+        *response = vcard_response_new_bytes(
+                        card, pki_applet->cert_buffer, size,
+                        apdu->a_Le, next ?
+                        VCARD7816_SW1_WARNING_CHANGE :
+                        VCARD7816_SW1_SUCCESS,
+                        next);
+        pki_applet->cert_buffer += size;
+        pki_applet->cert_buffer_len -= size;
+        if ((*response == NULL) || (next == 0)) {
+            pki_applet->cert_buffer=NULL;
+        }
+        if (*response == NULL) {
+            *response = vcard_make_response(
+                            VCARD7816_STATUS_EXC_ERROR_MEMORY_FAILURE);
+        }
+        return VCARD_DONE;
+    case CAC_SIGN_DECRYPT:
+        if (apdu->a_p2 != 0) {
+           *response = vcard_make_response(
+                            VCARD7816_STATUS_ERROR_P1_P2_INCORRECT);
+           break;
+        }
+        size = apdu->a_Lc;
+
+        sign_buffer = realloc(pki_applet->sign_buffer,
+                      pki_applet->sign_buffer_len+size);
+        if (sign_buffer == NULL) {
+            free(pki_applet->sign_buffer);
+            pki_applet->sign_buffer = NULL;
+            pki_applet->sign_buffer_len = 0;
+            *response = vcard_make_response(
+                            VCARD7816_STATUS_EXC_ERROR_MEMORY_FAILURE);
+            return VCARD_DONE;
+        }
+        memcpy(sign_buffer+pki_applet->sign_buffer_len, apdu->a_body, size);
+        size += pki_applet->sign_buffer_len;
+        switch (apdu->a_p1) {
+        case  0x80:
+            /* p1 == 0x80 means we haven't yet sent the whole buffer, wait for
+             * the rest */
+            pki_applet->sign_buffer = sign_buffer;
+            pki_applet->sign_buffer_len = size;
+            *response = vcard_make_response(VCARD7816_STATUS_SUCCESS);
+            return VCARD_DONE;
+        case 0x00:
+            /* we now have the whole buffer, do the operation, result will be
+             * in the sign_buffer */
+            status = vcard_emul_rsa_op(card, pki_applet->key,
+                                       sign_buffer, size);
+            if (status != VCARD7816_STATUS_SUCCESS) {
+                *response = vcard_make_response(status);
+                break;
+            }
+            *response = vcard_response_new(card, sign_buffer, size, apdu->a_Le,
+                                                     VCARD7816_STATUS_SUCCESS);
+            if (*response == NULL) {
+                *response = vcard_make_response(
+                                VCARD7816_STATUS_EXC_ERROR_MEMORY_FAILURE);
+            }
+            break;
+        default:
+           *response = vcard_make_response(
+                                VCARD7816_STATUS_ERROR_P1_P2_INCORRECT);
+            break;
+        }
+        free(sign_buffer);
+        pki_applet->sign_buffer = NULL;
+        pki_applet->sign_buffer_len = 0;
+        return VCARD_DONE;
+    case CAC_READ_BUFFER:
+        /* new CAC call, go ahead and use the old version for now */
+        /* TODO: implement */
+        *response = vcard_make_response(
+                                VCARD7816_STATUS_ERROR_COMMAND_NOT_SUPPORTED);
+        return VCARD_DONE;
+    }
+    return cac_common_process_apdu(card, apdu, response);
+}
+
+
+static VCardStatus
+cac_applet_id_process_apdu(VCard *card, VCardAPDU *apdu,
+                           VCardResponse **response)
+{
+    switch (apdu->a_ins) {
+    case CAC_UPDATE_BUFFER:
+        *response = vcard_make_response(
+                        VCARD7816_STATUS_ERROR_CONDITION_NOT_SATISFIED);
+        return VCARD_DONE;
+    case CAC_READ_BUFFER:
+        /* new CAC call, go ahead and use the old version for now */
+        /* TODO: implement */
+        *response = vcard_make_response(
+                        VCARD7816_STATUS_ERROR_COMMAND_NOT_SUPPORTED);
+        return VCARD_DONE;
+    }
+    return cac_common_process_apdu(card, apdu, response);
+}
+
+
+/*
+ * TODO: if we ever want to support general CAC middleware, we will need to
+ * implement the various containers.
+ */
+static VCardStatus
+cac_applet_container_process_apdu(VCard *card, VCardAPDU *apdu,
+                                  VCardResponse **response)
+{
+    switch (apdu->a_ins) {
+    case CAC_READ_BUFFER:
+    case CAC_UPDATE_BUFFER:
+        *response = vcard_make_response(
+                        VCARD7816_STATUS_ERROR_COMMAND_NOT_SUPPORTED);
+        return VCARD_DONE;
+    default:
+        break;
+    }
+    return cac_common_process_apdu(card, apdu, response);
+}
+
+/*
+ * utilities for creating and destroying the private applet data
+ */
+static void
+cac_delete_pki_applet_private(VCardAppletPrivate *applet_private)
+{
+    CACPKIAppletData *pki_applet_data = NULL;
+    if (pki_applet_data == NULL) {
+        return;
+    }
+    pki_applet_data = &(applet_private->u.pki_data);
+    if (pki_applet_data->cert != NULL) {
+        free(pki_applet_data->cert);
+    }
+    if (pki_applet_data->sign_buffer != NULL) {
+        free(pki_applet_data->sign_buffer);
+    }
+    if (pki_applet_data->key != NULL) {
+        vcard_emul_delete_key(pki_applet_data->key);
+    }
+    free(applet_private);
+}
+
+static VCardAppletPrivate *
+cac_new_pki_applet_private(const unsigned char *cert,
+                           int cert_len, VCardKey *key)
+{
+    CACPKIAppletData *pki_applet_data = NULL;
+    VCardAppletPrivate *applet_private = NULL;
+    applet_private = (VCardAppletPrivate *)malloc(sizeof(VCardAppletPrivate));
+
+    if (applet_private == NULL) {
+        goto fail;
+    }
+    pki_applet_data= &(applet_private->u.pki_data);
+    pki_applet_data->cert_buffer = NULL;
+    pki_applet_data->cert_buffer_len = 0;
+    pki_applet_data->sign_buffer = NULL;
+    pki_applet_data->sign_buffer_len = 0;
+    pki_applet_data->key = NULL;
+    pki_applet_data->cert = (unsigned char *)malloc(cert_len+1);
+    if (pki_applet_data->cert == NULL) {
+        goto fail;
+    }
+    /*
+     * if we want to support compression, then we simply change the 0 to a 1
+     * and compress the cert data with libz
+     */
+    pki_applet_data->cert[0] = 0; /* not compressed */
+    memcpy(&pki_applet_data->cert[1], cert, cert_len);
+    pki_applet_data->cert_len = cert_len+1;
+
+    pki_applet_data->key = key;
+    return applet_private;
+
+fail:
+    if (applet_private) {
+        cac_delete_pki_applet_private(applet_private);
+    }
+    return NULL;
+}
+
+
+/*
+ * create a new cac applet which links to a given cert
+ */
+static VCardApplet *
+cac_new_pki_applet(int i, const unsigned char *cert,
+                   int cert_len, VCardKey *key)
+{
+    VCardAppletPrivate *applet_private = NULL;
+    VCardApplet *applet = NULL;
+    unsigned char pki_aid[] = { 0xa0, 0x00, 0x00, 0x00, 0x79, 0x01, 0x00 };
+    int pki_aid_len = sizeof (pki_aid);
+
+    pki_aid[pki_aid_len-1] = i;
+
+    applet_private = cac_new_pki_applet_private(cert, cert_len, key);
+    if (applet_private == NULL) {
+        goto failure;
+    }
+    applet = vcard_new_applet(cac_applet_pki_process_apdu, cac_applet_pki_reset,
+                              pki_aid, pki_aid_len);
+    if (applet == NULL) {
+        goto failure;
+    }
+    vcard_set_applet_private(applet, applet_private,
+                             cac_delete_pki_applet_private);
+    applet_private = NULL;
+
+    return applet;
+
+failure:
+    if (applet_private != NULL) {
+        cac_delete_pki_applet_private(applet_private);
+    }
+    return NULL;
+}
+
+
+static unsigned char cac_default_container_aid[] =
+    { 0xa0, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00 };
+static unsigned char cac_id_aid[] =
+    { 0xa0, 0x00, 0x00, 0x00, 0x79, 0x03, 0x00 };
+/*
+ * Initialize the cac card. This is the only public function in this file. All
+ * the rest are connected through function pointers.
+ */
+VCardStatus
+cac_card_init(VReader *reader, VCard *card,
+              const char *params,
+              unsigned char * const *cert,
+              int cert_len[],
+              VCardKey *key[] /* adopt the keys*/,
+              int cert_count)
+{
+    int i;
+    VCardApplet *applet;
+
+    /* CAC Cards are VM Cards */
+    vcard_set_type(card,VCARD_VM);
+
+    /* create one PKI applet for each cert */
+    for (i=0; i < cert_count; i++) {
+        applet = cac_new_pki_applet(i, cert[i], cert_len[i], key[i]);
+        if (applet == NULL) {
+            goto failure;
+        }
+        vcard_add_applet(card, applet);
+    }
+
+    /* create a default blank container applet */
+    applet = vcard_new_applet(cac_applet_container_process_apdu,
+                              NULL, cac_default_container_aid,
+                              sizeof(cac_default_container_aid));
+    if (applet == NULL) {
+        goto failure;
+    }
+    vcard_add_applet(card, applet);
+
+    /* create a default blank container applet */
+    applet = vcard_new_applet(cac_applet_id_process_apdu,
+                              NULL, cac_id_aid,
+                              sizeof(cac_id_aid));
+    if (applet == NULL) {
+        goto failure;
+    }
+    vcard_add_applet(card, applet);
+    return VCARD_DONE;
+
+failure:
+    return VCARD_FAIL;
+}
+
diff --git a/libcacard/cac.h b/libcacard/cac.h
new file mode 100644
index 0000000..bb2a9f0
--- /dev/null
+++ b/libcacard/cac.h
@@ -0,0 +1,20 @@
+/*
+ * defines the entry point for the cac card. Only used by cac.c anc
+ * vcard_emul_type.c
+ */
+#ifndef CAC_H
+#define CAC_H 1
+#include "vcard.h"
+#include "vreader.h"
+/*
+ * Initialize the cac card. This is the only public function in this file. All
+ * the rest are connected through function pointers.
+ */
+VCardStatus cac_card_init(VReader *reader, VCard *card, const char *params,
+              unsigned char * const *cert, int cert_len[],
+              VCardKey *key[] /* adopt the keys*/,
+              int cert_count);
+
+/* not yet implemented */
+VCardStatus cac_is_cac_card(VReader *reader);
+#endif
diff --git a/libcacard/card_7816.c b/libcacard/card_7816.c
new file mode 100644
index 0000000..e2d8466
--- /dev/null
+++ b/libcacard/card_7816.c
@@ -0,0 +1,780 @@
+/*
+ * Implement the 7816 portion of the card spec
+ *
+ */
+
+#include "vcard.h"
+#include "vcard_emul.h"
+#include "card_7816.h"
+#include <stdlib.h>
+#include <string.h>
+
+/*
+ * set the status bytes based on the status word
+ */
+static void
+vcard_response_set_status(VCardResponse *response, vcard_7816_status_t status)
+{
+    unsigned char sw1, sw2;
+    response->b_status = status; /* make sure the status and swX representations
+                                  * are consistent */
+    sw1 = (status >> 8) & 0xff;
+    sw2 = status & 0xff;
+    response->b_sw1 = sw1;
+    response->b_sw2 = sw2;
+    response->b_data[response->b_len] = sw1;
+    response->b_data[response->b_len+1] = sw2;
+}
+
+/*
+ * set the status bytes in a response buffer
+ */
+static void
+vcard_response_set_status_bytes(VCardResponse *response,
+                               unsigned char sw1, unsigned char sw2)
+{
+    response->b_status = sw1 << 8 | sw2;
+    response->b_sw1 = sw1;
+    response->b_sw2 = sw2;
+    response->b_data[response->b_len] = sw1;
+    response->b_data[response->b_len+1] = sw2;
+}
+
+/*
+ * allocate a VCardResponse structure, plus space for the data buffer, and
+ * set up everything but the resonse bytes.
+ */
+VCardResponse *
+vcard_response_new_data(unsigned char *buf, int len)
+{
+    VCardResponse *new_response;
+
+    new_response = (VCardResponse *)malloc(sizeof(VCardResponse));
+    if (!new_response) {
+        return NULL;
+    }
+    new_response->b_data = malloc(len+2);
+    if (!new_response->b_data) {
+        free(new_response);
+        return NULL;
+    }
+    memcpy(new_response->b_data, buf, len);
+    new_response->b_total_len = len+2;
+    new_response->b_len = len;
+    new_response->b_type = VCARD_MALLOC;
+    return new_response;
+}
+
+static VCardResponse *
+vcard_init_buffer_response(VCard *card, unsigned char *buf, int len)
+{
+    VCardResponse *response;
+    VCardBufferResponse *buffer_response;
+
+    buffer_response =vcard_get_buffer_response(card);
+    if (buffer_response) {
+        vcard_set_buffer_response(card, NULL);
+        vcard_buffer_response_delete(buffer_response);
+    }
+    buffer_response = vcard_buffer_response_new(buf, len);
+    if (buffer_response == NULL) {
+        return NULL;
+    }
+    response = vcard_response_new_status_bytes(VCARD7816_SW1_RESPONSE_BYTES,
+                                               len > 255 ? 0 : len);
+    if (response == NULL) {
+        return NULL;
+    }
+    vcard_set_buffer_response(card,buffer_response);
+    return response;
+}
+
+/*
+ * general buffer to hold results from APDU calls
+ */
+VCardResponse *
+vcard_response_new(VCard *card, unsigned char *buf,
+                   int len, int Le, vcard_7816_status_t status)
+{
+    VCardResponse *new_response;
+
+    if (len > Le) {
+       return vcard_init_buffer_response(card, buf, len);
+    }
+    new_response = vcard_response_new_data(buf,len);
+    if (new_response == NULL) {
+        return NULL;
+    }
+    vcard_response_set_status(new_response,status);
+    return new_response;
+}
+
+/*
+ * general buffer to hold results from APDU calls
+ */
+VCardResponse *
+vcard_response_new_bytes(VCard *card, unsigned char *buf, int len, int Le,
+                         unsigned char sw1, unsigned char sw2)
+{
+    VCardResponse *new_response;
+
+    if (len > Le) {
+       return vcard_init_buffer_response(card, buf, len);
+    }
+    new_response = vcard_response_new_data(buf,len);
+    if (new_response == NULL) {
+        return NULL;
+    }
+    vcard_response_set_status_bytes(new_response,sw1,sw2);
+    return new_response;
+}
+
+/*
+ * get a new Reponse buffer that only has a status.
+ */
+static VCardResponse *
+vcard_response_new_status(vcard_7816_status_t status)
+{
+    VCardResponse *new_response;
+
+    new_response = (VCardResponse *)malloc(sizeof(VCardResponse));
+    if (!new_response) {
+        return NULL;
+    }
+    new_response->b_data = &new_response->b_sw1;
+    new_response->b_len = 0;
+    new_response->b_total_len = 2;
+    new_response->b_type = VCARD_MALLOC_STRUCT;
+    vcard_response_set_status(new_response,status);
+    return new_response;
+}
+
+/*
+ * same as above, but specify the status as separate bytes
+ */
+VCardResponse *
+vcard_response_new_status_bytes(unsigned char sw1, unsigned char sw2)
+{
+    VCardResponse *new_response;
+
+    new_response = (VCardResponse *)malloc(sizeof(VCardResponse));
+    if (!new_response) {
+        return NULL;
+    }
+    new_response->b_data = &new_response->b_sw1;
+    new_response->b_len = 0;
+    new_response->b_total_len = 2;
+    new_response->b_type = VCARD_MALLOC_STRUCT;
+    vcard_response_set_status_bytes(new_response, sw1, sw2);
+    return new_response;
+}
+
+
+/*
+ * free the response buffer. The Buffer has a type to handle the buffer
+ * allocated in other ways than through malloc.
+ */
+void
+vcard_response_delete(VCardResponse *response)
+{
+    if (response == NULL) {
+        return;
+    }
+    switch (response->b_type) {
+    case VCARD_MALLOC:
+        /* everything was malloc'ed */
+        if (response->b_data) {
+            free(response->b_data);
+        }
+        free(response);
+        break;
+    case VCARD_MALLOC_DATA:
+        /* only the data buffer was malloc'ed */
+        if (response->b_data) {
+            free(response->b_data);
+        }
+        break;
+     case VCARD_MALLOC_STRUCT:
+        /* only the structure was malloc'ed */
+        free(response);
+        break;
+     case VCARD_STATIC:
+        break;
+     }
+}
+
+/*
+ * decode the class bit and set our generic type field, channel, and
+ * secure messaging values.
+ */
+static vcard_7816_status_t
+vcard_apdu_set_class(VCardAPDU *apdu) {
+    apdu->a_channel = 0;
+    apdu->a_secure_messaging = 0;
+    apdu->a_type = apdu->a_cla & 0xf0;
+    apdu->a_gen_type = VCARD_7816_ISO;
+
+    /* parse the class  tables 8 & 9 of the 7816-4 Part 4 spec */
+    switch (apdu->a_type) {
+        /* we only support the basic types */
+    case 0x00:
+    case 0x80:
+    case 0x90:
+    case 0xa0:
+        apdu->a_channel = apdu->a_cla & 3;
+        apdu->a_secure_messaging = apdu->a_cla & 0xe;
+        break;
+    case 0xb0:
+    case 0xc0:
+        break;
+
+    case 0x10:
+    case 0x20:
+    case 0x30:
+    case 0x40:
+    case 0x50:
+    case 0x60:
+    case 0x70:
+        /* Reserved for future use */
+        apdu->a_gen_type = VCARD_7816_RFU;
+        break;
+    case 0xd0:
+    case 0xe0:
+    case 0xf0:
+    default:
+        apdu->a_gen_type =
+            (apdu->a_cla == 0xff)? VCARD_7816_PTS : VCARD_7816_PROPIETARY;
+        break;
+    }
+    return VCARD7816_STATUS_SUCCESS;
+}
+
+/*
+ * set the Le and Lc fiels according to table 5 of the
+ * 7816-4 part 4 spec
+ */
+static vcard_7816_status_t
+vcard_apdu_set_length(VCardAPDU *apdu)
+{
+    int L, Le;
+
+    /* process according to table 5 of the 7816-4 Part 4 spec.
+     * variable names match the variables in the spec */
+    L = apdu->a_len-4; /* fixed APDU header */
+    apdu->a_Lc = 0;
+    apdu->a_Le = 0;
+    apdu->a_body = NULL;
+    switch (L) {
+    case 0:
+        /* 1 minimal apdu */
+        return VCARD7816_STATUS_SUCCESS;
+    case 1:
+        /* 2S only return values apdu */
+        /*   zero maps to 256 here */
+        apdu->a_Le = apdu->a_header->ah_Le ?
+                         apdu->a_header->ah_Le : 256;
+        return VCARD7816_STATUS_SUCCESS;
+    default:
+        /* if the ah_Le byte is zero and we have more than
+         * 1 byte in the header, then we must be using extended Le and Lc.
+         * process the extended now. */
+        if (apdu->a_header->ah_Le == 0) {
+            if (L < 3) {
+                /* coding error, need at least 3 bytes */
+                return VCARD7816_STATUS_ERROR_WRONG_LENGTH;
+            }
+            /* calculate the first extended value. Could be either Le or Lc */
+            Le = (apdu->a_header->ah_body[0] << 8)
+               || apdu->a_header->ah_body[1];
+            if (L == 3) {
+                /* 2E extended, return data only */
+                /*   zero maps to 65536 */
+                apdu->a_Le = Le ? Le : 65536;
+                return VCARD7816_STATUS_SUCCESS;
+            }
+            if (Le == 0) {
+                /* reserved for future use, probably for next time we need
+                 * to extend the lengths */
+                return VCARD7816_STATUS_ERROR_WRONG_LENGTH;
+            }
+            /* we know that the first extended value is Lc now */
+            apdu->a_Lc = Le;
+            apdu->a_body = &apdu->a_header->ah_body[2];
+            if (L == Le+3) {
+                /* 3E extended, only body parameters */
+                return VCARD7816_STATUS_SUCCESS;
+            }
+            if (L == Le+5) {
+                /* 4E extended, parameters and return data */
+                Le = (apdu->a_data[apdu->a_len-2] << 8)
+                   || apdu->a_data[apdu->a_len-1];
+                apdu->a_Le = Le ? Le : 65536;
+                return VCARD7816_STATUS_SUCCESS;
+            }
+            return VCARD7816_STATUS_ERROR_WRONG_LENGTH;
+        }
+        /* not extended */
+        apdu->a_Lc= apdu->a_header->ah_Le;
+        apdu->a_body = &apdu->a_header->ah_body[0];
+        if (L ==  apdu->a_Lc + 1) {
+            /* 3S only body parameters */
+            return VCARD7816_STATUS_SUCCESS;
+        }
+        if (L ==  apdu->a_Lc + 2) {
+            /* 4S parameters and return data */
+            Le = apdu->a_data[apdu->a_len-1];
+            apdu->a_Le = Le ?  Le : 256;
+            return VCARD7816_STATUS_SUCCESS;
+        }
+        break;
+    }
+    return VCARD7816_STATUS_ERROR_WRONG_LENGTH;
+}
+
+/*
+ * create a new APDU from a raw set of bytes. This will decode all the
+ * above fields. users of VCARDAPDU's can then depend on the already decoded
+ * values.
+ */
+VCardAPDU *
+vcard_apdu_new(unsigned char *raw_apdu, int len, vcard_7816_status_t *status)
+{
+    VCardAPDU *new_apdu;
+
+    *status = VCARD7816_STATUS_EXC_ERROR_MEMORY_FAILURE;
+    if (len < 4) {
+        *status = VCARD7816_STATUS_ERROR_WRONG_LENGTH;
+        return NULL;
+    }
+
+    new_apdu = (VCardAPDU *)malloc(sizeof(VCardAPDU));
+    if (!new_apdu) {
+        return NULL;
+    }
+    new_apdu->a_data = malloc(len);
+    if (!new_apdu->a_data) {
+        free(new_apdu);
+        return NULL;
+    }
+    memcpy(new_apdu->a_data, raw_apdu, len);
+    new_apdu->a_len = len;
+    *status = vcard_apdu_set_class(new_apdu);
+    if (*status != VCARD7816_STATUS_SUCCESS) {
+        free(new_apdu);
+        return NULL;
+    }
+    *status = vcard_apdu_set_length(new_apdu);
+    if (*status != VCARD7816_STATUS_SUCCESS) {
+        free(new_apdu);
+        new_apdu = NULL;
+    }
+    return new_apdu;
+}
+
+void
+vcard_apdu_delete(VCardAPDU *apdu)
+{
+    if (apdu == NULL) {
+        return;
+    }
+    if (apdu->a_data) {
+        free(apdu->a_data);
+    }
+    free(apdu);
+}
+
+
+/*
+ * declare response buffers for all the 7816 defined error codes
+ */
+VCARD_RESPONSE_NEW_STATIC_STATUS(VCARD7816_STATUS_SUCCESS)
+VCARD_RESPONSE_NEW_STATIC_STATUS(VCARD7816_STATUS_WARNING)
+VCARD_RESPONSE_NEW_STATIC_STATUS(VCARD7816_STATUS_WARNING_RET_CORUPT)
+VCARD_RESPONSE_NEW_STATIC_STATUS(VCARD7816_STATUS_WARNING_BUF_END_BEFORE_LE)
+VCARD_RESPONSE_NEW_STATIC_STATUS(VCARD7816_STATUS_WARNING_INVALID_FILE_SELECTED)
+VCARD_RESPONSE_NEW_STATIC_STATUS(VCARD7816_STATUS_WARNING_FCI_FORMAT_INVALID)
+VCARD_RESPONSE_NEW_STATIC_STATUS(VCARD7816_STATUS_WARNING_CHANGE)
+VCARD_RESPONSE_NEW_STATIC_STATUS(VCARD7816_STATUS_WARNING_FILE_FILLED)
+VCARD_RESPONSE_NEW_STATIC_STATUS(VCARD7816_STATUS_EXC_ERROR)
+VCARD_RESPONSE_NEW_STATIC_STATUS(VCARD7816_STATUS_EXC_ERROR_CHANGE)
+VCARD_RESPONSE_NEW_STATIC_STATUS(VCARD7816_STATUS_EXC_ERROR_MEMORY_FAILURE)
+VCARD_RESPONSE_NEW_STATIC_STATUS(VCARD7816_STATUS_ERROR_WRONG_LENGTH)
+VCARD_RESPONSE_NEW_STATIC_STATUS(VCARD7816_STATUS_ERROR_CLA_NOT_SUPPORTED)
+VCARD_RESPONSE_NEW_STATIC_STATUS(VCARD7816_STATUS_ERROR_CHANNEL_NOT_SUPPORTED)
+VCARD_RESPONSE_NEW_STATIC_STATUS(VCARD7816_STATUS_ERROR_SECURE_NOT_SUPPORTED)
+VCARD_RESPONSE_NEW_STATIC_STATUS(VCARD7816_STATUS_ERROR_COMMAND_NOT_SUPPORTED)
+VCARD_RESPONSE_NEW_STATIC_STATUS(
+                    VCARD7816_STATUS_ERROR_COMMAND_INCOMPATIBLE_WITH_FILE)
+VCARD_RESPONSE_NEW_STATIC_STATUS(VCARD7816_STATUS_ERROR_SECURITY_NOT_SATISFIED)
+VCARD_RESPONSE_NEW_STATIC_STATUS(VCARD7816_STATUS_ERROR_AUTHENTICATION_BLOCKED)
+VCARD_RESPONSE_NEW_STATIC_STATUS(VCARD7816_STATUS_ERROR_DATA_INVALID)
+VCARD_RESPONSE_NEW_STATIC_STATUS(VCARD7816_STATUS_ERROR_CONDITION_NOT_SATISFIED)
+VCARD_RESPONSE_NEW_STATIC_STATUS(VCARD7816_STATUS_ERROR_DATA_NO_EF)
+VCARD_RESPONSE_NEW_STATIC_STATUS(VCARD7816_STATUS_ERROR_SM_OBJECT_MISSING)
+VCARD_RESPONSE_NEW_STATIC_STATUS(VCARD7816_STATUS_ERROR_SM_OBJECT_INCORRECT)
+VCARD_RESPONSE_NEW_STATIC_STATUS(VCARD7816_STATUS_ERROR_WRONG_PARAMETERS)
+VCARD_RESPONSE_NEW_STATIC_STATUS(
+                            VCARD7816_STATUS_ERROR_WRONG_PARAMETERS_IN_DATA)
+VCARD_RESPONSE_NEW_STATIC_STATUS(VCARD7816_STATUS_ERROR_FUNCTION_NOT_SUPPORTED)
+VCARD_RESPONSE_NEW_STATIC_STATUS(VCARD7816_STATUS_ERROR_FILE_NOT_FOUND)
+VCARD_RESPONSE_NEW_STATIC_STATUS(VCARD7816_STATUS_ERROR_RECORD_NOT_FOUND)
+VCARD_RESPONSE_NEW_STATIC_STATUS(VCARD7816_STATUS_ERROR_NO_SPACE_FOR_FILE)
+VCARD_RESPONSE_NEW_STATIC_STATUS(VCARD7816_STATUS_ERROR_LC_TLV_INCONSISTENT)
+VCARD_RESPONSE_NEW_STATIC_STATUS(VCARD7816_STATUS_ERROR_P1_P2_INCORRECT)
+VCARD_RESPONSE_NEW_STATIC_STATUS(VCARD7816_STATUS_ERROR_LC_P1_P2_INCONSISTENT)
+VCARD_RESPONSE_NEW_STATIC_STATUS(VCARD7816_STATUS_ERROR_DATA_NOT_FOUND)
+VCARD_RESPONSE_NEW_STATIC_STATUS(VCARD7816_STATUS_ERROR_WRONG_PARAMETERS_2)
+VCARD_RESPONSE_NEW_STATIC_STATUS(VCARD7816_STATUS_ERROR_INS_CODE_INVALID)
+VCARD_RESPONSE_NEW_STATIC_STATUS(VCARD7816_STATUS_ERROR_CLA_INVALID)
+VCARD_RESPONSE_NEW_STATIC_STATUS(VCARD7816_STATUS_ERROR_GENERAL)
+
+/*
+ * return a single response code. This function cannot fail. It will always
+ * return a response.
+ */
+VCardResponse *
+vcard_make_response(vcard_7816_status_t status)
+{
+    VCardResponse *response = NULL;
+
+    switch (status) {
+    /* known 7816 response codes */
+    case VCARD7816_STATUS_SUCCESS:
+        return VCARD_RESPONSE_GET_STATIC(
+                    VCARD7816_STATUS_SUCCESS);
+    case VCARD7816_STATUS_WARNING:
+        return VCARD_RESPONSE_GET_STATIC(
+                    VCARD7816_STATUS_WARNING);
+    case VCARD7816_STATUS_WARNING_RET_CORUPT:
+        return VCARD_RESPONSE_GET_STATIC(
+                    VCARD7816_STATUS_WARNING_RET_CORUPT);
+    case VCARD7816_STATUS_WARNING_BUF_END_BEFORE_LE:
+        return VCARD_RESPONSE_GET_STATIC(
+                    VCARD7816_STATUS_WARNING_BUF_END_BEFORE_LE);
+    case VCARD7816_STATUS_WARNING_INVALID_FILE_SELECTED:
+        return VCARD_RESPONSE_GET_STATIC(
+                    VCARD7816_STATUS_WARNING_INVALID_FILE_SELECTED);
+    case VCARD7816_STATUS_WARNING_FCI_FORMAT_INVALID:
+        return VCARD_RESPONSE_GET_STATIC(
+                    VCARD7816_STATUS_WARNING_FCI_FORMAT_INVALID);
+    case VCARD7816_STATUS_WARNING_CHANGE:
+        return VCARD_RESPONSE_GET_STATIC(
+                    VCARD7816_STATUS_WARNING_CHANGE);
+    case VCARD7816_STATUS_WARNING_FILE_FILLED:
+        return VCARD_RESPONSE_GET_STATIC(
+                    VCARD7816_STATUS_WARNING_FILE_FILLED);
+    case VCARD7816_STATUS_EXC_ERROR:
+        return VCARD_RESPONSE_GET_STATIC(
+                    VCARD7816_STATUS_EXC_ERROR);
+    case VCARD7816_STATUS_EXC_ERROR_CHANGE:
+        return VCARD_RESPONSE_GET_STATIC(
+                    VCARD7816_STATUS_EXC_ERROR_CHANGE);
+    case VCARD7816_STATUS_EXC_ERROR_MEMORY_FAILURE:
+        return VCARD_RESPONSE_GET_STATIC(
+                    VCARD7816_STATUS_EXC_ERROR_MEMORY_FAILURE);
+    case VCARD7816_STATUS_ERROR_WRONG_LENGTH:
+        return VCARD_RESPONSE_GET_STATIC(
+                    VCARD7816_STATUS_ERROR_WRONG_LENGTH);
+    case VCARD7816_STATUS_ERROR_CLA_NOT_SUPPORTED:
+        return VCARD_RESPONSE_GET_STATIC(
+                    VCARD7816_STATUS_ERROR_CLA_NOT_SUPPORTED);
+    case VCARD7816_STATUS_ERROR_CHANNEL_NOT_SUPPORTED:
+        return VCARD_RESPONSE_GET_STATIC(
+                    VCARD7816_STATUS_ERROR_CHANNEL_NOT_SUPPORTED);
+    case VCARD7816_STATUS_ERROR_SECURE_NOT_SUPPORTED:
+        return VCARD_RESPONSE_GET_STATIC(
+                    VCARD7816_STATUS_ERROR_SECURE_NOT_SUPPORTED);
+    case VCARD7816_STATUS_ERROR_COMMAND_NOT_SUPPORTED:
+        return VCARD_RESPONSE_GET_STATIC(
+                    VCARD7816_STATUS_ERROR_COMMAND_NOT_SUPPORTED);
+    case VCARD7816_STATUS_ERROR_COMMAND_INCOMPATIBLE_WITH_FILE:
+        return VCARD_RESPONSE_GET_STATIC(
+                    VCARD7816_STATUS_ERROR_COMMAND_INCOMPATIBLE_WITH_FILE);
+    case VCARD7816_STATUS_ERROR_SECURITY_NOT_SATISFIED:
+        return VCARD_RESPONSE_GET_STATIC(
+                    VCARD7816_STATUS_ERROR_SECURITY_NOT_SATISFIED);
+    case VCARD7816_STATUS_ERROR_AUTHENTICATION_BLOCKED:
+        return VCARD_RESPONSE_GET_STATIC(
+                    VCARD7816_STATUS_ERROR_AUTHENTICATION_BLOCKED);
+    case VCARD7816_STATUS_ERROR_DATA_INVALID:
+        return VCARD_RESPONSE_GET_STATIC(
+                    VCARD7816_STATUS_ERROR_DATA_INVALID);
+    case VCARD7816_STATUS_ERROR_CONDITION_NOT_SATISFIED:
+        return VCARD_RESPONSE_GET_STATIC(
+                    VCARD7816_STATUS_ERROR_CONDITION_NOT_SATISFIED);
+    case VCARD7816_STATUS_ERROR_DATA_NO_EF:
+        return VCARD_RESPONSE_GET_STATIC(
+                    VCARD7816_STATUS_ERROR_DATA_NO_EF);
+    case VCARD7816_STATUS_ERROR_SM_OBJECT_MISSING:
+        return VCARD_RESPONSE_GET_STATIC(
+                    VCARD7816_STATUS_ERROR_SM_OBJECT_MISSING);
+    case VCARD7816_STATUS_ERROR_SM_OBJECT_INCORRECT:
+        return VCARD_RESPONSE_GET_STATIC(
+                    VCARD7816_STATUS_ERROR_SM_OBJECT_INCORRECT);
+    case VCARD7816_STATUS_ERROR_WRONG_PARAMETERS:
+        return VCARD_RESPONSE_GET_STATIC(
+                    VCARD7816_STATUS_ERROR_WRONG_PARAMETERS);
+    case VCARD7816_STATUS_ERROR_WRONG_PARAMETERS_IN_DATA:
+        return VCARD_RESPONSE_GET_STATIC(
+                    VCARD7816_STATUS_ERROR_WRONG_PARAMETERS_IN_DATA);
+    case VCARD7816_STATUS_ERROR_FUNCTION_NOT_SUPPORTED:
+        return VCARD_RESPONSE_GET_STATIC(
+                    VCARD7816_STATUS_ERROR_FUNCTION_NOT_SUPPORTED);
+    case VCARD7816_STATUS_ERROR_FILE_NOT_FOUND:
+        return VCARD_RESPONSE_GET_STATIC(
+                    VCARD7816_STATUS_ERROR_FILE_NOT_FOUND);
+    case VCARD7816_STATUS_ERROR_RECORD_NOT_FOUND:
+        return VCARD_RESPONSE_GET_STATIC(
+                    VCARD7816_STATUS_ERROR_RECORD_NOT_FOUND);
+    case VCARD7816_STATUS_ERROR_NO_SPACE_FOR_FILE:
+        return VCARD_RESPONSE_GET_STATIC(
+                    VCARD7816_STATUS_ERROR_NO_SPACE_FOR_FILE);
+    case VCARD7816_STATUS_ERROR_LC_TLV_INCONSISTENT:
+        return VCARD_RESPONSE_GET_STATIC(
+                    VCARD7816_STATUS_ERROR_LC_TLV_INCONSISTENT);
+    case VCARD7816_STATUS_ERROR_P1_P2_INCORRECT:
+        return VCARD_RESPONSE_GET_STATIC(
+                    VCARD7816_STATUS_ERROR_P1_P2_INCORRECT);
+    case VCARD7816_STATUS_ERROR_LC_P1_P2_INCONSISTENT:
+        return VCARD_RESPONSE_GET_STATIC(
+                    VCARD7816_STATUS_ERROR_LC_P1_P2_INCONSISTENT);
+    case VCARD7816_STATUS_ERROR_DATA_NOT_FOUND:
+        return VCARD_RESPONSE_GET_STATIC(
+                    VCARD7816_STATUS_ERROR_DATA_NOT_FOUND);
+    case VCARD7816_STATUS_ERROR_WRONG_PARAMETERS_2:
+        return VCARD_RESPONSE_GET_STATIC(
+                    VCARD7816_STATUS_ERROR_WRONG_PARAMETERS_2);
+    case VCARD7816_STATUS_ERROR_INS_CODE_INVALID:
+        return VCARD_RESPONSE_GET_STATIC(
+                    VCARD7816_STATUS_ERROR_INS_CODE_INVALID);
+    case VCARD7816_STATUS_ERROR_CLA_INVALID:
+        return VCARD_RESPONSE_GET_STATIC(
+                    VCARD7816_STATUS_ERROR_CLA_INVALID);
+    case VCARD7816_STATUS_ERROR_GENERAL:
+        return VCARD_RESPONSE_GET_STATIC(
+                    VCARD7816_STATUS_ERROR_GENERAL);
+    default:
+        /* we don't know this status code, create a response buffer to
+         * hold it */
+        response = vcard_response_new_status(status);
+        if (response == NULL) {
+            /* couldn't allocate the buffer, return memmory error */
+            return VCARD_RESPONSE_GET_STATIC(
+                        VCARD7816_STATUS_EXC_ERROR_MEMORY_FAILURE);
+        }
+    }
+    ASSERT(response);
+    return response;
+}
+
+/*
+ * Add File card support here if you need it.
+ */
+static VCardStatus
+vcard7816_file_system_process_apdu(VCard *card, VCardAPDU *apdu,
+                                   VCardResponse **response)
+{
+    /* TODO: if we want to support a virtual file system card, we do it here.
+     * It would probably be a pkcs #15 card type */
+    *response = vcard_make_response(
+                    VCARD7816_STATUS_ERROR_COMMAND_NOT_SUPPORTED);
+    return VCARD_DONE;
+}
+
+/*
+ * VM card (including java cards)
+ */
+static VCardStatus
+vcard7816_vm_process_apdu(VCard *card, VCardAPDU *apdu,
+                          VCardResponse **response)
+{
+    int bytes_to_copy, next_byte_count, count;
+    VCardApplet *current_applet;
+    VCardBufferResponse *buffer_response;
+    vcard_7816_status_t status;
+
+    /* parse the class first */
+    if (apdu->a_gen_type !=  VCARD_7816_ISO) {
+        *response = vcard_make_response(
+                        VCARD7816_STATUS_ERROR_COMMAND_NOT_SUPPORTED);
+        return VCARD_DONE;
+    }
+
+    /* use a switch so that if we need to support secure channel stuff later,
+     * we know where to put it */
+    switch (apdu->a_secure_messaging) {
+    case 0x0: /* no SM */
+        break;
+    case 0x4: /* proprietary SM */
+    case 0x8: /* header not authenticated */
+    case 0xc: /* header authenticated */
+    default:
+        /* for now, don't try to support secure channel stuff in the
+         * virtual card. */
+        *response = vcard_make_response(
+                        VCARD7816_STATUS_ERROR_SECURE_NOT_SUPPORTED);
+        return VCARD_DONE;
+    }
+
+    /* now parse the instruction */
+    switch (apdu->a_ins) {
+    case  VCARD7816_INS_MANAGE_CHANNEL: /* secure channel op */
+    case  VCARD7816_INS_EXTERNAL_AUTHENTICATE: /* secure channel op */
+    case  VCARD7816_INS_GET_CHALLENGE: /* secure channel op */
+    case  VCARD7816_INS_INTERNAL_AUTHENTICATE: /* secure channel op */
+    case  VCARD7816_INS_ERASE_BINARY: /* applet control op */
+    case  VCARD7816_INS_READ_BINARY: /* applet control op */
+    case  VCARD7816_INS_WRITE_BINARY: /* applet control op */
+    case  VCARD7816_INS_UPDATE_BINARY: /* applet control op */
+    case  VCARD7816_INS_READ_RECORD: /* file op */
+    case  VCARD7816_INS_WRITE_RECORD: /* file op */
+    case  VCARD7816_INS_UPDATE_RECORD: /* file op */
+    case  VCARD7816_INS_APPEND_RECORD: /* file op */
+    case  VCARD7816_INS_ENVELOPE:
+    case  VCARD7816_INS_PUT_DATA:
+        *response = vcard_make_response(
+                            VCARD7816_STATUS_ERROR_COMMAND_NOT_SUPPORTED);
+        break;
+
+    case  VCARD7816_INS_SELECT_FILE:
+        if (apdu->a_p1 != 0x04) {
+            *response = vcard_make_response(
+                            VCARD7816_STATUS_ERROR_FUNCTION_NOT_SUPPORTED);
+            break;
+        }
+
+        /* side effect, deselect the current applet if no applet has been found
+         * */
+        current_applet = vcard_find_applet(card, apdu->a_body, apdu->a_Lc);
+        vcard_select_applet(card, apdu->a_channel, current_applet);
+        if (current_applet) {
+            unsigned char *aid;
+            int aid_len;
+            aid = vcard_applet_get_aid(current_applet, &aid_len);
+           *response = vcard_response_new(card, aid, aid_len, apdu->a_Le,
+                                          VCARD7816_STATUS_SUCCESS);
+        } else {
+           *response = vcard_make_response(
+                             VCARD7816_STATUS_ERROR_FILE_NOT_FOUND);
+        }
+        break;
+
+    case  VCARD7816_INS_VERIFY:
+        if ((apdu->a_p1 != 0x00) || (apdu->a_p2 != 0x00)) {
+            *response = vcard_make_response(
+                            VCARD7816_STATUS_ERROR_WRONG_PARAMETERS);
+        } else {
+            if (apdu->a_Lc == 0) {
+                /* handle pin count if possible */
+                count = vcard_emul_get_login_count(card);
+                if (count < 0) {
+                    *response = vcard_make_response(
+                                    VCARD7816_STATUS_ERROR_DATA_NOT_FOUND);
+                } else {
+                    if (count > 0xf) {
+                        count = 0xf;
+                    }
+                    *response = vcard_response_new_status_bytes(
+                                                VCARD7816_SW1_WARNING_CHANGE,
+                                                                0xc0 | count);
+                    if (*response == NULL) {
+                        *response = vcard_make_response(
+                                    VCARD7816_STATUS_EXC_ERROR_MEMORY_FAILURE);
+                    }
+                }
+            } else {
+                    status = vcard_emul_login(card, apdu->a_body, apdu->a_Lc);
+                *response = vcard_make_response(status);
+            }
+        }
+        break;
+
+    case VCARD7816_INS_GET_RESPONSE:
+        buffer_response = vcard_get_buffer_response(card);
+        if (!buffer_response) {
+            *response = vcard_make_response(
+                            VCARD7816_STATUS_ERROR_DATA_NOT_FOUND);
+            /* handle error */
+        }
+        bytes_to_copy = MIN(buffer_response->len, apdu->a_Le);
+        next_byte_count = MIN(256, buffer_response->len - bytes_to_copy);
+        *response = vcard_response_new_bytes(
+                        card, buffer_response->current, bytes_to_copy,
+                        apdu->a_Le,
+                        next_byte_count ?
+                        VCARD7816_SW1_RESPONSE_BYTES: VCARD7816_SW1_SUCCESS,
+                        next_byte_count);
+        buffer_response->current += bytes_to_copy;
+        buffer_response->len -= bytes_to_copy;
+        if (*response == NULL || (next_byte_count == 0)) {
+            vcard_set_buffer_response(card,NULL);
+            vcard_buffer_response_delete(buffer_response);
+        }
+        if (*response == NULL) {
+            *response =
+                vcard_make_response(VCARD7816_STATUS_EXC_ERROR_MEMORY_FAILURE);
+        }
+        break;
+
+    case VCARD7816_INS_GET_DATA:
+        *response =
+            vcard_make_response(VCARD7816_STATUS_ERROR_COMMAND_NOT_SUPPORTED);
+        break;
+
+    default:
+        *response =
+            vcard_make_response(VCARD7816_STATUS_ERROR_COMMAND_NOT_SUPPORTED);
+        break;
+    }
+
+    /* response should have been set somewhere */
+    ASSERT(*response != NULL);
+    return VCARD_DONE;
+}
+
+
+/*
+ * APDU processing starts here. This routes the card processing stuff to the
+ * right location.
+ */
+VCardStatus
+vcard_process_apdu(VCard *card, VCardAPDU *apdu, VCardResponse **response)
+{
+    VCardStatus status;
+    VCardBufferResponse *buffer_response;
+
+    /* first handle any PTS commands, which aren't really APDU's */
+    if (apdu->a_type == VCARD_7816_PTS) {
+        /* the PTS responses aren't really responses either */
+        *response = vcard_response_new_data(apdu->a_data, apdu->a_len);
+        /* PTS responses have no status bytes */
+        (*response)->b_total_len = (*response)->b_len;
+        return VCARD_DONE;
+    }
+    buffer_response = vcard_get_buffer_response(card);
+    if (buffer_response && apdu->a_ins != VCARD7816_INS_GET_RESPONSE) {
+       /* clear out buffer_response, return an error */
+        vcard_set_buffer_response(card,NULL);
+        vcard_buffer_response_delete(buffer_response);
+       *response = vcard_make_response(VCARD7816_STATUS_EXC_ERROR);
+       return VCARD_DONE;
+    }
+
+    status = vcard_process_applet_apdu(card, apdu, response);
+    if (status != VCARD_NEXT) {
+        return status;
+    }
+    switch (vcard_get_type(card)) {
+    case VCARD_FILE_SYSTEM:
+        return vcard7816_file_system_process_apdu(card,apdu,response);
+    case VCARD_VM:
+        return vcard7816_vm_process_apdu(card,apdu,response);
+    case VCARD_DIRECT:
+        /* if we are type direct, then the applet should handle everything */
+        assert("VCARD_DIRECT: applet failure");
+       break;
+    }
+    *response =
+        vcard_make_response(VCARD7816_STATUS_ERROR_COMMAND_NOT_SUPPORTED);
+    return VCARD_DONE;
+}
diff --git a/libcacard/card_7816.h b/libcacard/card_7816.h
new file mode 100644
index 0000000..4351b1c
--- /dev/null
+++ b/libcacard/card_7816.h
@@ -0,0 +1,60 @@
+/*
+ * Implement the 7816 portion of the card spec
+ *
+ */
+#ifndef CARD_7816_H
+#define CARD_7816_H  1
+
+#include "card_7816t.h"
+#include "vcardt.h"
+
+/*
+ * constructors for VCardResponse's
+ */
+/* response from a return buffer and a status */
+VCardResponse *vcard_response_new(VCard *card, unsigned char *buf, int len,
+                                  int Le, vcard_7816_status_t status);
+/* response from a return buffer and status bytes */
+VCardResponse *vcard_response_new_bytes(VCard *card, unsigned char *buf,
+                                        int len, int Le,
+                                        unsigned char sw1, unsigned char sw2);
+/* response from just status bytes */
+VCardResponse *vcard_response_new_status_bytes(unsigned char sw1,
+                                               unsigned char sw2);
+/* response from just status: NOTE this cannot fail, it will alwyas return a
+ * valid response, if it can't allocate memory, the response will be
+ * VCARD7816_STATUS_EXC_ERROR_MEMORY_FAILURE */
+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);
+
+
+
+
+/*
+ * destructor for VCardResponse.
+ *  Can be called with a NULL response
+ */
+void vcard_response_delete(VCardResponse *response);
+
+/*
+ * constructor for VCardAPDU
+ */
+VCardAPDU *vcard_apdu_new(unsigned char *raw_apdu, int len,
+                          unsigned short *status);
+
+/*
+ * destructor for VCardAPDU
+ *  Can be called with a NULL apdu
+ */
+void vcard_apdu_delete(VCardAPDU *apdu);
+
+/*
+ * APDU processing starts here. This routes the card processing stuff to the
+ * right location. Always returns a valid response.
+ */
+VCardStatus vcard_process_apdu(VCard *card, VCardAPDU *apdu,
+                               VCardResponse **response);
+
+#endif
diff --git a/libcacard/card_7816t.h b/libcacard/card_7816t.h
new file mode 100644
index 0000000..531455c
--- /dev/null
+++ b/libcacard/card_7816t.h
@@ -0,0 +1,163 @@
+/*
+ * Implement the 7816 portion of the card spec
+ *
+ */
+#ifndef CARD_7816T_H
+#define CARD_7816T_H 1
+
+typedef unsigned short vcard_7816_status_t;
+
+struct VCardResponseStruct {
+    unsigned char *b_data;
+    vcard_7816_status_t b_status;
+    unsigned char b_sw1;
+    unsigned char b_sw2;
+    int b_len;
+    int b_total_len;
+    enum VCardResponseBufferType {
+        VCARD_MALLOC,
+        VCARD_MALLOC_DATA,
+        VCARD_MALLOC_STRUCT,
+        VCARD_STATIC
+    } b_type;
+};
+
+#define VCARD_RESPONSE_NEW_STATIC_STATUS(stat) \
+static const VCardResponse VCardResponse##stat = \
+        {(unsigned char *)&VCardResponse##stat.b_sw1, (stat), ((stat) >> 8), \
+         ((stat) & 0xff), 0, 2, VCARD_STATIC};
+
+#define VCARD_RESPONSE_NEW_STATIC_STATUS_BYTES(sw1, sw2) \
+static const VCardResponse VCARDResponse##sw1 = \
+        {(unsigned char *)&VCardResponse##name.b_sw1, ((sw1) << 8 | (sw2)), \
+         (sw1), (sw2), 0, 2, VCARD_STATIC};
+
+/* cast away the const, callers need may need to 'free' the
+ * result, and const implies that they don't */
+#define VCARD_RESPONSE_GET_STATIC(name) \
+        ((VCardResponse *)(&VCardResponse##name))
+
+typedef enum {
+    VCARD_7816_ISO,
+    VCARD_7816_RFU,
+    VCARD_7816_PTS,
+    VCARD_7816_PROPIETARY
+} VCardAPDUType;
+
+
+/*
+ * 7816 header. All APDU's have this header.
+ * They must be laid out in this order.
+ */
+struct VCardAPDUHeader {
+    unsigned char ah_cla;
+    unsigned char ah_ins;
+    unsigned char ah_p1;
+    unsigned char ah_p2;
+    unsigned char ah_Le;
+    unsigned char ah_body[1]; /* indefinate length */
+};
+
+/*
+ * 7816 APDU structure. The raw bytes are stored in the union and can be
+ * accessed directly through u.data (which is aliased as a_data).
+ *
+ * Names of the fields match the 7816 documentation.
+ */
+struct VCardAPDUStruct {
+    int a_len;                /* length of the whole buffer, including header */
+    int a_Lc;                 /* 7816 Lc (parameter length) value */
+    int a_Le;                 /* 7816 Le (expected result length) value */
+    unsigned char *a_body;    /* pointer to the parameter */
+    int a_channel;            /* decoded channel */
+    int a_secure_messaging;   /* decoded secure messaging type */
+    int a_type;               /* decoded type from cla (top nibble of class) */
+    VCardAPDUType a_gen_type; /* generic type (7816, PROPRIETARY, RFU, etc) */
+    union {
+        struct VCardAPDUHeader *header;
+        unsigned char   *data;
+    } u;
+/* give the subfields a unified look */
+#define a_header u.header
+#define a_data u.data
+#define a_cla a_header->ah_cla /* class */
+#define a_ins a_header->ah_ins /* instruction */
+#define a_p1 a_header->ah_p1   /* parameter 1 */
+#define a_p2 a_header->ah_p2   /* parameter 2 */
+};
+
+/* 7816 status codes */
+#define VCARD7816_STATUS_SUCCESS                              0x9000
+#define VCARD7816_STATUS_WARNING                              0x6200
+#define VCARD7816_STATUS_WARNING_RET_CORUPT                   0x6281
+#define VCARD7816_STATUS_WARNING_BUF_END_BEFORE_LE            0x6282
+#define VCARD7816_STATUS_WARNING_INVALID_FILE_SELECTED        0x6283
+#define VCARD7816_STATUS_WARNING_FCI_FORMAT_INVALID           0x6284
+#define VCARD7816_STATUS_WARNING_CHANGE                       0x6300
+#define VCARD7816_STATUS_WARNING_FILE_FILLED                  0x6381
+#define VCARD7816_STATUS_EXC_ERROR                            0x6400
+#define VCARD7816_STATUS_EXC_ERROR_CHANGE                     0x6500
+#define VCARD7816_STATUS_EXC_ERROR_MEMORY_FAILURE             0x6581
+#define VCARD7816_STATUS_ERROR_WRONG_LENGTH                   0x6700
+#define VCARD7816_STATUS_ERROR_CLA_NOT_SUPPORTED              0x6800
+#define VCARD7816_STATUS_ERROR_CHANNEL_NOT_SUPPORTED          0x6881
+#define VCARD7816_STATUS_ERROR_SECURE_NOT_SUPPORTED           0x6882
+#define VCARD7816_STATUS_ERROR_COMMAND_NOT_SUPPORTED          0x6900
+#define VCARD7816_STATUS_ERROR_COMMAND_INCOMPATIBLE_WITH_FILE 0x6981
+#define VCARD7816_STATUS_ERROR_SECURITY_NOT_SATISFIED         0x6982
+#define VCARD7816_STATUS_ERROR_AUTHENTICATION_BLOCKED         0x6983
+#define VCARD7816_STATUS_ERROR_DATA_INVALID                   0x6984
+#define VCARD7816_STATUS_ERROR_CONDITION_NOT_SATISFIED        0x6985
+#define VCARD7816_STATUS_ERROR_DATA_NO_EF                     0x6986
+#define VCARD7816_STATUS_ERROR_SM_OBJECT_MISSING              0x6987
+#define VCARD7816_STATUS_ERROR_SM_OBJECT_INCORRECT            0x6988
+#define VCARD7816_STATUS_ERROR_WRONG_PARAMETERS               0x6a00
+#define VCARD7816_STATUS_ERROR_WRONG_PARAMETERS_IN_DATA       0x6a80
+#define VCARD7816_STATUS_ERROR_FUNCTION_NOT_SUPPORTED         0x6a81
+#define VCARD7816_STATUS_ERROR_FILE_NOT_FOUND                 0x6a82
+#define VCARD7816_STATUS_ERROR_RECORD_NOT_FOUND               0x6a83
+#define VCARD7816_STATUS_ERROR_NO_SPACE_FOR_FILE              0x6a84
+#define VCARD7816_STATUS_ERROR_LC_TLV_INCONSISTENT            0x6a85
+#define VCARD7816_STATUS_ERROR_P1_P2_INCORRECT                0x6a86
+#define VCARD7816_STATUS_ERROR_LC_P1_P2_INCONSISTENT          0x6a87
+#define VCARD7816_STATUS_ERROR_DATA_NOT_FOUND                 0x6a88
+#define VCARD7816_STATUS_ERROR_WRONG_PARAMETERS_2             0x6b00
+#define VCARD7816_STATUS_ERROR_INS_CODE_INVALID               0x6d00
+#define VCARD7816_STATUS_ERROR_CLA_INVALID                    0x6e00
+#define VCARD7816_STATUS_ERROR_GENERAL                        0x6f00
+/* 7816 sw1 codes */
+#define VCARD7816_SW1_SUCCESS               0x90
+#define VCARD7816_SW1_RESPONSE_BYTES        0x61
+#define VCARD7816_SW1_WARNING               0x62
+#define VCARD7816_SW1_WARNING_CHANGE        0x63
+#define VCARD7816_SW1_EXC_ERROR             0x64
+#define VCARD7816_SW1_EXC_ERROR_CHANGE      0x65
+#define VCARD7816_SW1_ERROR_WRONG_LENGTH    0x67
+#define VCARD7816_SW1_CLA_ERROR             0x68
+#define VCARD7816_SW1_COMMAND_ERROR         0x69
+#define VCARD7816_SW1_P1_P2_ERROR           0x6a
+#define VCARD7816_SW1_LE_ERROR              0x6c
+#define VCARD7816_SW1_INS_ERROR             0x6d
+#define VCARD7816_SW1_CLA_NOT_SUPPORTED     0x6e
+
+/* 7816 Instructions */
+#define VCARD7816_INS_MANAGE_CHANNEL        0x70
+#define VCARD7816_INS_EXTERNAL_AUTHENTICATE 0x82
+#define VCARD7816_INS_GET_CHALLENGE         0x84
+#define VCARD7816_INS_INTERNAL_AUTHENTICATE 0x88
+#define VCARD7816_INS_ERASE_BINARY          0x0e
+#define VCARD7816_INS_READ_BINARY           0xb0
+#define VCARD7816_INS_WRITE_BINARY          0xd0
+#define VCARD7816_INS_UPDATE_BINARY         0xd6
+#define VCARD7816_INS_READ_RECORD           0xb2
+#define VCARD7816_INS_WRITE_RECORD          0xd2
+#define VCARD7816_INS_UPDATE_RECORD         0xdc
+#define VCARD7816_INS_APPEND_RECORD         0xe2
+#define VCARD7816_INS_ENVELOPE              0xc2
+#define VCARD7816_INS_PUT_DATA              0xda
+#define VCARD7816_INS_GET_DATA              0xca
+#define VCARD7816_INS_SELECT_FILE           0xa4
+#define VCARD7816_INS_VERIFY                0x20
+#define VCARD7816_INS_GET_RESPONSE          0xc0
+
+#endif
diff --git a/libcacard/config.h b/libcacard/config.h
new file mode 100644
index 0000000..19775eb
--- /dev/null
+++ b/libcacard/config.h
@@ -0,0 +1,81 @@
+/* config.h.  Generated from config.h.in by configure.  */
+/* config.h.in.  Generated from configure.ac by autoheader.  */
+
+/* Define if building universal (internal helper macro) */
+/* #undef AC_APPLE_UNIVERSAL_BUILD */
+
+/* Define to 1 if you have the <dlfcn.h> header file. */
+#define HAVE_DLFCN_H 1
+
+/* Define to 1 if you have the <inttypes.h> header file. */
+#define HAVE_INTTYPES_H 1
+
+/* Define to 1 if you have the <memory.h> header file. */
+#define HAVE_MEMORY_H 1
+
+/* Define to 1 if you have the <stdint.h> header file. */
+#define HAVE_STDINT_H 1
+
+/* Define to 1 if you have the <stdlib.h> header file. */
+#define HAVE_STDLIB_H 1
+
+/* Define to 1 if you have the <strings.h> header file. */
+#define HAVE_STRINGS_H 1
+
+/* Define to 1 if you have the <string.h> header file. */
+#define HAVE_STRING_H 1
+
+/* Define to 1 if you have the <sys/stat.h> header file. */
+#define HAVE_SYS_STAT_H 1
+
+/* Define to 1 if you have the <sys/types.h> header file. */
+#define HAVE_SYS_TYPES_H 1
+
+/* Define to 1 if you have the <unistd.h> header file. */
+#define HAVE_UNISTD_H 1
+
+/* Define to the sub-directory in which libtool stores uninstalled libraries.
+   */
+#define LT_OBJDIR ".libs/"
+
+/* Define to 1 if your C compiler doesn't accept -c and -o together. */
+/* #undef NO_MINUS_C_MINUS_O */
+
+/* Name of package */
+#define PACKAGE "cac_card"
+
+/* Define to the address where bug reports for this package should be sent. */
+#define PACKAGE_BUGREPORT ""
+
+/* Define to the full name of this package. */
+#define PACKAGE_NAME "cac_card"
+
+/* Define to the full name and version of this package. */
+#define PACKAGE_STRING "cac_card 0.0.1"
+
+/* Define to the one symbol short name of this package. */
+#define PACKAGE_TARNAME "cac_card"
+
+/* Define to the home page for this package. */
+#define PACKAGE_URL ""
+
+/* Define to the version of this package. */
+#define PACKAGE_VERSION "0.0.1"
+
+/* Define to 1 if you have the ANSI C header files. */
+#define STDC_HEADERS 1
+
+/* Version number of package */
+#define VERSION "0.0.1"
+
+/* Define WORDS_BIGENDIAN to 1 if your processor stores words with the most
+   significant byte first (like Motorola and SPARC, unlike Intel). */
+#if defined AC_APPLE_UNIVERSAL_BUILD
+# if defined __BIG_ENDIAN__
+#  define WORDS_BIGENDIAN 1
+# endif
+#else
+# ifndef WORDS_BIGENDIAN
+/* #  undef WORDS_BIGENDIAN */
+# endif
+#endif
diff --git a/libcacard/event.c b/libcacard/event.c
new file mode 100644
index 0000000..25d99ae
--- /dev/null
+++ b/libcacard/event.c
@@ -0,0 +1,112 @@
+/*
+ *
+ */
+#include "vcard.h"
+#include "vreader.h"
+#include "vevent.h"
+
+/*
+ * OS includes
+ */
+#include <stdlib.h>
+
+/*
+ * from spice
+ */
+#include "mutex.h"
+
+VEvent *
+vevent_new(VEventType type, VReader *reader, VCard *card)
+{
+    VEvent *new_vevent;
+
+    new_vevent = (VEvent *)malloc(sizeof(VEvent));
+    if (new_vevent == NULL) {
+        return NULL;
+    }
+    new_vevent->next = NULL;
+    new_vevent->type = type;
+    new_vevent->reader = vreader_reference(reader);
+    new_vevent->card = vcard_reference(card);
+
+    return new_vevent;
+}
+
+void
+vevent_delete(VEvent *vevent)
+{
+    if (vevent == NULL) {
+        return;
+    }
+    vreader_free(vevent->reader);
+    vcard_free(vevent->card);
+    free(vevent);
+}
+
+/*
+ * VEvent queue management
+ */
+
+static VEvent *vevent_queue_head = NULL;
+static VEvent *vevent_queue_tail = NULL;
+static mutex_t vevent_queue_lock;
+static condition_t vevent_queue_condition;
+
+void vevent_queue_init(void)
+{
+    MUTEX_INIT(vevent_queue_lock);
+    CONDITION_INIT(vevent_queue_condition);
+    vevent_queue_head = vevent_queue_tail = NULL;
+}
+
+void
+vevent_queue_vevent(VEvent *vevent)
+{
+    vevent->next = NULL;
+    MUTEX_LOCK(vevent_queue_lock);
+    if (vevent_queue_head) {
+        assert(vevent_queue_tail);
+        vevent_queue_tail->next = vevent;
+    } else {
+        vevent_queue_head = vevent;
+    }
+    vevent_queue_tail = vevent;
+    CONDITION_NOTIFY(vevent_queue_condition);
+    MUTEX_UNLOCK(vevent_queue_lock);
+}
+
+/* must have lock */
+static VEvent *
+vevent_dequeue_vevent(void)
+{
+    VEvent *vevent = NULL;
+    if (vevent_queue_head) {
+        vevent = vevent_queue_head;
+        vevent_queue_head = vevent->next;
+        vevent->next = NULL;
+    }
+    return vevent;
+}
+
+VEvent * vevent_wait_next_vevent(void)
+{
+    VEvent *vevent;
+
+    MUTEX_LOCK(vevent_queue_lock);
+    while ((vevent = vevent_dequeue_vevent()) == NULL) {
+        CONDITION_WAIT(vevent_queue_condition, vevent_queue_lock);
+    }
+    MUTEX_UNLOCK(vevent_queue_lock);
+    return vevent;
+}
+
+VEvent * vevent_get_next_vevent(void)
+{
+    VEvent *vevent;
+
+    MUTEX_LOCK(vevent_queue_lock);
+    vevent = vevent_dequeue_vevent();
+    MUTEX_UNLOCK(vevent_queue_lock);
+    return vevent;
+}
+
diff --git a/libcacard/eventt.h b/libcacard/eventt.h
new file mode 100644
index 0000000..4c3df4f
--- /dev/null
+++ b/libcacard/eventt.h
@@ -0,0 +1,28 @@
+/*
+ *
+ */
+
+#ifndef EVENTT_H
+#define EVENTT_H 1
+#include "vreadert.h"
+#include "vcardt.h"
+
+typedef struct VEventStruct VEvent;
+
+typedef enum {
+    VEVENT_READER_INSERT,
+    VEVENT_READER_REMOVE,
+    VEVENT_CARD_INSERT,
+    VEVENT_CARD_REMOVE,
+    VEVENT_LAST,
+} VEventType;
+
+struct VEventStruct {
+    VEvent *next;
+    VEventType type;
+    VReader *reader;
+    VCard *card;
+};
+#endif
+
+
diff --git a/libcacard/link_test.c b/libcacard/link_test.c
new file mode 100644
index 0000000..40d2c6d
--- /dev/null
+++ b/libcacard/link_test.c
@@ -0,0 +1,20 @@
+/*
+ *
+ */
+#include <stdio.h>
+#include "vcard.h"
+
+VCardStatus cac_card_init(const char *flags, VCard *card,
+                const unsigned char *cert[],
+                int cert_len[], VCardKey *key[] /* adopt the keys*/,
+                int cert_count);
+/*
+ * this will crash... just test the linkage right now
+ */
+
+main(int argc, char **argv)
+{
+    VCard *card; /* no constructor yet */
+    cac_card_init("", card, NULL, 0, NULL, 0);
+}
+
diff --git a/libcacard/mutex.h b/libcacard/mutex.h
new file mode 100644
index 0000000..db44814
--- /dev/null
+++ b/libcacard/mutex.h
@@ -0,0 +1,59 @@
+/* -*- Mode: C; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+/*
+   Copyright (C) 2009 Red Hat, Inc.
+
+   This library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   This library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with this library; if not, see <http://www.gnu.org/licenses/>.
+*/
+
+/*
+ *  This header file provides a way of mapping windows and linux thread calls
+ *  to a set of macros.  Ideally this would be shared by whatever subsystem we
+ *  link with.
+ */
+
+#ifndef _H_MUTEX
+#define _H_MUTEX
+#ifdef _WIN32
+#include <windows.h>
+typedef CRITICAL_SECTION mutex_t;
+#define MUTEX_INIT(mutex) InitializeCriticalSection(&mutex)
+#define MUTEX_LOCK(mutex) EnterCriticalSection(&mutex)
+#define MUTEX_UNLOCK(mutex) LeaveCriticalSection(&mutex)
+typedef CONDITION_VARIABLE condition_t;
+#define CONDITION_INIT(cond) InitializeConditionVariable(&cond)
+#define CONDITION_WAIT(cond,mutex) \
+            SleepConditionVariableCS(&cond,&mutex,INFINTE)
+#define CONDITION_NOTIFY(cond) WakeConditionVariable(&cond)
+typedef uint32_t thread_t;
+typedef HANDLE thread_status_t;
+#define THREAD_CREATE(tid, func, arg) \
+        CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)func, arg, 0, &tid)
+#define THREAD_SUCCESS(status) ((status) !=  NULL)
+#else
+#include <pthread.h>
+typedef pthread_mutex_t mutex_t;
+#define MUTEX_INIT(mutex) pthread_mutex_init(&mutex, NULL)
+#define MUTEX_LOCK(mutex) pthread_mutex_lock(&mutex)
+#define MUTEX_UNLOCK(mutex) pthread_mutex_unlock(&mutex)
+typedef pthread_cond_t condition_t;
+#define CONDITION_INIT(cond) pthread_cond_init(&cond, NULL)
+#define CONDITION_WAIT(cond,mutex) pthread_cond_wait(&cond,&mutex)
+#define CONDITION_NOTIFY(cond) pthread_cond_signal(&cond)
+typedef pthread_t thread_t;
+typedef int thread_status_t;
+#define THREAD_CREATE(tid, func, arg) pthread_create(&tid, NULL, func, arg)
+#define THREAD_SUCCESS(status)  ((status) == 0)
+#endif
+
+#endif // _H_MUTEX
diff --git a/libcacard/passthru.c b/libcacard/passthru.c
new file mode 100644
index 0000000..09471c7
--- /dev/null
+++ b/libcacard/passthru.c
@@ -0,0 +1,612 @@
+/*
+ * implement the applets for the CAC card.
+ */
+#ifdef USE_PASSTHRU
+#include "vcard.h"
+#include "vcard_emul.h"
+#include "card_7816.h"
+#include "vreader.h"
+#include "mutex.h"
+#include "vcard_emul.h"
+#include "passthru.h"
+#include <stdlib.h>
+#include <string.h>
+#include <pcsclite.h>
+
+/*
+ * Passthru applet private data
+ */
+struct VCardAppletPrivateStruct {
+    char *reader_name;
+    /* pcsc-lite parameters */
+    SCARDHANDLE hCard;
+    uint32_t hProtocol;
+    SCARD_IO_REQUEST *send_io;
+    unsigned char atr[MAX_ATR_SIZE];
+    int atr_len;
+};
+
+static SCARDCONTEXT global_context = 0;
+
+#define MAX_RESPONSE_LENGTH 261 /*65537 */
+/*
+ * handle all the APDU's that are common to all CAC applets
+ */
+static VCardStatus
+passthru_process_apdu(VCard *card, VCardAPDU *apdu, VCardResponse **response)
+{
+    LONG rv;
+    unsigned char buf[MAX_RESPONSE_LENGTH];
+    uint32_t len = MAX_RESPONSE_LENGTH;
+    VCardAppletPrivate *applet_private = NULL;
+    SCARD_IO_REQUEST receive_io;
+
+    applet_private = vcard_get_current_applet_private(card, 0);
+    if (applet_private == NULL) {
+       *response = vcard_make_response(VCARD7816_STATUS_EXC_ERROR);
+       return VCARD_DONE;
+    }
+
+    rv = SCardTransmit(applet_private->hCard, applet_private->send_io,
+                       apdu->a_data, apdu->a_len, &receive_io, buf, &len);
+    if (rv != SCARD_S_SUCCESS) {
+       *response = vcard_make_response(VCARD7816_STATUS_EXC_ERROR);
+       return VCARD_DONE;
+    }
+
+    *response = vcard_response_new_data(buf,len);
+    if (*response == NULL) {
+       *response =
+            vcard_make_response(VCARD7816_STATUS_EXC_ERROR_MEMORY_FAILURE);
+    } else {
+       (*response)->b_total_len = (*response)->b_len;
+    }
+    return VCARD_DONE;
+}
+
+static void
+passthru_card_set_atr(VCard *card, unsigned char *atr, int atr_len)
+{
+    VCardAppletPrivate *applet_private = NULL;
+    applet_private = vcard_get_current_applet_private(card, 0);
+    if (applet_private == NULL) {
+        return;
+    }
+    applet_private->atr_len = MIN(atr_len, sizeof(applet_private->atr));
+    memcpy(applet_private->atr, atr, applet_private->atr_len);
+}
+
+static void passthru_card_get_atr(VCard *card, unsigned char *atr, int *atr_len)
+{
+    VCardAppletPrivate *applet_private = NULL;
+    SCARD_READERSTATE *state;
+
+    applet_private = vcard_get_current_applet_private(card, 0);
+    if ((applet_private == NULL) || (applet_private->atr_len == 0)) {
+        vcard_emul_get_atr(card, atr, atr_len);
+        return;
+    }
+    *atr_len = MIN(applet_private->atr_len, *atr_len);
+    memcpy(atr,applet_private->atr,*atr_len);
+    return;
+}
+
+/*
+ *  resest the inter call state between applet selects
+ */
+static VCardStatus
+passthru_reset(VCard *card, int channel)
+{
+    return VCARD_DONE;
+}
+
+static VCardStatus
+passthru_pcsc_lite_init()
+{
+    LONG rv;
+    if (global_context != 0) {
+        return VCARD_DONE;
+    }
+    rv = SCardEstablishContext(SCARD_SCOPE_SYSTEM, NULL, NULL, &global_context);
+    if (rv != SCARD_S_SUCCESS) {
+        return VCARD_FAIL;
+    }
+    return VCARD_DONE;
+}
+
+/*
+ *  match if s1 is completely contained in s2
+ */
+static int
+string_match(const char *s1, const char *s2)
+{
+    int len = strlen(s1);
+    const char *start;
+
+    for (start = strchr(s2, *s1); start; start = strchr(start+1, *s1)) {
+       if (strncmp(start, s1, len) == 0) {
+           return 1;
+       }
+    }
+    return 0;
+}
+
+
+/*
+ *  Look for the reader that best matches the name for VReader
+ */
+static char *
+passthru_get_reader_name(VReader *reader)
+{
+    const char *reader_name = vreader_get_name(reader);
+    char *reader_list = NULL;
+    char *reader_entry = NULL;
+    char *reader_match = NULL;
+    uint32_t reader_string_length;
+    VCardStatus status;
+    LONG rv;
+
+    if (reader_name == NULL) {
+        return NULL;
+    }
+
+    status = passthru_pcsc_lite_init();
+    if (status != VCARD_DONE) {
+       return NULL;
+    }
+
+
+    /* find the existing reader names */
+    rv = SCardListReaders(global_context, NULL, NULL, &reader_string_length);
+    if (rv !=  SCARD_S_SUCCESS) {
+       return NULL;
+    }
+    reader_list = (char *)malloc(reader_string_length);
+    rv = SCardListReaders(global_context, NULL, reader_list,
+                          &reader_string_length);
+    if (rv !=  SCARD_S_SUCCESS) {
+       goto cleanup;
+    }
+
+    /* match that name */
+    for (reader_entry= reader_list;*reader_entry;
+                                   reader_entry += strlen(reader_entry)+1) {
+       if (string_match(reader_entry, reader_name)) {
+           reader_match = strdup(reader_entry);
+           break;
+       }
+    }
+cleanup:
+    if (reader_list) {
+        free(reader_list);
+    }
+    return reader_match;
+}
+
+
+/*
+ * utilities for creating and destroying the private applet data
+ */
+static void
+passthru_delete_applet_private(VCardAppletPrivate *applet_private)
+{
+    if (applet_private == NULL) {
+        return;
+    }
+    if (applet_private->hCard) {
+        SCardDisconnect(applet_private->hCard,SCARD_LEAVE_CARD);
+    }
+    if (applet_private->reader_name != NULL) {
+        free(applet_private->reader_name);
+    }
+    free(applet_private);
+}
+
+static VCardAppletPrivate *
+passthru_new_applet_private(VReader *reader)
+{
+    VCardAppletPrivate *applet_private = NULL;
+    LONG rv;
+
+    applet_private = (VCardAppletPrivate *)malloc(sizeof(VCardAppletPrivate));
+
+    if (applet_private == NULL) {
+        goto fail;
+    }
+    applet_private->hCard = 0;
+    applet_private->reader_name = NULL;
+
+    applet_private->reader_name = passthru_get_reader_name(reader);
+    if (applet_private->reader_name == NULL) {
+        goto fail;
+    }
+
+    rv = SCardConnect( global_context, applet_private->reader_name,
+       SCARD_SHARE_DIRECT, SCARD_PROTOCOL_T0|SCARD_PROTOCOL_T1,
+        &applet_private->hCard,
+        &applet_private->hProtocol);
+    if (rv !=  SCARD_S_SUCCESS) {
+        goto fail;
+    }
+
+    if (applet_private->hProtocol == SCARD_PROTOCOL_T0) {
+        applet_private->send_io = SCARD_PCI_T0;
+    } else {
+        applet_private->send_io = SCARD_PCI_T1;
+    }
+    applet_private->atr_len = 0;
+    return applet_private;
+
+fail:
+    if (applet_private) {
+        passthru_delete_applet_private(applet_private);
+    }
+    return NULL;
+}
+
+
+/*
+ * create a new applet which links to our override function.
+ */
+static VCardApplet *
+passthru_new_applet(VReader *reader)
+{
+    VCardAppletPrivate *applet_private = NULL;
+    VCardApplet *applet = NULL;
+    unsigned char passthru_aid[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
+    int passthru_aid_len = sizeof (passthru_aid);
+
+    applet_private = passthru_new_applet_private(reader);
+    if (applet_private == NULL) {
+        goto failure;
+    }
+    applet = vcard_new_applet(passthru_process_apdu, passthru_reset,
+                              passthru_aid, passthru_aid_len);
+    if (applet == NULL) {
+        goto failure;
+    }
+    vcard_set_applet_private(applet, applet_private,
+                          passthru_delete_applet_private);
+    applet_private = NULL;
+
+    return applet;
+
+failure:
+    if (applet_private != NULL) {
+        passthru_delete_applet_private(applet_private);
+    }
+    return NULL;
+}
+
+
+
+/*
+ * Initialize the card. This is the only 'card type emulator' portion of this
+ * the rest are connected through function pointers.
+ */
+VCardStatus
+passthru_card_init(VReader *vreader, VCard *card,
+              const char *flags,
+              unsigned char * const *cert,
+              int cert_len[],
+              VCardKey *key[] /* adopt the keys*/,
+              int cert_count)
+{
+    int i;
+    VCardApplet *applet;
+
+    /* Don't do soft emulation of the 7816, pass everything to the card */
+    vcard_set_type(card,VCARD_DIRECT);
+
+    applet = passthru_new_applet(vreader);
+    if (applet == NULL) {
+        goto failure;
+    }
+
+    vcard_add_applet(card, applet);
+
+    /* we are adopting the keys, so free them now (since we don't use them) */
+    for (i=0; i < cert_count; i++) {
+      vcard_emul_delete_key(key[i]);
+    }
+
+    return VCARD_DONE;
+
+failure:
+    return VCARD_FAIL;
+}
+
+/*
+ *  Begin passthru_emul code. This emulator only works with the passthru card
+ *  type.
+ *
+ */
+
+/*
+ *  Get the state entry that matches this reader. If none found, return NULL
+ */
+static SCARD_READERSTATE_A *
+passthru_get_reader_state(SCARD_READERSTATE_A *reader_states,
+                          int reader_count, char *name)
+{
+    int i;
+
+    for (i=0; i < reader_count; i++) {
+        if (name == NULL && reader_states[i].szReader == NULL) {
+            // looking for a blank slot to return
+            return &reader_states[i];
+        }
+        if (name == NULL || reader_states[i].szReader == NULL) {
+            continue;
+        }
+        if (strcmp(name, reader_states[i].szReader) == 0) {
+            return &reader_states[i];
+        }
+    }
+    return NULL;
+}
+
+/*
+ * find a card slot that has been cleared out
+ */
+static SCARD_READERSTATE_A *
+passthru_get_blank_reader(SCARD_READERSTATE_A *reader_states, int reader_count)
+{
+    return passthru_get_reader_state(reader_states,  reader_count, NULL);
+}
+
+
+/*
+ *  This is the main work of the emulator, handling the thread that looks for
+ *  changes in the readers and the cards.
+ */
+static void *
+passthru_emul_event_thread(void *args)
+{
+    char *reader_list = NULL;
+    int reader_list_len = 0;
+    SCARD_READERSTATE_A *reader_states = NULL;
+    int reader_count = 0;     /* number of active readers */
+    int max_reader_count = 0; /* size of the reader_state array (including
+                                 inactive readers) */
+    LONG rv;
+    int timeout=1000;
+    int i;
+
+    do {
+        /* variables to hold on to our new values until we are ready to replace
+         * our old values */
+        char *new_reader_list = NULL;
+        int new_reader_list_len = 0;
+        int new_reader_count = 0;
+
+        /* other temps */
+        char * reader_entry;
+        VReader *reader;
+
+        /*
+         * First check to see if the reader list has changed
+         */
+        rv = SCardListReaders(global_context, NULL, NULL, &new_reader_list_len);
+        if (rv !=  SCARD_S_SUCCESS) {
+           goto next;
+        }
+        /*
+         * If the names have changed, we need to update our list and states.
+         * This is where we detect reader insertions and removals.
+         */
+        if (new_reader_list_len != reader_list_len) {
+            /* update the list */
+            new_reader_list = (char *)malloc(new_reader_list_len);
+            if (new_reader_list == NULL) {
+                goto next;
+            }
+            rv = SCardListReaders(global_context, NULL, new_reader_list,
+                                  &new_reader_list_len);
+            if (rv !=  SCARD_S_SUCCESS) {
+                free(new_reader_list);
+                goto next;
+            }
+            /* clear out our event state */
+            for (i=0; i < reader_count; i++) {
+                    reader_states[i].dwEventState = 0;
+            }
+            /* count the readers and mark the ones that are still with us */
+            for (reader_entry = new_reader_list; *reader_entry;
+                 reader_entry += strlen(reader_entry)+1) {
+                SCARD_READERSTATE_A *this_state;
+                new_reader_count++;
+                /* if the reader is still on the list, mark it present */
+                this_state = passthru_get_reader_state(reader_states,
+                                                       reader_count,
+                                                       reader_entry);
+                if (this_state) {
+                    this_state->dwEventState = SCARD_STATE_PRESENT;
+                }
+            }
+            /* eject any removed readers */
+            for (i=0; i < reader_count; i++) {
+                if (reader_states[i].dwEventState == SCARD_STATE_PRESENT) {
+                    reader_states[i].dwEventState = 0;
+                    continue;
+                }
+                reader = vreader_get_reader_by_name(reader_states[i].szReader);
+                vreader_remove_reader(reader);
+                vreader_free(reader);
+                reader_states[i].szReader = NULL;
+            }
+            /* handle the shrinking list */
+            if (new_reader_count < reader_count) {
+                /* fold all the valid entries at the end of our reader_states
+                 * array up into those locations vacated by ejected readers. */
+                for (i=reader_count-1; i < (new_reader_count -1); i--) {
+                        if (reader_states[i].szReader) {
+                            SCARD_READERSTATE_A *blank_reader;
+                            blank_reader =
+                                passthru_get_blank_reader(reader_states,
+                                                          new_reader_count);
+                            assert(blank_reader);
+                            *blank_reader = reader_states[i];
+                            reader_states[i].szReader = NULL;
+                        }
+                 }
+            }
+            /* handle the growing list */
+            if (new_reader_count >  max_reader_count) {
+                SCARD_READERSTATE_A *new_reader_states;
+
+                /* grow the list */
+                new_reader_states =
+                    (SCARD_READERSTATE_A *)realloc(reader_states,
+                        sizeof(SCARD_READERSTATE_A)*new_reader_count);
+                if (new_reader_states) {
+                    /* successful, update our current state */
+                    reader_states = new_reader_states;
+                    max_reader_count = new_reader_count;
+                } else {
+                    new_reader_count = max_reader_count; /* couldn't get enough
+                                                          * space to handle
+                                                          * all the new readers
+                                                          * */
+                }
+                /* mark our new entries as empty */
+                for (i=reader_count; i > new_reader_count; i++) {
+                    reader_states[i].szReader = NULL;
+                }
+            }
+            /* now walk the reader list, updating the state */
+            for (reader_entry = new_reader_list; *reader_entry;
+                 reader_entry += strlen(reader_entry)+1) {
+                SCARD_READERSTATE_A *this_state;
+                this_state = passthru_get_reader_state(reader_states,
+                                                       new_reader_count,
+                                                       reader_entry);
+                if (this_state) {
+                    /* replace the old copy of the string with the new copy.
+                     * This will allow us to free reader_list at the end */
+                    reader_states->szReader = reader_entry;
+                    continue;
+                }
+                /* this is a new reader, add it to the list */
+                this_state =
+                    passthru_get_blank_reader(reader_states, new_reader_count);
+                if (!this_state) {
+                    continue; /* this can happen of we couldn't get enough
+                                 slots in the grow list */
+                }
+                this_state->szReader = reader_entry;
+                this_state->dwCurrentState = SCARD_STATE_UNAWARE;
+                reader = vreader_new(reader_entry, NULL, NULL);
+                if (reader) {
+                    vreader_add_reader(reader);
+                }
+                vreader_free(reader);
+            }
+            /* finally update our current variables */
+            free(reader_list);
+            reader_list = new_reader_list;
+            reader_list_len = new_reader_list_len;
+            reader_count = new_reader_count;
+        }
+next:
+        rv = SCardGetStatusChange(global_context, timeout,
+                                  reader_states, reader_count);
+        if (rv == SCARD_E_TIMEOUT) {
+            continue; /* check for new readers */
+        }
+        if (rv != SCARD_S_SUCCESS) {
+            static int restarts = 0;
+            VCardStatus status;
+
+            /* try resetting the pcsc_lite subsystem */
+            SCardReleaseContext(global_context);
+            global_context = 0; /* should close it */
+            printf("***** SCard failure %x\n", rv);
+            restarts++;
+            if (restarts >= 3) {
+                printf("***** SCard failed %d times\n", restarts);
+                return; /* exit thread */
+            }
+            status = passthru_pcsc_lite_init();
+            assert(status == CARD_DONE);
+            sleep(1);
+            continue;
+        }
+        /* deal with card insertion/removal */
+        for (i=0; i < reader_count ; i++) {
+            if ((reader_states[i].dwEventState & SCARD_STATE_CHANGED) == 0) {
+                continue;
+            }
+            reader_states[i].dwCurrentState = reader_states[i].dwEventState;
+            reader = vreader_get_reader_by_name(reader_states[i].szReader);
+            if (reader == NULL) {
+                continue;
+            }
+            if (reader_states[i].dwEventState & SCARD_STATE_EMPTY) {
+                if (vreader_card_is_present(reader) == VREADER_OK) {
+                    vreader_insert_card(reader, NULL);
+                }
+            }
+            if (reader_states[i].dwEventState & SCARD_STATE_PRESENT) {
+                VCard *card;
+                VCardStatus status = VCARD_FAIL;
+                /* if there already was a card present, eject it before we
+                 * insert the new one */
+                if (vreader_card_is_present(reader) == VREADER_OK) {
+                    vreader_insert_card(reader, NULL);
+                }
+
+                card = vcard_new(NULL, NULL);
+                if (card != NULL) {
+                    status = passthru_card_init(reader, card, "",
+                                                NULL, NULL, NULL, 0);
+                    passthru_card_set_atr(card, reader_states[i].rgbAtr,
+                                  reader_states[i].cbAtr);
+                    vcard_set_atr_func(card, passthru_card_get_atr);
+                }
+                if (status == VCARD_DONE) {
+                    vreader_insert_card(reader, card);
+                }
+                vcard_free(card);
+            }
+            vreader_free(reader);
+        }
+
+     } while (1);
+     return NULL;
+}
+
+/*
+ *  Initializing the passthru emul is simply initializing pcsc-lite and
+ *  launching the event thread.
+ */
+VCardStatus
+passthru_emul_init(VCardEmulOptions *options)
+{
+    thread_t tid;
+    thread_status_t tstatus;
+    VCardStatus status;
+
+    vreader_init();
+    vevent_queue_init();
+
+    status = passthru_pcsc_lite_init();
+    if (status != VCARD_DONE) {
+        return status;
+    }
+
+    /* launch reader thread */
+    tstatus = THREAD_CREATE(tid, passthru_emul_event_thread, NULL);
+    if (!THREAD_SUCCESS(tstatus)) {
+        return VCARD_FAIL;
+    }
+    return VCARD_DONE;
+}
+
+
+VCardEmulOptions *
+passthru_emul_options(const char *args)
+{
+    return NULL;
+}
+#endif
diff --git a/libcacard/passthru.h b/libcacard/passthru.h
new file mode 100644
index 0000000..3589d12
--- /dev/null
+++ b/libcacard/passthru.h
@@ -0,0 +1,50 @@
+/*
+ * passthru card type emulator and passhtru emulator.
+ *
+ * passhtru card type emulator can be used with other low level card emulators,
+ * as long as they can recognize card insertion and removals.
+ *
+ * the passthru vcard_emulator, can only use passthru card types.
+ *
+ * Be careful using passthru. 1) passthru does not know the locking state of
+ * the card from the guest side, and thus does not try to get locks. This means
+ * client access can interfere with the guest use of the card. 2) passthru does
+ * not provide the guest and client unique login states for the card. That
+ * means that it is possible for the guest to access private data on the
+ * card without authenticating. You have been warned.
+ *
+ * Passthru is most useful in the following cases: 1) provisioning. Card type
+ *  emulators cannot emulate the open platform secure connections because the
+ *  client software does not have access to the global platform keys on the
+ *  card. Passthru drives these apdu's directly to the card. 2) odd cards. If
+ *  you have guest software the knows how to access the card, but no client
+ *  side PKCS #11 module, then passthru can provide access to those cards.
+ */
+
+#ifndef PASSTHRU_H
+#define PASSTHRU_H 1
+
+#include "vcard.h"
+#include "vcard_emul.h"
+#include "vreader.h"
+
+/*
+ * Initialize the card. This is the only 'card type emulator' portion of this
+ * the rest are connected through function pointers. NOTE: certs are ignored,
+ * keys are freed.
+ */
+VCardStatus passthru_card_init(VReader *vreader, VCard *card,
+              const char *flags, unsigned char * const *cert, int cert_len[],
+              VCardKey *key[], int cert_count);
+
+/*
+ * Use this instead of vcard_emul_init to initialize passthru.
+ * passthru is the exception to the rule that only one emul can be compiled
+ * at once. NOTE: you can still have only one emul active at once. The
+ * passhtru card type emul, however can be used with other emuls.
+ *
+ * passthru does not support other card type emuls.
+ */
+VCardStatus passthru_emul_init(VCardEmulOptions *options);
+VCardEmulOptions *passthru_emul_options(const char *args);
+#endif
diff --git a/libcacard/vcard.c b/libcacard/vcard.c
new file mode 100644
index 0000000..9672001
--- /dev/null
+++ b/libcacard/vcard.c
@@ -0,0 +1,350 @@
+/*
+ * implement the Java card standard.
+ *
+ */
+#include <stdlib.h>
+#include <string.h>
+#include "vcard.h"
+#include "vcard_emul.h"
+#include "card_7816t.h"
+
+struct VCardAppletStruct {
+    VCardApplet   *next;
+    VCardProcessAPDU process_apdu;
+    VCardResetApplet reset_applet;
+    unsigned char *aid;
+    int aid_len;
+    void *applet_private;
+    VCardAppletPrivateFree applet_private_free;
+};
+
+struct VCardStruct {
+    int reference_count;
+    VCardApplet *applet_list;
+    VCardApplet *current_applet[MAX_CHANNEL];
+    VCardBufferResponse *vcard_buffer_response;
+    VCardType type;
+    VCardEmul *vcard_private;
+    VCardEmulFree vcard_private_free;
+    VCardGetAtr vcard_get_atr;
+};
+
+VCardBufferResponse *
+vcard_buffer_response_new(unsigned char *buffer, int size)
+{
+    VCardBufferResponse *new_buffer;
+
+    new_buffer = (VCardBufferResponse *)malloc(sizeof(VCardBufferResponse));
+    if (new_buffer == NULL) {
+        return NULL;
+    }
+    new_buffer->buffer = (unsigned char *)malloc(size);
+    if (new_buffer->buffer == NULL) {
+        free(new_buffer);
+        return NULL;
+    }
+    memcpy(new_buffer->buffer, buffer, size);
+    new_buffer->buffer_len = size;
+    new_buffer->current = new_buffer->buffer;
+    new_buffer->len = size;
+    return new_buffer;
+}
+
+void
+vcard_buffer_response_delete(VCardBufferResponse *buffer_response)
+{
+    if (buffer_response == NULL) {
+        return;
+    }
+    if (buffer_response->buffer) {
+        free(buffer_response->buffer);
+    }
+    free(buffer_response);
+}
+
+
+/*
+ * clean up state after a reset
+ */
+void
+vcard_reset(VCard *card, VCardPower power)
+{
+    int i;
+    VCardApplet *applet = NULL;
+
+    if (card->type ==  VCARD_DIRECT) {
+       /* select the last applet */
+        VCardApplet *current_applet = NULL;
+        for (current_applet = card->applet_list; current_applet;
+                                       current_applet = current_applet->next) {
+           applet = current_applet;
+       }
+    }
+    for (i=0; i < MAX_CHANNEL; i++) {
+        card->current_applet[i] = applet;
+    }
+    if (card->vcard_buffer_response) {
+        vcard_buffer_response_delete(card->vcard_buffer_response);
+        card->vcard_buffer_response = NULL;
+    }
+    vcard_emul_reset(card, power);
+    if (applet) {
+        applet->reset_applet(card, 0);
+    }
+}
+
+/* applet utilities */
+
+/*
+ * applet utilities
+ */
+/* constructor */
+VCardApplet *
+vcard_new_applet(VCardProcessAPDU applet_process_function,
+                 VCardResetApplet applet_reset_function,
+                 unsigned char *aid, int aid_len)
+{
+    VCardApplet *applet;
+
+    applet = (VCardApplet *)malloc(sizeof(VCardApplet));
+    if (applet == NULL) {
+        return NULL;
+    }
+    applet->next = NULL;
+    applet->applet_private = NULL;
+    applet->applet_private_free = NULL;
+    applet->process_apdu = applet_process_function;
+    applet->reset_applet = applet_reset_function;
+
+    applet->aid = malloc(aid_len);
+    if (applet->aid == NULL) {
+        free(applet);
+        return NULL;
+    }
+    memcpy(applet->aid, aid, aid_len);
+    applet->aid_len = aid_len;
+    return applet;
+}
+
+/* destructor */
+void
+vcard_delete_applet(VCardApplet *applet)
+{
+    if (applet == NULL) {
+        return;
+    }
+    if (applet->applet_private_free) {
+        applet->applet_private_free(applet->applet_private);
+        applet->applet_private = NULL;
+    }
+    if (applet->aid) {
+        free(applet->aid);
+        applet->aid = NULL;
+    }
+    free(applet);
+}
+
+/* accessor */
+void
+vcard_set_applet_private(VCardApplet *applet, VCardAppletPrivate *private,
+                         VCardAppletPrivateFree private_free)
+{
+    if (applet->applet_private_free) {
+        applet->applet_private_free(applet->applet_private);
+    }
+    applet->applet_private = private;
+    applet->applet_private_free = private_free;
+}
+
+VCard *
+vcard_new(VCardEmul *private, VCardEmulFree private_free)
+{
+    VCard *new_card;
+    int i;
+
+    new_card = (VCard *)malloc(sizeof(VCard));
+    new_card->applet_list = NULL;
+    for (i=0; i < MAX_CHANNEL; i++) {
+        new_card->current_applet[i] = NULL;
+    }
+    new_card->vcard_buffer_response = NULL;
+    new_card->type = VCARD_VM;
+    new_card->vcard_private = private;
+    new_card->vcard_private_free = private_free;
+    new_card->vcard_get_atr = NULL;
+    new_card->reference_count = 1;
+    return new_card;
+}
+
+VCard *
+vcard_reference(VCard *vcard)
+{
+    if (vcard == NULL) {
+        return NULL;
+    }
+    vcard->reference_count++;
+    return vcard;
+}
+
+void
+vcard_free(VCard *vcard)
+{
+    VCardApplet *current_applet = NULL;
+    VCardApplet *next_applet = NULL;
+
+    if (vcard == NULL) {
+        return;
+    }
+    vcard->reference_count--;
+    if (vcard->reference_count != 0) {
+        return;
+    }
+    if (vcard->vcard_private_free) {
+        (*vcard->vcard_private_free)(vcard->vcard_private);
+        vcard->vcard_private_free = 0;
+        vcard->vcard_private = 0;
+    }
+    for (current_applet = vcard->applet_list; current_applet;
+                                        current_applet = next_applet) {
+        next_applet = current_applet->next;
+        vcard_delete_applet(current_applet);
+    }
+    vcard_buffer_response_delete(vcard->vcard_buffer_response);
+    free(vcard);
+    return;
+}
+
+void
+vcard_get_atr(VCard *vcard, unsigned char *atr, int *atr_len)
+{
+    if (vcard->vcard_get_atr) {
+       (*vcard->vcard_get_atr)(vcard, atr, atr_len);
+       return;
+    }
+    vcard_emul_get_atr(vcard, atr, atr_len);
+}
+
+void
+vcard_set_atr_func(VCard *card, VCardGetAtr vcard_get_atr)
+{
+    card-> vcard_get_atr = vcard_get_atr;
+}
+
+
+VCardStatus
+vcard_add_applet(VCard *card, VCardApplet *applet)
+{
+    applet->next = card->applet_list;
+    card->applet_list = applet;
+    /* if our card-type is direct, always call the applet */
+    if (card->type ==  VCARD_DIRECT) {
+        int i;
+
+        for (i=0; i < MAX_CHANNEL; i++) {
+            card->current_applet[i] = applet;
+        }
+    }
+    return VCARD_DONE;
+}
+
+/*
+ * manage applets
+ */
+VCardApplet *
+vcard_find_applet(VCard *card, unsigned char *aid, int aid_len)
+{
+    VCardApplet *current_applet;
+
+    for (current_applet = card->applet_list; current_applet;
+                                        current_applet = current_applet->next) {
+        if (current_applet->aid_len != aid_len) {
+            continue;
+        }
+        if (memcmp(current_applet->aid, aid, aid_len) == 0) {
+            break;
+        }
+    }
+    return current_applet;
+}
+
+unsigned char *
+vcard_applet_get_aid(VCardApplet *applet, int *aid_len)
+{
+    if (applet == NULL) {
+        return NULL;
+    }
+    *aid_len = applet->aid_len;
+    return applet->aid;
+}
+
+
+void
+vcard_select_applet(VCard *card, int channel, VCardApplet *applet)
+{
+    ASSERT(channel < MAX_CHANNEL);
+    card->current_applet[channel] = applet;
+    /* reset the applet */
+    if (applet && applet->reset_applet) {
+        applet->reset_applet(card, channel);
+    }
+}
+
+VCardAppletPrivate *
+vcard_get_current_applet_private(VCard *card, int channel)
+{
+    VCardApplet *applet = card->current_applet[channel];
+
+    if (applet == NULL) {
+        return NULL;
+    }
+    return applet->applet_private;
+}
+
+VCardStatus
+vcard_process_applet_apdu(VCard *card, VCardAPDU *apdu,
+                          VCardResponse **response)
+{
+    if (card->current_applet[apdu->a_channel]) {
+        return card->current_applet[apdu->a_channel]->process_apdu(
+                                                        card, apdu, response);
+    }
+    return VCARD_NEXT;
+}
+
+/*
+ * Accessor functions
+ */
+/* accessor functions for the response buffer */
+VCardBufferResponse *
+vcard_get_buffer_response(VCard *card)
+{
+    return card->vcard_buffer_response;
+}
+
+void
+vcard_set_buffer_response(VCard *card, VCardBufferResponse *buffer)
+{
+    card->vcard_buffer_response = buffer;
+}
+
+
+/* accessor functions for the type */
+VCardType
+vcard_get_type(VCard *card)
+{
+    return card->type;
+}
+
+void
+vcard_set_type(VCard *card, VCardType type)
+{
+    card->type = type;
+}
+
+/* accessor for private data */
+VCardEmul *
+vcard_get_private(VCard *vcard)
+{
+    return vcard->vcard_private;
+}
+
diff --git a/libcacard/vcard.h b/libcacard/vcard.h
new file mode 100644
index 0000000..8dbd761
--- /dev/null
+++ b/libcacard/vcard.h
@@ -0,0 +1,85 @@
+/*
+ *
+ */
+#ifndef VCARD_H
+#define VCARD_H 1
+
+#include "vcardt.h"
+
+/*
+ * response buffer constructors and destructors.
+ *
+ * response buffers are used when we need to return more data than will fit in
+ * a normal APDU response (nominally 254 bytes).
+ */
+VCardBufferResponse *vcard_buffer_response_new(unsigned char *buffer, int size);
+void vcard_buffer_response_delete(VCardBufferResponse *buffer_response);
+
+
+/*
+ * clean up state on reset
+ */
+void vcard_reset(VCard *card, VCardPower power);
+
+/*
+ * applet utilities
+ */
+/*
+ * Constructor for a VCardApplet
+ */
+VCardApplet *vcard_new_applet(VCardProcessAPDU applet_process_function,
+                              VCardResetApplet applet_reset_function,
+                              unsigned char *aid, int aid_len);
+
+/*
+ * destructor for a VCardApplet
+ *  Can be called with a NULL applet
+ */
+void vcard_delete_applet(VCardApplet *applet);
+
+/* accessor - set the card type specific private data */
+void vcard_set_applet_private(VCardApplet *applet, VCardAppletPrivate *_private,
+                              VCardAppletPrivateFree private_free);
+
+/* set type of vcard */
+void vcard_set_type(VCard *card, VCardType type);
+
+/*
+ * utilities interacting with the current applet
+ */
+/* add a new applet to a card */
+VCardStatus vcard_add_applet(VCard *card, VCardApplet *applet);
+/* find the applet on the card with the given aid */
+VCardApplet *vcard_find_applet(VCard *card, unsigned char *aid, int aid_len);
+/* set the following applet to be current on the given channel */
+void vcard_select_applet(VCard *card, int channel, VCardApplet *applet);
+/* get the card type specific private data on the given channel */
+VCardAppletPrivate * vcard_get_current_applet_private(VCard *card, int channel);
+/* fetch the applet's id */
+unsigned char *vcard_applet_get_aid(VCardApplet *applet, int *aid_len);
+
+/* process the apdu for the current selected applet/file */
+VCardStatus vcard_process_applet_apdu(VCard *card, VCardAPDU *apdu,
+                                      VCardResponse **response);
+/*
+ * VCard utilities
+ */
+/* constructor */
+VCard * vcard_new(VCardEmul *_private, VCardEmulFree private_free);
+/* get a reference */
+VCard * vcard_reference(VCard *);
+/* destructor (reference counted) */
+void vcard_free(VCard *);
+/* get the atr from the card */
+void vcard_get_atr(VCard *card, unsigned char *atr, int *atr_len);
+void vcard_set_atr_func(VCard *card, VCardGetAtr vcard_get_atr);
+
+/* accessor functions for the response buffer */
+VCardBufferResponse *vcard_get_buffer_response(VCard *card);
+void vcard_set_buffer_response(VCard *card, VCardBufferResponse *buffer);
+/* accessor functions for the type */
+VCardType vcard_get_type(VCard *card);
+/* get the private data */
+VCardEmul *vcard_get_private(VCard *card);
+
+#endif
diff --git a/libcacard/vcard_emul.h b/libcacard/vcard_emul.h
new file mode 100644
index 0000000..5df01a8
--- /dev/null
+++ b/libcacard/vcard_emul.h
@@ -0,0 +1,62 @@
+/*
+ * This is the actual card emulator.
+ *
+ * These functions can be implemented in different ways on different platforms
+ * using the underlying system primitives. For Linux it uses NSS, though direct
+ * to PKCS #11, openssl+pkcs11, or even gnu crypto libraries+pkcs #11 could be
+ * used. On Windows CAPI could be used.
+ */
+
+#ifndef VCARD_EMUL_H
+#define VCARD_EMUL_H 1
+
+#include "card_7816t.h"
+#include "vcard.h"
+#include "vcard_emul_type.h"
+
+/*
+ * types
+ */
+typedef enum {
+    VCARD_EMUL_OK =0,
+    VCARD_EMUL_FAIL,
+    // return values by vcard_emul_init
+    VCARD_EMUL_INIT_ALREADY_INITED,
+} VCardEmulError;
+
+/* options are emul specific. call card_emul_parse_args to change a string
+ * To an options struct */
+typedef struct VCardEmulOptionsStruct VCardEmulOptions;
+
+/*
+ * Login functions
+ */
+/* return the number of login attempts still possible on the card. if unknown,
+ * return -1 */
+int vcard_emul_get_login_count(VCard *card);
+/* login into the card, return the 7816 status word (sw2 || sw1) */
+vcard_7816_status_t vcard_emul_login(VCard *card, unsigned char *pin,
+                                     int pin_len);
+
+/*
+ * key functions
+ */
+/* delete a key */
+void vcard_emul_delete_key(VCardKey *key);
+/* RSA sign/decrypt with the key, signature happens 'in place' */
+vcard_7816_status_t vcard_emul_rsa_op(VCard *card, VCardKey *key,
+                                  unsigned char *buffer, int buffer_size);
+
+void vcard_emul_reset(VCard *card, VCardPower power);
+void vcard_emul_get_atr(VCard *card, unsigned char *atr, int *atr_len);
+
+/* Re-insert of a card that has been removed by force removal */
+VCardEmulError vcard_emul_force_card_insert(VReader *vreader);
+/* Force a card removal even if the card is not physically removed */
+VCardEmulError vcard_emul_force_card_remove(VReader *vreader);
+
+VCardEmulOptions *vcard_emul_options(const char *args);
+VCardEmulError vcard_emul_init(const VCardEmulOptions *options);
+void vcard_emul_replay_insertion_events(void);
+void vcard_emul_usage(void);
+#endif
diff --git a/libcacard/vcard_emul_nss.c b/libcacard/vcard_emul_nss.c
new file mode 100644
index 0000000..33404c2
--- /dev/null
+++ b/libcacard/vcard_emul_nss.c
@@ -0,0 +1,1171 @@
+/*
+ * This is the actual card emulator.
+ *
+ * These functions can be implemented in different ways on different platforms
+ * using the underlying system primitives. For Linux it uses NSS, though direct
+ * to PKCS #11, openssl+pkcs11, or even gnu crypto libraries+pkcs #11 could be
+ * used. On Windows CAPI could be used.
+ */
+#include "vcard.h"
+#include "card_7816t.h"
+#include "vcard_emul.h"
+#include "vreader.h"
+#include "vevent.h"
+
+/*
+ * NSS headers
+ */
+#include <nss.h>
+#include <pk11pub.h>
+#include <cert.h>
+#include <key.h>
+#include <secmod.h>
+#include <prthread.h>
+#include <secerr.h>
+
+/*
+ * system headers
+ */
+#include <stdlib.h>
+#include <string.h>
+
+
+struct VCardKeyStruct {
+    CERTCertificate *cert;
+    PK11SlotInfo *slot;
+    SECKEYPrivateKey *key;
+};
+
+
+typedef struct VirtualReaderOptionsStruct VirtualReaderOptions;
+
+struct VReaderEmulStruct {
+    PK11SlotInfo *slot;
+    VCardEmulType default_type;
+    char *type_params;
+    PRBool present;
+    int     series;
+    VCard *saved_vcard;
+};
+
+/*
+ *  NSS Specific options
+ */
+struct VirtualReaderOptionsStruct {
+    char *name;
+    char *vname;
+    VCardEmulType card_type;
+    char *type_params;
+    char **cert_name;
+    int cert_count;
+};
+
+struct VCardEmulOptionsStruct {
+    void *nss_db;
+    VirtualReaderOptions *vreader;
+    int vreader_count;
+    VCardEmulType hw_card_type;
+    const char *hw_type_params;
+    PRBool use_hw;
+};
+
+static int nss_emul_init = 0;
+
+/* if we have more that just the slot, define
+ * VCardEmulStruct here */
+
+/*
+ * allocate the set of arrays for certs, cert_len, key
+ */
+static PRBool
+vcard_emul_alloc_arrays(unsigned char ***certsp, int **cert_lenp,
+                        VCardKey ***keysp, int cert_count)
+{
+    *certsp = NULL;
+    *cert_lenp = NULL;
+    *keysp = NULL;
+    *certsp = (unsigned char **)malloc(sizeof(unsigned char *)*cert_count);
+    if (*certsp == NULL) {
+        return PR_FALSE;
+    }
+    *cert_lenp = (int *)malloc(sizeof(int)*cert_count);
+    if (*cert_lenp == NULL) {
+        free(*certsp);
+        *certsp = NULL;
+        return PR_FALSE;
+    }
+    *keysp = (VCardKey **)malloc(sizeof(VCardKey *)*cert_count);
+    if (*keysp != NULL) {
+        return PR_TRUE;
+    }
+    free(*cert_lenp);
+    free(*certsp);
+    *cert_lenp = NULL;
+    *certsp = NULL;
+    return PR_FALSE;
+}
+
+/*
+ * Emulator specific card information
+ */
+typedef struct CardEmulCardStruct CardEmulPrivate;
+
+static VCardEmul *
+vcard_emul_new_card(PK11SlotInfo *slot)
+{
+    PK11_ReferenceSlot(slot);
+    /* currently we don't need anything other than the slot */
+    return (VCardEmul *)slot;
+}
+
+static void
+vcard_emul_delete_card(VCardEmul *vcard_emul)
+{
+    PK11SlotInfo *slot = (PK11SlotInfo *)vcard_emul;
+    if (slot == NULL) {
+        return;
+    }
+    PK11_FreeSlot(slot);
+}
+
+static PK11SlotInfo *
+vcard_emul_card_get_slot(VCard *card)
+{
+    /* note, the card is holding the reference, no need to get another one */
+    return (PK11SlotInfo *)vcard_get_private(card);
+}
+
+
+/*
+ * key functions
+ */
+/* private constructure */
+static VCardKey *
+vcard_emul_make_key(PK11SlotInfo *slot, CERTCertificate *cert)
+{
+    VCardKey * key;
+
+    key = (VCardKey *)malloc(sizeof(VCardKey));
+    if (key == NULL) {
+        return NULL;
+    }
+    key->slot = PK11_ReferenceSlot(slot);
+    key->cert = CERT_DupCertificate(cert);
+    /* NOTE: if we aren't logged into the token, this could return NULL */
+    /* NOTE: the cert is a temp cert, not necessarily the cert in the token,
+     * use the DER version of this function */
+    key->key = PK11_FindKeyByDERCert(slot,cert, NULL);
+    return key;
+}
+
+/* destructor */
+void
+vcard_emul_delete_key(VCardKey *key)
+{
+    if (!nss_emul_init || (key == NULL)) {
+        return;
+    }
+    if (key->key) {
+        SECKEY_DestroyPrivateKey(key->key);
+        key->key = NULL;
+    }
+    if (key->cert) {
+        CERT_DestroyCertificate(key->cert);
+    }
+    if (key->slot) {
+        PK11_FreeSlot(key->slot);
+    }
+    return;
+}
+
+/*
+ * grab the nss key from a VCardKey. If it doesn't exist, try to look it up
+ */
+static SECKEYPrivateKey *
+vcard_emul_get_nss_key(VCardKey *key)
+{
+    if (key->key) {
+        return key->key;
+    }
+    /* NOTE: if we aren't logged into the token, this could return NULL */
+    key->key = PK11_FindPrivateKeyFromCert(key->slot,key->cert, NULL);
+    return key->key;
+}
+
+/*
+ * Map NSS errors to 7816 errors
+ */
+static vcard_7816_status_t
+vcard_emul_map_error(int error)
+{
+    switch (error) {
+    case SEC_ERROR_TOKEN_NOT_LOGGED_IN:
+        return VCARD7816_STATUS_ERROR_CONDITION_NOT_SATISFIED;
+    case SEC_ERROR_BAD_DATA:
+    case SEC_ERROR_OUTPUT_LEN:
+    case SEC_ERROR_INPUT_LEN:
+    case SEC_ERROR_INVALID_ARGS:
+    case SEC_ERROR_INVALID_ALGORITHM:
+    case SEC_ERROR_NO_KEY:
+    case SEC_ERROR_INVALID_KEY:
+    case SEC_ERROR_DECRYPTION_DISALLOWED:
+        return VCARD7816_STATUS_ERROR_DATA_INVALID;
+    case SEC_ERROR_NO_MEMORY:
+        return VCARD7816_STATUS_EXC_ERROR_MEMORY_FAILURE;
+    }
+    return VCARD7816_STATUS_EXC_ERROR_CHANGE;
+}
+
+/* RSA sign/decrypt with the key, signature happens 'in place' */
+vcard_7816_status_t
+vcard_emul_rsa_op(VCard *card, VCardKey *key,
+                  unsigned char *buffer, int buffer_size)
+{
+    SECKEYPrivateKey *priv_key;
+    unsigned signature_len;
+    SECStatus rv;
+
+    if ((!nss_emul_init) || (key == NULL)) {
+        /* couldn't get the key, indicate that we aren't logged in */
+        return VCARD7816_STATUS_ERROR_CONDITION_NOT_SATISFIED;
+    }
+    priv_key = vcard_emul_get_nss_key(key);
+
+    /*
+     * this is only true of the rsa signature
+     */
+    signature_len = PK11_SignatureLen(priv_key);
+    if (buffer_size != signature_len) {
+        return  VCARD7816_STATUS_ERROR_DATA_INVALID;
+    }
+    rv = PK11_PrivDecryptRaw(priv_key, buffer, &signature_len, signature_len,
+                             buffer, buffer_size);
+    if (rv != SECSuccess) {
+        return vcard_emul_map_error(PORT_GetError());
+    }
+    ASSERT(buffer_size == signature_len);
+    return VCARD7816_STATUS_SUCCESS;
+}
+
+/*
+ * Login functions
+ */
+/* return the number of login attempts still possible on the card. if unknown,
+ * return -1 */
+int
+vcard_emul_get_login_count(VCard *card)
+{
+    return -1;
+}
+
+/* login into the card, return the 7816 status word (sw2 || sw1) */
+vcard_7816_status_t
+vcard_emul_login(VCard *card, unsigned char *pin, int pin_len)
+{
+    PK11SlotInfo *slot;
+    unsigned char *pin_string = NULL;
+    int i;
+    SECStatus rv;
+
+    if (!nss_emul_init) {
+        return VCARD7816_STATUS_ERROR_CONDITION_NOT_SATISFIED;
+    }
+    slot = vcard_emul_card_get_slot(card);
+     /* We depend on the PKCS #11 module internal login state here because we
+      * create a separate process to handle each guest instance. If we needed
+      * to handle multiple guests from one process, then we would need to keep
+      * a lot of extra state in our card structure
+      * */
+    pin_string = malloc(pin_len+1);
+    if (pin_string == NULL) {
+        return VCARD7816_STATUS_EXC_ERROR_MEMORY_FAILURE;
+    }
+    memcpy(pin_string,pin,pin_len);
+    pin_string[pin_len] = 0;
+
+    /* handle CAC expanded pins correctly */
+    for (i = pin_len-1; i >= 0 && (pin_string[i] == 0xff); i--) {
+        pin_string[i] = 0;
+    }
+
+    rv = PK11_Authenticate(slot, PR_FALSE, pin_string);
+    memset(pin_string, 0, pin_len);  /* don't let the pin hang around in memory
+                                        to be snooped */
+    free(pin_string);
+    if (rv == SECSuccess) {
+        return VCARD7816_STATUS_SUCCESS;
+    }
+    /* map the error from port get error */
+    return VCARD7816_STATUS_ERROR_CONDITION_NOT_SATISFIED;
+}
+
+void
+vcard_emul_reset(VCard *card, VCardPower power)
+{
+    PK11SlotInfo *slot;
+
+    if (!nss_emul_init) {
+        return;
+    }
+
+    /* if we reset the card (either power on or power off), we loose our login
+     * state */
+    /* TODO: we may also need to send insertion/removal events? */
+    slot = vcard_emul_card_get_slot(card);
+    (void)PK11_Logout(slot);
+    return;
+}
+
+
+static VReader *
+vcard_emul_find_vreader_from_slot(PK11SlotInfo *slot)
+{
+    VReaderList *reader_list = vreader_get_reader_list();
+    VReaderListEntry *current_entry = NULL;
+
+    if (reader_list == NULL) {
+        return NULL;
+    }
+    for (current_entry= vreader_list_get_first(reader_list); current_entry;
+                        current_entry=vreader_list_get_next(current_entry)) {
+        VReader *reader = vreader_list_get_reader(current_entry);
+        VReaderEmul *reader_emul = vreader_get_private(reader);
+        if (reader_emul->slot == slot) {
+            return reader;
+        }
+        vreader_free(reader);
+    }
+
+    return NULL;
+}
+
+/*
+ * create a new reader emul
+ */
+static VReaderEmul *
+vreader_emul_new(PK11SlotInfo *slot, VCardEmulType type, const char *params)
+{
+    VReaderEmul *new_reader_emul;
+
+    new_reader_emul = (VReaderEmul *)malloc(sizeof(VReaderEmul));
+    if (new_reader_emul == NULL) {
+        return NULL;
+    }
+
+    new_reader_emul->slot = PK11_ReferenceSlot(slot);
+    new_reader_emul->default_type = type;
+    new_reader_emul->type_params = strdup(params);
+    new_reader_emul->present = PR_FALSE;
+    new_reader_emul->series = 0;
+    new_reader_emul->saved_vcard = NULL;
+    return new_reader_emul;
+}
+
+static void
+vreader_emul_delete(VReaderEmul *vreader_emul)
+{
+    if (vreader_emul == NULL) {
+        return;
+    }
+    if (vreader_emul->slot) {
+        PK11_FreeSlot(vreader_emul->slot);
+    }
+    if (vreader_emul->type_params) {
+        free(vreader_emul->type_params);
+    }
+    free(vreader_emul);
+}
+
+/*
+ *  TODO: move this to emulater non-specific file
+ */
+static VCardEmulType
+vcard_emul_get_type(VReader *vreader)
+{
+    VReaderEmul *vreader_emul;
+
+    vreader_emul = vreader_get_private(vreader);
+    if (vreader_emul && vreader_emul->default_type != VCARD_EMUL_NONE) {
+        return vreader_emul->default_type;
+    }
+
+    return vcard_emul_type_select(vreader);
+}
+/*
+ *  TODO: move this to emulater non-specific file
+ */
+static const char *
+vcard_emul_get_type_params(VReader *vreader)
+{
+    VReaderEmul *vreader_emul;
+
+    vreader_emul = vreader_get_private(vreader);
+    if (vreader_emul && vreader_emul->type_params) {
+        return vreader_emul->type_params;
+    }
+
+    return "";
+}
+
+/* pull the slot out of the reader private data */
+static PK11SlotInfo *
+vcard_emul_reader_get_slot(VReader *vreader)
+{
+    VReaderEmul *vreader_emul = vreader_get_private(vreader);
+    if (vreader_emul == NULL) {
+        return NULL;
+    }
+    return vreader_emul->slot;
+}
+
+/*
+ *  Card ATR's map to physical cards. VCARD_ATR_PREFIX will set appropriate
+ *  historical bytes for any software emulated card. The remaining bytes can be
+ *  used to indicate the actual emulator
+ */
+static const unsigned char nss_atr[] = { VCARD_ATR_PREFIX(3), 'N', 'S', 'S' };
+
+void
+vcard_emul_get_atr(VCard *card, unsigned char *atr, int *atr_len)
+{
+    int len = MIN(sizeof(nss_atr), *atr_len);
+    ASSERT(atr != NULL);
+
+    memcpy(atr, nss_atr, len);
+    *atr_len = len;
+    return;
+}
+
+/*
+ * create a new card from certs and keys
+ */
+static VCard *
+vcard_emul_make_card(VReader *reader,
+                     unsigned char * const *certs, int *cert_len,
+                     VCardKey *keys[], int cert_count)
+{
+    VCardEmul *vcard_emul;
+    VCard *vcard;
+    PK11SlotInfo *slot;
+    VCardEmulType type;
+    const char *params;
+
+    type = vcard_emul_get_type(reader);
+
+    /* ignore the inserted card */
+    if (type == VCARD_EMUL_NONE) {
+        return NULL;
+    }
+    slot = vcard_emul_reader_get_slot(reader);
+    if (slot == NULL) {
+        return NULL;
+    }
+
+    params = vcard_emul_get_type_params(reader);
+    /* params these can be NULL */
+
+    vcard_emul = vcard_emul_new_card(slot);
+    if (vcard_emul == NULL) {
+        return NULL;
+    }
+    vcard = vcard_new(vcard_emul, vcard_emul_delete_card);
+    if (vcard == NULL) {
+        vcard_emul_delete_card(vcard_emul);
+        return NULL;
+    }
+    vcard_init(reader, vcard, type, params, certs, cert_len, keys, cert_count);
+    return vcard;
+}
+
+
+/*
+ * 'clone' a physical card as a virtual card
+ */
+static VCard *
+vcard_emul_mirror_card(VReader *vreader)
+{
+    /*
+     * lookup certs using the C_FindObjects. The Stan Cert handle won't give
+     * us the real certs until we log in.
+     */
+    PK11GenericObject *firstObj, *thisObj;
+    int cert_count;
+    unsigned char **certs;
+    int *cert_len;
+    VCardKey **keys;
+    PK11SlotInfo *slot;
+    PRBool ret;
+
+    slot = vcard_emul_reader_get_slot(vreader);
+    if (slot == NULL) {
+        return NULL;
+    }
+
+    firstObj = PK11_FindGenericObjects(slot, CKO_CERTIFICATE);
+    if (firstObj == NULL) {
+        return NULL;
+    }
+
+    /* count the certs */
+    cert_count=0;
+    for (thisObj = firstObj; thisObj;
+                             thisObj = PK11_GetNextGenericObject(thisObj)) {
+        cert_count++;
+    }
+
+    if (cert_count == 0) {
+        PK11_DestroyGenericObjects(firstObj);
+        return NULL;
+    }
+
+    /* allocate the arrays */
+    ret = vcard_emul_alloc_arrays(&certs,&cert_len, &keys, cert_count);
+    if (ret == PR_FALSE) {
+        return NULL;
+    }
+
+    /* fill in the arrays */
+    cert_count = 0;
+    for (thisObj = firstObj; thisObj;
+                             thisObj = PK11_GetNextGenericObject(thisObj)) {
+        SECItem derCert;
+        CERTCertificate *cert;
+        SECStatus rv;
+
+        rv = PK11_ReadRawAttribute(PK11_TypeGeneric, thisObj,
+                                   CKA_VALUE, &derCert);
+        if (rv != SECSuccess) {
+            continue;
+        }
+        /* create floating temp cert. This gives us a cert structure even if
+         * the token isn't logged in */
+        cert = CERT_NewTempCertificate(CERT_GetDefaultCertDB(), &derCert,
+                                       NULL, PR_FALSE, PR_TRUE);
+        SECITEM_FreeItem(&derCert, PR_FALSE);
+        if (cert == NULL) {
+            continue;
+        }
+
+        certs[cert_count] = cert->derCert.data;
+        cert_len[cert_count] = cert->derCert.len;
+        keys[cert_count] = vcard_emul_make_key(slot, cert);
+        cert_count++;
+        CERT_DestroyCertificate(cert); /* key obj still has a reference */
+    }
+
+    /* now create the card */
+    return vcard_emul_make_card(vreader, certs, cert_len, keys, cert_count);
+}
+
+static VCardEmulType default_card_type = VCARD_EMUL_NONE;
+static const char *default_type_params = "";
+
+/*
+ * This thread looks for card and reader insertions and puts events on the
+ * event queue
+ */
+static void
+vcard_emul_event_thread(void *arg)
+{
+    PK11SlotInfo *slot;
+    VReader *vreader;
+    VReaderEmul *vreader_emul;
+    VCard *vcard;
+    SECMODModule *module = (SECMODModule *)arg;
+
+    do {
+        slot = SECMOD_WaitForAnyTokenEvent(module, 0, 500);
+        if (slot == NULL) {
+            break;
+        }
+        vreader = vcard_emul_find_vreader_from_slot(slot);
+        if (vreader == NULL) {
+            /* new vreader */
+            vreader_emul = vreader_emul_new(slot, default_card_type,
+                                            default_type_params);
+            vreader = vreader_new(PK11_GetSlotName(slot), vreader_emul,
+                                  vreader_emul_delete);
+            PK11_FreeSlot(slot);
+            slot = NULL;
+            vreader_add_reader(vreader);
+            vreader_free(vreader);
+            continue;
+        }
+        /* card remove/insert */
+        vreader_emul = vreader_get_private(vreader);
+        if (PK11_IsPresent(slot)) {
+            int series = PK11_GetSlotSeries(slot);
+            if (series != vreader_emul->series) {
+                if (vreader_emul->present) {
+                    vreader_insert_card(vreader, NULL);
+                }
+                vcard = vcard_emul_mirror_card(vreader);
+                vreader_insert_card(vreader, vcard);
+                vcard_free(vcard);
+            }
+            vreader_emul->series = series;
+            vreader_emul->present = 1;
+            vreader_free(vreader);
+            PK11_FreeSlot(slot);
+            continue;
+        }
+        if (vreader_emul->present) {
+            vreader_insert_card(vreader, NULL);
+        }
+        vreader_emul->series = 0;
+        vreader_emul->present = 0;
+        PK11_FreeSlot(slot);
+        vreader_free(vreader);
+    } while(1);
+}
+
+/* if the card is inserted when we start up, make sure our state is correct */
+static void
+vcard_emul_init_series(VReader *vreader, VCard *vcard)
+{
+    VReaderEmul *vreader_emul = vreader_get_private(vreader);
+    PK11SlotInfo *slot = vreader_emul->slot;
+
+    vreader_emul->present = PK11_IsPresent(slot);
+    vreader_emul->series = PK11_GetSlotSeries(slot);
+    if (vreader_emul->present == 0) {
+        vreader_insert_card(vreader, NULL);
+    }
+}
+
+/*
+ * each module has a separate wait call, create a thread for each module that
+ * we are using.
+ */
+static void
+vcard_emul_new_event_thread(SECMODModule *module)
+{
+     PR_CreateThread(PR_SYSTEM_THREAD, vcard_emul_event_thread,
+                     module, PR_PRIORITY_HIGH, PR_GLOBAL_THREAD,
+                     PR_UNJOINABLE_THREAD, 0);
+}
+
+static const VCardEmulOptions default_options = {
+    .nss_db = NULL,
+    .vreader = NULL,
+    .vreader_count = 0,
+    .hw_card_type = VCARD_EMUL_CAC,
+    .hw_type_params = "",
+    .use_hw = PR_TRUE
+};
+
+
+/*
+ *  NSS needs the app to supply a password prompt. In our case the only time
+ *  the password is supplied is as part of the Login APDU. The actual password
+ *  is passed in the pw_arg in that case. In all other cases pw_arg should be
+ *  NULL.
+ */
+static char *
+vcard_emul_get_password(PK11SlotInfo *slot, PRBool retries, void *pw_arg)
+{
+    /* if it didn't work the first time, don't keep trying */
+    if (retries) {
+        return NULL;
+    }
+    /* we are looking up a password when we don't have one in hand */
+    if (pw_arg == NULL) {
+        return NULL;
+    }
+    /* TODO: we really should verify that were are using the right slot */
+    return PORT_Strdup(pw_arg);
+}
+
+/* Force a card removal even if the card is not physically removed */
+VCardEmulError
+vcard_emul_force_card_remove(VReader *vreader)
+{
+    if (!nss_emul_init || (vreader_card_is_present(vreader) != VREADER_OK)) {
+        return VCARD_EMUL_FAIL; /* card is already removed */
+    }
+
+    /* OK, remove it */
+    vreader_insert_card(vreader, NULL);
+    return VCARD_EMUL_OK;
+}
+
+/* Re-insert of a card that has been removed by force removal */
+VCardEmulError
+vcard_emul_force_card_insert(VReader *vreader)
+{
+    VReaderEmul *vreader_emul;
+    VCard *vcard;
+
+    if (!nss_emul_init || (vreader_card_is_present(vreader) == VREADER_OK)) {
+        return VCARD_EMUL_FAIL; /* card is already removed */
+    }
+    vreader_emul = vreader_get_private(vreader);
+
+    /* if it's a softcard, get the saved vcard from the reader emul structure */
+    if (vreader_emul->saved_vcard) {
+        vcard = vcard_reference(vreader_emul->saved_vcard);
+    } else {
+        /* it must be a physical card, rebuild it */
+        if (!PK11_IsPresent(vreader_emul->slot)) {
+            /* physical card has been removed, not way to reinsert it */
+            return VCARD_EMUL_FAIL;
+        }
+        vcard = vcard_emul_mirror_card(vreader);
+    }
+    vreader_insert_card(vreader, vcard);
+    vcard_free(vcard);
+
+    return VCARD_EMUL_OK;
+}
+
+/* Previously we returned FAIL if no readers found. This makes
+ * no sense when using hardware, since there may be no readers connected
+ * at the time vcard_emul_init is called, but they will be properly
+ * recognized later. So Instead return FAIL only if no_hw==1 and no
+ * vcards can be created (indicates error with certificates provided
+ * or db), or if any other higher level error (NSS error, missing coolkey). */
+static int vcard_emul_init_called = 0;
+
+VCardEmulError
+vcard_emul_init(const VCardEmulOptions *options)
+{
+    SECStatus rv;
+    PRBool ret, has_readers=PR_FALSE, need_module;
+    VReader *vreader;
+    VReaderEmul *vreader_emul;
+    SECMODListLock *module_lock;
+    SECMODModuleList *module_list;
+    SECMODModuleList *mlp;
+    int i;
+
+    if (vcard_emul_init_called) {
+        return VCARD_EMUL_INIT_ALREADY_INITED;
+    }
+    vcard_emul_init_called = 1;
+    vreader_init();
+    vevent_queue_init();
+
+    if (options == NULL) {
+        options = &default_options;
+    }
+
+    /* first initialize NSS */
+    if (options->nss_db) {
+        rv = NSS_Init(options->nss_db);
+    } else {
+        rv = NSS_Init("sql:/etc/pki/nssdb");
+    }
+    if (rv != SECSuccess) {
+        return VCARD_EMUL_FAIL;
+    }
+    /* Set password callback function */
+    PK11_SetPasswordFunc(vcard_emul_get_password);
+
+    /* set up soft cards emulated by software certs rather than physical cards
+     * */
+    for (i = 0; i < options->vreader_count; i++) {
+        int j;
+        int cert_count;
+        unsigned char **certs;
+        int *cert_len;
+        VCardKey **keys;
+        PK11SlotInfo *slot;
+
+        slot = PK11_FindSlotByName(options->vreader[i].name);
+        if (slot == NULL) {
+            continue;
+        }
+        vreader_emul = vreader_emul_new(slot, options->vreader[i].card_type,
+                                        options->vreader[i].type_params);
+        vreader = vreader_new(options->vreader[i].vname, vreader_emul,
+                              vreader_emul_delete);
+        vreader_add_reader(vreader);
+        cert_count = options->vreader[i].cert_count;
+
+        ret = vcard_emul_alloc_arrays(&certs, &cert_len, &keys,
+                                      options->vreader[i].cert_count);
+        if (ret == PR_FALSE) {
+            continue;
+        }
+        cert_count = 0;
+        for (j=0; j < options->vreader[i].cert_count; j++) {
+            /* we should have a better way of identifying certs than by
+             * nickname here */
+            CERTCertificate *cert = PK11_FindCertFromNickname(
+                                        options->vreader[i].cert_name[j],
+                                        NULL);
+            if (cert == NULL) {
+                continue;
+            }
+            certs[cert_count] = cert->derCert.data;
+            cert_len[cert_count] = cert->derCert.len;
+            keys[cert_count] = vcard_emul_make_key(slot, cert);
+            /* this is safe because the key is still holding a cert reference */
+            CERT_DestroyCertificate(cert);
+            cert_count++;
+        }
+        if (cert_count) {
+            VCard *vcard = vcard_emul_make_card(vreader, certs, cert_len,
+                                                keys, cert_count);
+            vreader_insert_card(vreader, vcard);
+            vcard_emul_init_series(vreader, vcard);
+            /* allow insertion and removal of soft cards */
+            vreader_emul->saved_vcard = vcard_reference(vcard);
+            vcard_free(vcard);
+            vreader_free(vreader);
+            has_readers = PR_TRUE;
+        }
+    }
+
+    /* if we aren't suppose to use hw, skip looking up hardware tokens */
+    if (!options->use_hw) {
+        nss_emul_init = has_readers;
+        return has_readers ? VCARD_EMUL_OK : VCARD_EMUL_FAIL;
+    }
+
+    /* make sure we have some PKCS #11 module loaded */
+    module_lock = SECMOD_GetDefaultModuleListLock();
+    module_list = SECMOD_GetDefaultModuleList();
+    need_module = !has_readers;
+    SECMOD_GetReadLock(module_lock);
+    for (mlp = module_list; mlp; mlp = mlp->next) {
+        SECMODModule * module = mlp->module;
+        if (SECMOD_HasRemovableSlots(module)) {
+            need_module = PR_FALSE;
+            break;
+        }
+    }
+    SECMOD_ReleaseReadLock(module_lock);
+
+    if (need_module) {
+        SECMODModule *module;
+        module = SECMOD_LoadUserModule(
+                    (char*)"library=libcoolkeypk11.so name=Coolkey",
+                    NULL, PR_FALSE);
+        if (module == NULL) {
+            return VCARD_EMUL_FAIL;
+        }
+        SECMOD_DestroyModule(module); /* free our reference, Module will still
+                                       * be on the list.
+                                       * until we destroy it */
+    }
+
+    /* now examine all the slots, finding which should be readers */
+    /* We should control this with options. For now we mirror out any
+     * removable hardware slot */
+    default_card_type = options->hw_card_type;
+    default_type_params = strdup(options->hw_type_params);
+
+    SECMOD_GetReadLock(module_lock);
+    for (mlp = module_list; mlp; mlp = mlp->next) {
+        SECMODModule * module = mlp->module;
+        PRBool has_emul_slots = PR_FALSE;
+
+        if (module == NULL) {
+                continue;
+        }
+
+        for (i=0; i < module->slotCount; i++) {
+            PK11SlotInfo *slot = module->slots[i];
+
+            /* only map removable HW slots */
+            if (slot == NULL || !PK11_IsRemovable(slot) || !PK11_IsHW(slot)) {
+                continue;
+            }
+            vreader_emul = vreader_emul_new(slot, options->hw_card_type,
+                                            options->hw_type_params);
+            vreader = vreader_new(PK11_GetSlotName(slot), vreader_emul,
+                                  vreader_emul_delete);
+            vreader_add_reader(vreader);
+
+            has_readers = PR_TRUE;
+            has_emul_slots = PR_TRUE;
+
+            if (PK11_IsPresent(slot)) {
+                VCardEmulType type;
+                VCard *vcard;
+                type = vcard_emul_get_type(vreader);
+                vcard = vcard_emul_mirror_card(vreader);
+                vreader_insert_card(vreader, vcard);
+                vcard_emul_init_series(vreader, vcard);
+                vcard_free(vcard);
+            }
+        }
+        if (has_emul_slots) {
+            vcard_emul_new_event_thread(module);
+        }
+    }
+    SECMOD_ReleaseReadLock(module_lock);
+    nss_emul_init = has_readers;
+
+    return VCARD_EMUL_OK;
+}
+
+/* Recreate card insert events for all readers (user should
+ * deduce implied reader insert. perhaps do a reader insert as well?)
+ */
+void
+vcard_emul_replay_insertion_events(void)
+{
+    VReaderListEntry *current_entry;
+    VReaderListEntry *next_entry = NULL;
+    VReaderList *list = vreader_get_reader_list();
+
+    for (current_entry= vreader_list_get_first(list); current_entry;
+            current_entry=next_entry) {
+        VReader *vreader = vreader_list_get_reader(current_entry);
+        next_entry = vreader_list_get_next(current_entry);
+        vreader_queue_card_event(vreader);
+    }
+}
+
+/*
+ *  Silly little functions to help parsing our argument string
+ */
+static char *
+copy_string(const char *str, int str_len)
+{
+    char *new_str;
+
+    new_str = malloc(str_len+1);
+    memcpy(new_str, str, str_len);
+    new_str[str_len] = 0;
+    return new_str;
+}
+
+static int
+count_tokens(const char *str, char token, char token_end)
+{
+    int count = 0;
+
+    for (;*str;str++) {
+        if (*str == token) {
+            count++;
+        }
+        if (*str == token_end) {
+            break;
+        }
+    }
+    return count;
+}
+
+static const char *
+find_token(const char *str, char token, char token_end)
+{
+    /* just do the blind simple thing */
+    for (;*str;str++) {
+        if ((*str == token) || (*str == token_end)) {
+            break;
+        }
+    }
+    return str;
+}
+
+static const char *
+strip(const char *str)
+{
+    for(;*str; str++) {
+        if ((*str != ' ') && (*str != '\n') &&
+           (*str != '\t') && (*str != '\r')) {
+            break;
+        }
+    }
+    return str;
+}
+
+static const char *
+find_blank(const char *str)
+{
+    for(;*str; str++) {
+        if ((*str == ' ') || (*str == '\n') ||
+           (*str == '\t') || (*str == '\r')) {
+            break;
+        }
+    }
+    return str;
+}
+
+
+/*
+ *  We really want to use some existing argument parsing library here. That
+ *  would give us a consistant look */
+static VCardEmulOptions options;
+#define READER_STEP 4
+
+VCardEmulOptions *
+vcard_emul_options(const char *args)
+{
+    int reader_count = 0;
+    VCardEmulOptions *opts;
+    char type_str[100];
+    int type_len;
+
+    /* Allow the future use of allocating the options structure on the fly */
+    memcpy(&options, &default_options, sizeof(options));
+    opts = &options;
+
+    do {
+        args = strip(args); /* strip off the leading spaces */
+        if (*args == ',') {
+           continue;
+        }
+        /* soft=(slot_name,virt_name,emul_type,emul_flags,cert_1, (no eol)
+         *       cert_2,cert_3...) */
+        if (strncmp(args,"soft=",5) == 0) {
+            const char *name;
+            const char *vname;
+            const char *type_params;
+            VCardEmulType type;
+            int name_length, vname_length, type_params_length, count, i;
+            VirtualReaderOptions *vreaderOpt = NULL;
+
+            args = strip(args+5);
+            if (*args != '(') {
+                continue;
+            }
+            name = args;
+            args = find_token(args+1,',',')');
+            if (*args == 0) {
+                break;
+            }
+            if (*args == ')') {
+                args++;
+                continue;
+            }
+            args = strip(args+1);
+            name_length = args - name - 2;
+            vname = args;
+            args = find_token(args+1,',',')');
+            if (*args == 0) {
+                break;
+            }
+            if (*args == ')') {
+                args++;
+                continue;
+            }
+            vname_length = args - name - 2;
+            args = strip(args+1);
+            type_len = find_token(args,',',')') - args;
+            assert(sizeof(type_str) > type_len);
+            strncpy(type_str, args, type_len);
+            type_str[type_len] = 0;
+            type = vcard_emul_type_from_string(type_str);
+            args = find_token(args,',',')');
+            if (*args == 0) {
+                break;
+            }
+            if (*args == ')') {
+                args++;
+                continue;
+            }
+            args = strip(args++);
+            type_params=args;
+            args = find_token(args+1,',',')');
+            if (*args == 0) {
+                break;
+            }
+            if (*args == ')') {
+                args++;
+                continue;
+            }
+            type_params_length = args - name;
+            args = strip(args++);
+            if (*args == 0) {
+                break;
+            }
+
+            if (opts->vreader_count >= reader_count) {
+                reader_count += READER_STEP;
+                vreaderOpt = realloc(opts->vreader,
+                                reader_count*sizeof(*vreaderOpt));
+                if (vreaderOpt == NULL) {
+                    return opts; /* we're done */
+                }
+            }
+            opts->vreader = vreaderOpt;
+            vreaderOpt = &vreaderOpt[opts->vreader_count];
+            vreaderOpt->name = copy_string(name, name_length);
+            vreaderOpt->vname = copy_string(vname, vname_length);
+            vreaderOpt->card_type = type;
+            vreaderOpt->type_params = copy_string(name, name_length);
+            count = count_tokens(args,',',')');
+            vreaderOpt->cert_count = count;
+            vreaderOpt->cert_name = (char **)malloc(count*sizeof(char *));
+            for (i=0; i < count; i++) {
+                const char *cert = args + 1;
+                args = find_token(args + 1, ',', ')');
+                vreaderOpt->cert_name[i] = copy_string(cert, args - cert);
+            }
+            if (*args == ')') {
+                args++;
+            }
+            opts->vreader_count++;
+        /* use_hw= */
+        } else if (strncmp(args,"use_hw=",7) == 0) {
+            args = strip(args+7);
+            if (*args == '0' || *args == 'N' || *args == 'n' || *args == 'F') {
+                opts->use_hw = PR_FALSE;
+            } else {
+                opts->use_hw = PR_TRUE;
+            }
+            args = find_blank(args);
+        /* hw_type= */
+        } else if (strncmp(args,"hw_type=",8) == 0) {
+            args = strip(args+8);
+            opts->hw_card_type = vcard_emul_type_from_string(args);
+            args = find_blank(args);
+        /* hw_params= */
+        } else if (strncmp(args,"hw_params=",10) == 0) {
+            const char *params;
+            args = strip(args+10);
+            params= args;
+            args = find_blank(args);
+            opts->hw_type_params = copy_string(params, args-params);
+        /* db="/data/base/path" */
+        } else if (strncmp(args,"db=",3) == 0) {
+            const char *db;
+            args = strip(args+3);
+            if (*args != '"') {
+                continue;
+            }
+            args++;
+            db = args;
+            args = find_token(args, '"', '\n');
+            opts->nss_db = copy_string(db,args-db);
+            if (*args != 0) {
+                args++;
+            }
+        } else args = find_blank(args);
+    } while (*args != 0);
+
+    return opts;
+}
+
+void
+vcard_emul_usage(void)
+{
+   fprintf(stderr,
+"emul args: comma separated list of the following arguments\n"
+" db={nss_database}               (default sql:/etc/pki/nssdb)\n"
+" use_hw=[yes|no]                 (default yes)\n"
+" hw_type={card_type_to_emulate}  (default CAC)\n"
+" hw_param={param_for_card}       (default \"\")\n"
+" soft=({slot_name},{vreader_name},{card_type_to_emulate},{params_for_card},\n"
+"       {cert1},{cert2},{cert3}    (default none)\n"
+"\n"
+"  {nss_database}          The location of the NSS cert & key database\n"
+"  {card_type_to_emulate}  What card interface to present to the guest\n"
+"  {param_for_card}        Card interface specific parameters\n"
+"  {slot_name}             NSS slot that contains the certs\n"
+"  {vreader_name}          Virutal reader name to present to the guest\n"
+"  {certN}                 Nickname of the certificate n on the virtual card\n"
+"\n"
+"These parameters come as a single string separated by blanks or newlines."
+"\n"
+"Unless use_hw is set to no, all tokens that look like removable hardware\n"
+"tokens will be presented to the guest using the emulator specified by \n"
+"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");
+}
diff --git a/libcacard/vcard_emul_type.c b/libcacard/vcard_emul_type.c
new file mode 100644
index 0000000..adbc54b
--- /dev/null
+++ b/libcacard/vcard_emul_type.c
@@ -0,0 +1,60 @@
+/*
+ *  This file contains utility functions which abstract the different card
+ *  types.  The goal is that new card types can easily be added by simply
+ *  changing this file and vcard_emul_type.h. It is currently not a requirement
+ *  to dynamically add new card types.
+ */
+
+#include <strings.h>
+#include "vcardt.h"
+#include "vcard_emul_type.h"
+#include "cac.h"
+#include "passthru.h"
+
+VCardStatus vcard_init(VReader *vreader, VCard *vcard,
+                       VCardEmulType type, const char * params,
+                       unsigned char *const *cert, int cert_len[],
+                       VCardKey *key[], int cert_count)
+{
+    switch (type) {
+    case VCARD_EMUL_NONE:
+        break;
+    case VCARD_EMUL_CAC:
+        return cac_card_init(vreader, vcard, params,
+                             cert, cert_len, key,  cert_count);
+#ifdef USE_PASSTHRU
+    case VCARD_EMUL_PASSTHRU:
+        return passthru_card_init(vreader, vcard, params,
+                                  cert, cert_len, key,  cert_count);
+#endif
+    /* add new ones here */
+    default:
+        break;
+    }
+    return VCARD_FAIL;
+}
+
+VCardEmulType vcard_emul_type_select(VReader *vreader)
+{
+#ifdef notdef
+    /* since there is only one emulator no need to call this function */
+    if (cac_is_cac_card(vreader) == VCARD_DONE) {
+        return VCARD_EMUL_CAC;
+    }
+#endif
+    /* return the default */
+    return VCARD_EMUL_CAC;
+}
+
+VCardEmulType vcard_emul_type_from_string(const char *type_string)
+{
+     if (strcasecmp(type_string,"CAC") == 0) {
+        return VCARD_EMUL_CAC;
+     }
+#ifdef USE_PASSTHRU
+     if (strcasecmp(type_string,"PASSTHRU") == 0) {
+        return VCARD_EMUL_PASSTHRU;
+     }
+#endif
+     return VCARD_EMUL_NONE;
+}
diff --git a/libcacard/vcard_emul_type.h b/libcacard/vcard_emul_type.h
new file mode 100644
index 0000000..da15528
--- /dev/null
+++ b/libcacard/vcard_emul_type.h
@@ -0,0 +1,29 @@
+/*
+ *  This header file abstracts the different card types. The goal is new card
+ *  types can easily be added by simply changing this file and
+ *  vcard_emul_type.c. It is currently not a requirement to dynamically add new
+ *  card types.
+ */
+
+#ifndef VCARD_EMUL_TYPE_H
+#define VCARD_EMUL_TYPE_H 1
+#include "vcardt.h"
+#include "vreadert.h"
+
+/*
+ * types
+ */
+typedef enum {
+     VCARD_EMUL_NONE =0,
+     VCARD_EMUL_CAC,
+     VCARD_EMUL_PASSTHRU
+} VCardEmulType;
+
+/* functions used by the rest of the emulator */
+VCardStatus vcard_init(VReader *vreader, VCard *vcard, VCardEmulType type,
+                       const char *params, unsigned char * const *cert,
+                       int cert_len[], VCardKey *key[], int cert_count);
+VCardEmulType vcard_emul_type_select(VReader *vreader);
+VCardEmulType vcard_emul_type_from_string(const char *type_string);
+
+#endif
diff --git a/libcacard/vcardt.h b/libcacard/vcardt.h
new file mode 100644
index 0000000..e371451
--- /dev/null
+++ b/libcacard/vcardt.h
@@ -0,0 +1,66 @@
+/*
+ *
+ */
+#ifndef VCARDT_H
+#define VCARDT_H 1
+
+/*
+ * these should come from some common spice header file
+ */
+#include <assert.h>
+#ifndef ASSERT
+#define ASSERT assert
+#endif
+#ifndef MIN
+#define MIN(x,y) ((x)>(y)?(y):(x))
+#define MAX(x,y) ((x)>(y)?(x):(y))
+#endif
+
+typedef struct VCardStruct VCard;
+typedef struct VCardAPDUStruct VCardAPDU;
+typedef struct VCardResponseStruct VCardResponse;
+typedef struct VCardBufferResponseStruct VCardBufferResponse;
+typedef struct VCardAppletStruct VCardApplet;
+typedef struct VCardAppletPrivateStruct VCardAppletPrivate;
+typedef struct VCardKeyStruct VCardKey;  /* opaque */
+typedef struct VCardEmulStruct VCardEmul;
+
+#define MAX_CHANNEL 4
+
+/* create an ATR with appropriate historical bytes */
+#define VCARD_ATR_PREFIX(size) 0x3b, 0x66+(size), 0x00, 0xff, \
+                               'V', 'C', 'A', 'R', 'D', '_'
+
+
+typedef enum {
+    VCARD_DONE,
+    VCARD_NEXT,
+    VCARD_FAIL
+} VCardStatus;
+
+typedef enum {
+    VCARD_FILE_SYSTEM,
+    VCARD_VM,
+    VCARD_DIRECT
+} VCardType;
+
+typedef enum {
+    VCARD_POWER_ON,
+    VCARD_POWER_OFF
+} VCardPower;
+
+typedef VCardStatus (*VCardProcessAPDU)(VCard *card, VCardAPDU *apdu,
+                                        VCardResponse **response);
+typedef VCardStatus (*VCardResetApplet)(VCard *card, int channel);
+typedef void (*VCardAppletPrivateFree) (VCardAppletPrivate *);
+typedef void (*VCardEmulFree) (VCardEmul *);
+typedef void (*VCardGetAtr) (VCard *, unsigned char *atr, int *atr_len);
+
+struct VCardBufferResponseStruct {
+    unsigned char *buffer;
+    int buffer_len;
+    unsigned char *current;
+    int len;
+};
+
+#endif
diff --git a/libcacard/vevent.h b/libcacard/vevent.h
new file mode 100644
index 0000000..f202ea8
--- /dev/null
+++ b/libcacard/vevent.h
@@ -0,0 +1,26 @@
+/*
+ *
+ */
+#ifndef EVENT_H
+#define EVENT_H 1
+#include "eventt.h"
+#include "vreadert.h"
+#include "vcardt.h"
+
+VEvent *vevent_new(VEventType type, VReader *reader, VCard *card);
+void vevent_delete(VEvent *);
+
+/*
+ * VEvent queueing services
+ */
+void vevent_queue_vevent(VEvent *);
+void vevent_queue_init(void);
+
+/*
+ *  VEvent dequeing services
+ */
+VEvent *vevent_wait_next_vevent(void);
+VEvent *vevent_get_next_vevent(void);
+
+
+#endif
diff --git a/libcacard/vreader.c b/libcacard/vreader.c
new file mode 100644
index 0000000..5e46019
--- /dev/null
+++ b/libcacard/vreader.c
@@ -0,0 +1,526 @@
+/*
+ * emulate the reader
+ */
+#include "vcard.h"
+#include "vcard_emul.h"
+#include "card_7816.h"
+#include "vreader.h"
+#include "vevent.h"
+
+/*
+ * System includes
+ */
+#include <stdlib.h>
+#include <string.h>
+
+/*
+ * spice includes
+ */
+#include "mutex.h"
+
+struct VReaderStruct {
+    int    reference_count;
+    VCard *card;
+    char *name;
+    vreader_id_t id;
+    mutex_t lock;
+    VReaderEmul  *reader_private;
+    VReaderEmulFree reader_private_free;
+};
+
+/* manage locking */
+static inline void
+vreader_lock(VReader *reader)
+{
+    MUTEX_LOCK(reader->lock);
+}
+
+static inline void
+vreader_unlock(VReader *reader)
+{
+    MUTEX_UNLOCK(reader->lock);
+}
+
+/*
+ * vreader constructor
+ */
+VReader *
+vreader_new(const char *name, VReaderEmul *private,
+            VReaderEmulFree private_free)
+{
+    VReader *reader;
+
+    reader = (VReader *)malloc(sizeof(VReader));
+    if (reader == NULL) {
+        return NULL;
+    }
+    MUTEX_INIT(reader->lock);
+    reader->reference_count = 1;
+    reader->name = name ? strdup(name) : NULL;
+    reader->card = NULL;
+    reader->id = (vreader_id_t)-1;
+    reader->reader_private = private;
+    reader->reader_private_free = private_free;
+    return reader;
+}
+
+/* get a reference */
+VReader*
+vreader_reference(VReader *reader)
+{
+    if (reader == NULL) {
+        return NULL;
+    }
+    vreader_lock(reader);
+    reader->reference_count++;
+    vreader_unlock(reader);
+    return reader;
+}
+
+/* free a reference */
+void
+vreader_free(VReader *reader)
+{
+    if (reader == NULL) {
+        return;
+    }
+    vreader_lock(reader);
+    if (reader->reference_count-- > 1) {
+        vreader_unlock(reader);
+        return;
+    }
+    vreader_unlock(reader);
+    if (reader->card) {
+        vcard_free(reader->card);
+    }
+    if (reader->name) {
+        free(reader->name);
+    }
+    if (reader->reader_private_free) {
+        reader->reader_private_free(reader->reader_private);
+    }
+    free(reader);
+    return;
+}
+
+static VCard *
+vreader_get_card(VReader *reader)
+{
+    VCard *card;
+
+    vreader_lock(reader);
+    card = vcard_reference(reader->card);
+    vreader_unlock(reader);
+    return card;
+}
+
+VReaderStatus
+vreader_card_is_present(VReader *reader)
+{
+    VCard *card = vreader_get_card(reader);
+
+    if (card == NULL) {
+        return VREADER_NO_CARD;
+    }
+    vcard_free(card);
+    return VREADER_OK;
+}
+
+vreader_id_t
+vreader_get_id(VReader *reader)
+{
+    if (reader == NULL) {
+        return (vreader_id_t)-1;
+    }
+    return reader->id;
+}
+
+VReaderStatus
+vreader_set_id(VReader *reader, vreader_id_t id)
+{
+    if (reader == NULL) {
+        return VREADER_NO_CARD;
+    }
+    reader->id = id;
+    return VREADER_OK;
+}
+
+const char *
+vreader_get_name(VReader *reader)
+{
+    if (reader == NULL) {
+        return NULL;
+    }
+    return reader->name;
+}
+
+VReaderEmul *
+vreader_get_private(VReader *reader)
+{
+    return reader->reader_private;
+}
+
+static VReaderStatus
+vreader_reset(VReader *reader, VCardPower power, unsigned char *atr, int *len)
+{
+    VCard *card = vreader_get_card(reader);
+
+    if (card == NULL) {
+        return VREADER_NO_CARD;
+    }
+    /*
+     * clean up our state
+     */
+    vcard_reset(card, power);
+    if (atr) {
+        vcard_get_atr(card, atr, len);
+    }
+    vcard_free(card); /* free our reference */
+    return VREADER_OK;
+}
+
+VReaderStatus
+vreader_power_on(VReader *reader, unsigned char *atr, int *len)
+{
+    return vreader_reset(reader, VCARD_POWER_ON, atr, len );
+}
+
+VReaderStatus
+vreader_power_off(VReader *reader)
+{
+    return vreader_reset(reader, VCARD_POWER_OFF, NULL, 0);
+}
+
+
+VReaderStatus
+vreader_xfr_bytes(VReader *reader,
+                  unsigned char *send_buf, int send_buf_len,
+                  unsigned char *receive_buf, int *receive_buf_len)
+{
+    VCardAPDU *apdu;
+    VCardResponse *response = NULL;
+    VCardStatus card_status;
+    unsigned short status;
+    VCard *card = vreader_get_card(reader);
+
+    if (card == NULL) {
+        return VREADER_NO_CARD;
+    }
+
+    apdu = vcard_apdu_new(send_buf, send_buf_len, &status);
+    if (apdu == NULL) {
+        response = vcard_make_response(status);
+        card_status = VCARD_DONE;
+    } else {
+        card_status = vcard_process_apdu(card, apdu, &response);
+    }
+    ASSERT(card_status == VCARD_DONE);
+    if (card_status == VCARD_DONE) {
+        int size = MIN(*receive_buf_len, response->b_total_len);
+        memcpy(receive_buf, response->b_data, size);
+        *receive_buf_len = size;
+    }
+    vcard_response_delete(response);
+    vcard_apdu_delete(apdu);
+    vcard_free(card); /* free our reference */
+    return VREADER_OK;
+}
+
+struct VReaderListStruct {
+    VReaderListEntry *head;
+    VReaderListEntry *tail;
+};
+
+struct VReaderListEntryStruct {
+    VReaderListEntry *next;
+    VReaderListEntry *prev;
+    VReader *reader;
+};
+
+
+static VReaderListEntry *
+vreader_list_entry_new(VReader *reader)
+{
+    VReaderListEntry *new_reader_list_entry;
+
+    new_reader_list_entry = (VReaderListEntry *)
+                               malloc(sizeof(VReaderListEntry));
+    if (new_reader_list_entry == NULL) {
+        return NULL;
+    }
+    new_reader_list_entry->next = NULL;
+    new_reader_list_entry->prev = NULL;
+    new_reader_list_entry->reader = vreader_reference(reader);
+    return new_reader_list_entry;
+}
+
+static void
+vreader_list_entry_delete(VReaderListEntry *entry)
+{
+    if (entry == NULL) {
+        return;
+    }
+    vreader_free(entry->reader);
+    free(entry);
+}
+
+
+static VReaderList *
+vreader_list_new(void)
+{
+    VReaderList *new_reader_list;
+
+    new_reader_list = (VReaderList *)malloc(sizeof(VReaderList));
+    if (new_reader_list == NULL) {
+        return NULL;
+    }
+    new_reader_list->head = NULL;
+    new_reader_list->tail = NULL;
+    return new_reader_list;
+}
+
+void
+vreader_list_delete(VReaderList *list)
+{
+    VReaderListEntry *current_entry;
+    VReaderListEntry *next_entry = NULL;
+    for (current_entry= vreader_list_get_first(list); current_entry;
+            current_entry=next_entry) {
+        next_entry = vreader_list_get_next(current_entry);
+        vreader_list_entry_delete(current_entry);
+    }
+    list->head = NULL;
+    list->tail = NULL;
+    free(list);
+}
+
+
+VReaderListEntry *
+vreader_list_get_first(VReaderList *list)
+{
+    return list ? list->head : NULL;
+}
+
+VReaderListEntry *
+vreader_list_get_next(VReaderListEntry *current)
+{
+    return current ? current->next : NULL;
+}
+
+VReader *
+vreader_list_get_reader(VReaderListEntry *entry)
+{
+    return entry ? vreader_reference(entry->reader) : NULL;
+}
+
+static void
+vreader_queue(VReaderList *list, VReaderListEntry *entry)
+{
+    if (entry == NULL) {
+        return;
+    }
+    entry->next = NULL;
+    entry->prev = list->tail;
+    if (list->head) {
+        list->tail->next = entry;
+    } else {
+        list->head = entry;
+    }
+    list->tail = entry;
+}
+
+static void
+vreader_dequeue(VReaderList *list, VReaderListEntry *entry)
+{
+    if (entry == NULL) {
+        return;
+    }
+    if (entry->next == NULL) {
+        list->tail = entry->prev;
+    } else if (entry->prev == NULL) {
+        list->head = entry->next;
+    } else {
+        entry->prev->next = entry->next;
+        entry->next->prev = entry->prev;
+    }
+    if ((list->tail == NULL) || (list->head == NULL)) {
+        list->head = list->tail = NULL;
+    }
+    entry->next = entry->prev = NULL;
+}
+
+static VReaderList *vreader_list = NULL;
+static mutex_t vreader_list_mutex;
+
+static void
+vreader_list_init(void)
+{
+    vreader_list = vreader_list_new();
+    MUTEX_INIT(vreader_list_mutex);
+}
+
+static void
+vreader_list_lock(void)
+{
+    MUTEX_LOCK(vreader_list_mutex);
+}
+
+static void
+vreader_list_unlock(void)
+{
+    MUTEX_UNLOCK(vreader_list_mutex);
+}
+
+static VReaderList *
+vreader_copy_list(VReaderList *list)
+{
+    VReaderList *new_list = NULL;
+    VReaderListEntry *current_entry = NULL;
+
+    new_list = vreader_list_new();
+    if (new_list == NULL) {
+        return NULL;
+    }
+    for (current_entry= vreader_list_get_first(list); current_entry;
+                       current_entry=vreader_list_get_next(current_entry)) {
+        VReader *reader = vreader_list_get_reader(current_entry);
+        VReaderListEntry *new_entry = vreader_list_entry_new(reader);
+
+        vreader_free(reader);
+        vreader_queue(new_list, new_entry);
+    }
+    return new_list;
+}
+
+VReaderList *
+vreader_get_reader_list(void)
+{
+    VReaderList *new_reader_list;
+
+    vreader_list_lock();
+    new_reader_list = vreader_copy_list(vreader_list);
+    vreader_list_unlock();
+    return new_reader_list;
+}
+
+VReader *
+vreader_get_reader_by_id(vreader_id_t id)
+{
+    VReader *reader = NULL;
+    VReaderListEntry *current_entry = NULL;
+
+    if (id == (vreader_id_t) -1) {
+        return NULL;
+    }
+
+    vreader_list_lock();
+    for (current_entry = vreader_list_get_first(vreader_list); current_entry;
+            current_entry = vreader_list_get_next(current_entry)) {
+        VReader *creader = vreader_list_get_reader(current_entry);
+        if (creader->id == id) {
+            reader = creader;
+            break;
+        }
+        vreader_free(creader);
+    }
+    vreader_list_unlock();
+    return reader;
+}
+
+VReader *
+vreader_get_reader_by_name(const char *name)
+{
+    VReader *reader = NULL;
+    VReaderListEntry *current_entry = NULL;
+
+    vreader_list_lock();
+    for (current_entry = vreader_list_get_first(vreader_list); current_entry;
+            current_entry = vreader_list_get_next(current_entry)) {
+        VReader *creader = vreader_list_get_reader(current_entry);
+        if (strcmp(creader->name, name) == 0) {
+            reader = creader;
+            break;
+        }
+        vreader_free(creader);
+    }
+    vreader_list_unlock();
+    return reader;
+}
+
+/* called from card_emul to initialize the readers */
+VReaderStatus
+vreader_add_reader(VReader *reader)
+{
+    VReaderListEntry *reader_entry;
+
+    reader_entry = vreader_list_entry_new(reader);
+    if (reader_entry == NULL) {
+        return VREADER_OUT_OF_MEMORY;
+    }
+    vreader_list_lock();
+    vreader_queue(vreader_list, reader_entry);
+    vreader_list_unlock();
+    vevent_queue_vevent(vevent_new(VEVENT_READER_INSERT, reader, NULL));
+    return VREADER_OK;
+}
+
+
+VReaderStatus
+vreader_remove_reader(VReader *reader)
+{
+    VReaderListEntry *current_entry;
+
+    vreader_list_lock();
+    for (current_entry= vreader_list_get_first(vreader_list); current_entry;
+                       current_entry=vreader_list_get_next(current_entry)) {
+        if (current_entry->reader == reader) {
+            break;
+        }
+    }
+    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));
+    return VREADER_OK;
+}
+
+/*
+ * Generate VEVENT_CARD_INSERT or VEVENT_CARD_REMOVE based on vreader
+ * state. Separated from vreader_insert_card to allow replaying events
+ * for a given state.
+ */
+void
+vreader_queue_card_event(VReader *reader)
+{
+    vevent_queue_vevent(vevent_new(
+        reader->card ? VEVENT_CARD_INSERT : VEVENT_CARD_REMOVE, reader,
+        reader->card));
+}
+
+/*
+ * insert/remove a new card. for removal, card == NULL
+ */
+VReaderStatus
+vreader_insert_card(VReader *reader, VCard *card)
+{
+    vreader_lock(reader);
+    if (reader->card) {
+        /* decrement reference count */
+        vcard_free(reader->card);
+        reader->card = NULL;
+    }
+    reader->card = vcard_reference(card);
+    vreader_unlock(reader);
+    vreader_queue_card_event(reader);
+    return VREADER_OK;
+}
+
+/*
+ * initialize all the static reader structures
+ */
+void
+vreader_init(void)
+{
+    vreader_list_init();
+}
+
diff --git a/libcacard/vreader.h b/libcacard/vreader.h
new file mode 100644
index 0000000..c7054da
--- /dev/null
+++ b/libcacard/vreader.h
@@ -0,0 +1,54 @@
+/*
+ *
+ */
+
+#ifndef VREADER_H
+#define VREADER_H 1
+
+#include "eventt.h"
+#include "vreadert.h"
+#include "vcardt.h"
+
+/*
+ * calls for reader front end
+ */
+VReaderStatus vreader_power_on(VReader *reader, unsigned char *atr, int *len);
+VReaderStatus vreader_power_off(VReader *reader);
+VReaderStatus vreader_xfr_bytes(VReader *reader, unsigned char *send_buf,
+                                int send_buf_len, unsigned char *receive_buf,
+                                int *receive_buf_len);
+
+/* constructor */
+VReader *vreader_new(const char *readerName, VReaderEmul *emul_private,
+                     VReaderEmulFree private_free);
+/* get a new reference to a reader */
+VReader *vreader_reference(VReader *reader);
+/* "destructor" (readers are reference counted) */
+void vreader_free(VReader *reader);
+
+/* accessors */
+VReaderEmul *vreader_get_private(VReader *);
+VReaderStatus vreader_card_is_present(VReader *reader);
+void vreader_queue_card_event(VReader *reader);
+const char *vreader_get_name(VReader *reader);
+vreader_id_t vreader_get_id(VReader *reader);
+VReaderStatus vreader_set_id(VReader *reader, vreader_id_t id);
+
+/* list operations */
+VReaderList *vreader_get_reader_list(void);
+void vreader_list_delete(VReaderList *list);
+VReader *vreader_list_get_reader(VReaderListEntry *entry);
+VReaderListEntry *vreader_list_get_first(VReaderList *list);
+VReaderListEntry *vreader_list_get_next(VReaderListEntry *list);
+VReader *vreader_get_reader_by_id(vreader_id_t id);
+VReader *vreader_get_reader_by_name(const char *name);
+
+/*
+ * list tools for vcard_emul
+ */
+void vreader_init(void);
+VReaderStatus vreader_add_reader(VReader *reader);
+VReaderStatus vreader_remove_reader(VReader *reader);
+VReaderStatus vreader_insert_card(VReader *reader, VCard *card);
+
+#endif
diff --git a/libcacard/vreadert.h b/libcacard/vreadert.h
new file mode 100644
index 0000000..4c01259
--- /dev/null
+++ b/libcacard/vreadert.h
@@ -0,0 +1,23 @@
+/*
+ *
+ */
+
+#ifndef VREADERT_H
+#define VREADERT_H 1
+
+typedef enum {
+    VREADER_OK=0,
+    VREADER_NO_CARD,
+    VREADER_OUT_OF_MEMORY
+} VReaderStatus;
+
+typedef unsigned int vreader_id_t;
+typedef struct VReaderStruct VReader;
+typedef struct VReaderListStruct VReaderList;
+typedef struct VReaderListEntryStruct VReaderListEntry;
+
+typedef struct VReaderEmulStruct VReaderEmul;
+typedef void (*VReaderEmulFree)(VReaderEmul *);
+
+#endif
+
diff --git a/libcacard/vscclient.c b/libcacard/vscclient.c
new file mode 100644
index 0000000..4dd8a35
--- /dev/null
+++ b/libcacard/vscclient.c
@@ -0,0 +1,710 @@
+#include <sys/types.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netdb.h>
+#include <netinet/in.h>
+
+#include "vscard_common.h"
+
+#include "vreader.h"
+#include "vcard_emul.h"
+#include "vevent.h"
+#include "passthru.h"
+
+#include "mutex.h"
+
+int verbose = 0;
+
+int sock;
+
+static void
+print_byte_array (
+    uint8_t *arrBytes,
+    unsigned int nSize
+) {
+    int i;
+    for (i=0; i < nSize; i++) {
+        printf ("%02X ", arrBytes[i]);
+    }
+    printf ("\n");
+}
+
+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
+    vcard_emul_usage();
+}
+
+static char*
+ip_numeric_to_char(
+    uint32_t ip
+) {
+    char buf[4*4];
+
+    sprintf(buf, "%d.%d.%d.%d", (ip & 0xff000000) >> 24, (ip & 0xff0000) >> 16,
+        (ip & 0xff00) >> 8, ip & 0xff);
+    return strdup(buf);
+}
+
+static mutex_t write_lock;
+
+static int
+send_msg (
+    VSCMsgType type,
+    uint32_t reader_id,
+    const void *msg,
+    unsigned int length
+) {
+    int rv;
+    VSCMsgHeader mhHeader;
+
+    MUTEX_LOCK(write_lock);
+
+    if (verbose > 10) {
+        printf("sending type=%d id=%d, len =%d (0x%x)\n",
+               type, reader_id, length, length);
+    }
+
+    mhHeader.type = type;
+    mhHeader.reader_id = 0;
+    mhHeader.length = length;
+    rv = write (
+        sock,
+        &mhHeader,
+        sizeof (mhHeader)
+    );
+    if (rv < 0) {
+        /* Error */
+        printf ("write header error\n");
+        close (sock);
+        MUTEX_UNLOCK(write_lock);
+        return (16);
+    }
+    rv = write (
+        sock,
+        msg,
+        length
+    );
+    if (rv < 0) {
+        /* Error */
+        printf ("write error\n");
+        close (sock);
+        MUTEX_UNLOCK(write_lock);
+        return (16);
+    }
+    MUTEX_UNLOCK(write_lock);
+
+    return (0);
+}
+
+static VReader *pending_reader = NULL;
+static mutex_t pending_reader_lock;
+static condition_t pending_reader_condition;
+
+#define MAX_ATR_LEN 40
+static void *
+event_thread(void *arg)
+{
+    unsigned char atr[ MAX_ATR_LEN];
+    int atr_len = MAX_ATR_LEN;
+    VEvent *event = NULL;
+    unsigned int reader_id;
+
+
+    while (1) {
+        const char *reader_name;
+
+        event = vevent_wait_next_vevent();
+        if (event == NULL) {
+            break;
+        }
+        reader_id = vreader_get_id(event->reader);
+        if (reader_id == VSCARD_UNDEFINED_READER_ID &&
+            event->type != VEVENT_READER_INSERT) {
+            /* ignore events from readers qemu has rejected */
+            /* if qemu is still deciding on this reader, wait to see if need to
+             * forward this event */
+            MUTEX_LOCK(pending_reader_lock);
+            if (!pending_reader || (pending_reader != event->reader)) {
+                /* wasn't for a pending reader, this reader has already been
+                 * rejected by qemu */
+                MUTEX_UNLOCK(pending_reader_lock);
+                vevent_delete(event);
+                continue;
+            }
+            /* this reader hasn't been told it's status from qemu yet, wait for
+             * that status */
+            while (pending_reader != NULL) {
+                CONDITION_WAIT(pending_reader_condition,pending_reader_lock);
+            }
+            MUTEX_UNLOCK(pending_reader_lock);
+            /* now recheck the id */
+            reader_id = vreader_get_id(event->reader);
+            if (reader_id == VSCARD_UNDEFINED_READER_ID) {
+                /* this reader was rejected */
+                vevent_delete(event);
+                continue;
+            }
+            /* reader was accepted, now forward the event */
+        }
+        switch (event->type) {
+        case VEVENT_READER_INSERT:
+            /* tell qemu to insert a new CCID reader */
+            /* wait until qemu has responded to our first reader insert
+             * before we send a second. That way we won't confuse the responses
+             * */
+            MUTEX_LOCK(pending_reader_lock);
+            while (pending_reader != NULL) {
+                CONDITION_WAIT(pending_reader_condition,pending_reader_lock);
+            }
+            pending_reader = vreader_reference(event->reader);
+            MUTEX_UNLOCK(pending_reader_lock);
+            reader_name = vreader_get_name(event->reader);
+            if (verbose > 10) {
+                printf (" READER INSERT: %s\n", reader_name);
+            }
+            send_msg (
+                VSC_ReaderAdd,
+                reader_id, /* currerntly VSCARD_UNDEFINED_READER_ID */
+                NULL, 0
+                /*reader_name,
+                strlen(reader_name) */
+            );
+
+            break;
+        case VEVENT_READER_REMOVE:
+            /* future, tell qemu that an old CCID reader has been removed */
+            if (verbose > 10) {
+                printf (" READER REMOVE: %d \n", reader_id);
+            }
+            send_msg(
+                VSC_ReaderRemove,
+                reader_id,
+                NULL,
+                0
+            );
+            break;
+        case VEVENT_CARD_INSERT:
+            /* get the ATR (intended as a response to a power on from the
+             * reader */
+            atr_len = MAX_ATR_LEN;
+            vreader_power_on(event->reader, atr, &atr_len);
+            /* ATR call functions as a Card Insert event */
+            if (verbose > 10) {
+                printf (" CARD INSERT %d: ", reader_id);
+                print_byte_array (atr, atr_len);
+            }
+            send_msg (
+                VSC_ATR,
+                reader_id,
+                atr,
+                atr_len
+            );
+            break;
+        case VEVENT_CARD_REMOVE:
+            // Card removed
+            if (verbose > 10) {
+                printf (" CARD REMOVE %d: \n", reader_id);
+            }
+            send_msg (
+                VSC_CardRemove,
+                reader_id,
+                NULL,
+                0
+            );
+            break;
+        default:
+            break;
+        }
+        vevent_delete(event);
+    }
+    return NULL;
+}
+
+
+static unsigned int
+get_id_from_string(char *string, unsigned int default_id)
+{
+    unsigned int id = atoi(string);
+
+    /* don't accidentally swith to zero because no numbers have been supplied */
+    if ((id == 0) && *string != '0') {
+        return default_id;
+    }
+    return id;
+}
+
+static void
+do_command(void)
+{
+    char inbuf[255];
+    char *string;
+    VCardEmulError error;
+    static unsigned int default_reader_id = 0;
+    unsigned int reader_id;
+    VReader *reader = NULL;
+
+    reader_id = default_reader_id;
+    string = fgets(inbuf, sizeof(inbuf), stdin);
+    if (string != NULL) {
+        if (strncmp(string,"exit",4) == 0) {
+            /* remove all the readers */
+            VReaderList *list = vreader_get_reader_list();
+            VReaderListEntry *reader_entry;
+            printf("Active Readers:\n");
+            for (reader_entry = vreader_list_get_first(list); reader_entry;
+                 reader_entry = vreader_list_get_next(reader_entry)) {
+                VReader *reader = vreader_list_get_reader(reader_entry);
+                vreader_id_t reader_id;
+                reader_id=vreader_get_id(reader);
+                if (reader_id == -1) {
+                    continue;
+                }
+                /* be nice and signal card removal first (qemu probably should
+                 * do this itself) */
+                if (vreader_card_is_present(reader) == VREADER_OK) {
+                    send_msg (
+                        VSC_CardRemove,
+                        reader_id,
+                        NULL,
+                        0
+                    );
+                }
+                send_msg (
+                    VSC_ReaderRemove,
+                    reader_id,
+                    NULL,
+                    0
+                );
+            }
+            exit(0);
+        } else if (strncmp(string,"insert",6) == 0) {
+            if (string[6] == ' ') {
+                reader_id = get_id_from_string(&string[7], reader_id);
+            }
+            reader = vreader_get_reader_by_id(reader_id);
+            error = vcard_emul_force_card_insert(reader);
+            printf("insert %s, returned %d\n", reader ? vreader_get_name(reader)
+                                               : "invalid reader", error);
+        } else if (strncmp(string,"remove",6) == 0) {
+            if (string[6] == ' ') {
+                reader_id = get_id_from_string(&string[7], reader_id);
+            }
+            reader = vreader_get_reader_by_id(reader_id);
+            error = vcard_emul_force_card_remove(reader);
+            printf("remove %s, returned %d\n", reader ? vreader_get_name(reader)
+                                               : "invalid reader", error);
+        } else if (strncmp(string,"select",6) == 0) {
+            if (string[6] == ' ') {
+                reader_id = get_id_from_string(&string[7],
+                                               VSCARD_UNDEFINED_READER_ID);
+            }
+            if (reader_id != VSCARD_UNDEFINED_READER_ID) {
+                reader = vreader_get_reader_by_id(reader_id);
+            }
+            if (reader) {
+                printf("Selecting reader %d, %s\n", reader_id,
+                        vreader_get_name(reader));
+                default_reader_id = reader_id;
+            } else {
+                printf("Reader with id %d not found\n", reader_id);
+            }
+        } else if (strncmp(string,"debug",5) == 0) {
+            if (string[5] == ' ') {
+                verbose = get_id_from_string(&string[6],0);
+            }
+            printf ("debug level = %d\n", verbose);
+        } else if (strncmp(string,"list",4) == 0) {
+            VReaderList *list = vreader_get_reader_list();
+            VReaderListEntry *reader_entry;
+            printf("Active Readers:\n");
+            for (reader_entry = vreader_list_get_first(list); reader_entry;
+                 reader_entry = vreader_list_get_next(reader_entry)) {
+                VReader *reader = vreader_list_get_reader(reader_entry);
+                vreader_id_t reader_id;
+                reader_id=vreader_get_id(reader);
+                if (reader_id == -1) {
+                    continue;
+                }
+                printf("%3d %s %s\n",reader_id,
+                       vreader_card_is_present(reader) == VREADER_OK ?
+                       "CARD_PRESENT": "            ",
+                       vreader_get_name(reader));
+            }
+            printf("Inactive Readers:\n");
+            for (reader_entry = vreader_list_get_first(list); reader_entry;
+                 reader_entry = vreader_list_get_next(reader_entry)) {
+                VReader *reader = vreader_list_get_reader(reader_entry);
+                vreader_id_t reader_id;
+                reader_id=vreader_get_id(reader);
+                if (reader_id != -1) {
+                    continue;
+                }
+
+                printf("INA %s %s\n",
+                       vreader_card_is_present(reader) == VREADER_OK ?
+                       "CARD_PRESENT": "            ",
+                       vreader_get_name(reader));
+            }
+        } else if (*string != 0) {
+            printf("valid commands: \n");
+            printf("insert [reader_id]\n");
+            printf("remove [reader_id]\n");
+            printf("select reader_id\n");
+            printf("list\n");
+            printf("debug [level]\n");
+            printf("exit\n");
+        }
+    }
+    vreader_free(reader);
+    printf("> ");
+    fflush(stdout);
+}
+
+
+#define APDUBufSize 270
+
+// just for ease of parsing command line arguments.
+#define MAX_CERTS 100
+
+static int
+connect_to_qemu (
+    const char *ip,
+    uint32_t port
+) {
+    struct addrinfo hints;
+    struct addrinfo* server;
+    int ret;
+    char port_str[10];
+
+    sock = socket (
+        AF_INET,
+        SOCK_STREAM,
+        0
+    );
+    if (sock < 0) {
+        // Error
+        printf ("Error opening socket!\n");
+    }
+
+    memset(&hints, 0, sizeof(struct addrinfo));
+    hints.ai_family = AF_INET;
+    hints.ai_socktype = SOCK_STREAM;
+    hints.ai_flags = 0;
+    hints.ai_protocol = 0;          /* Any protocol */
+    snprintf(port_str, sizeof(port_str) - 1, "%d", port);
+
+    ret = getaddrinfo(ip, port_str, &hints, &server);
+
+    if (ret != 0) {
+        printf ("getaddrinfo failed\n");
+        return (5);
+    }
+
+    if (connect (
+            sock,
+            server->ai_addr,
+            server->ai_addrlen
+        ) < 0
+    ) {
+        // Error
+        printf ("Could not connect\n");
+        return (5);
+    }
+    if (verbose) {
+        printf ("Connected (sizeof Header=%zd)!\n", sizeof (VSCMsgHeader));
+    }
+    return sock;
+}
+
+int
+main (
+    int argc,
+    char *argv[]
+) {
+    char* qemu_ip;
+    uint16_t qemu_port;
+    VSCMsgHeader mhHeader;
+    VSCMsgError *error_msg;
+
+    int rv;
+    int dwSendLength;
+    int dwRecvLength;
+    uint8_t pbRecvBuffer[APDUBufSize];
+    uint8_t pbSendBuffer[APDUBufSize];
+     VReaderStatus reader_status;
+    VReader *reader = NULL;
+    VCardEmulOptions *command_line_options = NULL;
+    pthread_t thread_id;
+    int passthru = 0;
+
+    char* cert_names[MAX_CERTS];
+    char* emul_args = NULL;
+    int cert_count = 0;
+    int c;
+
+    while ((c = getopt(argc, argv, "c:e:pd:")) != -1) {
+        switch (c) {
+            case 'c':
+                if (cert_count >= MAX_CERTS) {
+                    printf("too many certificates (max = %d)\n", MAX_CERTS);
+                    exit (5);
+                }
+                cert_names[cert_count++] = optarg;
+                break;
+            case 'e':
+                emul_args = optarg;
+                break;
+            case 'p':
+#ifdef USE_PASSTHRU
+                passthru = 1;
+#else
+                print_usage();
+                exit(4);
+#endif
+                break;
+            case 'd':
+                verbose = get_id_from_string(optarg,1);
+                break;
+        }
+    }
+
+    if (argc - optind != 2) {
+        print_usage();
+        exit (4);
+    }
+
+    if (!passthru && cert_count > 0) {
+        char *new_args;
+        int len, i;
+        /* if we've given some -c options, we clearly we want do so some
+         * software emulation.  add that emulation now. this is NSS Emulator
+         * specific */
+        if (emul_args == NULL) {
+            emul_args = (char*)"db=\"/etc/pki/nssdb\"";
+        }
+#define SOFT_STRING ",soft=(,Virtual Reader,CAC,,"
+             /* 2 == close paren & null */
+        len = strlen(emul_args) + strlen(SOFT_STRING) + 2;
+        for (i=0; i < cert_count; i++) {
+            len +=strlen(cert_names[i])+1; /* 1 == comma */
+        }
+        new_args = malloc(len);
+        strcpy(new_args,emul_args);
+        strcat(new_args,SOFT_STRING);
+        for (i=0; i < cert_count; i++) {
+            strcat(new_args,cert_names[i]);
+            strcat(new_args,",");
+        }
+        strcat(new_args,")");
+        emul_args = new_args;
+    }
+    if (emul_args) {
+#ifdef USE_PASSTHRU
+        command_line_options = passthru ? passthru_emul_options(emul_args) :
+#else
+        command_line_options =
+#endif
+                                          vcard_emul_options(emul_args);
+    }
+
+    qemu_ip = strdup(argv[argc - 2]);
+    qemu_port = (uint16_t)atoi(argv[argc -1]);
+    sock = connect_to_qemu(qemu_ip, qemu_port);
+
+    /* remove whatever reader might be left in qemu,
+     * in case of a unclean previous exit. */
+    send_msg(
+        VSC_ReaderRemove,
+        VSCARD_MINIMAL_READER_ID,
+        NULL,
+        0
+    );
+
+    MUTEX_INIT(write_lock);
+    MUTEX_INIT(pending_reader_lock);
+    CONDITION_INIT(pending_reader_condition);
+
+#ifdef USE_PASSTHRU
+    if (passthru) {
+        passthru_emul_init(command_line_options);
+    } else
+#endif
+        vcard_emul_init(command_line_options);
+
+    /* launch the event_thread. This will trigger reader adds for all the
+     * existing readers */
+    rv = pthread_create(&thread_id, NULL, event_thread, reader);
+    if (rv < 0) {
+        perror("pthread_create");
+        exit (1);
+    }
+
+    printf("> ");
+    fflush(stdout);
+
+    do {
+        fd_set fds;
+
+        FD_ZERO(&fds);
+        FD_SET(1,&fds);
+        FD_SET(sock,&fds);
+
+        /* waiting on input from the socket */
+        rv = select(sock+1, &fds, NULL, NULL, NULL);
+        if (rv < 0) {
+            /* handle error */
+            perror("select");
+            return (7);
+        }
+        if (FD_ISSET(1,&fds)) {
+            do_command();
+        }
+        if (!FD_ISSET(sock,&fds)) {
+            continue;
+        }
+
+        rv = read (
+            sock,
+            &mhHeader,
+            sizeof (mhHeader)
+        );
+        if (rv < sizeof(mhHeader)) {
+            /* Error */
+            if (rv < 0) {
+                perror("header read error\n");
+            } else {
+                printf ("header short read %d\n", rv);
+            }
+            return (8);
+        }
+        if (verbose) {
+            printf ("Header: type=%d, reader_id=%d length=%d (0x%x)\n",
+                    mhHeader.type, mhHeader.reader_id, mhHeader.length,
+                                               mhHeader.length);
+        }
+        switch (mhHeader.type) {
+            case VSC_APDU:
+                rv = read (
+                    sock,
+                    pbSendBuffer,
+                    mhHeader.length
+                );
+                if (rv < 0) {
+                    /* Error */
+                    printf ("read error\n");
+                    close (sock);
+                    return (8);
+                }
+                if (verbose) {
+                    printf (" recv APDU: ");
+                    print_byte_array (pbSendBuffer, mhHeader.length);
+                }
+                /* Transmit recieved APDU */
+                dwSendLength = mhHeader.length;
+                dwRecvLength = sizeof(pbRecvBuffer);
+                reader = vreader_get_reader_by_id(mhHeader.reader_id);
+                reader_status = vreader_xfr_bytes(reader,
+                    pbSendBuffer, dwSendLength,
+                    pbRecvBuffer, &dwRecvLength);
+                if (reader_status == VREADER_OK) {
+                    mhHeader.length = dwRecvLength;
+                if (verbose) {
+                    printf (" send response: ");
+                    print_byte_array (pbRecvBuffer, mhHeader.length);
+                }
+                    send_msg (
+                        VSC_APDU,
+                        mhHeader.reader_id,
+                        pbRecvBuffer,
+                        dwRecvLength
+                    );
+                } else {
+                       rv = reader_status; /* warning: not meaningful */
+                    send_msg (
+                        VSC_Error,
+                        mhHeader.reader_id,
+                        &rv,
+                        sizeof (uint32_t)
+                    );
+                }
+                vreader_free(reader);
+                reader = NULL; /* we've freed it, don't use it by accident
+                                  again */
+                break;
+            case VSC_Reconnect:
+                {
+                    VSCMsgReconnect reconnect;
+
+                    if (read(sock, (char*)&reconnect, mhHeader.length) < 0) {
+                        printf ("read error\n");
+                        close (sock);
+                        return (8);
+                    }
+                    if (reconnect.ip != 0) {
+                        reconnect.ip = ntohl(reconnect.ip);
+                        free(qemu_ip);
+                        qemu_ip = ip_numeric_to_char(reconnect.ip);
+                        qemu_port = reconnect.port;
+                    } else {
+                        printf("info: reconnect with no target ip:port: "
+                               "bumping port by one and reconnecting\n");
+                        qemu_port = qemu_port + 1;
+                    }
+                    /* sent when qemu is migrating, we need to close the socket
+                     * and reconnect. */
+                    close(sock);
+                    printf("reconnecting to %s:%d\n", qemu_ip, qemu_port);
+                    sock = connect_to_qemu(qemu_ip, qemu_port);
+                }
+                break;
+            case VSC_ReaderAddResponse:
+               MUTEX_LOCK(pending_reader_lock);
+                if (pending_reader) {
+                    vreader_set_id(pending_reader, mhHeader.reader_id);
+                    vreader_free(pending_reader);
+                    pending_reader = NULL;
+                    CONDITION_NOTIFY(pending_reader_condition);
+                }
+                MUTEX_UNLOCK(pending_reader_lock);
+                break;
+            case VSC_Error:
+                rv = read (
+                    sock,
+                    pbSendBuffer,
+                    mhHeader.length
+                );
+                error_msg = (VSCMsgError *) pbSendBuffer;
+                printf("error: qemu refused to add reader\n");
+                if (error_msg->code == VSC_CANNOT_ADD_MORE_READERS) {
+                    /* clear pending reader, qemu can't handle any more */
+                    MUTEX_LOCK(pending_reader_lock);
+                    if (pending_reader) {
+                        pending_reader = NULL;
+                        /* make sure the event loop doesn't hang */
+                        CONDITION_NOTIFY(pending_reader_condition);
+                    }
+                    MUTEX_UNLOCK(pending_reader_lock);
+                }
+                break;
+            default:
+                printf ("Default\n");
+                return 0;
+        }
+    } while (rv >= 0);
+
+
+    return (0);
+}
-- 
1.7.3.4

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

* [Qemu-devel] [PATCH 4/7] ccid: add ccid-card-emulated device (v2)
  2011-01-11  8:42 [Qemu-devel] [PATCH 0/7] usb-ccid (v15) Alon Levy
                   ` (2 preceding siblings ...)
  2011-01-11  8:42 ` [Qemu-devel] [PATCH 3/7] libcacard: initial commit after coding style fixes Alon Levy
@ 2011-01-11  8:42 ` Alon Levy
  2011-01-25 14:21   ` Anthony Liguori
  2011-01-11  8:42 ` [Qemu-devel] [PATCH 5/7] ccid: add docs Alon Levy
                   ` (4 subsequent siblings)
  8 siblings, 1 reply; 23+ messages in thread
From: Alon Levy @ 2011-01-11  8:42 UTC (permalink / raw)
  To: qemu-devel

This devices uses libcacard (internal) to emulate a smartcard conforming
to the CAC standard. It attaches to the usb-ccid bus. Usage instructions
(example command lines) are in the following patch in docs/ccid.txt. It
uses libcacard which uses nss, so it can work with both hw cards and
certificates (files).

changes from v1:
    remove stale comments, use only c-style comments
    bugfix, forgot to set recv_len
    change reader name to 'Virtual Reader'

Signed-off-by: Alon Levy <alevy@redhat.com>
---
 Makefile.objs           |    2 +-
 hw/ccid-card-emulated.c |  534 +++++++++++++++++++++++++++++++++++++++++++++++
 hw/ccid-card-passthru.c |    1 -
 3 files changed, 535 insertions(+), 2 deletions(-)
 create mode 100644 hw/ccid-card-emulated.c

diff --git a/Makefile.objs b/Makefile.objs
index 6a0030b..303b642 100644
--- a/Makefile.objs
+++ b/Makefile.objs
@@ -197,7 +197,7 @@ hw-obj-$(CONFIG_FDC) += fdc.o
 hw-obj-$(CONFIG_ACPI) += acpi.o acpi_piix4.o
 hw-obj-$(CONFIG_APM) += pm_smbus.o apm.o
 hw-obj-$(CONFIG_DMA) += dma.o
-hw-obj-$(CONFIG_SMARTCARD) += usb-ccid.o ccid-card-passthru.o
+hw-obj-$(CONFIG_SMARTCARD) += usb-ccid.o ccid-card-passthru.o ccid-card-emulated.o
 
 # PPC devices
 hw-obj-$(CONFIG_OPENPIC) += openpic.o
diff --git a/hw/ccid-card-emulated.c b/hw/ccid-card-emulated.c
new file mode 100644
index 0000000..5531ce1
--- /dev/null
+++ b/hw/ccid-card-emulated.c
@@ -0,0 +1,534 @@
+/*
+ * CCID Card Device. Emulated card.
+ *
+ * It can be used to provide access to the local hardware in a non exclusive
+ * way, or it can use certificates. It requires the usb-ccid bus.
+ *
+ * Usage 1: standard, mirror hardware reader+card:
+ * qemu .. -usb -device usb-ccid -device ccid-card-emulated
+ *
+ * Usage 2: use certificates, no hardware required
+ * one time: create the certificates:
+ *  for i in 1 2 3; do certutil -d /etc/pki/nssdb -x -t "CT,CT,CT" -S -s "CN=user$i" -n user$i; done
+ * qemu .. -usb -device usb-ccid -device ccid-card-emulated,cert1=user1,cert2=user2,cert3=user3
+ *
+ * If you use a non default db for the certificates you can specify it using the db parameter.
+ *
+ *
+ * Copyright (c) 2010 Red Hat.
+ * Written by Alon Levy.
+ *
+ * This code is licenced under the LGPL.
+ */
+
+#include <pthread.h>
+#include <eventt.h>
+#include <vevent.h>
+#include <vreader.h>
+#include <vcard_emul.h>
+#include "qemu-char.h"
+#include "monitor.h"
+#include "hw/ccid.h"
+
+#define DPRINTF(card, lvl, fmt, ...) \
+do { if (lvl <= card->debug) { printf("ccid-card-emul: %s: " fmt , __func__, ## __VA_ARGS__); } } while (0)
+
+#define EMULATED_DEV_NAME "ccid-card-emulated"
+
+#define BACKEND_NSS_EMULATED "nss-emulated" /* the default */
+#define BACKEND_CERTIFICATES "certificates"
+
+typedef struct EmulatedState EmulatedState;
+
+enum {
+    EMUL_READER_INSERT = 0,
+    EMUL_READER_REMOVE,
+    EMUL_CARD_INSERT,
+    EMUL_CARD_REMOVE,
+    EMUL_GUEST_APDU,
+    EMUL_RESPONSE_APDU,
+    EMUL_ERROR,
+};
+
+static const char* emul_event_to_string(uint32_t emul_event)
+{
+    switch (emul_event) {
+        case EMUL_READER_INSERT: return "EMUL_READER_INSERT";
+        case EMUL_READER_REMOVE: return "EMUL_READER_REMOVE";
+        case EMUL_CARD_INSERT: return "EMUL_CARD_INSERT";
+        case EMUL_CARD_REMOVE: return "EMUL_CARD_REMOVE";
+        case EMUL_GUEST_APDU: return "EMUL_GUEST_APDU";
+        case EMUL_RESPONSE_APDU: return "EMUL_RESPONSE_APDU";
+        case EMUL_ERROR: return "EMUL_ERROR";
+        default:
+            break;
+    }
+    return "UNKNOWN";
+}
+
+typedef struct EmulEvent {
+    QSIMPLEQ_ENTRY(EmulEvent) entry;
+    union {
+        struct {
+            uint32_t type;
+        } gen;
+        struct {
+            uint32_t type;
+            uint64_t code;
+        } error;
+        struct {
+            uint32_t type;
+            uint32_t len;
+            uint8_t data[];
+        } data;
+    } p;
+} EmulEvent;
+
+#define MAX_ATR_SIZE 40
+struct EmulatedState {
+    CCIDCardState base;
+    uint8_t  debug;
+    char*    backend;
+    char*    cert1;
+    char*    cert2;
+    char*    cert3;
+    char*    db;
+    uint8_t  atr[MAX_ATR_SIZE];
+    uint8_t  atr_length;
+    QSIMPLEQ_HEAD(event_list, EmulEvent) event_list;
+    pthread_mutex_t event_list_mutex;
+    VReader *reader;
+    QSIMPLEQ_HEAD(guest_apdu_list, EmulEvent) guest_apdu_list;
+    pthread_mutex_t vreader_mutex; /* and guest_apdu_list mutex */
+    pthread_mutex_t handle_apdu_mutex;
+    pthread_cond_t handle_apdu_cond;
+    int      pipe[2];
+    int      quit_apdu_thread;
+    pthread_mutex_t apdu_thread_quit_mutex;
+    pthread_cond_t apdu_thread_quit_cond;
+};
+
+static void emulated_apdu_from_guest(CCIDCardState *base, const uint8_t *apdu, uint32_t len)
+{
+    EmulatedState *card = DO_UPCAST(EmulatedState, base, base);
+    EmulEvent *event = (EmulEvent*)malloc(sizeof(EmulEvent) + len);
+
+    assert(event);
+    event->p.data.type = EMUL_GUEST_APDU;
+    event->p.data.len = len;
+    memcpy(event->p.data.data, apdu, len);
+    pthread_mutex_lock(&card->vreader_mutex);
+    QSIMPLEQ_INSERT_TAIL(&card->guest_apdu_list, event, entry);
+    pthread_mutex_unlock(&card->vreader_mutex);
+    pthread_mutex_lock(&card->handle_apdu_mutex);
+    pthread_cond_signal(&card->handle_apdu_cond);
+    pthread_mutex_unlock(&card->handle_apdu_mutex);
+}
+
+static const uint8_t* emulated_get_atr(CCIDCardState *base, uint32_t *len)
+{
+    EmulatedState *card = DO_UPCAST(EmulatedState, base, base);
+
+    *len = card->atr_length;
+    return card->atr;
+}
+
+static void emulated_push_event(EmulatedState *card, EmulEvent *event)
+{
+    pthread_mutex_lock(&card->event_list_mutex);
+    QSIMPLEQ_INSERT_TAIL(&(card->event_list), event, entry);
+    pthread_mutex_unlock(&card->event_list_mutex);
+    if (write(card->pipe[1], card, 1) != 1) {
+        DPRINTF(card, 1, "write to pipe failed\n");
+    }
+}
+
+static void emulated_push_type(EmulatedState *card, uint32_t type)
+{
+    EmulEvent *event = (EmulEvent*)malloc(sizeof(EmulEvent));
+
+    assert(event);
+    event->p.gen.type = type;
+    emulated_push_event(card, event);
+}
+
+static void emulated_push_error(EmulatedState *card, uint64_t code)
+{
+    EmulEvent *event = (EmulEvent*)malloc(sizeof(EmulEvent));
+
+    assert(event);
+    event->p.error.type = EMUL_ERROR;
+    event->p.error.code = code;
+    emulated_push_event(card, event);
+}
+
+static void emulated_push_data_type(EmulatedState *card, uint32_t type,
+    const uint8_t *data, uint32_t len)
+{
+    EmulEvent *event = (EmulEvent*)malloc(sizeof(EmulEvent) + len);
+
+    assert(event);
+    event->p.data.type = type;
+    event->p.data.len = len;
+    memcpy(event->p.data.data, data, len);
+    emulated_push_event(card, event);
+}
+
+static void emulated_push_reader_insert(EmulatedState *card)
+{
+    emulated_push_type(card, EMUL_READER_INSERT);
+}
+
+static void emulated_push_reader_remove(EmulatedState *card)
+{
+    emulated_push_type(card, EMUL_READER_REMOVE);
+}
+
+static void emulated_push_card_insert(EmulatedState *card,
+    const uint8_t *atr, uint32_t len)
+{
+    emulated_push_data_type(card, EMUL_CARD_INSERT, atr, len);
+}
+
+static void emulated_push_card_remove(EmulatedState *card)
+{
+    emulated_push_type(card, EMUL_CARD_REMOVE);
+}
+
+static void emulated_push_response_apdu(EmulatedState *card,
+    const uint8_t* apdu, uint32_t len)
+{
+    emulated_push_data_type(card, EMUL_RESPONSE_APDU, apdu, len);
+}
+
+#define APDU_BUF_SIZE 270
+static void *handle_apdu_thread(void* arg)
+{
+    EmulatedState *card = arg;
+    uint8_t recv_data[APDU_BUF_SIZE];
+    int recv_len;
+    VReaderStatus reader_status;
+    EmulEvent *event;
+
+    while (1) {
+        pthread_mutex_lock(&card->handle_apdu_mutex);
+        pthread_cond_wait(&card->handle_apdu_cond, &card->handle_apdu_mutex);
+        pthread_mutex_unlock(&card->handle_apdu_mutex);
+        if (card->quit_apdu_thread) {
+            card->quit_apdu_thread = 0; // debugging
+            break;
+        }
+        pthread_mutex_lock(&card->vreader_mutex);
+        while (!QSIMPLEQ_EMPTY(&card->guest_apdu_list)) {
+            event = QSIMPLEQ_FIRST(&card->guest_apdu_list);
+            assert((unsigned long)event > 1000);
+            QSIMPLEQ_REMOVE_HEAD(&card->guest_apdu_list, entry);
+            if (event->p.data.type != EMUL_GUEST_APDU) {
+                DPRINTF(card, 1, "unexpected message in handle_apdu_thread\n");
+                free(event);
+                continue;
+            }
+            if (card->reader == NULL) {
+                DPRINTF(card, 1, "reader is NULL\n");
+                free(event);
+                continue;
+            }
+            recv_len = sizeof(recv_data);
+            reader_status = vreader_xfr_bytes(card->reader,
+                    event->p.data.data, event->p.data.len,
+                    recv_data, &recv_len);
+            DPRINTF(card, 2, "got back apdu of length %d\n", recv_len);
+            if (reader_status == VREADER_OK) {
+                emulated_push_response_apdu(card, recv_data, recv_len);
+            } else {
+                emulated_push_error(card, reader_status);
+            }
+            free(event);
+        }
+        pthread_mutex_unlock(&card->vreader_mutex);
+    }
+    pthread_mutex_lock(&card->apdu_thread_quit_mutex);
+    pthread_cond_signal(&card->apdu_thread_quit_cond);
+    pthread_mutex_unlock(&card->apdu_thread_quit_mutex);
+    return NULL;
+}
+
+static void *event_thread(void *arg)
+{
+    int atr_len = MAX_ATR_SIZE;
+    uint8_t atr[MAX_ATR_SIZE];
+    VEvent *event = NULL;
+    EmulatedState *card = arg;
+
+    while (1) {
+        const char *reader_name;
+
+        event = vevent_wait_next_vevent();
+        if (event == NULL || event->type == VEVENT_LAST) {
+            break;
+        }
+        if (event->type != VEVENT_READER_INSERT) {
+            if (card->reader == NULL && event->reader != NULL) {
+                // Happens after device_add followed by card remove or insert.
+                // XXX: create synthetic add_reader events if vcard_emul_init
+                // already called, which happens if device_del and device_add are
+                // called
+                card->reader = vreader_reference(event->reader);
+            } else {
+                if (event->reader != card->reader) {
+                    fprintf(stderr, "ERROR: wrong reader: quiting event_thread\n");
+                    break;
+                }
+            }
+        }
+        switch (event->type) {
+        case VEVENT_READER_INSERT:
+            /* TODO: take a specific reader. i.e. track which reader
+             * we are seeing here, check it is the one we want (the first,
+             * or by a particular name), and ignore if we don't want it.
+             */
+            reader_name = vreader_get_name(event->reader);
+            if (card->reader != NULL) {
+                DPRINTF(card, 2, "READER INSERT - replacing %s with %s\n",
+                    vreader_get_name(card->reader), reader_name);
+                pthread_mutex_lock(&card->vreader_mutex);
+                vreader_free(card->reader);
+                pthread_mutex_unlock(&card->vreader_mutex);
+                emulated_push_reader_remove(card);
+            }
+            pthread_mutex_lock(&card->vreader_mutex);
+            DPRINTF(card, 2, "READER INSERT %s\n", reader_name);
+            card->reader = vreader_reference(event->reader);
+            pthread_mutex_unlock(&card->vreader_mutex);
+            emulated_push_reader_insert(card);
+            break;
+        case VEVENT_READER_REMOVE:
+            DPRINTF(card, 2, " READER REMOVE: %s \n",
+                    vreader_get_name(event->reader));
+            pthread_mutex_lock(&card->vreader_mutex);
+            vreader_free(card->reader);
+            card->reader = NULL;
+            pthread_mutex_unlock(&card->vreader_mutex);
+            emulated_push_reader_remove(card);
+            break;
+        case VEVENT_CARD_INSERT:
+            /* get the ATR (intended as a response to a power on from the
+             * reader */
+            atr_len = MAX_ATR_SIZE;
+            vreader_power_on(event->reader, atr, &atr_len);
+            card->atr_length = (uint8_t)atr_len;
+            DPRINTF(card, 2, " CARD INSERT\n");
+            emulated_push_card_insert(card, atr, atr_len);
+            break;
+        case VEVENT_CARD_REMOVE:
+            DPRINTF(card, 2, " CARD REMOVE\n");
+            emulated_push_card_remove(card);
+            break;
+        case VEVENT_LAST: /* quit */
+            vevent_delete(event);
+            return NULL;
+            break;
+        default:
+            break;
+        }
+        vevent_delete(event);
+    }
+    return NULL;
+}
+
+static void pipe_read(void *opaque)
+{
+    EmulatedState *card = opaque;
+    EmulEvent *event, *next;
+    char dummy;
+    int len;
+
+    do {
+        len = read(card->pipe[0], &dummy, sizeof(dummy));
+    } while (len == sizeof(dummy));
+    pthread_mutex_lock(&card->event_list_mutex);
+    QSIMPLEQ_FOREACH_SAFE(event, &card->event_list, entry, next) {
+        DPRINTF(card, 2, "event %s\n", emul_event_to_string(event->p.gen.type));
+        switch (event->p.gen.type) {
+            case EMUL_RESPONSE_APDU:
+                ccid_card_send_apdu_to_guest(&card->base, event->p.data.data,
+                    event->p.data.len);
+                break;
+            case EMUL_READER_INSERT:
+                ccid_card_ccid_attach(&card->base);
+                break;
+            case EMUL_READER_REMOVE:
+                ccid_card_ccid_detach(&card->base);
+                break;
+            case EMUL_CARD_INSERT:
+                assert(event->p.data.len <= MAX_ATR_SIZE);
+                card->atr_length = event->p.data.len;
+                memcpy(card->atr, event->p.data.data, card->atr_length);
+                ccid_card_card_inserted(&card->base);
+                break;
+            case EMUL_CARD_REMOVE:
+                ccid_card_card_removed(&card->base);
+                break;
+            case EMUL_ERROR:
+                ccid_card_card_error(&card->base, event->p.error.code);
+                break;
+            default:
+                DPRINTF(card, 2, "unexpected event\n");
+                break;
+        }
+        free(event);
+    }
+    QSIMPLEQ_INIT(&card->event_list);
+    pthread_mutex_unlock(&card->event_list_mutex);
+}
+
+static int init_pipe_signaling(EmulatedState *card)
+{
+   if (pipe(card->pipe) < 0) {
+       DPRINTF(card, 2, "pipe creation failed\n");
+       return -1;
+   }
+   fcntl(card->pipe[0], F_SETFL, O_NONBLOCK);
+   fcntl(card->pipe[1], F_SETFL, O_NONBLOCK);
+   fcntl(card->pipe[0], F_SETOWN, getpid());
+   qemu_set_fd_handler(card->pipe[0], pipe_read, NULL, card);
+   return 0;
+}
+
+#define CERTIFICATES_DEFAULT_DB "/etc/pki/nssdb"
+#define CERTIFICATES_ARGS_TEMPLATE "db=\"%s\" use_hw=no soft=(,Virtual Reader,CAC,,%s,%s,%s)"
+
+static int wrap_vcard_emul_init(VCardEmulOptions *options)
+{
+    static int called = 0;
+    static int options_was_null = 0;
+
+    if (called) {
+        if ((options == NULL) != options_was_null) {
+            printf("%s: warning: running emulated with certificates and emulated side by side is not supported\n", __FUNCTION__);
+            return VCARD_EMUL_FAIL;
+        }
+        vcard_emul_replay_insertion_events();
+        return VCARD_EMUL_OK;
+    }
+    options_was_null = (options == NULL);
+    called = 1;
+    return vcard_emul_init(options);
+}
+
+static int emulated_initialize_vcard_from_certificates(EmulatedState *card)
+{
+    char emul_args[200];
+    VCardEmulOptions *options = NULL;
+
+    snprintf(emul_args, sizeof(emul_args) - 1, CERTIFICATES_ARGS_TEMPLATE,
+        card->db ? card->db : CERTIFICATES_DEFAULT_DB,
+        card->cert1, card->cert2, card->cert3);
+    options = vcard_emul_options(emul_args);
+    if (options == NULL) {
+        printf("%s: warning: not using certificates due to initialization error\n", __func__);
+    }
+    return wrap_vcard_emul_init(options);
+}
+
+static int emulated_initfn(CCIDCardState *base)
+{
+    EmulatedState *card = DO_UPCAST(EmulatedState, base, base);
+    int rv;
+    pthread_t thread_id;
+    VCardEmulError ret;
+
+    QSIMPLEQ_INIT(&card->event_list);
+    QSIMPLEQ_INIT(&card->guest_apdu_list);
+    pthread_mutex_init(&card->event_list_mutex, NULL);
+    pthread_mutex_init(&card->vreader_mutex, NULL);
+    pthread_mutex_init(&card->handle_apdu_mutex, NULL);
+    pthread_cond_init(&card->handle_apdu_cond, NULL);
+    card->reader = NULL;
+    card->quit_apdu_thread = 0;
+    if (init_pipe_signaling(card) < 0) {
+        return -1;
+    }
+    if (!card->backend) {
+        card->backend = strdup((char*)BACKEND_NSS_EMULATED);
+    }
+    /* TODO: a passthru backened that works on local machine. third card type? */
+    if (strcmp(BACKEND_CERTIFICATES, card->backend) == 0
+        && card->cert1 != NULL && card->cert2 != NULL && card->cert3 != NULL) {
+        ret = emulated_initialize_vcard_from_certificates(card);
+    } else {
+        if (strcmp(BACKEND_CERTIFICATES, card->backend) == 0) {
+            printf("%s: you must provide all three certs for certificates backend\n",
+                EMULATED_DEV_NAME);
+            return -1;
+        }
+        if (card->backend && strcmp(BACKEND_NSS_EMULATED, card->backend) != 0) {
+            printf("%s: bad backend specified. The options are:\n%s (default), %s.\n",
+                EMULATED_DEV_NAME, BACKEND_NSS_EMULATED, BACKEND_CERTIFICATES);
+            return -1;
+        }
+        /* default to mirroring the local hardware readers */
+        ret = wrap_vcard_emul_init(NULL);
+    }
+    if (ret != VCARD_EMUL_OK) {
+        printf("%s: failed to initialize vcard\n", EMULATED_DEV_NAME);
+        return -1;
+    }
+    rv = pthread_create(&thread_id, NULL, event_thread, card);
+    if (rv < 0) {
+        printf("%s: error creating event thread\n", EMULATED_DEV_NAME);
+        return -1;
+    }
+    rv = pthread_create(&thread_id, NULL, handle_apdu_thread, card);
+    if (rv < 0) {
+        printf("%s: error creating handle_apdu thread\n", EMULATED_DEV_NAME);
+        return -1;
+    }
+    return 0;
+}
+
+static int emulated_exitfn(CCIDCardState *base)
+{
+    EmulatedState *card = DO_UPCAST(EmulatedState, base, base);
+    VEvent *vevent = vevent_new(VEVENT_LAST, NULL, NULL);
+
+    vevent_queue_vevent(vevent); /* stop vevent thread */
+    pthread_mutex_lock(&card->apdu_thread_quit_mutex);
+    card->quit_apdu_thread = 1; /* stop handle_apdu thread */
+    pthread_cond_signal(&card->handle_apdu_cond);
+    pthread_cond_wait(&card->apdu_thread_quit_cond, &card->apdu_thread_quit_mutex);
+    /* handle_apdu thread stopped, can destroy all of it's mutexes */
+    pthread_cond_destroy(&card->handle_apdu_cond);
+    pthread_cond_destroy(&card->apdu_thread_quit_cond);
+    pthread_mutex_destroy(&card->apdu_thread_quit_mutex);
+    pthread_mutex_destroy(&card->handle_apdu_mutex);
+    pthread_mutex_destroy(&card->vreader_mutex);
+    pthread_mutex_destroy(&card->event_list_mutex);
+    return 0;
+}
+
+static CCIDCardInfo emulated_card_info = {
+    .qdev.name = EMULATED_DEV_NAME,
+    .qdev.size = sizeof(EmulatedState),
+    .initfn = emulated_initfn,
+    .exitfn = emulated_exitfn,
+    .get_atr = emulated_get_atr,
+    .apdu_from_guest = emulated_apdu_from_guest,
+    .qdev.unplug    = qdev_simple_unplug_cb,
+    .qdev.props     = (Property[]) {
+        DEFINE_PROP_STRING("backend", EmulatedState, backend),
+        DEFINE_PROP_STRING("cert1", EmulatedState, cert1),
+        DEFINE_PROP_STRING("cert2", EmulatedState, cert2),
+        DEFINE_PROP_STRING("cert3", EmulatedState, cert3),
+        DEFINE_PROP_STRING("db", EmulatedState, db),
+        DEFINE_PROP_UINT8("debug", EmulatedState, debug, 0),
+        DEFINE_PROP_END_OF_LIST(),
+    },
+};
+
+static void ccid_card_emulated_register_devices(void)
+{
+    ccid_card_qdev_register(&emulated_card_info);
+}
+
+device_init(ccid_card_emulated_register_devices)
diff --git a/hw/ccid-card-passthru.c b/hw/ccid-card-passthru.c
index 6ec4f21..f9fb82c 100644
--- a/hw/ccid-card-passthru.c
+++ b/hw/ccid-card-passthru.c
@@ -253,7 +253,6 @@ static CCIDCardInfo passthru_card_info = {
     .exitfn = passthru_exitfn,
     .get_atr = passthru_get_atr,
     .apdu_from_guest = passthru_apdu_from_guest,
-    .qdev.unplug    = qdev_simple_unplug_cb,
     .qdev.props     = (Property[]) {
         DEFINE_PROP_CHR("chardev", PassthruState, cs),
         DEFINE_PROP_UINT8("debug", PassthruState, debug, 0),
-- 
1.7.3.4

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

* [Qemu-devel] [PATCH 5/7] ccid: add docs
  2011-01-11  8:42 [Qemu-devel] [PATCH 0/7] usb-ccid (v15) Alon Levy
                   ` (3 preceding siblings ...)
  2011-01-11  8:42 ` [Qemu-devel] [PATCH 4/7] ccid: add ccid-card-emulated device (v2) Alon Levy
@ 2011-01-11  8:42 ` Alon Levy
  2011-01-11  8:42 ` [Qemu-devel] [PATCH 6/7] ccid: configure: add --enable/disable and nss only disable Alon Levy
                   ` (3 subsequent siblings)
  8 siblings, 0 replies; 23+ messages in thread
From: Alon Levy @ 2011-01-11  8:42 UTC (permalink / raw)
  To: qemu-devel

Add documentation for the usb-ccid device and accompanying two card
devices, ccid-card-emulated and ccid-card-passthru.
---
 docs/ccid.txt |  135 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 files changed, 135 insertions(+), 0 deletions(-)
 create mode 100644 docs/ccid.txt

diff --git a/docs/ccid.txt b/docs/ccid.txt
new file mode 100644
index 0000000..b8e504a
--- /dev/null
+++ b/docs/ccid.txt
@@ -0,0 +1,135 @@
+Qemu CCID Device Documentation.
+
+Contents
+1. USB CCID device
+2. Building
+3. Using ccid-card-emulated with hardware
+4. Using ccid-card-emulated with certificates
+5. Using ccid-card-passthru with client side hardware
+6. Using ccid-card-passthru with client side certificates
+7. Passthrough protocol scenario
+8. libcacard
+
+1. USB CCID device
+
+The USB CCID device is a USB device implementing the CCID specification, which
+lets one connect smart card readers that implement the same spec. For more
+information see the specification:
+
+ Universal Serial Bus
+ Device Class: Smart Card
+ CCID
+ Specification for
+ Integrated Circuit(s) Cards Interface Devices
+ Revision 1.1
+ April 22rd, 2005
+
+Smartcard are used for authentication, single sign on, decryption in
+public/private schemes and digital signatures. A smartcard reader on the client
+cannot be used on a guest with simple usb passthrough since it will then not be
+available on the client, possibly locking the computer when it is "removed". On
+the other hand this device can let you use the smartcard on both the client and
+the guest machine. It is also possible to have a completely virtual smart card
+reader and smart card (i.e. not backed by a physical device) using this device.
+
+2. Building
+
+The cryptographic functions and access to the physical card is done via NSS.
+
+Installing NSS:
+
+In redhat/fedora:
+    yum install nss-devel
+In ubuntu/debian:
+    apt-get install libnss3-dev
+    (not tested on ubuntu)
+
+Configuring and building:
+    ./configure --enable-smartcard && make
+
+3. Using ccid-card-emulated with hardware
+
+Assuming you have a working smartcard on the host with the current
+user, using NSS, qemu acts as another NSS client using ccid-card-emulated:
+
+    qemu -usb -device usb-ccid -device ccid-card-emualated
+
+4. Using ccid-card-emulated with certificates
+
+You must create the certificates. This is a one time process. We use NSS
+certificates:
+
+    certutil -d /etc/pki/nssdb -x -t "CT,CT,CT" -S -s "CN=cert1" -n cert1
+
+Note: you must have exactly three certificates.
+
+Assuming the current user can access the certificates (use certutil -L to
+verify), you can use the emulated card type with the certificates backend:
+
+    qemu -usb -device usb-ccid -device ccid-card-emulated,backend=certificates,cert1=cert1,cert2=cert2,cert3=cert3
+
+5. Using ccid-card-passthru with client side hardware
+
+on the host specify the ccid-card-passthru device with a suitable chardev:
+
+    qemu -chardev socket,server,host=0.0.0.0,port=2001,id=ccid,nowait -usb -device usb-ccid -device ccid-card-passthru,chardev=ccid
+
+on the client run vscclient, built when you built the libcacard library:
+    libcacard/vscclient <qemu-host> 2001
+
+6. Using ccid-card-passthru with client side certificates
+
+Run qemu as per #5, and run vscclient as follows:
+(Note: vscclient command line interface is in a state of change)
+
+    libcacard/vscclient -e "db=\"/etc/pki/nssdb\" use_hw=no soft=(,Test,CAC,,cert1,cert2,cert3)" <qemu-host> 2001
+
+7. Passthrough protocol scenario
+
+This is a typical interchange of messages when using the passthru card device.
+usb-ccid is a usb device. It defaults to an unattached usb device on startup.
+usb-ccid expects a chardev and expects the protocol defined in
+cac_card/vscard_common.h to be passed over that.
+The usb-ccid device can be in one of three modes:
+ * detached
+ * attached with no card
+ * attached with card
+
+A typical interchange is: (the arrow shows who started each exchange, it can be client
+originated or guest originated)
+
+client event      |      vscclient           |    passthru    |     usb-ccid  |  guest event
+----------------------------------------------------------------------------------------------
+                  |      VSC_Init            |                |               |
+                  |      VSC_ReaderAdd       |                |     attach    |
+                  |                          |                |               |  sees new usb device.
+card inserted ->  |                          |                |               |
+                  |      VSC_ATR             |   insert       |     insert    |  see new card
+                  |                          |                |               |
+                  |      VSC_APDU            |   VSC_APDU     |               | <- guest sends APDU
+client<->physical |                          |                |               |
+card APDU exchange|                          |                |               |
+client response ->|      VSC_APDU            |   VSC_APDU     |               |  receive APDU response
+                                                    ...
+                                    [APDU<->APDU repeats several times]
+                                                    ...
+card removed  ->  |                          |                |               |
+                  |      VSC_CardRemove      |   remove       |    remove     |   card removed
+                                                    ...
+                                    [(card insert, apdu's, card remove) repeat]
+                                                    ...
+kill/quit         |                          |                |               |
+  vscclient       |                          |                |               |
+                  |      VSC_ReaderRemove    |                |    detach     |
+                  |                          |                |               |   usb device removed.
+
+
+8. libcacard
+
+ccid-card-passthru and vscclient use libcacard as the card emulator.
+libcacard implements a completely virtual CAC (DoD standard for smart cards)
+compliant card and uses NSS to actually retrive certificates and do any
+encryption using the backend (real reader + card or file backed certificates).
+
+For documentation of cac_card see README in libcacard subdirectory.
+
-- 
1.7.3.4

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

* [Qemu-devel] [PATCH 6/7] ccid: configure: add --enable/disable and nss only disable
  2011-01-11  8:42 [Qemu-devel] [PATCH 0/7] usb-ccid (v15) Alon Levy
                   ` (4 preceding siblings ...)
  2011-01-11  8:42 ` [Qemu-devel] [PATCH 5/7] ccid: add docs Alon Levy
@ 2011-01-11  8:42 ` Alon Levy
  2011-01-11  8:42 ` [Qemu-devel] [PATCH 6/6] ccid: configure: add --enable-smartcard and --disable-smartcard Alon Levy
                   ` (2 subsequent siblings)
  8 siblings, 0 replies; 23+ messages in thread
From: Alon Levy @ 2011-01-11  8:42 UTC (permalink / raw)
  To: qemu-devel

add --enable-smartcard and --disable-smartcard flags, and let the nss
check only disable building the ccid-card-emulated device, since both
the usb-ccid and ccid-card-passthru don't depend on it.
---
 Makefile.objs   |    3 ++-
 Makefile.target |    2 +-
 configure       |   39 ++++++++++++++++++++++++++++-----------
 3 files changed, 31 insertions(+), 13 deletions(-)

diff --git a/Makefile.objs b/Makefile.objs
index 303b642..03cc91e 100644
--- a/Makefile.objs
+++ b/Makefile.objs
@@ -197,7 +197,8 @@ hw-obj-$(CONFIG_FDC) += fdc.o
 hw-obj-$(CONFIG_ACPI) += acpi.o acpi_piix4.o
 hw-obj-$(CONFIG_APM) += pm_smbus.o apm.o
 hw-obj-$(CONFIG_DMA) += dma.o
-hw-obj-$(CONFIG_SMARTCARD) += usb-ccid.o ccid-card-passthru.o ccid-card-emulated.o
+hw-obj-$(CONFIG_SMARTCARD) += usb-ccid.o ccid-card-passthru.o
+hw-obj-$(CONFIG_SMARTCARD_NSS) += ccid-card-emulated.o
 
 # PPC devices
 hw-obj-$(CONFIG_OPENPIC) += openpic.o
diff --git a/Makefile.target b/Makefile.target
index 419a530..0222c6b 100644
--- a/Makefile.target
+++ b/Makefile.target
@@ -335,7 +335,7 @@ obj-y += $(addprefix $(HWDIR)/, $(hw-obj-y))
 
 endif # CONFIG_SOFTMMU
 
-obj-y += $(addprefix ../libcacard/, $(libcacard-$(CONFIG_SMARTCARD)))
+obj-y += $(addprefix ../libcacard/, $(libcacard-$(CONFIG_SMARTCARD_NSS)))
 
 obj-y += $(addprefix ../, $(trace-obj-y))
 obj-$(CONFIG_GDBSTUB_XML) += gdbstub-xml.o
diff --git a/configure b/configure
index 4567057..de2e498 100755
--- a/configure
+++ b/configure
@@ -335,6 +335,7 @@ trace_file="trace"
 spice=""
 rbd=""
 smartcard="yes"
+smartcard_nss="yes"
 
 # OS specific
 if check_define __linux__ ; then
@@ -748,6 +749,10 @@ for opt do
   ;;
   --enable-rbd) rbd="yes"
   ;;
+  --disable-smartcard) smartcard="no"
+  ;;
+  --enable-smartcard) smartcard="yes"
+  ;;
   *) echo "ERROR: unknown option $opt"; show_help="yes"
   ;;
   esac
@@ -942,6 +947,8 @@ echo "                           Default:trace-<pid>"
 echo "  --disable-spice          disable spice"
 echo "  --enable-spice           enable spice"
 echo "  --enable-rbd             enable building the rados block device (rbd)"
+echo "  --disable-smartcard      disable smartcard support"
+echo "  --enable-smartcard       enable smartcard support"
 echo ""
 echo "NOTE: The object files are built at the place where configure is launched"
 exit 1
@@ -2210,16 +2217,21 @@ EOF
 fi
 
 # check for libcacard for smartcard support
-smartcard_cflags="-I\$(SRC_PATH)/libcacard"
-libcacard_libs=$($pkgconfig --libs nss 2>/dev/null)
-libcacard_cflags=$($pkgconfig --cflags nss)
-# TODO - what's the minimal nss version we support?
-if $pkgconfig --atleast-version=3.12.8 nss; then
-    smartcard="yes"
-    QEMU_CFLAGS="$QEMU_CFLAGS $smartcard_cflags $libcacard_cflags"
-    LIBS="$libcacard_libs $LIBS"
-else
-    smartcard="no"
+if test "$smartcard" != "no" ; then
+    smartcard_cflags="-I\$(SRC_PATH)/libcacard"
+    libcacard_libs=$($pkgconfig --libs nss 2>/dev/null)
+    libcacard_cflags=$($pkgconfig --cflags nss 2>/dev/null)
+    # TODO - what's the minimal nss version we support?
+    if $pkgconfig --atleast-version=3.12.8 nss >/dev/null 2>&1 ; then
+        smartcard_nss="yes"
+        QEMU_CFLAGS="$QEMU_CFLAGS $smartcard_cflags $libcacard_cflags"
+        LIBS="$libcacard_libs $LIBS"
+    else
+        smartcard_nss="no"
+    fi
+fi
+if test "$smartcard" == "no" ; then
+    smartcard_nss="no"
 fi
 
 ##########################################
@@ -2456,6 +2468,7 @@ echo "spice support     $spice"
 echo "rbd support       $rbd"
 echo "xfsctl support    $xfs"
 echo "smartcard support $smartcard"
+echo "   \_ nss support $smartcard_nss"
 
 if test $sdl_too_old = "yes"; then
 echo "-> Your SDL version is too old - please upgrade to have SDL support"
@@ -2729,6 +2742,10 @@ if test "$smartcard" = "yes" ; then
   echo "CONFIG_SMARTCARD=y" >> $config_host_mak
 fi
 
+if test "$smartcard_nss" = "yes" ; then
+  echo "CONFIG_SMARTCARD_NSS=y" >> $config_host_mak
+fi
+
 # XXX: suppress that
 if [ "$bsd" = "yes" ] ; then
   echo "CONFIG_BSD=y" >> $config_host_mak
@@ -3059,7 +3076,7 @@ fi
 if test "$target_darwin_user" = "yes" ; then
   echo "CONFIG_DARWIN_USER=y" >> $config_target_mak
 fi
-if test "$smartcard" = "yes" ; then
+if test "$smartcard_nss" = "yes" ; then
   echo "subdir-$target: subdir-libcacard" >> $config_host_mak
   echo "libcacard_libs=$libcacard_libs" >> $config_host_mak
   echo "libcacard_cflags=$libcacard_cflags" >> $config_host_mak
-- 
1.7.3.4

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

* [Qemu-devel] [PATCH 6/6] ccid: configure: add --enable-smartcard and --disable-smartcard
  2011-01-11  8:42 [Qemu-devel] [PATCH 0/7] usb-ccid (v15) Alon Levy
                   ` (5 preceding siblings ...)
  2011-01-11  8:42 ` [Qemu-devel] [PATCH 6/7] ccid: configure: add --enable/disable and nss only disable Alon Levy
@ 2011-01-11  8:42 ` Alon Levy
  2011-01-11  9:03   ` Alon Levy
  2011-01-11  8:42 ` [Qemu-devel] [PATCH 7/7] ccid: add qdev description strings Alon Levy
  2011-01-17 15:56 ` [Qemu-devel] [PATCH 0/7] usb-ccid (v15) Alon Levy
  8 siblings, 1 reply; 23+ messages in thread
From: Alon Levy @ 2011-01-11  8:42 UTC (permalink / raw)
  To: qemu-devel

---
 configure |   28 ++++++++++++++++++----------
 1 files changed, 18 insertions(+), 10 deletions(-)

diff --git a/configure b/configure
index 4567057..ebc8250 100755
--- a/configure
+++ b/configure
@@ -748,6 +748,10 @@ for opt do
   ;;
   --enable-rbd) rbd="yes"
   ;;
+  --disable-smartcard) smartcard="no"
+  ;;
+  --enable-smartcard) smartcard="yes"
+  ;;
   *) echo "ERROR: unknown option $opt"; show_help="yes"
   ;;
   esac
@@ -942,6 +946,8 @@ echo "                           Default:trace-<pid>"
 echo "  --disable-spice          disable spice"
 echo "  --enable-spice           enable spice"
 echo "  --enable-rbd             enable building the rados block device (rbd)"
+echo "  --disable-smartcard      disable smartcard support"
+echo "  --enable-smartcard       enable smartcard support"
 echo ""
 echo "NOTE: The object files are built at the place where configure is launched"
 exit 1
@@ -2210,16 +2216,18 @@ EOF
 fi
 
 # check for libcacard for smartcard support
-smartcard_cflags="-I\$(SRC_PATH)/libcacard"
-libcacard_libs=$($pkgconfig --libs nss 2>/dev/null)
-libcacard_cflags=$($pkgconfig --cflags nss)
-# TODO - what's the minimal nss version we support?
-if $pkgconfig --atleast-version=3.12.8 nss; then
-    smartcard="yes"
-    QEMU_CFLAGS="$QEMU_CFLAGS $smartcard_cflags $libcacard_cflags"
-    LIBS="$libcacard_libs $LIBS"
-else
-    smartcard="no"
+if test "$smartcard" != "no" ; then
+    smartcard_cflags="-I\$(SRC_PATH)/libcacard"
+    libcacard_libs=$($pkgconfig --libs nss 2>/dev/null)
+    libcacard_cflags=$($pkgconfig --cflags nss)
+    # TODO - what's the minimal nss version we support?
+    if $pkgconfig --atleast-version=3.12.8 nss; then
+        smartcard="yes"
+        QEMU_CFLAGS="$QEMU_CFLAGS $smartcard_cflags $libcacard_cflags"
+        LIBS="$libcacard_libs $LIBS"
+    else
+        smartcard="no"
+    fi
 fi
 
 ##########################################
-- 
1.7.3.4

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

* [Qemu-devel] [PATCH 7/7] ccid: add qdev description strings
  2011-01-11  8:42 [Qemu-devel] [PATCH 0/7] usb-ccid (v15) Alon Levy
                   ` (6 preceding siblings ...)
  2011-01-11  8:42 ` [Qemu-devel] [PATCH 6/6] ccid: configure: add --enable-smartcard and --disable-smartcard Alon Levy
@ 2011-01-11  8:42 ` Alon Levy
  2011-01-17 15:56 ` [Qemu-devel] [PATCH 0/7] usb-ccid (v15) Alon Levy
  8 siblings, 0 replies; 23+ messages in thread
From: Alon Levy @ 2011-01-11  8:42 UTC (permalink / raw)
  To: qemu-devel

---
 hw/ccid-card-emulated.c |    1 +
 hw/ccid-card-passthru.c |    1 +
 hw/usb-ccid.c           |    1 +
 3 files changed, 3 insertions(+), 0 deletions(-)

diff --git a/hw/ccid-card-emulated.c b/hw/ccid-card-emulated.c
index 5531ce1..5e85e8e 100644
--- a/hw/ccid-card-emulated.c
+++ b/hw/ccid-card-emulated.c
@@ -509,6 +509,7 @@ static int emulated_exitfn(CCIDCardState *base)
 
 static CCIDCardInfo emulated_card_info = {
     .qdev.name = EMULATED_DEV_NAME,
+    .qdev.desc = "emulated smartcard",
     .qdev.size = sizeof(EmulatedState),
     .initfn = emulated_initfn,
     .exitfn = emulated_exitfn,
diff --git a/hw/ccid-card-passthru.c b/hw/ccid-card-passthru.c
index f9fb82c..351651c 100644
--- a/hw/ccid-card-passthru.c
+++ b/hw/ccid-card-passthru.c
@@ -247,6 +247,7 @@ static VMStateDescription passthru_vmstate = {
 
 static CCIDCardInfo passthru_card_info = {
     .qdev.name = PASSTHRU_DEV_NAME,
+    .qdev.desc = "passthrough smartcard",
     .qdev.size = sizeof(PassthruState),
     .qdev.vmsd = &passthru_vmstate,
     .initfn = passthru_initfn,
diff --git a/hw/usb-ccid.c b/hw/usb-ccid.c
index 58f69a6..7b2ea2a 100644
--- a/hw/usb-ccid.c
+++ b/hw/usb-ccid.c
@@ -1330,6 +1330,7 @@ static VMStateDescription ccid_vmstate = {
 static struct USBDeviceInfo ccid_info = {
     .product_desc   = "QEMU USB CCID",
     .qdev.name      = CCID_DEV_NAME,
+    .qdev.desc      = "CCID Rev 1.1 smartcard reader",
     .qdev.size      = sizeof(USBCCIDState),
     .init           = ccid_initfn,
     .handle_packet  = usb_generic_handle_packet,
-- 
1.7.3.4

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

* Re: [Qemu-devel] [PATCH 6/6] ccid: configure: add --enable-smartcard and --disable-smartcard
  2011-01-11  8:42 ` [Qemu-devel] [PATCH 6/6] ccid: configure: add --enable-smartcard and --disable-smartcard Alon Levy
@ 2011-01-11  9:03   ` Alon Levy
  0 siblings, 0 replies; 23+ messages in thread
From: Alon Levy @ 2011-01-11  9:03 UTC (permalink / raw)
  To: qemu-devel

On Tue, Jan 11, 2011 at 10:42:38AM +0200, Alon Levy wrote:
> ---
>  configure |   28 ++++++++++++++++++----------
>  1 files changed, 18 insertions(+), 10 deletions(-)
> 
[snip]

Another oops, not a good morning. Ignore this patch, the real 6/7
will be arriving momentarily. (forgot to clear the patches.usb_ccid.v15
directory).

Alon

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

* Re: [Qemu-devel] [PATCH 0/7] usb-ccid (v15)
  2011-01-11  8:42 [Qemu-devel] [PATCH 0/7] usb-ccid (v15) Alon Levy
                   ` (7 preceding siblings ...)
  2011-01-11  8:42 ` [Qemu-devel] [PATCH 7/7] ccid: add qdev description strings Alon Levy
@ 2011-01-17 15:56 ` Alon Levy
  8 siblings, 0 replies; 23+ messages in thread
From: Alon Levy @ 2011-01-17 15:56 UTC (permalink / raw)
  To: qemu-devel

Ping

On Tue, Jan 11, 2011 at 10:42:31AM +0200, Alon Levy wrote:
> This patchset adds three new devices, usb-ccid, ccid-card-passthru and
> ccid-card-emulated, providing a CCID bus, a simple passthru protocol
> implementing card requiring a client, and a standalone emulated card.
> 
> It also introduces a new directory libcaccard with CAC card emulation,
> CAC is a type of ISO 7816 smart card.
> 
> Tree for pull: git://anongit.freedesktop.org/~alon/qemu usb_ccid.v15
> 
> v14-v15 changes:
>  * add patch with --enable-smartcard and --disable-smartcard and only
>   disable ccid-card-emulated if nss not found.
>  * add patch with description strings
>  * s/libcaccard/libcacard/ in docs/ccid.txt
> 
> v13-v14 changes:
>  - support device_del/device_add on ccid-card-* and usb-ccid
>  * usb-ccid:
>   * lose card reference when card device deleted
>   * check slot number and deny adding a slot if one is already added.
>  * ccid-card-*: use qdev_simple_unplug_cb in both emulated and passthru ccid cards,
>    the exitfn already takes care of triggering card removal in the usb dev.
>  * libcacard:
>   * remove double include of config-host.mak
>   * add replay of card events to libcacard to support second and more emulation
>   * don't initialize more then once (doesn't support it right now, so one
>    thread, NSS thread, is left when device_del is done)
>   * add VCARD_EMUL_INIT_ALREADY_INITED
>  * ccid-card-emulated:
>   * take correct mutexes on signaling to fix deadlocks on device_del
>   * allow card insertion/removal event without proper reader insertion event
> 
> v12-v13 changes:
>  * libcacard:
>   * fix Makefile clean to remove vscclient
>   * fix double include of config-host in Makefile
>  * usb-ccid: remove attach/detach logic, usb is always attached. Guest
>   doesn't care if there is a reader attached with no card anyway.
>  * ccid-card-passthru: don't close chr_dev on removal, makes it possible
>   to use device_del/device_add to create remove/insertion for debugging.
> 
> v11-v12 changes:
>  * fix out of tree build
> 
> v10-v11 changes:
>  * fix last patch that removed one of the doc files.
>  * updated flow table in docs/ccid.txt
> 
> v8-v10 changes:
>  * usb-ccid:
>   * add slot for future use (Gerd)
>   * ifdef ENABLE_MIGRATION for migration support on account of usb
>    migration not being ready in general. (Gerd)
>  * verbosified commit messages. (Gerd)
>  * put libcacard docs in libcacard commit. (Gerd)
> 
> v8-v9 changes:
>  * Blue Swirl comments:
>   * white space fixes
>   * enabled by default, disabled only if missing nss
>   * forgotten fix from v8 (don't build libcacard.so)
>  * added a note about device being little endian
>  * library renamed from libcaccard to libcacard
>  * squashed both of libcacard patches, they touched different files anyway.
> 
> v7-v8 changes:
>  * Blue Swirl comments:
>   * usb-ccid: deannonymize some structs
>   * usb-ccid: coding style change - answer_t and bulk_in_t fixed
>   * usb-ccid: handle endianess conversion between guest and host
>  * usb-ccid: s/ccid_bulk_in_copy_out/ccid_bulk_in_copy_to_guest/
>  * ccid-card-emulated: fix segfault if backend not specified
>  * ccid-card-emulated: let last reader inserted win
>  * libcaccard: remove double vscard_common.h
> 
> v6->v7 changes:
>  * external libcaccard became internal directory libcaccard
>   * statically link object files into qemu
>   * produce libcaccard.so for usage by external projects
>   * applied coding style to new code (please check me)
>   - did not use the qemu options parsing for libcaccard, since
>    it seems to draw large amounts of qemu code (monitor for instance).
> 
> v5->v6 changes:
>  * really remove static debug (I apologize for claiming to have done so before)
> 
> v4->v5 changes:
>  * rebased to latest
>  * remove static debug in card devices
>  * fix --enable-smartcard to link
>  * stall instead of assert when exceeding BULK_OUT_DATA_SIZE
>  * make ccid_reserve_recv_buf for too large len discard message, not exit
>  * make ccid_reserve_recv_buf return void*
>  * fix typo
>  * remove commented code in VMState
> 
> v3->v4:
>  * remove ccid field in CCIDBus
>  * remove static debug in bus
>  * add back docs
> 
> v2->v3:
>  * split into bus (usb-ccid.c, uses ccid.h) and card (ccid-card-passthru.c).
>  * removed documentation (being revised).
> 
> v1->v2:
>  * all QSIMPLEQ turned into fixed sized rings
>  * all allocated buffers turned into fixed size buffers
>  * added migration support
>  * added a message to tell client qemu has migrated to ip:port
>   * for lack of monitor commands ip:port are 0:0, which causes the updated
>    vscclient to connect to one port higher on the same host. will add monitor
>    commands in a separate patch. tested with current setup.
> 
> Alon Levy (6):
>   usb-ccid: add CCID bus
>   ccid: add passthru card device
>   ccid: add ccid-card-emulated device (v2)
>   ccid: add docs
>   ccid: configure: add --enable/disable and nss only disable
>   ccid: add qdev description strings
> 
> Robert Relyea (1):
>   libcacard: initial commit after coding style fixes
> 
>  Makefile                    |    6 +-
>  Makefile.objs               |    7 +
>  Makefile.target             |    2 +
>  configure                   |   48 ++
>  docs/ccid.txt               |  135 +++++
>  docs/libcacard.txt          |  483 +++++++++++++++
>  hw/ccid-card-emulated.c     |  535 +++++++++++++++++
>  hw/ccid-card-passthru.c     |  273 +++++++++
>  hw/ccid.h                   |   35 ++
>  hw/usb-ccid.c               | 1356 +++++++++++++++++++++++++++++++++++++++++++
>  libcacard/Makefile          |   14 +
>  libcacard/cac.c             |  411 +++++++++++++
>  libcacard/cac.h             |   20 +
>  libcacard/card_7816.c       |  780 +++++++++++++++++++++++++
>  libcacard/card_7816.h       |   60 ++
>  libcacard/card_7816t.h      |  163 ++++++
>  libcacard/config.h          |   81 +++
>  libcacard/event.c           |  112 ++++
>  libcacard/eventt.h          |   28 +
>  libcacard/link_test.c       |   20 +
>  libcacard/mutex.h           |   59 ++
>  libcacard/passthru.c        |  612 +++++++++++++++++++
>  libcacard/passthru.h        |   50 ++
>  libcacard/vcard.c           |  350 +++++++++++
>  libcacard/vcard.h           |   85 +++
>  libcacard/vcard_emul.h      |   62 ++
>  libcacard/vcard_emul_nss.c  | 1171 +++++++++++++++++++++++++++++++++++++
>  libcacard/vcard_emul_type.c |   60 ++
>  libcacard/vcard_emul_type.h |   29 +
>  libcacard/vcardt.h          |   66 +++
>  libcacard/vevent.h          |   26 +
>  libcacard/vreader.c         |  526 +++++++++++++++++
>  libcacard/vreader.h         |   54 ++
>  libcacard/vreadert.h        |   23 +
>  libcacard/vscard_common.h   |  130 ++++
>  libcacard/vscclient.c       |  710 ++++++++++++++++++++++
>  36 files changed, 8580 insertions(+), 2 deletions(-)
>  create mode 100644 docs/ccid.txt
>  create mode 100644 docs/libcacard.txt
>  create mode 100644 hw/ccid-card-emulated.c
>  create mode 100644 hw/ccid-card-passthru.c
>  create mode 100644 hw/ccid.h
>  create mode 100644 hw/usb-ccid.c
>  create mode 100644 libcacard/Makefile
>  create mode 100644 libcacard/cac.c
>  create mode 100644 libcacard/cac.h
>  create mode 100644 libcacard/card_7816.c
>  create mode 100644 libcacard/card_7816.h
>  create mode 100644 libcacard/card_7816t.h
>  create mode 100644 libcacard/config.h
>  create mode 100644 libcacard/event.c
>  create mode 100644 libcacard/eventt.h
>  create mode 100644 libcacard/link_test.c
>  create mode 100644 libcacard/mutex.h
>  create mode 100644 libcacard/passthru.c
>  create mode 100644 libcacard/passthru.h
>  create mode 100644 libcacard/vcard.c
>  create mode 100644 libcacard/vcard.h
>  create mode 100644 libcacard/vcard_emul.h
>  create mode 100644 libcacard/vcard_emul_nss.c
>  create mode 100644 libcacard/vcard_emul_type.c
>  create mode 100644 libcacard/vcard_emul_type.h
>  create mode 100644 libcacard/vcardt.h
>  create mode 100644 libcacard/vevent.h
>  create mode 100644 libcacard/vreader.c
>  create mode 100644 libcacard/vreader.h
>  create mode 100644 libcacard/vreadert.h
>  create mode 100644 libcacard/vscard_common.h
>  create mode 100644 libcacard/vscclient.c
> 
> -- 
> 1.7.3.4
> 
> 

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

* Re: [Qemu-devel] [PATCH 2/7] ccid: add passthru card device
  2011-01-11  8:42 ` [Qemu-devel] [PATCH 2/7] ccid: add passthru card device Alon Levy
@ 2011-01-25 14:17   ` Anthony Liguori
  2011-01-25 16:21     ` Alon Levy
                       ` (2 more replies)
  0 siblings, 3 replies; 23+ messages in thread
From: Anthony Liguori @ 2011-01-25 14:17 UTC (permalink / raw)
  To: Alon Levy; +Cc: qemu-devel

On 01/11/2011 02:42 AM, Alon Levy wrote:
> diff --git a/libcacard/vscard_common.h b/libcacard/vscard_common.h
> new file mode 100644
> index 0000000..9ff1295
> --- /dev/null
> +++ b/libcacard/vscard_common.h
>    

This file (and the .c file) need a coding style pass to fixup comments 
and the use of _ as a prefix but I want to focus on the protocol itself.

First, let's get a written spec into the wiki.  I think it's important 
that all of our compatibility protocols are documented in a more formal 
way such that can be reviewed by a wider audience.

> @@ -0,0 +1,130 @@
> +/* Virtual Smart Card protocol definition
> + *
> + * This protocol is between a host implementing a group of virtual smart card
> + * reader, and a client implementing a virtual smart card, or passthrough to
> + * a real card.
> + *
> + * The current implementation passes the raw APDU's from 7816 and additionally
> + * contains messages to setup and teardown readers, handle insertion and
> + * removal of cards, negotiate the protocol and provide for error responses.
> + *
> + * Copyright (c) 2010 Red Hat.
> + *
> + * This code is licensed under the LGPL.
> + */
> +
> +#ifndef _VSCARD_COMMON_H
> +#define _VSCARD_COMMON_H
> +
> +#include<stdint.h>
> +
> +#define VERSION_MAJOR_BITS 11
> +#define VERSION_MIDDLE_BITS 11
> +#define VERSION_MINOR_BITS 10
>    

Distros make versioning not enough.  Inevitably, someone wants to back 
port a bug fix or a feature for some RHEL7.2 release or something like that.

Feature negotiation has worked pretty well for us and I'd suggest using 
it within the protocol.

> +#define MAKE_VERSION(major, middle, minor) \
> +     (  (major<<  (VERSION_MINOR_BITS + VERSION_MIDDLE_BITS)) \
> +      | (middle<<   VERSION_MINOR_BITS) \
> +      | (minor)  )
> +
> +/** IMPORTANT NOTE on VERSION
> + *
> + * The version below MUST be changed whenever a change in this file is made.
> + *
> + * The last digit, the minor, is for bug fix changes only.
> + *
> + * The middle digit is for backward / forward compatible changes, updates
> + * to the existing messages, addition of fields.
> + *
> + * The major digit is for a breaking change of protocol, presumably
> + * something that cannot be accomodated with the existing protocol.
> + */
> +
> +#define VSCARD_VERSION MAKE_VERSION(0,0,1)
> +
> +typedef enum {
> +    VSC_Init,
> +    VSC_Error,
> +    VSC_ReaderAdd,
> +    VSC_ReaderAddResponse,
> +    VSC_ReaderRemove,
> +    VSC_ATR,
> +    VSC_CardRemove,
> +    VSC_APDU,
> +    VSC_Reconnect
> +} VSCMsgType;
>    

Should number the enum to be specific at least.

> +
> +typedef enum {
> +    VSC_GENERAL_ERROR=1,
> +    VSC_CANNOT_ADD_MORE_READERS,
> +} VSCErrorCode;
> +
> +typedef uint32_t reader_id_t;
>    

This namespace is reserved by C.

> +#define VSCARD_UNDEFINED_READER_ID 0xffffffff
> +#define VSCARD_MINIMAL_READER_ID    0
> +
> +typedef struct VSCMsgHeader {
> +    VSCMsgType type;
> +    reader_id_t   reader_id;
> +    uint32_t   length;
>    

Is length just the data length or the whole message length?

> +    uint8_t    data[0];
> +} VSCMsgHeader;
> +
> +/* VSCMsgInit               Client<->  Host
> + * Host replies with allocated reader id in ReaderAddResponse
> + * */
> +typedef struct VSCMsgInit {
> +    uint32_t   version;
> +} VSCMsgInit;
> +
> +/* VSCMsgError              Client<->  Host
> + * */
> +typedef struct VSCMsgError {
> +    uint32_t   code;
> +} VSCMsgError;
> +
> +/* VSCMsgReaderAdd          Client ->  Host
> + * Host replies with allocated reader id in ReaderAddResponse
> + * name - name of the reader on client side.
> + * */
> +typedef struct VSCMsgReaderAdd {
> +    uint8_t    name[0];
>    

Is this a string?

> +} VSCMsgReaderAdd;
> +
> +/* VSCMsgReaderAddResponse  Host ->  Client
> + * Reply to ReaderAdd
> + * */
> +typedef struct VSCMsgReaderAddResponse {
> +} VSCMsgReaderAddResponse;
> +
> +/* VSCMsgReaderRemove       Client ->  Host
> + * */
> +typedef struct VSCMsgReaderRemove {
> +} VSCMsgReaderRemove;
> +
> +/* VSCMsgATR                Client ->  Host
> + * Answer to reset. Sent for card insertion or card reset.
> + * */
> +typedef struct VSCMsgATR {
> +    uint8_t     atr[0];
> +} VSCMsgATR;
> +
> +/* VSCMsgCardRemove         Client ->  Host
> + * */
> +typedef struct VSCMsgCardRemove {
> +} VSCMsgCardRemove;
> +
> +/* VSCMsgAPDU               Client<->  Host
> + * */
> +typedef struct VSCMsgAPDU {
> +    uint8_t    data[0];
> +} VSCMsgAPDU;
> +
> +/* VSCMsgReconnect          Host ->  Client
> + * */
> +typedef struct VSCMsgReconnect {
> +    uint32_t   ip;
>    

This is not ipv6 friendly.  Two strings would be a better choice.

Regards,

Anthony Liguori

> +    uint16_t   port;
> +} VSCMsgReconnect;
> +
> +#endif
>    

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

* Re: [Qemu-devel] [PATCH 3/7] libcacard: initial commit after coding style fixes
  2011-01-11  8:42 ` [Qemu-devel] [PATCH 3/7] libcacard: initial commit after coding style fixes Alon Levy
@ 2011-01-25 14:19   ` Anthony Liguori
  0 siblings, 0 replies; 23+ messages in thread
From: Anthony Liguori @ 2011-01-25 14:19 UTC (permalink / raw)
  To: Alon Levy; +Cc: qemu-devel

On 01/11/2011 02:42 AM, Alon Levy wrote:
> From: Robert Relyea<rrelyea@redhat.com>
>
> libcacard emulates a Common Access Card (CAC) which is a standard
> for smartcards. It is used by the emulated ccid card introduced in
> a following patch. Docs are available in docs/libcacard.txt
>
> Signed-off-by: Alon Levy<alevy@redhat.com>
>    

I think importing the code like this is a reasonable place to start.

Regards,

Anthony Liguori

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

* Re: [Qemu-devel] [PATCH 4/7] ccid: add ccid-card-emulated device (v2)
  2011-01-11  8:42 ` [Qemu-devel] [PATCH 4/7] ccid: add ccid-card-emulated device (v2) Alon Levy
@ 2011-01-25 14:21   ` Anthony Liguori
  2011-01-25 16:24     ` Alon Levy
  2011-01-31 19:28     ` Alon Levy
  0 siblings, 2 replies; 23+ messages in thread
From: Anthony Liguori @ 2011-01-25 14:21 UTC (permalink / raw)
  To: Alon Levy; +Cc: qemu-devel

On 01/11/2011 02:42 AM, Alon Levy wrote:
> This devices uses libcacard (internal) to emulate a smartcard conforming
> to the CAC standard. It attaches to the usb-ccid bus. Usage instructions
> (example command lines) are in the following patch in docs/ccid.txt. It
> uses libcacard which uses nss, so it can work with both hw cards and
> certificates (files).
>
> changes from v1:
>      remove stale comments, use only c-style comments
>      bugfix, forgot to set recv_len
>      change reader name to 'Virtual Reader'
>
> Signed-off-by: Alon Levy<alevy@redhat.com>
> ---
>   Makefile.objs           |    2 +-
>   hw/ccid-card-emulated.c |  534 +++++++++++++++++++++++++++++++++++++++++++++++
>   hw/ccid-card-passthru.c |    1 -
>   3 files changed, 535 insertions(+), 2 deletions(-)
>   create mode 100644 hw/ccid-card-emulated.c
>
> diff --git a/Makefile.objs b/Makefile.objs
> index 6a0030b..303b642 100644
> --- a/Makefile.objs
> +++ b/Makefile.objs
> @@ -197,7 +197,7 @@ hw-obj-$(CONFIG_FDC) += fdc.o
>   hw-obj-$(CONFIG_ACPI) += acpi.o acpi_piix4.o
>   hw-obj-$(CONFIG_APM) += pm_smbus.o apm.o
>   hw-obj-$(CONFIG_DMA) += dma.o
> -hw-obj-$(CONFIG_SMARTCARD) += usb-ccid.o ccid-card-passthru.o
> +hw-obj-$(CONFIG_SMARTCARD) += usb-ccid.o ccid-card-passthru.o ccid-card-emulated.o
>
>   # PPC devices
>   hw-obj-$(CONFIG_OPENPIC) += openpic.o
> diff --git a/hw/ccid-card-emulated.c b/hw/ccid-card-emulated.c
> new file mode 100644
> index 0000000..5531ce1
> --- /dev/null
> +++ b/hw/ccid-card-emulated.c
> @@ -0,0 +1,534 @@
> +/*
> + * CCID Card Device. Emulated card.
> + *
> + * It can be used to provide access to the local hardware in a non exclusive
> + * way, or it can use certificates. It requires the usb-ccid bus.
> + *
> + * Usage 1: standard, mirror hardware reader+card:
> + * qemu .. -usb -device usb-ccid -device ccid-card-emulated
> + *
> + * Usage 2: use certificates, no hardware required
> + * one time: create the certificates:
> + *  for i in 1 2 3; do certutil -d /etc/pki/nssdb -x -t "CT,CT,CT" -S -s "CN=user$i" -n user$i; done
> + * qemu .. -usb -device usb-ccid -device ccid-card-emulated,cert1=user1,cert2=user2,cert3=user3
> + *
> + * If you use a non default db for the certificates you can specify it using the db parameter.
> + *
> + *
> + * Copyright (c) 2010 Red Hat.
> + * Written by Alon Levy.
> + *
> + * This code is licenced under the LGPL.
> + */
> +
> +#include<pthread.h>
> +#include<eventt.h>
> +#include<vevent.h>
> +#include<vreader.h>
> +#include<vcard_emul.h>
> +#include "qemu-char.h"
> +#include "monitor.h"
> +#include "hw/ccid.h"
> +
> +#define DPRINTF(card, lvl, fmt, ...) \
> +do { if (lvl<= card->debug) { printf("ccid-card-emul: %s: " fmt , __func__, ## __VA_ARGS__); } } while (0)
> +
> +#define EMULATED_DEV_NAME "ccid-card-emulated"
> +
> +#define BACKEND_NSS_EMULATED "nss-emulated" /* the default */
> +#define BACKEND_CERTIFICATES "certificates"
> +
> +typedef struct EmulatedState EmulatedState;
> +
> +enum {
> +    EMUL_READER_INSERT = 0,
> +    EMUL_READER_REMOVE,
> +    EMUL_CARD_INSERT,
> +    EMUL_CARD_REMOVE,
> +    EMUL_GUEST_APDU,
> +    EMUL_RESPONSE_APDU,
> +    EMUL_ERROR,
> +};
> +
> +static const char* emul_event_to_string(uint32_t emul_event)
> +{
> +    switch (emul_event) {
> +        case EMUL_READER_INSERT: return "EMUL_READER_INSERT";
> +        case EMUL_READER_REMOVE: return "EMUL_READER_REMOVE";
> +        case EMUL_CARD_INSERT: return "EMUL_CARD_INSERT";
> +        case EMUL_CARD_REMOVE: return "EMUL_CARD_REMOVE";
> +        case EMUL_GUEST_APDU: return "EMUL_GUEST_APDU";
> +        case EMUL_RESPONSE_APDU: return "EMUL_RESPONSE_APDU";
> +        case EMUL_ERROR: return "EMUL_ERROR";
> +        default:
> +            break;
> +    }
> +    return "UNKNOWN";
> +}
> +
> +typedef struct EmulEvent {
> +    QSIMPLEQ_ENTRY(EmulEvent) entry;
> +    union {
> +        struct {
> +            uint32_t type;
> +        } gen;
> +        struct {
> +            uint32_t type;
> +            uint64_t code;
> +        } error;
> +        struct {
> +            uint32_t type;
> +            uint32_t len;
> +            uint8_t data[];
> +        } data;
> +    } p;
> +} EmulEvent;
> +
> +#define MAX_ATR_SIZE 40
> +struct EmulatedState {
> +    CCIDCardState base;
> +    uint8_t  debug;
> +    char*    backend;
> +    char*    cert1;
> +    char*    cert2;
> +    char*    cert3;
> +    char*    db;
> +    uint8_t  atr[MAX_ATR_SIZE];
> +    uint8_t  atr_length;
> +    QSIMPLEQ_HEAD(event_list, EmulEvent) event_list;
> +    pthread_mutex_t event_list_mutex;
> +    VReader *reader;
> +    QSIMPLEQ_HEAD(guest_apdu_list, EmulEvent) guest_apdu_list;
> +    pthread_mutex_t vreader_mutex; /* and guest_apdu_list mutex */
> +    pthread_mutex_t handle_apdu_mutex;
> +    pthread_cond_t handle_apdu_cond;
> +    int      pipe[2];
> +    int      quit_apdu_thread;
> +    pthread_mutex_t apdu_thread_quit_mutex;
> +    pthread_cond_t apdu_thread_quit_cond;
> +};
> +
> +static void emulated_apdu_from_guest(CCIDCardState *base, const uint8_t *apdu, uint32_t len)
> +{
> +    EmulatedState *card = DO_UPCAST(EmulatedState, base, base);
> +    EmulEvent *event = (EmulEvent*)malloc(sizeof(EmulEvent) + len);
> +
> +    assert(event);
> +    event->p.data.type = EMUL_GUEST_APDU;
> +    event->p.data.len = len;
> +    memcpy(event->p.data.data, apdu, len);
> +    pthread_mutex_lock(&card->vreader_mutex);
> +    QSIMPLEQ_INSERT_TAIL(&card->guest_apdu_list, event, entry);
> +    pthread_mutex_unlock(&card->vreader_mutex);
> +    pthread_mutex_lock(&card->handle_apdu_mutex);
> +    pthread_cond_signal(&card->handle_apdu_cond);
> +    pthread_mutex_unlock(&card->handle_apdu_mutex);
> +}
> +
> +static const uint8_t* emulated_get_atr(CCIDCardState *base, uint32_t *len)
> +{
> +    EmulatedState *card = DO_UPCAST(EmulatedState, base, base);
> +
> +    *len = card->atr_length;
> +    return card->atr;
> +}
> +
> +static void emulated_push_event(EmulatedState *card, EmulEvent *event)
> +{
> +    pthread_mutex_lock(&card->event_list_mutex);
> +    QSIMPLEQ_INSERT_TAIL(&(card->event_list), event, entry);
> +    pthread_mutex_unlock(&card->event_list_mutex);
> +    if (write(card->pipe[1], card, 1) != 1) {
> +        DPRINTF(card, 1, "write to pipe failed\n");
> +    }
> +}
> +
> +static void emulated_push_type(EmulatedState *card, uint32_t type)
> +{
> +    EmulEvent *event = (EmulEvent*)malloc(sizeof(EmulEvent));
> +
> +    assert(event);
> +    event->p.gen.type = type;
> +    emulated_push_event(card, event);
> +}
> +
> +static void emulated_push_error(EmulatedState *card, uint64_t code)
> +{
> +    EmulEvent *event = (EmulEvent*)malloc(sizeof(EmulEvent));
> +
> +    assert(event);
> +    event->p.error.type = EMUL_ERROR;
> +    event->p.error.code = code;
> +    emulated_push_event(card, event);
> +}
> +
> +static void emulated_push_data_type(EmulatedState *card, uint32_t type,
> +    const uint8_t *data, uint32_t len)
> +{
> +    EmulEvent *event = (EmulEvent*)malloc(sizeof(EmulEvent) + len);
> +
> +    assert(event);
> +    event->p.data.type = type;
> +    event->p.data.len = len;
> +    memcpy(event->p.data.data, data, len);
> +    emulated_push_event(card, event);
> +}
> +
> +static void emulated_push_reader_insert(EmulatedState *card)
> +{
> +    emulated_push_type(card, EMUL_READER_INSERT);
> +}
> +
> +static void emulated_push_reader_remove(EmulatedState *card)
> +{
> +    emulated_push_type(card, EMUL_READER_REMOVE);
> +}
> +
> +static void emulated_push_card_insert(EmulatedState *card,
> +    const uint8_t *atr, uint32_t len)
> +{
> +    emulated_push_data_type(card, EMUL_CARD_INSERT, atr, len);
> +}
> +
> +static void emulated_push_card_remove(EmulatedState *card)
> +{
> +    emulated_push_type(card, EMUL_CARD_REMOVE);
> +}
> +
> +static void emulated_push_response_apdu(EmulatedState *card,
> +    const uint8_t* apdu, uint32_t len)
> +{
> +    emulated_push_data_type(card, EMUL_RESPONSE_APDU, apdu, len);
> +}
> +
> +#define APDU_BUF_SIZE 270
> +static void *handle_apdu_thread(void* arg)
> +{
> +    EmulatedState *card = arg;
> +    uint8_t recv_data[APDU_BUF_SIZE];
> +    int recv_len;
> +    VReaderStatus reader_status;
> +    EmulEvent *event;
> +
> +    while (1) {
> +        pthread_mutex_lock(&card->handle_apdu_mutex);
> +        pthread_cond_wait(&card->handle_apdu_cond,&card->handle_apdu_mutex);
> +        pthread_mutex_unlock(&card->handle_apdu_mutex);
> +        if (card->quit_apdu_thread) {
> +            card->quit_apdu_thread = 0; // debugging
> +            break;
> +        }
> +        pthread_mutex_lock(&card->vreader_mutex);
> +        while (!QSIMPLEQ_EMPTY(&card->guest_apdu_list)) {
> +            event = QSIMPLEQ_FIRST(&card->guest_apdu_list);
> +            assert((unsigned long)event>  1000);
> +            QSIMPLEQ_REMOVE_HEAD(&card->guest_apdu_list, entry);
> +            if (event->p.data.type != EMUL_GUEST_APDU) {
> +                DPRINTF(card, 1, "unexpected message in handle_apdu_thread\n");
> +                free(event);
> +                continue;
> +            }
> +            if (card->reader == NULL) {
> +                DPRINTF(card, 1, "reader is NULL\n");
> +                free(event);
> +                continue;
> +            }
> +            recv_len = sizeof(recv_data);
> +            reader_status = vreader_xfr_bytes(card->reader,
> +                    event->p.data.data, event->p.data.len,
> +                    recv_data,&recv_len);
> +            DPRINTF(card, 2, "got back apdu of length %d\n", recv_len);
> +            if (reader_status == VREADER_OK) {
> +                emulated_push_response_apdu(card, recv_data, recv_len);
> +            } else {
> +                emulated_push_error(card, reader_status);
> +            }
> +            free(event);
> +        }
> +        pthread_mutex_unlock(&card->vreader_mutex);
> +    }
> +    pthread_mutex_lock(&card->apdu_thread_quit_mutex);
> +    pthread_cond_signal(&card->apdu_thread_quit_cond);
> +    pthread_mutex_unlock(&card->apdu_thread_quit_mutex);
> +    return NULL;
> +}
> +
> +static void *event_thread(void *arg)
> +{
> +    int atr_len = MAX_ATR_SIZE;
> +    uint8_t atr[MAX_ATR_SIZE];
> +    VEvent *event = NULL;
> +    EmulatedState *card = arg;
> +
> +    while (1) {
> +        const char *reader_name;
> +
> +        event = vevent_wait_next_vevent();
> +        if (event == NULL || event->type == VEVENT_LAST) {
> +            break;
> +        }
> +        if (event->type != VEVENT_READER_INSERT) {
> +            if (card->reader == NULL&&  event->reader != NULL) {
> +                // Happens after device_add followed by card remove or insert.
> +                // XXX: create synthetic add_reader events if vcard_emul_init
> +                // already called, which happens if device_del and device_add are
> +                // called
> +                card->reader = vreader_reference(event->reader);
> +            } else {
> +                if (event->reader != card->reader) {
> +                    fprintf(stderr, "ERROR: wrong reader: quiting event_thread\n");
> +                    break;
> +                }
> +            }
> +        }
> +        switch (event->type) {
> +        case VEVENT_READER_INSERT:
> +            /* TODO: take a specific reader. i.e. track which reader
> +             * we are seeing here, check it is the one we want (the first,
> +             * or by a particular name), and ignore if we don't want it.
> +             */
> +            reader_name = vreader_get_name(event->reader);
> +            if (card->reader != NULL) {
> +                DPRINTF(card, 2, "READER INSERT - replacing %s with %s\n",
> +                    vreader_get_name(card->reader), reader_name);
> +                pthread_mutex_lock(&card->vreader_mutex);
> +                vreader_free(card->reader);
> +                pthread_mutex_unlock(&card->vreader_mutex);
> +                emulated_push_reader_remove(card);
> +            }
> +            pthread_mutex_lock(&card->vreader_mutex);
> +            DPRINTF(card, 2, "READER INSERT %s\n", reader_name);
> +            card->reader = vreader_reference(event->reader);
> +            pthread_mutex_unlock(&card->vreader_mutex);
> +            emulated_push_reader_insert(card);
> +            break;
> +        case VEVENT_READER_REMOVE:
> +            DPRINTF(card, 2, " READER REMOVE: %s \n",
> +                    vreader_get_name(event->reader));
> +            pthread_mutex_lock(&card->vreader_mutex);
> +            vreader_free(card->reader);
> +            card->reader = NULL;
> +            pthread_mutex_unlock(&card->vreader_mutex);
> +            emulated_push_reader_remove(card);
> +            break;
> +        case VEVENT_CARD_INSERT:
> +            /* get the ATR (intended as a response to a power on from the
> +             * reader */
> +            atr_len = MAX_ATR_SIZE;
> +            vreader_power_on(event->reader, atr,&atr_len);
> +            card->atr_length = (uint8_t)atr_len;
> +            DPRINTF(card, 2, " CARD INSERT\n");
> +            emulated_push_card_insert(card, atr, atr_len);
> +            break;
> +        case VEVENT_CARD_REMOVE:
> +            DPRINTF(card, 2, " CARD REMOVE\n");
> +            emulated_push_card_remove(card);
> +            break;
> +        case VEVENT_LAST: /* quit */
> +            vevent_delete(event);
> +            return NULL;
> +            break;
> +        default:
> +            break;
> +        }
> +        vevent_delete(event);
> +    }
> +    return NULL;
> +}
> +
> +static void pipe_read(void *opaque)
> +{
> +    EmulatedState *card = opaque;
> +    EmulEvent *event, *next;
> +    char dummy;
> +    int len;
> +
> +    do {
> +        len = read(card->pipe[0],&dummy, sizeof(dummy));
> +    } while (len == sizeof(dummy));
> +    pthread_mutex_lock(&card->event_list_mutex);
> +    QSIMPLEQ_FOREACH_SAFE(event,&card->event_list, entry, next) {
> +        DPRINTF(card, 2, "event %s\n", emul_event_to_string(event->p.gen.type));
> +        switch (event->p.gen.type) {
> +            case EMUL_RESPONSE_APDU:
> +                ccid_card_send_apdu_to_guest(&card->base, event->p.data.data,
> +                    event->p.data.len);
> +                break;
> +            case EMUL_READER_INSERT:
> +                ccid_card_ccid_attach(&card->base);
> +                break;
> +            case EMUL_READER_REMOVE:
> +                ccid_card_ccid_detach(&card->base);
> +                break;
> +            case EMUL_CARD_INSERT:
> +                assert(event->p.data.len<= MAX_ATR_SIZE);
> +                card->atr_length = event->p.data.len;
> +                memcpy(card->atr, event->p.data.data, card->atr_length);
> +                ccid_card_card_inserted(&card->base);
> +                break;
> +            case EMUL_CARD_REMOVE:
> +                ccid_card_card_removed(&card->base);
> +                break;
> +            case EMUL_ERROR:
> +                ccid_card_card_error(&card->base, event->p.error.code);
> +                break;
> +            default:
> +                DPRINTF(card, 2, "unexpected event\n");
> +                break;
> +        }
> +        free(event);
> +    }
> +    QSIMPLEQ_INIT(&card->event_list);
> +    pthread_mutex_unlock(&card->event_list_mutex);
> +}
> +
> +static int init_pipe_signaling(EmulatedState *card)
> +{
> +   if (pipe(card->pipe)<  0) {
> +       DPRINTF(card, 2, "pipe creation failed\n");
> +       return -1;
> +   }
> +   fcntl(card->pipe[0], F_SETFL, O_NONBLOCK);
> +   fcntl(card->pipe[1], F_SETFL, O_NONBLOCK);
> +   fcntl(card->pipe[0], F_SETOWN, getpid());
> +   qemu_set_fd_handler(card->pipe[0], pipe_read, NULL, card);
> +   return 0;
> +}
> +
> +#define CERTIFICATES_DEFAULT_DB "/etc/pki/nssdb"
> +#define CERTIFICATES_ARGS_TEMPLATE "db=\"%s\" use_hw=no soft=(,Virtual Reader,CAC,,%s,%s,%s)"
> +
> +static int wrap_vcard_emul_init(VCardEmulOptions *options)
> +{
> +    static int called = 0;
> +    static int options_was_null = 0;
> +
> +    if (called) {
> +        if ((options == NULL) != options_was_null) {
> +            printf("%s: warning: running emulated with certificates and emulated side by side is not supported\n", __FUNCTION__);
> +            return VCARD_EMUL_FAIL;
> +        }
> +        vcard_emul_replay_insertion_events();
> +        return VCARD_EMUL_OK;
> +    }
> +    options_was_null = (options == NULL);
> +    called = 1;
> +    return vcard_emul_init(options);
> +}
> +
> +static int emulated_initialize_vcard_from_certificates(EmulatedState *card)
> +{
> +    char emul_args[200];
> +    VCardEmulOptions *options = NULL;
> +
> +    snprintf(emul_args, sizeof(emul_args) - 1, CERTIFICATES_ARGS_TEMPLATE,
> +        card->db ? card->db : CERTIFICATES_DEFAULT_DB,
> +        card->cert1, card->cert2, card->cert3);
> +    options = vcard_emul_options(emul_args);
> +    if (options == NULL) {
> +        printf("%s: warning: not using certificates due to initialization error\n", __func__);
> +    }
> +    return wrap_vcard_emul_init(options);
> +}
> +
> +static int emulated_initfn(CCIDCardState *base)
> +{
> +    EmulatedState *card = DO_UPCAST(EmulatedState, base, base);
> +    int rv;
> +    pthread_t thread_id;
> +    VCardEmulError ret;
> +
> +    QSIMPLEQ_INIT(&card->event_list);
> +    QSIMPLEQ_INIT(&card->guest_apdu_list);
> +    pthread_mutex_init(&card->event_list_mutex, NULL);
> +    pthread_mutex_init(&card->vreader_mutex, NULL);
> +    pthread_mutex_init(&card->handle_apdu_mutex, NULL);
> +    pthread_cond_init(&card->handle_apdu_cond, NULL);
> +    card->reader = NULL;
> +    card->quit_apdu_thread = 0;
> +    if (init_pipe_signaling(card)<  0) {
> +        return -1;
> +    }
> +    if (!card->backend) {
> +        card->backend = strdup((char*)BACKEND_NSS_EMULATED);
> +    }
> +    /* TODO: a passthru backened that works on local machine. third card type? */
> +    if (strcmp(BACKEND_CERTIFICATES, card->backend) == 0
> +&&  card->cert1 != NULL&&  card->cert2 != NULL&&  card->cert3 != NULL) {
> +        ret = emulated_initialize_vcard_from_certificates(card);
> +    } else {
> +        if (strcmp(BACKEND_CERTIFICATES, card->backend) == 0) {
> +            printf("%s: you must provide all three certs for certificates backend\n",
> +                EMULATED_DEV_NAME);
> +            return -1;
> +        }
> +        if (card->backend&&  strcmp(BACKEND_NSS_EMULATED, card->backend) != 0) {
> +            printf("%s: bad backend specified. The options are:\n%s (default), %s.\n",
> +                EMULATED_DEV_NAME, BACKEND_NSS_EMULATED, BACKEND_CERTIFICATES);
> +            return -1;
> +        }
> +        /* default to mirroring the local hardware readers */
> +        ret = wrap_vcard_emul_init(NULL);
> +    }
> +    if (ret != VCARD_EMUL_OK) {
> +        printf("%s: failed to initialize vcard\n", EMULATED_DEV_NAME);
> +        return -1;
> +    }
> +    rv = pthread_create(&thread_id, NULL, event_thread, card);
> +    if (rv<  0) {
> +        printf("%s: error creating event thread\n", EMULATED_DEV_NAME);
> +        return -1;
> +    }
>    

Why can't this be implemented with qemu_set_fd_handler?  The event 
thread si just spinning on read.

Regards,

Anthony Liguori

> +    rv = pthread_create(&thread_id, NULL, handle_apdu_thread, card);
> +    if (rv<  0) {
> +        printf("%s: error creating handle_apdu thread\n", EMULATED_DEV_NAME);
> +        return -1;
> +    }
> +    return 0;
> +}
> +
> +static int emulated_exitfn(CCIDCardState *base)
> +{
> +    EmulatedState *card = DO_UPCAST(EmulatedState, base, base);
> +    VEvent *vevent = vevent_new(VEVENT_LAST, NULL, NULL);
> +
> +    vevent_queue_vevent(vevent); /* stop vevent thread */
> +    pthread_mutex_lock(&card->apdu_thread_quit_mutex);
> +    card->quit_apdu_thread = 1; /* stop handle_apdu thread */
> +    pthread_cond_signal(&card->handle_apdu_cond);
> +    pthread_cond_wait(&card->apdu_thread_quit_cond,&card->apdu_thread_quit_mutex);
> +    /* handle_apdu thread stopped, can destroy all of it's mutexes */
> +    pthread_cond_destroy(&card->handle_apdu_cond);
> +    pthread_cond_destroy(&card->apdu_thread_quit_cond);
> +    pthread_mutex_destroy(&card->apdu_thread_quit_mutex);
> +    pthread_mutex_destroy(&card->handle_apdu_mutex);
> +    pthread_mutex_destroy(&card->vreader_mutex);
> +    pthread_mutex_destroy(&card->event_list_mutex);
> +    return 0;
> +}
> +
> +static CCIDCardInfo emulated_card_info = {
> +    .qdev.name = EMULATED_DEV_NAME,
> +    .qdev.size = sizeof(EmulatedState),
> +    .initfn = emulated_initfn,
> +    .exitfn = emulated_exitfn,
> +    .get_atr = emulated_get_atr,
> +    .apdu_from_guest = emulated_apdu_from_guest,
> +    .qdev.unplug    = qdev_simple_unplug_cb,
> +    .qdev.props     = (Property[]) {
> +        DEFINE_PROP_STRING("backend", EmulatedState, backend),
> +        DEFINE_PROP_STRING("cert1", EmulatedState, cert1),
> +        DEFINE_PROP_STRING("cert2", EmulatedState, cert2),
> +        DEFINE_PROP_STRING("cert3", EmulatedState, cert3),
> +        DEFINE_PROP_STRING("db", EmulatedState, db),
> +        DEFINE_PROP_UINT8("debug", EmulatedState, debug, 0),
> +        DEFINE_PROP_END_OF_LIST(),
> +    },
> +};
> +
> +static void ccid_card_emulated_register_devices(void)
> +{
> +    ccid_card_qdev_register(&emulated_card_info);
> +}
> +
> +device_init(ccid_card_emulated_register_devices)
> diff --git a/hw/ccid-card-passthru.c b/hw/ccid-card-passthru.c
> index 6ec4f21..f9fb82c 100644
> --- a/hw/ccid-card-passthru.c
> +++ b/hw/ccid-card-passthru.c
> @@ -253,7 +253,6 @@ static CCIDCardInfo passthru_card_info = {
>       .exitfn = passthru_exitfn,
>       .get_atr = passthru_get_atr,
>       .apdu_from_guest = passthru_apdu_from_guest,
> -    .qdev.unplug    = qdev_simple_unplug_cb,
>       .qdev.props     = (Property[]) {
>           DEFINE_PROP_CHR("chardev", PassthruState, cs),
>           DEFINE_PROP_UINT8("debug", PassthruState, debug, 0),
>    

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

* Re: [Qemu-devel] [PATCH 2/7] ccid: add passthru card device
  2011-01-25 14:17   ` Anthony Liguori
@ 2011-01-25 16:21     ` Alon Levy
  2011-01-25 16:24       ` Anthony Liguori
  2011-01-27 21:13     ` Alon Levy
  2011-01-30 17:35     ` Alon Levy
  2 siblings, 1 reply; 23+ messages in thread
From: Alon Levy @ 2011-01-25 16:21 UTC (permalink / raw)
  To: Anthony Liguori; +Cc: qemu-devel

On Tue, Jan 25, 2011 at 08:17:32AM -0600, Anthony Liguori wrote:
> On 01/11/2011 02:42 AM, Alon Levy wrote:
> >diff --git a/libcacard/vscard_common.h b/libcacard/vscard_common.h
> >new file mode 100644
> >index 0000000..9ff1295
> >--- /dev/null
> >+++ b/libcacard/vscard_common.h
> 
> This file (and the .c file) need a coding style pass to fixup
> comments and the use of _ as a prefix but I want to focus on the
> protocol itself.
> 
> First, let's get a written spec into the wiki.  I think it's
> important that all of our compatibility protocols are documented in
> a more formal way such that can be reviewed by a wider audience.

ok, I'll create Features/Smartcard/Protocol

> 
> >@@ -0,0 +1,130 @@
> >+/* Virtual Smart Card protocol definition
> >+ *
> >+ * This protocol is between a host implementing a group of virtual smart card
> >+ * reader, and a client implementing a virtual smart card, or passthrough to
> >+ * a real card.
> >+ *
> >+ * The current implementation passes the raw APDU's from 7816 and additionally
> >+ * contains messages to setup and teardown readers, handle insertion and
> >+ * removal of cards, negotiate the protocol and provide for error responses.
> >+ *
> >+ * Copyright (c) 2010 Red Hat.
> >+ *
> >+ * This code is licensed under the LGPL.
> >+ */
> >+
> >+#ifndef _VSCARD_COMMON_H
> >+#define _VSCARD_COMMON_H
> >+
> >+#include<stdint.h>
> >+
> >+#define VERSION_MAJOR_BITS 11
> >+#define VERSION_MIDDLE_BITS 11
> >+#define VERSION_MINOR_BITS 10
> 
> Distros make versioning not enough.  Inevitably, someone wants to
> back port a bug fix or a feature for some RHEL7.2 release or
> something like that.
> 
> Feature negotiation has worked pretty well for us and I'd suggest
> using it within the protocol.
> 

Suggestion accepted.

> >+#define MAKE_VERSION(major, middle, minor) \
> >+     (  (major<<  (VERSION_MINOR_BITS + VERSION_MIDDLE_BITS)) \
> >+      | (middle<<   VERSION_MINOR_BITS) \
> >+      | (minor)  )
> >+
> >+/** IMPORTANT NOTE on VERSION
> >+ *
> >+ * The version below MUST be changed whenever a change in this file is made.
> >+ *
> >+ * The last digit, the minor, is for bug fix changes only.
> >+ *
> >+ * The middle digit is for backward / forward compatible changes, updates
> >+ * to the existing messages, addition of fields.
> >+ *
> >+ * The major digit is for a breaking change of protocol, presumably
> >+ * something that cannot be accomodated with the existing protocol.
> >+ */
> >+
> >+#define VSCARD_VERSION MAKE_VERSION(0,0,1)
> >+
> >+typedef enum {
> >+    VSC_Init,
> >+    VSC_Error,
> >+    VSC_ReaderAdd,
> >+    VSC_ReaderAddResponse,
> >+    VSC_ReaderRemove,
> >+    VSC_ATR,
> >+    VSC_CardRemove,
> >+    VSC_APDU,
> >+    VSC_Reconnect
> >+} VSCMsgType;
> 
> Should number the enum to be specific at least.

will fix.

> 
> >+
> >+typedef enum {
> >+    VSC_GENERAL_ERROR=1,
> >+    VSC_CANNOT_ADD_MORE_READERS,
> >+} VSCErrorCode;
> >+
> >+typedef uint32_t reader_id_t;
> 
> This namespace is reserved by C.

reader_id_t is reserved?

> 
> >+#define VSCARD_UNDEFINED_READER_ID 0xffffffff
> >+#define VSCARD_MINIMAL_READER_ID    0
> >+
> >+typedef struct VSCMsgHeader {
> >+    VSCMsgType type;
> >+    reader_id_t   reader_id;
> >+    uint32_t   length;
> 
> Is length just the data length or the whole message length?
> 

data length, I'll add a comment.

> >+    uint8_t    data[0];
> >+} VSCMsgHeader;
> >+
> >+/* VSCMsgInit               Client<->  Host
> >+ * Host replies with allocated reader id in ReaderAddResponse
> >+ * */
> >+typedef struct VSCMsgInit {
> >+    uint32_t   version;
> >+} VSCMsgInit;
> >+
> >+/* VSCMsgError              Client<->  Host
> >+ * */
> >+typedef struct VSCMsgError {
> >+    uint32_t   code;
> >+} VSCMsgError;
> >+
> >+/* VSCMsgReaderAdd          Client ->  Host
> >+ * Host replies with allocated reader id in ReaderAddResponse
> >+ * name - name of the reader on client side.
> >+ * */
> >+typedef struct VSCMsgReaderAdd {
> >+    uint8_t    name[0];
> 
> Is this a string?
> 

Yes. You expect char?

> >+} VSCMsgReaderAdd;
> >+
> >+/* VSCMsgReaderAddResponse  Host ->  Client
> >+ * Reply to ReaderAdd
> >+ * */
> >+typedef struct VSCMsgReaderAddResponse {
> >+} VSCMsgReaderAddResponse;
> >+
> >+/* VSCMsgReaderRemove       Client ->  Host
> >+ * */
> >+typedef struct VSCMsgReaderRemove {
> >+} VSCMsgReaderRemove;
> >+
> >+/* VSCMsgATR                Client ->  Host
> >+ * Answer to reset. Sent for card insertion or card reset.
> >+ * */
> >+typedef struct VSCMsgATR {
> >+    uint8_t     atr[0];
> >+} VSCMsgATR;
> >+
> >+/* VSCMsgCardRemove         Client ->  Host
> >+ * */
> >+typedef struct VSCMsgCardRemove {
> >+} VSCMsgCardRemove;
> >+
> >+/* VSCMsgAPDU               Client<->  Host
> >+ * */
> >+typedef struct VSCMsgAPDU {
> >+    uint8_t    data[0];
> >+} VSCMsgAPDU;
> >+
> >+/* VSCMsgReconnect          Host ->  Client
> >+ * */
> >+typedef struct VSCMsgReconnect {
> >+    uint32_t   ip;
> 
> This is not ipv6 friendly.  Two strings would be a better choice.
> 

Will fix.

> Regards,
> 
> Anthony Liguori
> 
> >+    uint16_t   port;
> >+} VSCMsgReconnect;
> >+
> >+#endif
> 

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

* Re: [Qemu-devel] [PATCH 4/7] ccid: add ccid-card-emulated device (v2)
  2011-01-25 14:21   ` Anthony Liguori
@ 2011-01-25 16:24     ` Alon Levy
  2011-01-25 16:27       ` Anthony Liguori
  2011-01-31 19:28     ` Alon Levy
  1 sibling, 1 reply; 23+ messages in thread
From: Alon Levy @ 2011-01-25 16:24 UTC (permalink / raw)
  To: Anthony Liguori; +Cc: qemu-devel

On Tue, Jan 25, 2011 at 08:21:13AM -0600, Anthony Liguori wrote:
> On 01/11/2011 02:42 AM, Alon Levy wrote:
> >This devices uses libcacard (internal) to emulate a smartcard conforming
> >to the CAC standard. It attaches to the usb-ccid bus. Usage instructions
> >(example command lines) are in the following patch in docs/ccid.txt. It
> >uses libcacard which uses nss, so it can work with both hw cards and
> >certificates (files).
> >
> >changes from v1:
> >     remove stale comments, use only c-style comments
> >     bugfix, forgot to set recv_len
> >     change reader name to 'Virtual Reader'
> >
> >Signed-off-by: Alon Levy<alevy@redhat.com>
> >---
> >  Makefile.objs           |    2 +-
> >  hw/ccid-card-emulated.c |  534 +++++++++++++++++++++++++++++++++++++++++++++++
> >  hw/ccid-card-passthru.c |    1 -
> >  3 files changed, 535 insertions(+), 2 deletions(-)
> >  create mode 100644 hw/ccid-card-emulated.c
> >
> >diff --git a/Makefile.objs b/Makefile.objs
> >index 6a0030b..303b642 100644
> >--- a/Makefile.objs
> >+++ b/Makefile.objs
> >@@ -197,7 +197,7 @@ hw-obj-$(CONFIG_FDC) += fdc.o
> >  hw-obj-$(CONFIG_ACPI) += acpi.o acpi_piix4.o
> >  hw-obj-$(CONFIG_APM) += pm_smbus.o apm.o
> >  hw-obj-$(CONFIG_DMA) += dma.o
> >-hw-obj-$(CONFIG_SMARTCARD) += usb-ccid.o ccid-card-passthru.o
> >+hw-obj-$(CONFIG_SMARTCARD) += usb-ccid.o ccid-card-passthru.o ccid-card-emulated.o
> >
> >  # PPC devices
> >  hw-obj-$(CONFIG_OPENPIC) += openpic.o
> >diff --git a/hw/ccid-card-emulated.c b/hw/ccid-card-emulated.c
> >new file mode 100644
> >index 0000000..5531ce1
> >--- /dev/null
> >+++ b/hw/ccid-card-emulated.c
> >@@ -0,0 +1,534 @@
> >+/*
> >+ * CCID Card Device. Emulated card.
> >+ *
> >+ * It can be used to provide access to the local hardware in a non exclusive
> >+ * way, or it can use certificates. It requires the usb-ccid bus.
> >+ *
> >+ * Usage 1: standard, mirror hardware reader+card:
> >+ * qemu .. -usb -device usb-ccid -device ccid-card-emulated
> >+ *
> >+ * Usage 2: use certificates, no hardware required
> >+ * one time: create the certificates:
> >+ *  for i in 1 2 3; do certutil -d /etc/pki/nssdb -x -t "CT,CT,CT" -S -s "CN=user$i" -n user$i; done
> >+ * qemu .. -usb -device usb-ccid -device ccid-card-emulated,cert1=user1,cert2=user2,cert3=user3
> >+ *
> >+ * If you use a non default db for the certificates you can specify it using the db parameter.
> >+ *
> >+ *
> >+ * Copyright (c) 2010 Red Hat.
> >+ * Written by Alon Levy.
> >+ *
> >+ * This code is licenced under the LGPL.
> >+ */
> >+
> >+#include<pthread.h>
> >+#include<eventt.h>
> >+#include<vevent.h>
> >+#include<vreader.h>
> >+#include<vcard_emul.h>
> >+#include "qemu-char.h"
> >+#include "monitor.h"
> >+#include "hw/ccid.h"
> >+
> >+#define DPRINTF(card, lvl, fmt, ...) \
> >+do { if (lvl<= card->debug) { printf("ccid-card-emul: %s: " fmt , __func__, ## __VA_ARGS__); } } while (0)
> >+
> >+#define EMULATED_DEV_NAME "ccid-card-emulated"
> >+
> >+#define BACKEND_NSS_EMULATED "nss-emulated" /* the default */
> >+#define BACKEND_CERTIFICATES "certificates"
> >+
> >+typedef struct EmulatedState EmulatedState;
> >+
> >+enum {
> >+    EMUL_READER_INSERT = 0,
> >+    EMUL_READER_REMOVE,
> >+    EMUL_CARD_INSERT,
> >+    EMUL_CARD_REMOVE,
> >+    EMUL_GUEST_APDU,
> >+    EMUL_RESPONSE_APDU,
> >+    EMUL_ERROR,
> >+};
> >+
> >+static const char* emul_event_to_string(uint32_t emul_event)
> >+{
> >+    switch (emul_event) {
> >+        case EMUL_READER_INSERT: return "EMUL_READER_INSERT";
> >+        case EMUL_READER_REMOVE: return "EMUL_READER_REMOVE";
> >+        case EMUL_CARD_INSERT: return "EMUL_CARD_INSERT";
> >+        case EMUL_CARD_REMOVE: return "EMUL_CARD_REMOVE";
> >+        case EMUL_GUEST_APDU: return "EMUL_GUEST_APDU";
> >+        case EMUL_RESPONSE_APDU: return "EMUL_RESPONSE_APDU";
> >+        case EMUL_ERROR: return "EMUL_ERROR";
> >+        default:
> >+            break;
> >+    }
> >+    return "UNKNOWN";
> >+}
> >+
> >+typedef struct EmulEvent {
> >+    QSIMPLEQ_ENTRY(EmulEvent) entry;
> >+    union {
> >+        struct {
> >+            uint32_t type;
> >+        } gen;
> >+        struct {
> >+            uint32_t type;
> >+            uint64_t code;
> >+        } error;
> >+        struct {
> >+            uint32_t type;
> >+            uint32_t len;
> >+            uint8_t data[];
> >+        } data;
> >+    } p;
> >+} EmulEvent;
> >+
> >+#define MAX_ATR_SIZE 40
> >+struct EmulatedState {
> >+    CCIDCardState base;
> >+    uint8_t  debug;
> >+    char*    backend;
> >+    char*    cert1;
> >+    char*    cert2;
> >+    char*    cert3;
> >+    char*    db;
> >+    uint8_t  atr[MAX_ATR_SIZE];
> >+    uint8_t  atr_length;
> >+    QSIMPLEQ_HEAD(event_list, EmulEvent) event_list;
> >+    pthread_mutex_t event_list_mutex;
> >+    VReader *reader;
> >+    QSIMPLEQ_HEAD(guest_apdu_list, EmulEvent) guest_apdu_list;
> >+    pthread_mutex_t vreader_mutex; /* and guest_apdu_list mutex */
> >+    pthread_mutex_t handle_apdu_mutex;
> >+    pthread_cond_t handle_apdu_cond;
> >+    int      pipe[2];
> >+    int      quit_apdu_thread;
> >+    pthread_mutex_t apdu_thread_quit_mutex;
> >+    pthread_cond_t apdu_thread_quit_cond;
> >+};
> >+
> >+static void emulated_apdu_from_guest(CCIDCardState *base, const uint8_t *apdu, uint32_t len)
> >+{
> >+    EmulatedState *card = DO_UPCAST(EmulatedState, base, base);
> >+    EmulEvent *event = (EmulEvent*)malloc(sizeof(EmulEvent) + len);
> >+
> >+    assert(event);
> >+    event->p.data.type = EMUL_GUEST_APDU;
> >+    event->p.data.len = len;
> >+    memcpy(event->p.data.data, apdu, len);
> >+    pthread_mutex_lock(&card->vreader_mutex);
> >+    QSIMPLEQ_INSERT_TAIL(&card->guest_apdu_list, event, entry);
> >+    pthread_mutex_unlock(&card->vreader_mutex);
> >+    pthread_mutex_lock(&card->handle_apdu_mutex);
> >+    pthread_cond_signal(&card->handle_apdu_cond);
> >+    pthread_mutex_unlock(&card->handle_apdu_mutex);
> >+}
> >+
> >+static const uint8_t* emulated_get_atr(CCIDCardState *base, uint32_t *len)
> >+{
> >+    EmulatedState *card = DO_UPCAST(EmulatedState, base, base);
> >+
> >+    *len = card->atr_length;
> >+    return card->atr;
> >+}
> >+
> >+static void emulated_push_event(EmulatedState *card, EmulEvent *event)
> >+{
> >+    pthread_mutex_lock(&card->event_list_mutex);
> >+    QSIMPLEQ_INSERT_TAIL(&(card->event_list), event, entry);
> >+    pthread_mutex_unlock(&card->event_list_mutex);
> >+    if (write(card->pipe[1], card, 1) != 1) {
> >+        DPRINTF(card, 1, "write to pipe failed\n");
> >+    }
> >+}
> >+
> >+static void emulated_push_type(EmulatedState *card, uint32_t type)
> >+{
> >+    EmulEvent *event = (EmulEvent*)malloc(sizeof(EmulEvent));
> >+
> >+    assert(event);
> >+    event->p.gen.type = type;
> >+    emulated_push_event(card, event);
> >+}
> >+
> >+static void emulated_push_error(EmulatedState *card, uint64_t code)
> >+{
> >+    EmulEvent *event = (EmulEvent*)malloc(sizeof(EmulEvent));
> >+
> >+    assert(event);
> >+    event->p.error.type = EMUL_ERROR;
> >+    event->p.error.code = code;
> >+    emulated_push_event(card, event);
> >+}
> >+
> >+static void emulated_push_data_type(EmulatedState *card, uint32_t type,
> >+    const uint8_t *data, uint32_t len)
> >+{
> >+    EmulEvent *event = (EmulEvent*)malloc(sizeof(EmulEvent) + len);
> >+
> >+    assert(event);
> >+    event->p.data.type = type;
> >+    event->p.data.len = len;
> >+    memcpy(event->p.data.data, data, len);
> >+    emulated_push_event(card, event);
> >+}
> >+
> >+static void emulated_push_reader_insert(EmulatedState *card)
> >+{
> >+    emulated_push_type(card, EMUL_READER_INSERT);
> >+}
> >+
> >+static void emulated_push_reader_remove(EmulatedState *card)
> >+{
> >+    emulated_push_type(card, EMUL_READER_REMOVE);
> >+}
> >+
> >+static void emulated_push_card_insert(EmulatedState *card,
> >+    const uint8_t *atr, uint32_t len)
> >+{
> >+    emulated_push_data_type(card, EMUL_CARD_INSERT, atr, len);
> >+}
> >+
> >+static void emulated_push_card_remove(EmulatedState *card)
> >+{
> >+    emulated_push_type(card, EMUL_CARD_REMOVE);
> >+}
> >+
> >+static void emulated_push_response_apdu(EmulatedState *card,
> >+    const uint8_t* apdu, uint32_t len)
> >+{
> >+    emulated_push_data_type(card, EMUL_RESPONSE_APDU, apdu, len);
> >+}
> >+
> >+#define APDU_BUF_SIZE 270
> >+static void *handle_apdu_thread(void* arg)
> >+{
> >+    EmulatedState *card = arg;
> >+    uint8_t recv_data[APDU_BUF_SIZE];
> >+    int recv_len;
> >+    VReaderStatus reader_status;
> >+    EmulEvent *event;
> >+
> >+    while (1) {
> >+        pthread_mutex_lock(&card->handle_apdu_mutex);
> >+        pthread_cond_wait(&card->handle_apdu_cond,&card->handle_apdu_mutex);
> >+        pthread_mutex_unlock(&card->handle_apdu_mutex);
> >+        if (card->quit_apdu_thread) {
> >+            card->quit_apdu_thread = 0; // debugging
> >+            break;
> >+        }
> >+        pthread_mutex_lock(&card->vreader_mutex);
> >+        while (!QSIMPLEQ_EMPTY(&card->guest_apdu_list)) {
> >+            event = QSIMPLEQ_FIRST(&card->guest_apdu_list);
> >+            assert((unsigned long)event>  1000);
> >+            QSIMPLEQ_REMOVE_HEAD(&card->guest_apdu_list, entry);
> >+            if (event->p.data.type != EMUL_GUEST_APDU) {
> >+                DPRINTF(card, 1, "unexpected message in handle_apdu_thread\n");
> >+                free(event);
> >+                continue;
> >+            }
> >+            if (card->reader == NULL) {
> >+                DPRINTF(card, 1, "reader is NULL\n");
> >+                free(event);
> >+                continue;
> >+            }
> >+            recv_len = sizeof(recv_data);
> >+            reader_status = vreader_xfr_bytes(card->reader,
> >+                    event->p.data.data, event->p.data.len,
> >+                    recv_data,&recv_len);
> >+            DPRINTF(card, 2, "got back apdu of length %d\n", recv_len);
> >+            if (reader_status == VREADER_OK) {
> >+                emulated_push_response_apdu(card, recv_data, recv_len);
> >+            } else {
> >+                emulated_push_error(card, reader_status);
> >+            }
> >+            free(event);
> >+        }
> >+        pthread_mutex_unlock(&card->vreader_mutex);
> >+    }
> >+    pthread_mutex_lock(&card->apdu_thread_quit_mutex);
> >+    pthread_cond_signal(&card->apdu_thread_quit_cond);
> >+    pthread_mutex_unlock(&card->apdu_thread_quit_mutex);
> >+    return NULL;
> >+}
> >+
> >+static void *event_thread(void *arg)
> >+{
> >+    int atr_len = MAX_ATR_SIZE;
> >+    uint8_t atr[MAX_ATR_SIZE];
> >+    VEvent *event = NULL;
> >+    EmulatedState *card = arg;
> >+
> >+    while (1) {
> >+        const char *reader_name;
> >+
> >+        event = vevent_wait_next_vevent();
> >+        if (event == NULL || event->type == VEVENT_LAST) {
> >+            break;
> >+        }
> >+        if (event->type != VEVENT_READER_INSERT) {
> >+            if (card->reader == NULL&&  event->reader != NULL) {
> >+                // Happens after device_add followed by card remove or insert.
> >+                // XXX: create synthetic add_reader events if vcard_emul_init
> >+                // already called, which happens if device_del and device_add are
> >+                // called
> >+                card->reader = vreader_reference(event->reader);
> >+            } else {
> >+                if (event->reader != card->reader) {
> >+                    fprintf(stderr, "ERROR: wrong reader: quiting event_thread\n");
> >+                    break;
> >+                }
> >+            }
> >+        }
> >+        switch (event->type) {
> >+        case VEVENT_READER_INSERT:
> >+            /* TODO: take a specific reader. i.e. track which reader
> >+             * we are seeing here, check it is the one we want (the first,
> >+             * or by a particular name), and ignore if we don't want it.
> >+             */
> >+            reader_name = vreader_get_name(event->reader);
> >+            if (card->reader != NULL) {
> >+                DPRINTF(card, 2, "READER INSERT - replacing %s with %s\n",
> >+                    vreader_get_name(card->reader), reader_name);
> >+                pthread_mutex_lock(&card->vreader_mutex);
> >+                vreader_free(card->reader);
> >+                pthread_mutex_unlock(&card->vreader_mutex);
> >+                emulated_push_reader_remove(card);
> >+            }
> >+            pthread_mutex_lock(&card->vreader_mutex);
> >+            DPRINTF(card, 2, "READER INSERT %s\n", reader_name);
> >+            card->reader = vreader_reference(event->reader);
> >+            pthread_mutex_unlock(&card->vreader_mutex);
> >+            emulated_push_reader_insert(card);
> >+            break;
> >+        case VEVENT_READER_REMOVE:
> >+            DPRINTF(card, 2, " READER REMOVE: %s \n",
> >+                    vreader_get_name(event->reader));
> >+            pthread_mutex_lock(&card->vreader_mutex);
> >+            vreader_free(card->reader);
> >+            card->reader = NULL;
> >+            pthread_mutex_unlock(&card->vreader_mutex);
> >+            emulated_push_reader_remove(card);
> >+            break;
> >+        case VEVENT_CARD_INSERT:
> >+            /* get the ATR (intended as a response to a power on from the
> >+             * reader */
> >+            atr_len = MAX_ATR_SIZE;
> >+            vreader_power_on(event->reader, atr,&atr_len);
> >+            card->atr_length = (uint8_t)atr_len;
> >+            DPRINTF(card, 2, " CARD INSERT\n");
> >+            emulated_push_card_insert(card, atr, atr_len);
> >+            break;
> >+        case VEVENT_CARD_REMOVE:
> >+            DPRINTF(card, 2, " CARD REMOVE\n");
> >+            emulated_push_card_remove(card);
> >+            break;
> >+        case VEVENT_LAST: /* quit */
> >+            vevent_delete(event);
> >+            return NULL;
> >+            break;
> >+        default:
> >+            break;
> >+        }
> >+        vevent_delete(event);
> >+    }
> >+    return NULL;
> >+}
> >+
> >+static void pipe_read(void *opaque)
> >+{
> >+    EmulatedState *card = opaque;
> >+    EmulEvent *event, *next;
> >+    char dummy;
> >+    int len;
> >+
> >+    do {
> >+        len = read(card->pipe[0],&dummy, sizeof(dummy));
> >+    } while (len == sizeof(dummy));
> >+    pthread_mutex_lock(&card->event_list_mutex);
> >+    QSIMPLEQ_FOREACH_SAFE(event,&card->event_list, entry, next) {
> >+        DPRINTF(card, 2, "event %s\n", emul_event_to_string(event->p.gen.type));
> >+        switch (event->p.gen.type) {
> >+            case EMUL_RESPONSE_APDU:
> >+                ccid_card_send_apdu_to_guest(&card->base, event->p.data.data,
> >+                    event->p.data.len);
> >+                break;
> >+            case EMUL_READER_INSERT:
> >+                ccid_card_ccid_attach(&card->base);
> >+                break;
> >+            case EMUL_READER_REMOVE:
> >+                ccid_card_ccid_detach(&card->base);
> >+                break;
> >+            case EMUL_CARD_INSERT:
> >+                assert(event->p.data.len<= MAX_ATR_SIZE);
> >+                card->atr_length = event->p.data.len;
> >+                memcpy(card->atr, event->p.data.data, card->atr_length);
> >+                ccid_card_card_inserted(&card->base);
> >+                break;
> >+            case EMUL_CARD_REMOVE:
> >+                ccid_card_card_removed(&card->base);
> >+                break;
> >+            case EMUL_ERROR:
> >+                ccid_card_card_error(&card->base, event->p.error.code);
> >+                break;
> >+            default:
> >+                DPRINTF(card, 2, "unexpected event\n");
> >+                break;
> >+        }
> >+        free(event);
> >+    }
> >+    QSIMPLEQ_INIT(&card->event_list);
> >+    pthread_mutex_unlock(&card->event_list_mutex);
> >+}
> >+
> >+static int init_pipe_signaling(EmulatedState *card)
> >+{
> >+   if (pipe(card->pipe)<  0) {
> >+       DPRINTF(card, 2, "pipe creation failed\n");
> >+       return -1;
> >+   }
> >+   fcntl(card->pipe[0], F_SETFL, O_NONBLOCK);
> >+   fcntl(card->pipe[1], F_SETFL, O_NONBLOCK);
> >+   fcntl(card->pipe[0], F_SETOWN, getpid());
> >+   qemu_set_fd_handler(card->pipe[0], pipe_read, NULL, card);
> >+   return 0;
> >+}
> >+
> >+#define CERTIFICATES_DEFAULT_DB "/etc/pki/nssdb"
> >+#define CERTIFICATES_ARGS_TEMPLATE "db=\"%s\" use_hw=no soft=(,Virtual Reader,CAC,,%s,%s,%s)"
> >+
> >+static int wrap_vcard_emul_init(VCardEmulOptions *options)
> >+{
> >+    static int called = 0;
> >+    static int options_was_null = 0;
> >+
> >+    if (called) {
> >+        if ((options == NULL) != options_was_null) {
> >+            printf("%s: warning: running emulated with certificates and emulated side by side is not supported\n", __FUNCTION__);
> >+            return VCARD_EMUL_FAIL;
> >+        }
> >+        vcard_emul_replay_insertion_events();
> >+        return VCARD_EMUL_OK;
> >+    }
> >+    options_was_null = (options == NULL);
> >+    called = 1;
> >+    return vcard_emul_init(options);
> >+}
> >+
> >+static int emulated_initialize_vcard_from_certificates(EmulatedState *card)
> >+{
> >+    char emul_args[200];
> >+    VCardEmulOptions *options = NULL;
> >+
> >+    snprintf(emul_args, sizeof(emul_args) - 1, CERTIFICATES_ARGS_TEMPLATE,
> >+        card->db ? card->db : CERTIFICATES_DEFAULT_DB,
> >+        card->cert1, card->cert2, card->cert3);
> >+    options = vcard_emul_options(emul_args);
> >+    if (options == NULL) {
> >+        printf("%s: warning: not using certificates due to initialization error\n", __func__);
> >+    }
> >+    return wrap_vcard_emul_init(options);
> >+}
> >+
> >+static int emulated_initfn(CCIDCardState *base)
> >+{
> >+    EmulatedState *card = DO_UPCAST(EmulatedState, base, base);
> >+    int rv;
> >+    pthread_t thread_id;
> >+    VCardEmulError ret;
> >+
> >+    QSIMPLEQ_INIT(&card->event_list);
> >+    QSIMPLEQ_INIT(&card->guest_apdu_list);
> >+    pthread_mutex_init(&card->event_list_mutex, NULL);
> >+    pthread_mutex_init(&card->vreader_mutex, NULL);
> >+    pthread_mutex_init(&card->handle_apdu_mutex, NULL);
> >+    pthread_cond_init(&card->handle_apdu_cond, NULL);
> >+    card->reader = NULL;
> >+    card->quit_apdu_thread = 0;
> >+    if (init_pipe_signaling(card)<  0) {
> >+        return -1;
> >+    }
> >+    if (!card->backend) {
> >+        card->backend = strdup((char*)BACKEND_NSS_EMULATED);
> >+    }
> >+    /* TODO: a passthru backened that works on local machine. third card type? */
> >+    if (strcmp(BACKEND_CERTIFICATES, card->backend) == 0
> >+&&  card->cert1 != NULL&&  card->cert2 != NULL&&  card->cert3 != NULL) {
> >+        ret = emulated_initialize_vcard_from_certificates(card);
> >+    } else {
> >+        if (strcmp(BACKEND_CERTIFICATES, card->backend) == 0) {
> >+            printf("%s: you must provide all three certs for certificates backend\n",
> >+                EMULATED_DEV_NAME);
> >+            return -1;
> >+        }
> >+        if (card->backend&&  strcmp(BACKEND_NSS_EMULATED, card->backend) != 0) {
> >+            printf("%s: bad backend specified. The options are:\n%s (default), %s.\n",
> >+                EMULATED_DEV_NAME, BACKEND_NSS_EMULATED, BACKEND_CERTIFICATES);
> >+            return -1;
> >+        }
> >+        /* default to mirroring the local hardware readers */
> >+        ret = wrap_vcard_emul_init(NULL);
> >+    }
> >+    if (ret != VCARD_EMUL_OK) {
> >+        printf("%s: failed to initialize vcard\n", EMULATED_DEV_NAME);
> >+        return -1;
> >+    }
> >+    rv = pthread_create(&thread_id, NULL, event_thread, card);
> >+    if (rv<  0) {
> >+        printf("%s: error creating event thread\n", EMULATED_DEV_NAME);
> >+        return -1;
> >+    }
> 
> Why can't this be implemented with qemu_set_fd_handler?  The event
> thread si just spinning on read.
> 

The issue is not letting NSS block any of qemu's threads.

> Regards,
> 
> Anthony Liguori
> 
> >+    rv = pthread_create(&thread_id, NULL, handle_apdu_thread, card);
> >+    if (rv<  0) {
> >+        printf("%s: error creating handle_apdu thread\n", EMULATED_DEV_NAME);
> >+        return -1;
> >+    }
> >+    return 0;
> >+}
> >+
> >+static int emulated_exitfn(CCIDCardState *base)
> >+{
> >+    EmulatedState *card = DO_UPCAST(EmulatedState, base, base);
> >+    VEvent *vevent = vevent_new(VEVENT_LAST, NULL, NULL);
> >+
> >+    vevent_queue_vevent(vevent); /* stop vevent thread */
> >+    pthread_mutex_lock(&card->apdu_thread_quit_mutex);
> >+    card->quit_apdu_thread = 1; /* stop handle_apdu thread */
> >+    pthread_cond_signal(&card->handle_apdu_cond);
> >+    pthread_cond_wait(&card->apdu_thread_quit_cond,&card->apdu_thread_quit_mutex);
> >+    /* handle_apdu thread stopped, can destroy all of it's mutexes */
> >+    pthread_cond_destroy(&card->handle_apdu_cond);
> >+    pthread_cond_destroy(&card->apdu_thread_quit_cond);
> >+    pthread_mutex_destroy(&card->apdu_thread_quit_mutex);
> >+    pthread_mutex_destroy(&card->handle_apdu_mutex);
> >+    pthread_mutex_destroy(&card->vreader_mutex);
> >+    pthread_mutex_destroy(&card->event_list_mutex);
> >+    return 0;
> >+}
> >+
> >+static CCIDCardInfo emulated_card_info = {
> >+    .qdev.name = EMULATED_DEV_NAME,
> >+    .qdev.size = sizeof(EmulatedState),
> >+    .initfn = emulated_initfn,
> >+    .exitfn = emulated_exitfn,
> >+    .get_atr = emulated_get_atr,
> >+    .apdu_from_guest = emulated_apdu_from_guest,
> >+    .qdev.unplug    = qdev_simple_unplug_cb,
> >+    .qdev.props     = (Property[]) {
> >+        DEFINE_PROP_STRING("backend", EmulatedState, backend),
> >+        DEFINE_PROP_STRING("cert1", EmulatedState, cert1),
> >+        DEFINE_PROP_STRING("cert2", EmulatedState, cert2),
> >+        DEFINE_PROP_STRING("cert3", EmulatedState, cert3),
> >+        DEFINE_PROP_STRING("db", EmulatedState, db),
> >+        DEFINE_PROP_UINT8("debug", EmulatedState, debug, 0),
> >+        DEFINE_PROP_END_OF_LIST(),
> >+    },
> >+};
> >+
> >+static void ccid_card_emulated_register_devices(void)
> >+{
> >+    ccid_card_qdev_register(&emulated_card_info);
> >+}
> >+
> >+device_init(ccid_card_emulated_register_devices)
> >diff --git a/hw/ccid-card-passthru.c b/hw/ccid-card-passthru.c
> >index 6ec4f21..f9fb82c 100644
> >--- a/hw/ccid-card-passthru.c
> >+++ b/hw/ccid-card-passthru.c
> >@@ -253,7 +253,6 @@ static CCIDCardInfo passthru_card_info = {
> >      .exitfn = passthru_exitfn,
> >      .get_atr = passthru_get_atr,
> >      .apdu_from_guest = passthru_apdu_from_guest,
> >-    .qdev.unplug    = qdev_simple_unplug_cb,
> >      .qdev.props     = (Property[]) {
> >          DEFINE_PROP_CHR("chardev", PassthruState, cs),
> >          DEFINE_PROP_UINT8("debug", PassthruState, debug, 0),
> 

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

* Re: [Qemu-devel] [PATCH 2/7] ccid: add passthru card device
  2011-01-25 16:21     ` Alon Levy
@ 2011-01-25 16:24       ` Anthony Liguori
  2011-01-25 16:50         ` Alon Levy
  0 siblings, 1 reply; 23+ messages in thread
From: Anthony Liguori @ 2011-01-25 16:24 UTC (permalink / raw)
  To: qemu-devel

On 01/25/2011 10:21 AM, Alon Levy wrote:
> On Tue, Jan 25, 2011 at 08:17:32AM -0600, Anthony Liguori wrote:
>    
>> On 01/11/2011 02:42 AM, Alon Levy wrote:
>>      
>>> diff --git a/libcacard/vscard_common.h b/libcacard/vscard_common.h
>>> new file mode 100644
>>> index 0000000..9ff1295
>>> --- /dev/null
>>> +++ b/libcacard/vscard_common.h
>>>        
>> This file (and the .c file) need a coding style pass to fixup
>> comments and the use of _ as a prefix but I want to focus on the
>> protocol itself.
>>
>> First, let's get a written spec into the wiki.  I think it's
>> important that all of our compatibility protocols are documented in
>> a more formal way such that can be reviewed by a wider audience.
>>      
> ok, I'll create Features/Smartcard/Protocol
>
>    
>>      
>>> @@ -0,0 +1,130 @@
>>> +/* Virtual Smart Card protocol definition
>>> + *
>>> + * This protocol is between a host implementing a group of virtual smart card
>>> + * reader, and a client implementing a virtual smart card, or passthrough to
>>> + * a real card.
>>> + *
>>> + * The current implementation passes the raw APDU's from 7816 and additionally
>>> + * contains messages to setup and teardown readers, handle insertion and
>>> + * removal of cards, negotiate the protocol and provide for error responses.
>>> + *
>>> + * Copyright (c) 2010 Red Hat.
>>> + *
>>> + * This code is licensed under the LGPL.
>>> + */
>>> +
>>> +#ifndef _VSCARD_COMMON_H
>>> +#define _VSCARD_COMMON_H
>>> +
>>> +#include<stdint.h>
>>> +
>>> +#define VERSION_MAJOR_BITS 11
>>> +#define VERSION_MIDDLE_BITS 11
>>> +#define VERSION_MINOR_BITS 10
>>>        
>> Distros make versioning not enough.  Inevitably, someone wants to
>> back port a bug fix or a feature for some RHEL7.2 release or
>> something like that.
>>
>> Feature negotiation has worked pretty well for us and I'd suggest
>> using it within the protocol.
>>
>>      
> Suggestion accepted.
>
>    
>>> +#define MAKE_VERSION(major, middle, minor) \
>>> +     (  (major<<   (VERSION_MINOR_BITS + VERSION_MIDDLE_BITS)) \
>>> +      | (middle<<    VERSION_MINOR_BITS) \
>>> +      | (minor)  )
>>> +
>>> +/** IMPORTANT NOTE on VERSION
>>> + *
>>> + * The version below MUST be changed whenever a change in this file is made.
>>> + *
>>> + * The last digit, the minor, is for bug fix changes only.
>>> + *
>>> + * The middle digit is for backward / forward compatible changes, updates
>>> + * to the existing messages, addition of fields.
>>> + *
>>> + * The major digit is for a breaking change of protocol, presumably
>>> + * something that cannot be accomodated with the existing protocol.
>>> + */
>>> +
>>> +#define VSCARD_VERSION MAKE_VERSION(0,0,1)
>>> +
>>> +typedef enum {
>>> +    VSC_Init,
>>> +    VSC_Error,
>>> +    VSC_ReaderAdd,
>>> +    VSC_ReaderAddResponse,
>>> +    VSC_ReaderRemove,
>>> +    VSC_ATR,
>>> +    VSC_CardRemove,
>>> +    VSC_APDU,
>>> +    VSC_Reconnect
>>> +} VSCMsgType;
>>>        
>> Should number the enum to be specific at least.
>>      
> will fix.
>
>    
>>      
>>> +
>>> +typedef enum {
>>> +    VSC_GENERAL_ERROR=1,
>>> +    VSC_CANNOT_ADD_MORE_READERS,
>>> +} VSCErrorCode;
>>> +
>>> +typedef uint32_t reader_id_t;
>>>        
>> This namespace is reserved by C.
>>      
> reader_id_t is reserved?
>    

Anything with the suffix '_t' is reserved by the standard library.

It's a widely violated rule, but we have run into problems from not 
obeying it.

>>> +/* VSCMsgReaderAdd          Client ->   Host
>>> + * Host replies with allocated reader id in ReaderAddResponse
>>> + * name - name of the reader on client side.
>>> + * */
>>> +typedef struct VSCMsgReaderAdd {
>>> +    uint8_t    name[0];
>>>        
>> Is this a string?
>>
>>      
> Yes. You expect char?
>    

Yes, also, what's the encoding (UTF-8)?

Regards,

Anthony Liguori

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

* Re: [Qemu-devel] [PATCH 4/7] ccid: add ccid-card-emulated device (v2)
  2011-01-25 16:24     ` Alon Levy
@ 2011-01-25 16:27       ` Anthony Liguori
  0 siblings, 0 replies; 23+ messages in thread
From: Anthony Liguori @ 2011-01-25 16:27 UTC (permalink / raw)
  To: qemu-devel

On 01/25/2011 10:24 AM, Alon Levy wrote:
> On Tue, Jan 25, 2011 at 08:21:13AM -0600, Anthony Liguori wrote:
>    
>> On 01/11/2011 02:42 AM, Alon Levy wrote:
>>      
>>> This devices uses libcacard (internal) to emulate a smartcard conforming
>>> to the CAC standard. It attaches to the usb-ccid bus. Usage instructions
>>> (example command lines) are in the following patch in docs/ccid.txt. It
>>> uses libcacard which uses nss, so it can work with both hw cards and
>>> certificates (files).
>>>
>>> changes from v1:
>>>      remove stale comments, use only c-style comments
>>>      bugfix, forgot to set recv_len
>>>      change reader name to 'Virtual Reader'
>>>
>>> Signed-off-by: Alon Levy<alevy@redhat.com>
>>> ---
>>>   Makefile.objs           |    2 +-
>>>   hw/ccid-card-emulated.c |  534 +++++++++++++++++++++++++++++++++++++++++++++++
>>>   hw/ccid-card-passthru.c |    1 -
>>>   3 files changed, 535 insertions(+), 2 deletions(-)
>>>   create mode 100644 hw/ccid-card-emulated.c
>>>
>>> diff --git a/Makefile.objs b/Makefile.objs
>>> index 6a0030b..303b642 100644
>>> --- a/Makefile.objs
>>> +++ b/Makefile.objs
>>> @@ -197,7 +197,7 @@ hw-obj-$(CONFIG_FDC) += fdc.o
>>>   hw-obj-$(CONFIG_ACPI) += acpi.o acpi_piix4.o
>>>   hw-obj-$(CONFIG_APM) += pm_smbus.o apm.o
>>>   hw-obj-$(CONFIG_DMA) += dma.o
>>> -hw-obj-$(CONFIG_SMARTCARD) += usb-ccid.o ccid-card-passthru.o
>>> +hw-obj-$(CONFIG_SMARTCARD) += usb-ccid.o ccid-card-passthru.o ccid-card-emulated.o
>>>
>>>   # PPC devices
>>>   hw-obj-$(CONFIG_OPENPIC) += openpic.o
>>> diff --git a/hw/ccid-card-emulated.c b/hw/ccid-card-emulated.c
>>> new file mode 100644
>>> index 0000000..5531ce1
>>> --- /dev/null
>>> +++ b/hw/ccid-card-emulated.c
>>> @@ -0,0 +1,534 @@
>>> +/*
>>> + * CCID Card Device. Emulated card.
>>> + *
>>> + * It can be used to provide access to the local hardware in a non exclusive
>>> + * way, or it can use certificates. It requires the usb-ccid bus.
>>> + *
>>> + * Usage 1: standard, mirror hardware reader+card:
>>> + * qemu .. -usb -device usb-ccid -device ccid-card-emulated
>>> + *
>>> + * Usage 2: use certificates, no hardware required
>>> + * one time: create the certificates:
>>> + *  for i in 1 2 3; do certutil -d /etc/pki/nssdb -x -t "CT,CT,CT" -S -s "CN=user$i" -n user$i; done
>>> + * qemu .. -usb -device usb-ccid -device ccid-card-emulated,cert1=user1,cert2=user2,cert3=user3
>>> + *
>>> + * If you use a non default db for the certificates you can specify it using the db parameter.
>>> + *
>>> + *
>>> + * Copyright (c) 2010 Red Hat.
>>> + * Written by Alon Levy.
>>> + *
>>> + * This code is licenced under the LGPL.
>>> + */
>>> +
>>> +#include<pthread.h>
>>> +#include<eventt.h>
>>> +#include<vevent.h>
>>> +#include<vreader.h>
>>> +#include<vcard_emul.h>
>>> +#include "qemu-char.h"
>>> +#include "monitor.h"
>>> +#include "hw/ccid.h"
>>> +
>>> +#define DPRINTF(card, lvl, fmt, ...) \
>>> +do { if (lvl<= card->debug) { printf("ccid-card-emul: %s: " fmt , __func__, ## __VA_ARGS__); } } while (0)
>>> +
>>> +#define EMULATED_DEV_NAME "ccid-card-emulated"
>>> +
>>> +#define BACKEND_NSS_EMULATED "nss-emulated" /* the default */
>>> +#define BACKEND_CERTIFICATES "certificates"
>>> +
>>> +typedef struct EmulatedState EmulatedState;
>>> +
>>> +enum {
>>> +    EMUL_READER_INSERT = 0,
>>> +    EMUL_READER_REMOVE,
>>> +    EMUL_CARD_INSERT,
>>> +    EMUL_CARD_REMOVE,
>>> +    EMUL_GUEST_APDU,
>>> +    EMUL_RESPONSE_APDU,
>>> +    EMUL_ERROR,
>>> +};
>>> +
>>> +static const char* emul_event_to_string(uint32_t emul_event)
>>> +{
>>> +    switch (emul_event) {
>>> +        case EMUL_READER_INSERT: return "EMUL_READER_INSERT";
>>> +        case EMUL_READER_REMOVE: return "EMUL_READER_REMOVE";
>>> +        case EMUL_CARD_INSERT: return "EMUL_CARD_INSERT";
>>> +        case EMUL_CARD_REMOVE: return "EMUL_CARD_REMOVE";
>>> +        case EMUL_GUEST_APDU: return "EMUL_GUEST_APDU";
>>> +        case EMUL_RESPONSE_APDU: return "EMUL_RESPONSE_APDU";
>>> +        case EMUL_ERROR: return "EMUL_ERROR";
>>> +        default:
>>> +            break;
>>> +    }
>>> +    return "UNKNOWN";
>>> +}
>>> +
>>> +typedef struct EmulEvent {
>>> +    QSIMPLEQ_ENTRY(EmulEvent) entry;
>>> +    union {
>>> +        struct {
>>> +            uint32_t type;
>>> +        } gen;
>>> +        struct {
>>> +            uint32_t type;
>>> +            uint64_t code;
>>> +        } error;
>>> +        struct {
>>> +            uint32_t type;
>>> +            uint32_t len;
>>> +            uint8_t data[];
>>> +        } data;
>>> +    } p;
>>> +} EmulEvent;
>>> +
>>> +#define MAX_ATR_SIZE 40
>>> +struct EmulatedState {
>>> +    CCIDCardState base;
>>> +    uint8_t  debug;
>>> +    char*    backend;
>>> +    char*    cert1;
>>> +    char*    cert2;
>>> +    char*    cert3;
>>> +    char*    db;
>>> +    uint8_t  atr[MAX_ATR_SIZE];
>>> +    uint8_t  atr_length;
>>> +    QSIMPLEQ_HEAD(event_list, EmulEvent) event_list;
>>> +    pthread_mutex_t event_list_mutex;
>>> +    VReader *reader;
>>> +    QSIMPLEQ_HEAD(guest_apdu_list, EmulEvent) guest_apdu_list;
>>> +    pthread_mutex_t vreader_mutex; /* and guest_apdu_list mutex */
>>> +    pthread_mutex_t handle_apdu_mutex;
>>> +    pthread_cond_t handle_apdu_cond;
>>> +    int      pipe[2];
>>> +    int      quit_apdu_thread;
>>> +    pthread_mutex_t apdu_thread_quit_mutex;
>>> +    pthread_cond_t apdu_thread_quit_cond;
>>> +};
>>> +
>>> +static void emulated_apdu_from_guest(CCIDCardState *base, const uint8_t *apdu, uint32_t len)
>>> +{
>>> +    EmulatedState *card = DO_UPCAST(EmulatedState, base, base);
>>> +    EmulEvent *event = (EmulEvent*)malloc(sizeof(EmulEvent) + len);
>>> +
>>> +    assert(event);
>>> +    event->p.data.type = EMUL_GUEST_APDU;
>>> +    event->p.data.len = len;
>>> +    memcpy(event->p.data.data, apdu, len);
>>> +    pthread_mutex_lock(&card->vreader_mutex);
>>> +    QSIMPLEQ_INSERT_TAIL(&card->guest_apdu_list, event, entry);
>>> +    pthread_mutex_unlock(&card->vreader_mutex);
>>> +    pthread_mutex_lock(&card->handle_apdu_mutex);
>>> +    pthread_cond_signal(&card->handle_apdu_cond);
>>> +    pthread_mutex_unlock(&card->handle_apdu_mutex);
>>> +}
>>> +
>>> +static const uint8_t* emulated_get_atr(CCIDCardState *base, uint32_t *len)
>>> +{
>>> +    EmulatedState *card = DO_UPCAST(EmulatedState, base, base);
>>> +
>>> +    *len = card->atr_length;
>>> +    return card->atr;
>>> +}
>>> +
>>> +static void emulated_push_event(EmulatedState *card, EmulEvent *event)
>>> +{
>>> +    pthread_mutex_lock(&card->event_list_mutex);
>>> +    QSIMPLEQ_INSERT_TAIL(&(card->event_list), event, entry);
>>> +    pthread_mutex_unlock(&card->event_list_mutex);
>>> +    if (write(card->pipe[1], card, 1) != 1) {
>>> +        DPRINTF(card, 1, "write to pipe failed\n");
>>> +    }
>>> +}
>>> +
>>> +static void emulated_push_type(EmulatedState *card, uint32_t type)
>>> +{
>>> +    EmulEvent *event = (EmulEvent*)malloc(sizeof(EmulEvent));
>>> +
>>> +    assert(event);
>>> +    event->p.gen.type = type;
>>> +    emulated_push_event(card, event);
>>> +}
>>> +
>>> +static void emulated_push_error(EmulatedState *card, uint64_t code)
>>> +{
>>> +    EmulEvent *event = (EmulEvent*)malloc(sizeof(EmulEvent));
>>> +
>>> +    assert(event);
>>> +    event->p.error.type = EMUL_ERROR;
>>> +    event->p.error.code = code;
>>> +    emulated_push_event(card, event);
>>> +}
>>> +
>>> +static void emulated_push_data_type(EmulatedState *card, uint32_t type,
>>> +    const uint8_t *data, uint32_t len)
>>> +{
>>> +    EmulEvent *event = (EmulEvent*)malloc(sizeof(EmulEvent) + len);
>>> +
>>> +    assert(event);
>>> +    event->p.data.type = type;
>>> +    event->p.data.len = len;
>>> +    memcpy(event->p.data.data, data, len);
>>> +    emulated_push_event(card, event);
>>> +}
>>> +
>>> +static void emulated_push_reader_insert(EmulatedState *card)
>>> +{
>>> +    emulated_push_type(card, EMUL_READER_INSERT);
>>> +}
>>> +
>>> +static void emulated_push_reader_remove(EmulatedState *card)
>>> +{
>>> +    emulated_push_type(card, EMUL_READER_REMOVE);
>>> +}
>>> +
>>> +static void emulated_push_card_insert(EmulatedState *card,
>>> +    const uint8_t *atr, uint32_t len)
>>> +{
>>> +    emulated_push_data_type(card, EMUL_CARD_INSERT, atr, len);
>>> +}
>>> +
>>> +static void emulated_push_card_remove(EmulatedState *card)
>>> +{
>>> +    emulated_push_type(card, EMUL_CARD_REMOVE);
>>> +}
>>> +
>>> +static void emulated_push_response_apdu(EmulatedState *card,
>>> +    const uint8_t* apdu, uint32_t len)
>>> +{
>>> +    emulated_push_data_type(card, EMUL_RESPONSE_APDU, apdu, len);
>>> +}
>>> +
>>> +#define APDU_BUF_SIZE 270
>>> +static void *handle_apdu_thread(void* arg)
>>> +{
>>> +    EmulatedState *card = arg;
>>> +    uint8_t recv_data[APDU_BUF_SIZE];
>>> +    int recv_len;
>>> +    VReaderStatus reader_status;
>>> +    EmulEvent *event;
>>> +
>>> +    while (1) {
>>> +        pthread_mutex_lock(&card->handle_apdu_mutex);
>>> +        pthread_cond_wait(&card->handle_apdu_cond,&card->handle_apdu_mutex);
>>> +        pthread_mutex_unlock(&card->handle_apdu_mutex);
>>> +        if (card->quit_apdu_thread) {
>>> +            card->quit_apdu_thread = 0; // debugging
>>> +            break;
>>> +        }
>>> +        pthread_mutex_lock(&card->vreader_mutex);
>>> +        while (!QSIMPLEQ_EMPTY(&card->guest_apdu_list)) {
>>> +            event = QSIMPLEQ_FIRST(&card->guest_apdu_list);
>>> +            assert((unsigned long)event>   1000);
>>> +            QSIMPLEQ_REMOVE_HEAD(&card->guest_apdu_list, entry);
>>> +            if (event->p.data.type != EMUL_GUEST_APDU) {
>>> +                DPRINTF(card, 1, "unexpected message in handle_apdu_thread\n");
>>> +                free(event);
>>> +                continue;
>>> +            }
>>> +            if (card->reader == NULL) {
>>> +                DPRINTF(card, 1, "reader is NULL\n");
>>> +                free(event);
>>> +                continue;
>>> +            }
>>> +            recv_len = sizeof(recv_data);
>>> +            reader_status = vreader_xfr_bytes(card->reader,
>>> +                    event->p.data.data, event->p.data.len,
>>> +                    recv_data,&recv_len);
>>> +            DPRINTF(card, 2, "got back apdu of length %d\n", recv_len);
>>> +            if (reader_status == VREADER_OK) {
>>> +                emulated_push_response_apdu(card, recv_data, recv_len);
>>> +            } else {
>>> +                emulated_push_error(card, reader_status);
>>> +            }
>>> +            free(event);
>>> +        }
>>> +        pthread_mutex_unlock(&card->vreader_mutex);
>>> +    }
>>> +    pthread_mutex_lock(&card->apdu_thread_quit_mutex);
>>> +    pthread_cond_signal(&card->apdu_thread_quit_cond);
>>> +    pthread_mutex_unlock(&card->apdu_thread_quit_mutex);
>>> +    return NULL;
>>> +}
>>> +
>>> +static void *event_thread(void *arg)
>>> +{
>>> +    int atr_len = MAX_ATR_SIZE;
>>> +    uint8_t atr[MAX_ATR_SIZE];
>>> +    VEvent *event = NULL;
>>> +    EmulatedState *card = arg;
>>> +
>>> +    while (1) {
>>> +        const char *reader_name;
>>> +
>>> +        event = vevent_wait_next_vevent();
>>> +        if (event == NULL || event->type == VEVENT_LAST) {
>>> +            break;
>>> +        }
>>> +        if (event->type != VEVENT_READER_INSERT) {
>>> +            if (card->reader == NULL&&   event->reader != NULL) {
>>> +                // Happens after device_add followed by card remove or insert.
>>> +                // XXX: create synthetic add_reader events if vcard_emul_init
>>> +                // already called, which happens if device_del and device_add are
>>> +                // called
>>> +                card->reader = vreader_reference(event->reader);
>>> +            } else {
>>> +                if (event->reader != card->reader) {
>>> +                    fprintf(stderr, "ERROR: wrong reader: quiting event_thread\n");
>>> +                    break;
>>> +                }
>>> +            }
>>> +        }
>>> +        switch (event->type) {
>>> +        case VEVENT_READER_INSERT:
>>> +            /* TODO: take a specific reader. i.e. track which reader
>>> +             * we are seeing here, check it is the one we want (the first,
>>> +             * or by a particular name), and ignore if we don't want it.
>>> +             */
>>> +            reader_name = vreader_get_name(event->reader);
>>> +            if (card->reader != NULL) {
>>> +                DPRINTF(card, 2, "READER INSERT - replacing %s with %s\n",
>>> +                    vreader_get_name(card->reader), reader_name);
>>> +                pthread_mutex_lock(&card->vreader_mutex);
>>> +                vreader_free(card->reader);
>>> +                pthread_mutex_unlock(&card->vreader_mutex);
>>> +                emulated_push_reader_remove(card);
>>> +            }
>>> +            pthread_mutex_lock(&card->vreader_mutex);
>>> +            DPRINTF(card, 2, "READER INSERT %s\n", reader_name);
>>> +            card->reader = vreader_reference(event->reader);
>>> +            pthread_mutex_unlock(&card->vreader_mutex);
>>> +            emulated_push_reader_insert(card);
>>> +            break;
>>> +        case VEVENT_READER_REMOVE:
>>> +            DPRINTF(card, 2, " READER REMOVE: %s \n",
>>> +                    vreader_get_name(event->reader));
>>> +            pthread_mutex_lock(&card->vreader_mutex);
>>> +            vreader_free(card->reader);
>>> +            card->reader = NULL;
>>> +            pthread_mutex_unlock(&card->vreader_mutex);
>>> +            emulated_push_reader_remove(card);
>>> +            break;
>>> +        case VEVENT_CARD_INSERT:
>>> +            /* get the ATR (intended as a response to a power on from the
>>> +             * reader */
>>> +            atr_len = MAX_ATR_SIZE;
>>> +            vreader_power_on(event->reader, atr,&atr_len);
>>> +            card->atr_length = (uint8_t)atr_len;
>>> +            DPRINTF(card, 2, " CARD INSERT\n");
>>> +            emulated_push_card_insert(card, atr, atr_len);
>>> +            break;
>>> +        case VEVENT_CARD_REMOVE:
>>> +            DPRINTF(card, 2, " CARD REMOVE\n");
>>> +            emulated_push_card_remove(card);
>>> +            break;
>>> +        case VEVENT_LAST: /* quit */
>>> +            vevent_delete(event);
>>> +            return NULL;
>>> +            break;
>>> +        default:
>>> +            break;
>>> +        }
>>> +        vevent_delete(event);
>>> +    }
>>> +    return NULL;
>>> +}
>>> +
>>> +static void pipe_read(void *opaque)
>>> +{
>>> +    EmulatedState *card = opaque;
>>> +    EmulEvent *event, *next;
>>> +    char dummy;
>>> +    int len;
>>> +
>>> +    do {
>>> +        len = read(card->pipe[0],&dummy, sizeof(dummy));
>>> +    } while (len == sizeof(dummy));
>>> +    pthread_mutex_lock(&card->event_list_mutex);
>>> +    QSIMPLEQ_FOREACH_SAFE(event,&card->event_list, entry, next) {
>>> +        DPRINTF(card, 2, "event %s\n", emul_event_to_string(event->p.gen.type));
>>> +        switch (event->p.gen.type) {
>>> +            case EMUL_RESPONSE_APDU:
>>> +                ccid_card_send_apdu_to_guest(&card->base, event->p.data.data,
>>> +                    event->p.data.len);
>>> +                break;
>>> +            case EMUL_READER_INSERT:
>>> +                ccid_card_ccid_attach(&card->base);
>>> +                break;
>>> +            case EMUL_READER_REMOVE:
>>> +                ccid_card_ccid_detach(&card->base);
>>> +                break;
>>> +            case EMUL_CARD_INSERT:
>>> +                assert(event->p.data.len<= MAX_ATR_SIZE);
>>> +                card->atr_length = event->p.data.len;
>>> +                memcpy(card->atr, event->p.data.data, card->atr_length);
>>> +                ccid_card_card_inserted(&card->base);
>>> +                break;
>>> +            case EMUL_CARD_REMOVE:
>>> +                ccid_card_card_removed(&card->base);
>>> +                break;
>>> +            case EMUL_ERROR:
>>> +                ccid_card_card_error(&card->base, event->p.error.code);
>>> +                break;
>>> +            default:
>>> +                DPRINTF(card, 2, "unexpected event\n");
>>> +                break;
>>> +        }
>>> +        free(event);
>>> +    }
>>> +    QSIMPLEQ_INIT(&card->event_list);
>>> +    pthread_mutex_unlock(&card->event_list_mutex);
>>> +}
>>> +
>>> +static int init_pipe_signaling(EmulatedState *card)
>>> +{
>>> +   if (pipe(card->pipe)<   0) {
>>> +       DPRINTF(card, 2, "pipe creation failed\n");
>>> +       return -1;
>>> +   }
>>> +   fcntl(card->pipe[0], F_SETFL, O_NONBLOCK);
>>> +   fcntl(card->pipe[1], F_SETFL, O_NONBLOCK);
>>> +   fcntl(card->pipe[0], F_SETOWN, getpid());
>>> +   qemu_set_fd_handler(card->pipe[0], pipe_read, NULL, card);
>>> +   return 0;
>>> +}
>>> +
>>> +#define CERTIFICATES_DEFAULT_DB "/etc/pki/nssdb"
>>> +#define CERTIFICATES_ARGS_TEMPLATE "db=\"%s\" use_hw=no soft=(,Virtual Reader,CAC,,%s,%s,%s)"
>>> +
>>> +static int wrap_vcard_emul_init(VCardEmulOptions *options)
>>> +{
>>> +    static int called = 0;
>>> +    static int options_was_null = 0;
>>> +
>>> +    if (called) {
>>> +        if ((options == NULL) != options_was_null) {
>>> +            printf("%s: warning: running emulated with certificates and emulated side by side is not supported\n", __FUNCTION__);
>>> +            return VCARD_EMUL_FAIL;
>>> +        }
>>> +        vcard_emul_replay_insertion_events();
>>> +        return VCARD_EMUL_OK;
>>> +    }
>>> +    options_was_null = (options == NULL);
>>> +    called = 1;
>>> +    return vcard_emul_init(options);
>>> +}
>>> +
>>> +static int emulated_initialize_vcard_from_certificates(EmulatedState *card)
>>> +{
>>> +    char emul_args[200];
>>> +    VCardEmulOptions *options = NULL;
>>> +
>>> +    snprintf(emul_args, sizeof(emul_args) - 1, CERTIFICATES_ARGS_TEMPLATE,
>>> +        card->db ? card->db : CERTIFICATES_DEFAULT_DB,
>>> +        card->cert1, card->cert2, card->cert3);
>>> +    options = vcard_emul_options(emul_args);
>>> +    if (options == NULL) {
>>> +        printf("%s: warning: not using certificates due to initialization error\n", __func__);
>>> +    }
>>> +    return wrap_vcard_emul_init(options);
>>> +}
>>> +
>>> +static int emulated_initfn(CCIDCardState *base)
>>> +{
>>> +    EmulatedState *card = DO_UPCAST(EmulatedState, base, base);
>>> +    int rv;
>>> +    pthread_t thread_id;
>>> +    VCardEmulError ret;
>>> +
>>> +    QSIMPLEQ_INIT(&card->event_list);
>>> +    QSIMPLEQ_INIT(&card->guest_apdu_list);
>>> +    pthread_mutex_init(&card->event_list_mutex, NULL);
>>> +    pthread_mutex_init(&card->vreader_mutex, NULL);
>>> +    pthread_mutex_init(&card->handle_apdu_mutex, NULL);
>>> +    pthread_cond_init(&card->handle_apdu_cond, NULL);
>>> +    card->reader = NULL;
>>> +    card->quit_apdu_thread = 0;
>>> +    if (init_pipe_signaling(card)<   0) {
>>> +        return -1;
>>> +    }
>>> +    if (!card->backend) {
>>> +        card->backend = strdup((char*)BACKEND_NSS_EMULATED);
>>> +    }
>>> +    /* TODO: a passthru backened that works on local machine. third card type? */
>>> +    if (strcmp(BACKEND_CERTIFICATES, card->backend) == 0
>>> +&&   card->cert1 != NULL&&   card->cert2 != NULL&&   card->cert3 != NULL) {
>>> +        ret = emulated_initialize_vcard_from_certificates(card);
>>> +    } else {
>>> +        if (strcmp(BACKEND_CERTIFICATES, card->backend) == 0) {
>>> +            printf("%s: you must provide all three certs for certificates backend\n",
>>> +                EMULATED_DEV_NAME);
>>> +            return -1;
>>> +        }
>>> +        if (card->backend&&   strcmp(BACKEND_NSS_EMULATED, card->backend) != 0) {
>>> +            printf("%s: bad backend specified. The options are:\n%s (default), %s.\n",
>>> +                EMULATED_DEV_NAME, BACKEND_NSS_EMULATED, BACKEND_CERTIFICATES);
>>> +            return -1;
>>> +        }
>>> +        /* default to mirroring the local hardware readers */
>>> +        ret = wrap_vcard_emul_init(NULL);
>>> +    }
>>> +    if (ret != VCARD_EMUL_OK) {
>>> +        printf("%s: failed to initialize vcard\n", EMULATED_DEV_NAME);
>>> +        return -1;
>>> +    }
>>> +    rv = pthread_create(&thread_id, NULL, event_thread, card);
>>> +    if (rv<   0) {
>>> +        printf("%s: error creating event thread\n", EMULATED_DEV_NAME);
>>> +        return -1;
>>> +    }
>>>        
>> Why can't this be implemented with qemu_set_fd_handler?  The event
>> thread si just spinning on read.
>>
>>      
> The issue is not letting NSS block any of qemu's threads.
>    

I'd rather use a thread pool to execute NSS calls asynchronously.  None 
of QEMU is really thread safe which means you'll have to be really 
careful about what goes into these threads.  I think this leads to code 
that is very subtle and easy to break.

Regards,

Anthony Liguori

>    
>> Regards,
>>
>> Anthony Liguori
>>
>>      
>>> +    rv = pthread_create(&thread_id, NULL, handle_apdu_thread, card);
>>> +    if (rv<   0) {
>>> +        printf("%s: error creating handle_apdu thread\n", EMULATED_DEV_NAME);
>>> +        return -1;
>>> +    }
>>> +    return 0;
>>> +}
>>> +
>>> +static int emulated_exitfn(CCIDCardState *base)
>>> +{
>>> +    EmulatedState *card = DO_UPCAST(EmulatedState, base, base);
>>> +    VEvent *vevent = vevent_new(VEVENT_LAST, NULL, NULL);
>>> +
>>> +    vevent_queue_vevent(vevent); /* stop vevent thread */
>>> +    pthread_mutex_lock(&card->apdu_thread_quit_mutex);
>>> +    card->quit_apdu_thread = 1; /* stop handle_apdu thread */
>>> +    pthread_cond_signal(&card->handle_apdu_cond);
>>> +    pthread_cond_wait(&card->apdu_thread_quit_cond,&card->apdu_thread_quit_mutex);
>>> +    /* handle_apdu thread stopped, can destroy all of it's mutexes */
>>> +    pthread_cond_destroy(&card->handle_apdu_cond);
>>> +    pthread_cond_destroy(&card->apdu_thread_quit_cond);
>>> +    pthread_mutex_destroy(&card->apdu_thread_quit_mutex);
>>> +    pthread_mutex_destroy(&card->handle_apdu_mutex);
>>> +    pthread_mutex_destroy(&card->vreader_mutex);
>>> +    pthread_mutex_destroy(&card->event_list_mutex);
>>> +    return 0;
>>> +}
>>> +
>>> +static CCIDCardInfo emulated_card_info = {
>>> +    .qdev.name = EMULATED_DEV_NAME,
>>> +    .qdev.size = sizeof(EmulatedState),
>>> +    .initfn = emulated_initfn,
>>> +    .exitfn = emulated_exitfn,
>>> +    .get_atr = emulated_get_atr,
>>> +    .apdu_from_guest = emulated_apdu_from_guest,
>>> +    .qdev.unplug    = qdev_simple_unplug_cb,
>>> +    .qdev.props     = (Property[]) {
>>> +        DEFINE_PROP_STRING("backend", EmulatedState, backend),
>>> +        DEFINE_PROP_STRING("cert1", EmulatedState, cert1),
>>> +        DEFINE_PROP_STRING("cert2", EmulatedState, cert2),
>>> +        DEFINE_PROP_STRING("cert3", EmulatedState, cert3),
>>> +        DEFINE_PROP_STRING("db", EmulatedState, db),
>>> +        DEFINE_PROP_UINT8("debug", EmulatedState, debug, 0),
>>> +        DEFINE_PROP_END_OF_LIST(),
>>> +    },
>>> +};
>>> +
>>> +static void ccid_card_emulated_register_devices(void)
>>> +{
>>> +    ccid_card_qdev_register(&emulated_card_info);
>>> +}
>>> +
>>> +device_init(ccid_card_emulated_register_devices)
>>> diff --git a/hw/ccid-card-passthru.c b/hw/ccid-card-passthru.c
>>> index 6ec4f21..f9fb82c 100644
>>> --- a/hw/ccid-card-passthru.c
>>> +++ b/hw/ccid-card-passthru.c
>>> @@ -253,7 +253,6 @@ static CCIDCardInfo passthru_card_info = {
>>>       .exitfn = passthru_exitfn,
>>>       .get_atr = passthru_get_atr,
>>>       .apdu_from_guest = passthru_apdu_from_guest,
>>> -    .qdev.unplug    = qdev_simple_unplug_cb,
>>>       .qdev.props     = (Property[]) {
>>>           DEFINE_PROP_CHR("chardev", PassthruState, cs),
>>>           DEFINE_PROP_UINT8("debug", PassthruState, debug, 0),
>>>        
>>      

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

* Re: [Qemu-devel] [PATCH 2/7] ccid: add passthru card device
  2011-01-25 16:24       ` Anthony Liguori
@ 2011-01-25 16:50         ` Alon Levy
  0 siblings, 0 replies; 23+ messages in thread
From: Alon Levy @ 2011-01-25 16:50 UTC (permalink / raw)
  To: Anthony Liguori; +Cc: qemu-devel

On Tue, Jan 25, 2011 at 10:24:53AM -0600, Anthony Liguori wrote:
> On 01/25/2011 10:21 AM, Alon Levy wrote:
> >On Tue, Jan 25, 2011 at 08:17:32AM -0600, Anthony Liguori wrote:
> >>On 01/11/2011 02:42 AM, Alon Levy wrote:
> >>>diff --git a/libcacard/vscard_common.h b/libcacard/vscard_common.h
> >>>new file mode 100644
> >>>index 0000000..9ff1295
> >>>--- /dev/null
> >>>+++ b/libcacard/vscard_common.h
> >>This file (and the .c file) need a coding style pass to fixup
> >>comments and the use of _ as a prefix but I want to focus on the
> >>protocol itself.
> >>
> >>First, let's get a written spec into the wiki.  I think it's
> >>important that all of our compatibility protocols are documented in
> >>a more formal way such that can be reviewed by a wider audience.
> >ok, I'll create Features/Smartcard/Protocol
> >
> >>>@@ -0,0 +1,130 @@
> >>>+/* Virtual Smart Card protocol definition
> >>>+ *
> >>>+ * This protocol is between a host implementing a group of virtual smart card
> >>>+ * reader, and a client implementing a virtual smart card, or passthrough to
> >>>+ * a real card.
> >>>+ *
> >>>+ * The current implementation passes the raw APDU's from 7816 and additionally
> >>>+ * contains messages to setup and teardown readers, handle insertion and
> >>>+ * removal of cards, negotiate the protocol and provide for error responses.
> >>>+ *
> >>>+ * Copyright (c) 2010 Red Hat.
> >>>+ *
> >>>+ * This code is licensed under the LGPL.
> >>>+ */
> >>>+
> >>>+#ifndef _VSCARD_COMMON_H
> >>>+#define _VSCARD_COMMON_H
> >>>+
> >>>+#include<stdint.h>
> >>>+
> >>>+#define VERSION_MAJOR_BITS 11
> >>>+#define VERSION_MIDDLE_BITS 11
> >>>+#define VERSION_MINOR_BITS 10
> >>Distros make versioning not enough.  Inevitably, someone wants to
> >>back port a bug fix or a feature for some RHEL7.2 release or
> >>something like that.
> >>
> >>Feature negotiation has worked pretty well for us and I'd suggest
> >>using it within the protocol.
> >>
> >Suggestion accepted.
> >
> >>>+#define MAKE_VERSION(major, middle, minor) \
> >>>+     (  (major<<   (VERSION_MINOR_BITS + VERSION_MIDDLE_BITS)) \
> >>>+      | (middle<<    VERSION_MINOR_BITS) \
> >>>+      | (minor)  )
> >>>+
> >>>+/** IMPORTANT NOTE on VERSION
> >>>+ *
> >>>+ * The version below MUST be changed whenever a change in this file is made.
> >>>+ *
> >>>+ * The last digit, the minor, is for bug fix changes only.
> >>>+ *
> >>>+ * The middle digit is for backward / forward compatible changes, updates
> >>>+ * to the existing messages, addition of fields.
> >>>+ *
> >>>+ * The major digit is for a breaking change of protocol, presumably
> >>>+ * something that cannot be accomodated with the existing protocol.
> >>>+ */
> >>>+
> >>>+#define VSCARD_VERSION MAKE_VERSION(0,0,1)
> >>>+
> >>>+typedef enum {
> >>>+    VSC_Init,
> >>>+    VSC_Error,
> >>>+    VSC_ReaderAdd,
> >>>+    VSC_ReaderAddResponse,
> >>>+    VSC_ReaderRemove,
> >>>+    VSC_ATR,
> >>>+    VSC_CardRemove,
> >>>+    VSC_APDU,
> >>>+    VSC_Reconnect
> >>>+} VSCMsgType;
> >>Should number the enum to be specific at least.
> >will fix.
> >
> >>>+
> >>>+typedef enum {
> >>>+    VSC_GENERAL_ERROR=1,
> >>>+    VSC_CANNOT_ADD_MORE_READERS,
> >>>+} VSCErrorCode;
> >>>+
> >>>+typedef uint32_t reader_id_t;
> >>This namespace is reserved by C.
> >reader_id_t is reserved?
> 
> Anything with the suffix '_t' is reserved by the standard library.
> 
> It's a widely violated rule, but we have run into problems from not
> obeying it.

I thought qemu coding style said something explicitly about using _t for
types that alias basic types - this is actually a change I did to comply..

> 
> >>>+/* VSCMsgReaderAdd          Client ->   Host
> >>>+ * Host replies with allocated reader id in ReaderAddResponse
> >>>+ * name - name of the reader on client side.
> >>>+ * */
> >>>+typedef struct VSCMsgReaderAdd {
> >>>+    uint8_t    name[0];
> >>Is this a string?
> >>
> >Yes. You expect char?
> 
> Yes, also, what's the encoding (UTF-8)?

It's not actually printed anywhere right now, so I'd say unspecififed. I'll
document UTF-8.

> 
> Regards,
> 
> Anthony Liguori
> 

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

* Re: [Qemu-devel] [PATCH 2/7] ccid: add passthru card device
  2011-01-25 14:17   ` Anthony Liguori
  2011-01-25 16:21     ` Alon Levy
@ 2011-01-27 21:13     ` Alon Levy
  2011-01-27 21:42       ` Anthony Liguori
  2011-01-30 17:35     ` Alon Levy
  2 siblings, 1 reply; 23+ messages in thread
From: Alon Levy @ 2011-01-27 21:13 UTC (permalink / raw)
  To: Anthony Liguori; +Cc: qemu-devel

On Tue, Jan 25, 2011 at 08:17:32AM -0600, Anthony Liguori wrote:
> On 01/11/2011 02:42 AM, Alon Levy wrote:
> >diff --git a/libcacard/vscard_common.h b/libcacard/vscard_common.h
> >new file mode 100644
> >index 0000000..9ff1295
> >--- /dev/null
> >+++ b/libcacard/vscard_common.h
> 
> This file (and the .c file) need a coding style pass to fixup
> comments and the use of _ as a prefix but I want to focus on the
> protocol itself.
> 
> First, let's get a written spec into the wiki.  I think it's
> important that all of our compatibility protocols are documented in
> a more formal way such that can be reviewed by a wider audience.
> 
> >@@ -0,0 +1,130 @@
> >+/* Virtual Smart Card protocol definition
> >+ *
> >+ * This protocol is between a host implementing a group of virtual smart card
> >+ * reader, and a client implementing a virtual smart card, or passthrough to
> >+ * a real card.
> >+ *
> >+ * The current implementation passes the raw APDU's from 7816 and additionally
> >+ * contains messages to setup and teardown readers, handle insertion and
> >+ * removal of cards, negotiate the protocol and provide for error responses.
> >+ *
> >+ * Copyright (c) 2010 Red Hat.
> >+ *
> >+ * This code is licensed under the LGPL.
> >+ */
> >+
> >+#ifndef _VSCARD_COMMON_H
> >+#define _VSCARD_COMMON_H
> >+
> >+#include<stdint.h>
> >+
> >+#define VERSION_MAJOR_BITS 11
> >+#define VERSION_MIDDLE_BITS 11
> >+#define VERSION_MINOR_BITS 10
> 
> Distros make versioning not enough.  Inevitably, someone wants to
> back port a bug fix or a feature for some RHEL7.2 release or
> something like that.
> 
> Feature negotiation has worked pretty well for us and I'd suggest
> using it within the protocol.
> 
> >+#define MAKE_VERSION(major, middle, minor) \
> >+     (  (major<<  (VERSION_MINOR_BITS + VERSION_MIDDLE_BITS)) \
> >+      | (middle<<   VERSION_MINOR_BITS) \
> >+      | (minor)  )
> >+
> >+/** IMPORTANT NOTE on VERSION
> >+ *
> >+ * The version below MUST be changed whenever a change in this file is made.
> >+ *
> >+ * The last digit, the minor, is for bug fix changes only.
> >+ *
> >+ * The middle digit is for backward / forward compatible changes, updates
> >+ * to the existing messages, addition of fields.
> >+ *
> >+ * The major digit is for a breaking change of protocol, presumably
> >+ * something that cannot be accomodated with the existing protocol.
> >+ */
> >+
> >+#define VSCARD_VERSION MAKE_VERSION(0,0,1)
> >+
> >+typedef enum {
> >+    VSC_Init,
> >+    VSC_Error,
> >+    VSC_ReaderAdd,
> >+    VSC_ReaderAddResponse,
> >+    VSC_ReaderRemove,
> >+    VSC_ATR,
> >+    VSC_CardRemove,
> >+    VSC_APDU,
> >+    VSC_Reconnect
> >+} VSCMsgType;
> 
> Should number the enum to be specific at least.
> 
> >+
> >+typedef enum {
> >+    VSC_GENERAL_ERROR=1,
> >+    VSC_CANNOT_ADD_MORE_READERS,
> >+} VSCErrorCode;
> >+
> >+typedef uint32_t reader_id_t;
> 
> This namespace is reserved by C.
> 
> >+#define VSCARD_UNDEFINED_READER_ID 0xffffffff
> >+#define VSCARD_MINIMAL_READER_ID    0
> >+
> >+typedef struct VSCMsgHeader {
> >+    VSCMsgType type;
> >+    reader_id_t   reader_id;
> >+    uint32_t   length;
> 
> Is length just the data length or the whole message length?
> 

The data length. Is this enough to document?

> >+    uint8_t    data[0];
> >+} VSCMsgHeader;
> >+
> >+/* VSCMsgInit               Client<->  Host
> >+ * Host replies with allocated reader id in ReaderAddResponse
> >+ * */
> >+typedef struct VSCMsgInit {
> >+    uint32_t   version;
> >+} VSCMsgInit;
> >+
> >+/* VSCMsgError              Client<->  Host
> >+ * */
> >+typedef struct VSCMsgError {
> >+    uint32_t   code;
> >+} VSCMsgError;
> >+
> >+/* VSCMsgReaderAdd          Client ->  Host
> >+ * Host replies with allocated reader id in ReaderAddResponse
> >+ * name - name of the reader on client side.
> >+ * */
> >+typedef struct VSCMsgReaderAdd {
> >+    uint8_t    name[0];
> 
> Is this a string?
> 
> >+} VSCMsgReaderAdd;
> >+
> >+/* VSCMsgReaderAddResponse  Host ->  Client
> >+ * Reply to ReaderAdd
> >+ * */
> >+typedef struct VSCMsgReaderAddResponse {
> >+} VSCMsgReaderAddResponse;
> >+
> >+/* VSCMsgReaderRemove       Client ->  Host
> >+ * */
> >+typedef struct VSCMsgReaderRemove {
> >+} VSCMsgReaderRemove;
> >+
> >+/* VSCMsgATR                Client ->  Host
> >+ * Answer to reset. Sent for card insertion or card reset.
> >+ * */
> >+typedef struct VSCMsgATR {
> >+    uint8_t     atr[0];
> >+} VSCMsgATR;
> >+
> >+/* VSCMsgCardRemove         Client ->  Host
> >+ * */
> >+typedef struct VSCMsgCardRemove {
> >+} VSCMsgCardRemove;
> >+
> >+/* VSCMsgAPDU               Client<->  Host
> >+ * */
> >+typedef struct VSCMsgAPDU {
> >+    uint8_t    data[0];
> >+} VSCMsgAPDU;
> >+
> >+/* VSCMsgReconnect          Host ->  Client
> >+ * */
> >+typedef struct VSCMsgReconnect {
> >+    uint32_t   ip;
> 
> This is not ipv6 friendly.  Two strings would be a better choice.

A string for host makes sense, why for port? isn't a 32 bit port enough?

> 
> Regards,
> 
> Anthony Liguori
> 
> >+    uint16_t   port;
> >+} VSCMsgReconnect;
> >+
> >+#endif
> 

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

* Re: [Qemu-devel] [PATCH 2/7] ccid: add passthru card device
  2011-01-27 21:13     ` Alon Levy
@ 2011-01-27 21:42       ` Anthony Liguori
  0 siblings, 0 replies; 23+ messages in thread
From: Anthony Liguori @ 2011-01-27 21:42 UTC (permalink / raw)
  To: qemu-devel; +Cc: Alon Levy

On 01/27/2011 03:13 PM, Alon Levy wrote:
>> This is not ipv6 friendly.  Two strings would be a better choice.
>>      
> A string for host makes sense, why for port? isn't a 32 bit port enough?
>    

For an protocol, an integer is probably fine.  For an API, a string is 
nice to allow service names too.

Regards,

Anthony Liguori

>> Regards,
>>
>> Anthony Liguori
>>
>>      
>>> +    uint16_t   port;
>>> +} VSCMsgReconnect;
>>> +
>>> +#endif
>>>        
>>      

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

* Re: [Qemu-devel] [PATCH 2/7] ccid: add passthru card device
  2011-01-25 14:17   ` Anthony Liguori
  2011-01-25 16:21     ` Alon Levy
  2011-01-27 21:13     ` Alon Levy
@ 2011-01-30 17:35     ` Alon Levy
  2 siblings, 0 replies; 23+ messages in thread
From: Alon Levy @ 2011-01-30 17:35 UTC (permalink / raw)
  To: Anthony Liguori; +Cc: qemu-devel

On Tue, Jan 25, 2011 at 08:17:32AM -0600, Anthony Liguori wrote:
> On 01/11/2011 02:42 AM, Alon Levy wrote:
> >diff --git a/libcacard/vscard_common.h b/libcacard/vscard_common.h
> >new file mode 100644
> >index 0000000..9ff1295
> >--- /dev/null
> >+++ b/libcacard/vscard_common.h
> 
> This file (and the .c file) need a coding style pass to fixup
> comments and the use of _ as a prefix but I want to focus on the
> protocol itself.
> 
> First, let's get a written spec into the wiki.  I think it's
> important that all of our compatibility protocols are documented in
> a more formal way such that can be reviewed by a wider audience.

http://wiki.qeum.org/Features/Smartcard

I'm still working on the rest, but you can review and comment on it. I've
done a number of changes from the submitted here. I guess the idea is that
iterations on the wiki can be faster? The changes done to the protocol:

 Removed Reconnect - doesn't scale easily, the same work should be done
  by whomever is initiating the migration, or via other mechanisms (i.e.
  spice)
 Added Flush/FlushComplete - still need to be able to tell client to wrap
  up the outstanding operations in any way. I'm planning on implementing
  this using register_savevm_live.
 Fixes suggested by you - set the enum, removed _ from surrounding #ifdef
  (btw - why does no one use #pragma once? IIUC it's supported by gcc?)

The major issue I haven't tackled yet is the thread removal in ccid-card-emulated.c
is that a blocker for integration? Can it be tackled later?

Alon

[snip]

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

* Re: [Qemu-devel] [PATCH 4/7] ccid: add ccid-card-emulated device (v2)
  2011-01-25 14:21   ` Anthony Liguori
  2011-01-25 16:24     ` Alon Levy
@ 2011-01-31 19:28     ` Alon Levy
  1 sibling, 0 replies; 23+ messages in thread
From: Alon Levy @ 2011-01-31 19:28 UTC (permalink / raw)
  To: Anthony Liguori; +Cc: qemu-devel

On Tue, Jan 25, 2011 at 08:21:13AM -0600, Anthony Liguori wrote:
> On 01/11/2011 02:42 AM, Alon Levy wrote:
> >This devices uses libcacard (internal) to emulate a smartcard conforming
> >to the CAC standard. It attaches to the usb-ccid bus. Usage instructions
> >(example command lines) are in the following patch in docs/ccid.txt. It
> >uses libcacard which uses nss, so it can work with both hw cards and
> >certificates (files).
> >
> >changes from v1:
> >     remove stale comments, use only c-style comments
> >     bugfix, forgot to set recv_len
> >     change reader name to 'Virtual Reader'
> >
> >Signed-off-by: Alon Levy<alevy@redhat.com>
> >---
> >  Makefile.objs           |    2 +-
> >  hw/ccid-card-emulated.c |  534 +++++++++++++++++++++++++++++++++++++++++++++++
> >  hw/ccid-card-passthru.c |    1 -
> >  3 files changed, 535 insertions(+), 2 deletions(-)
> >  create mode 100644 hw/ccid-card-emulated.c
> >
> >diff --git a/Makefile.objs b/Makefile.objs
> >index 6a0030b..303b642 100644
> >--- a/Makefile.objs
> >+++ b/Makefile.objs
> >@@ -197,7 +197,7 @@ hw-obj-$(CONFIG_FDC) += fdc.o
> >  hw-obj-$(CONFIG_ACPI) += acpi.o acpi_piix4.o
> >  hw-obj-$(CONFIG_APM) += pm_smbus.o apm.o
> >  hw-obj-$(CONFIG_DMA) += dma.o
> >-hw-obj-$(CONFIG_SMARTCARD) += usb-ccid.o ccid-card-passthru.o
> >+hw-obj-$(CONFIG_SMARTCARD) += usb-ccid.o ccid-card-passthru.o ccid-card-emulated.o
> >
> >  # PPC devices
> >  hw-obj-$(CONFIG_OPENPIC) += openpic.o
> >diff --git a/hw/ccid-card-emulated.c b/hw/ccid-card-emulated.c
> >new file mode 100644
> >index 0000000..5531ce1
> >--- /dev/null
> >+++ b/hw/ccid-card-emulated.c
> >@@ -0,0 +1,534 @@
> >+/*
> >+ * CCID Card Device. Emulated card.
> >+ *
> >+ * It can be used to provide access to the local hardware in a non exclusive
> >+ * way, or it can use certificates. It requires the usb-ccid bus.
> >+ *
> >+ * Usage 1: standard, mirror hardware reader+card:
> >+ * qemu .. -usb -device usb-ccid -device ccid-card-emulated
> >+ *
> >+ * Usage 2: use certificates, no hardware required
> >+ * one time: create the certificates:
> >+ *  for i in 1 2 3; do certutil -d /etc/pki/nssdb -x -t "CT,CT,CT" -S -s "CN=user$i" -n user$i; done
> >+ * qemu .. -usb -device usb-ccid -device ccid-card-emulated,cert1=user1,cert2=user2,cert3=user3
> >+ *
> >+ * If you use a non default db for the certificates you can specify it using the db parameter.
> >+ *
> >+ *
> >+ * Copyright (c) 2010 Red Hat.
> >+ * Written by Alon Levy.
> >+ *
> >+ * This code is licenced under the LGPL.
> >+ */
> >+
> >+#include<pthread.h>
> >+#include<eventt.h>
> >+#include<vevent.h>
> >+#include<vreader.h>
> >+#include<vcard_emul.h>
> >+#include "qemu-char.h"
> >+#include "monitor.h"
> >+#include "hw/ccid.h"
> >+
> >+#define DPRINTF(card, lvl, fmt, ...) \
> >+do { if (lvl<= card->debug) { printf("ccid-card-emul: %s: " fmt , __func__, ## __VA_ARGS__); } } while (0)
> >+
> >+#define EMULATED_DEV_NAME "ccid-card-emulated"
> >+
> >+#define BACKEND_NSS_EMULATED "nss-emulated" /* the default */
> >+#define BACKEND_CERTIFICATES "certificates"
> >+
> >+typedef struct EmulatedState EmulatedState;
> >+
> >+enum {
> >+    EMUL_READER_INSERT = 0,
> >+    EMUL_READER_REMOVE,
> >+    EMUL_CARD_INSERT,
> >+    EMUL_CARD_REMOVE,
> >+    EMUL_GUEST_APDU,
> >+    EMUL_RESPONSE_APDU,
> >+    EMUL_ERROR,
> >+};
> >+
> >+static const char* emul_event_to_string(uint32_t emul_event)
> >+{
> >+    switch (emul_event) {
> >+        case EMUL_READER_INSERT: return "EMUL_READER_INSERT";
> >+        case EMUL_READER_REMOVE: return "EMUL_READER_REMOVE";
> >+        case EMUL_CARD_INSERT: return "EMUL_CARD_INSERT";
> >+        case EMUL_CARD_REMOVE: return "EMUL_CARD_REMOVE";
> >+        case EMUL_GUEST_APDU: return "EMUL_GUEST_APDU";
> >+        case EMUL_RESPONSE_APDU: return "EMUL_RESPONSE_APDU";
> >+        case EMUL_ERROR: return "EMUL_ERROR";
> >+        default:
> >+            break;
> >+    }
> >+    return "UNKNOWN";
> >+}
> >+
> >+typedef struct EmulEvent {
> >+    QSIMPLEQ_ENTRY(EmulEvent) entry;
> >+    union {
> >+        struct {
> >+            uint32_t type;
> >+        } gen;
> >+        struct {
> >+            uint32_t type;
> >+            uint64_t code;
> >+        } error;
> >+        struct {
> >+            uint32_t type;
> >+            uint32_t len;
> >+            uint8_t data[];
> >+        } data;
> >+    } p;
> >+} EmulEvent;
> >+
> >+#define MAX_ATR_SIZE 40
> >+struct EmulatedState {
> >+    CCIDCardState base;
> >+    uint8_t  debug;
> >+    char*    backend;
> >+    char*    cert1;
> >+    char*    cert2;
> >+    char*    cert3;
> >+    char*    db;
> >+    uint8_t  atr[MAX_ATR_SIZE];
> >+    uint8_t  atr_length;
> >+    QSIMPLEQ_HEAD(event_list, EmulEvent) event_list;
> >+    pthread_mutex_t event_list_mutex;
> >+    VReader *reader;
> >+    QSIMPLEQ_HEAD(guest_apdu_list, EmulEvent) guest_apdu_list;
> >+    pthread_mutex_t vreader_mutex; /* and guest_apdu_list mutex */
> >+    pthread_mutex_t handle_apdu_mutex;
> >+    pthread_cond_t handle_apdu_cond;
> >+    int      pipe[2];
> >+    int      quit_apdu_thread;
> >+    pthread_mutex_t apdu_thread_quit_mutex;
> >+    pthread_cond_t apdu_thread_quit_cond;
> >+};
> >+
> >+static void emulated_apdu_from_guest(CCIDCardState *base, const uint8_t *apdu, uint32_t len)
> >+{
> >+    EmulatedState *card = DO_UPCAST(EmulatedState, base, base);
> >+    EmulEvent *event = (EmulEvent*)malloc(sizeof(EmulEvent) + len);
> >+
> >+    assert(event);
> >+    event->p.data.type = EMUL_GUEST_APDU;
> >+    event->p.data.len = len;
> >+    memcpy(event->p.data.data, apdu, len);
> >+    pthread_mutex_lock(&card->vreader_mutex);
> >+    QSIMPLEQ_INSERT_TAIL(&card->guest_apdu_list, event, entry);
> >+    pthread_mutex_unlock(&card->vreader_mutex);
> >+    pthread_mutex_lock(&card->handle_apdu_mutex);
> >+    pthread_cond_signal(&card->handle_apdu_cond);
> >+    pthread_mutex_unlock(&card->handle_apdu_mutex);
> >+}
> >+
> >+static const uint8_t* emulated_get_atr(CCIDCardState *base, uint32_t *len)
> >+{
> >+    EmulatedState *card = DO_UPCAST(EmulatedState, base, base);
> >+
> >+    *len = card->atr_length;
> >+    return card->atr;
> >+}
> >+
> >+static void emulated_push_event(EmulatedState *card, EmulEvent *event)
> >+{
> >+    pthread_mutex_lock(&card->event_list_mutex);
> >+    QSIMPLEQ_INSERT_TAIL(&(card->event_list), event, entry);
> >+    pthread_mutex_unlock(&card->event_list_mutex);
> >+    if (write(card->pipe[1], card, 1) != 1) {
> >+        DPRINTF(card, 1, "write to pipe failed\n");
> >+    }
> >+}
> >+
> >+static void emulated_push_type(EmulatedState *card, uint32_t type)
> >+{
> >+    EmulEvent *event = (EmulEvent*)malloc(sizeof(EmulEvent));
> >+
> >+    assert(event);
> >+    event->p.gen.type = type;
> >+    emulated_push_event(card, event);
> >+}
> >+
> >+static void emulated_push_error(EmulatedState *card, uint64_t code)
> >+{
> >+    EmulEvent *event = (EmulEvent*)malloc(sizeof(EmulEvent));
> >+
> >+    assert(event);
> >+    event->p.error.type = EMUL_ERROR;
> >+    event->p.error.code = code;
> >+    emulated_push_event(card, event);
> >+}
> >+
> >+static void emulated_push_data_type(EmulatedState *card, uint32_t type,
> >+    const uint8_t *data, uint32_t len)
> >+{
> >+    EmulEvent *event = (EmulEvent*)malloc(sizeof(EmulEvent) + len);
> >+
> >+    assert(event);
> >+    event->p.data.type = type;
> >+    event->p.data.len = len;
> >+    memcpy(event->p.data.data, data, len);
> >+    emulated_push_event(card, event);
> >+}
> >+
> >+static void emulated_push_reader_insert(EmulatedState *card)
> >+{
> >+    emulated_push_type(card, EMUL_READER_INSERT);
> >+}
> >+
> >+static void emulated_push_reader_remove(EmulatedState *card)
> >+{
> >+    emulated_push_type(card, EMUL_READER_REMOVE);
> >+}
> >+
> >+static void emulated_push_card_insert(EmulatedState *card,
> >+    const uint8_t *atr, uint32_t len)
> >+{
> >+    emulated_push_data_type(card, EMUL_CARD_INSERT, atr, len);
> >+}
> >+
> >+static void emulated_push_card_remove(EmulatedState *card)
> >+{
> >+    emulated_push_type(card, EMUL_CARD_REMOVE);
> >+}
> >+
> >+static void emulated_push_response_apdu(EmulatedState *card,
> >+    const uint8_t* apdu, uint32_t len)
> >+{
> >+    emulated_push_data_type(card, EMUL_RESPONSE_APDU, apdu, len);
> >+}
> >+
> >+#define APDU_BUF_SIZE 270
> >+static void *handle_apdu_thread(void* arg)
> >+{
> >+    EmulatedState *card = arg;
> >+    uint8_t recv_data[APDU_BUF_SIZE];
> >+    int recv_len;
> >+    VReaderStatus reader_status;
> >+    EmulEvent *event;
> >+
> >+    while (1) {
> >+        pthread_mutex_lock(&card->handle_apdu_mutex);
> >+        pthread_cond_wait(&card->handle_apdu_cond,&card->handle_apdu_mutex);
> >+        pthread_mutex_unlock(&card->handle_apdu_mutex);
> >+        if (card->quit_apdu_thread) {
> >+            card->quit_apdu_thread = 0; // debugging
> >+            break;
> >+        }
> >+        pthread_mutex_lock(&card->vreader_mutex);
> >+        while (!QSIMPLEQ_EMPTY(&card->guest_apdu_list)) {
> >+            event = QSIMPLEQ_FIRST(&card->guest_apdu_list);
> >+            assert((unsigned long)event>  1000);
> >+            QSIMPLEQ_REMOVE_HEAD(&card->guest_apdu_list, entry);
> >+            if (event->p.data.type != EMUL_GUEST_APDU) {
> >+                DPRINTF(card, 1, "unexpected message in handle_apdu_thread\n");
> >+                free(event);
> >+                continue;
> >+            }
> >+            if (card->reader == NULL) {
> >+                DPRINTF(card, 1, "reader is NULL\n");
> >+                free(event);
> >+                continue;
> >+            }
> >+            recv_len = sizeof(recv_data);
> >+            reader_status = vreader_xfr_bytes(card->reader,
> >+                    event->p.data.data, event->p.data.len,
> >+                    recv_data,&recv_len);
> >+            DPRINTF(card, 2, "got back apdu of length %d\n", recv_len);
> >+            if (reader_status == VREADER_OK) {
> >+                emulated_push_response_apdu(card, recv_data, recv_len);
> >+            } else {
> >+                emulated_push_error(card, reader_status);
> >+            }
> >+            free(event);
> >+        }
> >+        pthread_mutex_unlock(&card->vreader_mutex);
> >+    }
> >+    pthread_mutex_lock(&card->apdu_thread_quit_mutex);
> >+    pthread_cond_signal(&card->apdu_thread_quit_cond);
> >+    pthread_mutex_unlock(&card->apdu_thread_quit_mutex);
> >+    return NULL;
> >+}
> >+
> >+static void *event_thread(void *arg)
> >+{
> >+    int atr_len = MAX_ATR_SIZE;
> >+    uint8_t atr[MAX_ATR_SIZE];
> >+    VEvent *event = NULL;
> >+    EmulatedState *card = arg;
> >+
> >+    while (1) {
> >+        const char *reader_name;
> >+
> >+        event = vevent_wait_next_vevent();
> >+        if (event == NULL || event->type == VEVENT_LAST) {
> >+            break;
> >+        }
> >+        if (event->type != VEVENT_READER_INSERT) {
> >+            if (card->reader == NULL&&  event->reader != NULL) {
> >+                // Happens after device_add followed by card remove or insert.
> >+                // XXX: create synthetic add_reader events if vcard_emul_init
> >+                // already called, which happens if device_del and device_add are
> >+                // called
> >+                card->reader = vreader_reference(event->reader);
> >+            } else {
> >+                if (event->reader != card->reader) {
> >+                    fprintf(stderr, "ERROR: wrong reader: quiting event_thread\n");
> >+                    break;
> >+                }
> >+            }
> >+        }
> >+        switch (event->type) {
> >+        case VEVENT_READER_INSERT:
> >+            /* TODO: take a specific reader. i.e. track which reader
> >+             * we are seeing here, check it is the one we want (the first,
> >+             * or by a particular name), and ignore if we don't want it.
> >+             */
> >+            reader_name = vreader_get_name(event->reader);
> >+            if (card->reader != NULL) {
> >+                DPRINTF(card, 2, "READER INSERT - replacing %s with %s\n",
> >+                    vreader_get_name(card->reader), reader_name);
> >+                pthread_mutex_lock(&card->vreader_mutex);
> >+                vreader_free(card->reader);
> >+                pthread_mutex_unlock(&card->vreader_mutex);
> >+                emulated_push_reader_remove(card);
> >+            }
> >+            pthread_mutex_lock(&card->vreader_mutex);
> >+            DPRINTF(card, 2, "READER INSERT %s\n", reader_name);
> >+            card->reader = vreader_reference(event->reader);
> >+            pthread_mutex_unlock(&card->vreader_mutex);
> >+            emulated_push_reader_insert(card);
> >+            break;
> >+        case VEVENT_READER_REMOVE:
> >+            DPRINTF(card, 2, " READER REMOVE: %s \n",
> >+                    vreader_get_name(event->reader));
> >+            pthread_mutex_lock(&card->vreader_mutex);
> >+            vreader_free(card->reader);
> >+            card->reader = NULL;
> >+            pthread_mutex_unlock(&card->vreader_mutex);
> >+            emulated_push_reader_remove(card);
> >+            break;
> >+        case VEVENT_CARD_INSERT:
> >+            /* get the ATR (intended as a response to a power on from the
> >+             * reader */
> >+            atr_len = MAX_ATR_SIZE;
> >+            vreader_power_on(event->reader, atr,&atr_len);
> >+            card->atr_length = (uint8_t)atr_len;
> >+            DPRINTF(card, 2, " CARD INSERT\n");
> >+            emulated_push_card_insert(card, atr, atr_len);
> >+            break;
> >+        case VEVENT_CARD_REMOVE:
> >+            DPRINTF(card, 2, " CARD REMOVE\n");
> >+            emulated_push_card_remove(card);
> >+            break;
> >+        case VEVENT_LAST: /* quit */
> >+            vevent_delete(event);
> >+            return NULL;
> >+            break;
> >+        default:
> >+            break;
> >+        }
> >+        vevent_delete(event);
> >+    }
> >+    return NULL;
> >+}
> >+
> >+static void pipe_read(void *opaque)
> >+{
> >+    EmulatedState *card = opaque;
> >+    EmulEvent *event, *next;
> >+    char dummy;
> >+    int len;
> >+
> >+    do {
> >+        len = read(card->pipe[0],&dummy, sizeof(dummy));
> >+    } while (len == sizeof(dummy));
> >+    pthread_mutex_lock(&card->event_list_mutex);
> >+    QSIMPLEQ_FOREACH_SAFE(event,&card->event_list, entry, next) {
> >+        DPRINTF(card, 2, "event %s\n", emul_event_to_string(event->p.gen.type));
> >+        switch (event->p.gen.type) {
> >+            case EMUL_RESPONSE_APDU:
> >+                ccid_card_send_apdu_to_guest(&card->base, event->p.data.data,
> >+                    event->p.data.len);
> >+                break;
> >+            case EMUL_READER_INSERT:
> >+                ccid_card_ccid_attach(&card->base);
> >+                break;
> >+            case EMUL_READER_REMOVE:
> >+                ccid_card_ccid_detach(&card->base);
> >+                break;
> >+            case EMUL_CARD_INSERT:
> >+                assert(event->p.data.len<= MAX_ATR_SIZE);
> >+                card->atr_length = event->p.data.len;
> >+                memcpy(card->atr, event->p.data.data, card->atr_length);
> >+                ccid_card_card_inserted(&card->base);
> >+                break;
> >+            case EMUL_CARD_REMOVE:
> >+                ccid_card_card_removed(&card->base);
> >+                break;
> >+            case EMUL_ERROR:
> >+                ccid_card_card_error(&card->base, event->p.error.code);
> >+                break;
> >+            default:
> >+                DPRINTF(card, 2, "unexpected event\n");
> >+                break;
> >+        }
> >+        free(event);
> >+    }
> >+    QSIMPLEQ_INIT(&card->event_list);
> >+    pthread_mutex_unlock(&card->event_list_mutex);
> >+}
> >+
> >+static int init_pipe_signaling(EmulatedState *card)
> >+{
> >+   if (pipe(card->pipe)<  0) {
> >+       DPRINTF(card, 2, "pipe creation failed\n");
> >+       return -1;
> >+   }
> >+   fcntl(card->pipe[0], F_SETFL, O_NONBLOCK);
> >+   fcntl(card->pipe[1], F_SETFL, O_NONBLOCK);
> >+   fcntl(card->pipe[0], F_SETOWN, getpid());
> >+   qemu_set_fd_handler(card->pipe[0], pipe_read, NULL, card);
> >+   return 0;
> >+}
> >+
> >+#define CERTIFICATES_DEFAULT_DB "/etc/pki/nssdb"
> >+#define CERTIFICATES_ARGS_TEMPLATE "db=\"%s\" use_hw=no soft=(,Virtual Reader,CAC,,%s,%s,%s)"
> >+
> >+static int wrap_vcard_emul_init(VCardEmulOptions *options)
> >+{
> >+    static int called = 0;
> >+    static int options_was_null = 0;
> >+
> >+    if (called) {
> >+        if ((options == NULL) != options_was_null) {
> >+            printf("%s: warning: running emulated with certificates and emulated side by side is not supported\n", __FUNCTION__);
> >+            return VCARD_EMUL_FAIL;
> >+        }
> >+        vcard_emul_replay_insertion_events();
> >+        return VCARD_EMUL_OK;
> >+    }
> >+    options_was_null = (options == NULL);
> >+    called = 1;
> >+    return vcard_emul_init(options);
> >+}
> >+
> >+static int emulated_initialize_vcard_from_certificates(EmulatedState *card)
> >+{
> >+    char emul_args[200];
> >+    VCardEmulOptions *options = NULL;
> >+
> >+    snprintf(emul_args, sizeof(emul_args) - 1, CERTIFICATES_ARGS_TEMPLATE,
> >+        card->db ? card->db : CERTIFICATES_DEFAULT_DB,
> >+        card->cert1, card->cert2, card->cert3);
> >+    options = vcard_emul_options(emul_args);
> >+    if (options == NULL) {
> >+        printf("%s: warning: not using certificates due to initialization error\n", __func__);
> >+    }
> >+    return wrap_vcard_emul_init(options);
> >+}
> >+
> >+static int emulated_initfn(CCIDCardState *base)
> >+{
> >+    EmulatedState *card = DO_UPCAST(EmulatedState, base, base);
> >+    int rv;
> >+    pthread_t thread_id;
> >+    VCardEmulError ret;
> >+
> >+    QSIMPLEQ_INIT(&card->event_list);
> >+    QSIMPLEQ_INIT(&card->guest_apdu_list);
> >+    pthread_mutex_init(&card->event_list_mutex, NULL);
> >+    pthread_mutex_init(&card->vreader_mutex, NULL);
> >+    pthread_mutex_init(&card->handle_apdu_mutex, NULL);
> >+    pthread_cond_init(&card->handle_apdu_cond, NULL);
> >+    card->reader = NULL;
> >+    card->quit_apdu_thread = 0;
> >+    if (init_pipe_signaling(card)<  0) {
> >+        return -1;
> >+    }
> >+    if (!card->backend) {
> >+        card->backend = strdup((char*)BACKEND_NSS_EMULATED);
> >+    }
> >+    /* TODO: a passthru backened that works on local machine. third card type? */
> >+    if (strcmp(BACKEND_CERTIFICATES, card->backend) == 0
> >+&&  card->cert1 != NULL&&  card->cert2 != NULL&&  card->cert3 != NULL) {
> >+        ret = emulated_initialize_vcard_from_certificates(card);
> >+    } else {
> >+        if (strcmp(BACKEND_CERTIFICATES, card->backend) == 0) {
> >+            printf("%s: you must provide all three certs for certificates backend\n",
> >+                EMULATED_DEV_NAME);
> >+            return -1;
> >+        }
> >+        if (card->backend&&  strcmp(BACKEND_NSS_EMULATED, card->backend) != 0) {
> >+            printf("%s: bad backend specified. The options are:\n%s (default), %s.\n",
> >+                EMULATED_DEV_NAME, BACKEND_NSS_EMULATED, BACKEND_CERTIFICATES);
> >+            return -1;
> >+        }
> >+        /* default to mirroring the local hardware readers */
> >+        ret = wrap_vcard_emul_init(NULL);
> >+    }
> >+    if (ret != VCARD_EMUL_OK) {
> >+        printf("%s: failed to initialize vcard\n", EMULATED_DEV_NAME);
> >+        return -1;
> >+    }
> >+    rv = pthread_create(&thread_id, NULL, event_thread, card);
> >+    if (rv<  0) {
> >+        printf("%s: error creating event thread\n", EMULATED_DEV_NAME);
> >+        return -1;
> >+    }
> 
> Why can't this be implemented with qemu_set_fd_handler?  The event
> thread si just spinning on read.
> 

I've looked a little more closely at why I did it exactly this way.

The short answer is that both threads are not spinning, they are waiting
on conditions, the event_thread on the libcacard vevent_queue condition, and
the apdu_thread on the ccid-card-emulated internal guest_apdu queue condition,
triggered by the main thread.

This is the ccid-card-emulated threads and flow (hope it's understandable):

libcacard details:

 vcard_emul_event_thread for each reader
  blocking on SECMOD_WaitForAnyTokenEvent
   pushes events to vevent queue

ccid-card-emulated details:

 event_thread
  read vevent queue -> {reader,card}x{insert,remove} -> write to apdu_pipe

 (writes to apdu_pipe are mutex protected)

 handle_apdu_thread
  pop guest_apdu_list -> vreader_xfr_bytes -> write result to apdu_pipe

 main_thread
  usb event
   apdu from guest -> push to guest_apdu_list
  apdu_pipe read
   apdu to guest
   ccid attach
   ccid detach
   ccid card removed
   ccid card inserted

If I had a mechanism to let the qemu main loop wait on a mutex or condition
variable I could remove both event_thread and handle_apdu_thread. (reading
a little more about this let me to believe the solution I came up with, i.e.
a queue and a condition variable, is commonly used for this in POSIX).

But right now:
 I need a separate thread to block on the vevent queue condition (pthread_cond_t/CONDITION_VARIABLE in linux/windows).
 I need a separate thread to call vreader_xfr_bytes (it may block on the NSS thread).

Alternatives:
 launch a thread for each apdu from guest:
  since the apdu's are linear any way (i.e. each one would wait for the next) this seems wasteful.
 Since we need to wait on a condition signaled by NSS, there is no alternative then having a separate thread for that.

Alon

> Regards,
> 
> Anthony Liguori
> 
> >+    rv = pthread_create(&thread_id, NULL, handle_apdu_thread, card);
> >+    if (rv<  0) {
> >+        printf("%s: error creating handle_apdu thread\n", EMULATED_DEV_NAME);
> >+        return -1;
> >+    }
> >+    return 0;
> >+}
> >+
> >+static int emulated_exitfn(CCIDCardState *base)
> >+{
> >+    EmulatedState *card = DO_UPCAST(EmulatedState, base, base);
> >+    VEvent *vevent = vevent_new(VEVENT_LAST, NULL, NULL);
> >+
> >+    vevent_queue_vevent(vevent); /* stop vevent thread */
> >+    pthread_mutex_lock(&card->apdu_thread_quit_mutex);
> >+    card->quit_apdu_thread = 1; /* stop handle_apdu thread */
> >+    pthread_cond_signal(&card->handle_apdu_cond);
> >+    pthread_cond_wait(&card->apdu_thread_quit_cond,&card->apdu_thread_quit_mutex);
> >+    /* handle_apdu thread stopped, can destroy all of it's mutexes */
> >+    pthread_cond_destroy(&card->handle_apdu_cond);
> >+    pthread_cond_destroy(&card->apdu_thread_quit_cond);
> >+    pthread_mutex_destroy(&card->apdu_thread_quit_mutex);
> >+    pthread_mutex_destroy(&card->handle_apdu_mutex);
> >+    pthread_mutex_destroy(&card->vreader_mutex);
> >+    pthread_mutex_destroy(&card->event_list_mutex);
> >+    return 0;
> >+}
> >+
> >+static CCIDCardInfo emulated_card_info = {
> >+    .qdev.name = EMULATED_DEV_NAME,
> >+    .qdev.size = sizeof(EmulatedState),
> >+    .initfn = emulated_initfn,
> >+    .exitfn = emulated_exitfn,
> >+    .get_atr = emulated_get_atr,
> >+    .apdu_from_guest = emulated_apdu_from_guest,
> >+    .qdev.unplug    = qdev_simple_unplug_cb,
> >+    .qdev.props     = (Property[]) {
> >+        DEFINE_PROP_STRING("backend", EmulatedState, backend),
> >+        DEFINE_PROP_STRING("cert1", EmulatedState, cert1),
> >+        DEFINE_PROP_STRING("cert2", EmulatedState, cert2),
> >+        DEFINE_PROP_STRING("cert3", EmulatedState, cert3),
> >+        DEFINE_PROP_STRING("db", EmulatedState, db),
> >+        DEFINE_PROP_UINT8("debug", EmulatedState, debug, 0),
> >+        DEFINE_PROP_END_OF_LIST(),
> >+    },
> >+};
> >+
> >+static void ccid_card_emulated_register_devices(void)
> >+{
> >+    ccid_card_qdev_register(&emulated_card_info);
> >+}
> >+
> >+device_init(ccid_card_emulated_register_devices)
> >diff --git a/hw/ccid-card-passthru.c b/hw/ccid-card-passthru.c
> >index 6ec4f21..f9fb82c 100644
> >--- a/hw/ccid-card-passthru.c
> >+++ b/hw/ccid-card-passthru.c
> >@@ -253,7 +253,6 @@ static CCIDCardInfo passthru_card_info = {
> >      .exitfn = passthru_exitfn,
> >      .get_atr = passthru_get_atr,
> >      .apdu_from_guest = passthru_apdu_from_guest,
> >-    .qdev.unplug    = qdev_simple_unplug_cb,
> >      .qdev.props     = (Property[]) {
> >          DEFINE_PROP_CHR("chardev", PassthruState, cs),
> >          DEFINE_PROP_UINT8("debug", PassthruState, debug, 0),
> 

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

end of thread, other threads:[~2011-01-31 19:28 UTC | newest]

Thread overview: 23+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2011-01-11  8:42 [Qemu-devel] [PATCH 0/7] usb-ccid (v15) Alon Levy
2011-01-11  8:42 ` [Qemu-devel] [PATCH 1/7] usb-ccid: add CCID bus Alon Levy
2011-01-11  8:42 ` [Qemu-devel] [PATCH 2/7] ccid: add passthru card device Alon Levy
2011-01-25 14:17   ` Anthony Liguori
2011-01-25 16:21     ` Alon Levy
2011-01-25 16:24       ` Anthony Liguori
2011-01-25 16:50         ` Alon Levy
2011-01-27 21:13     ` Alon Levy
2011-01-27 21:42       ` Anthony Liguori
2011-01-30 17:35     ` Alon Levy
2011-01-11  8:42 ` [Qemu-devel] [PATCH 3/7] libcacard: initial commit after coding style fixes Alon Levy
2011-01-25 14:19   ` Anthony Liguori
2011-01-11  8:42 ` [Qemu-devel] [PATCH 4/7] ccid: add ccid-card-emulated device (v2) Alon Levy
2011-01-25 14:21   ` Anthony Liguori
2011-01-25 16:24     ` Alon Levy
2011-01-25 16:27       ` Anthony Liguori
2011-01-31 19:28     ` Alon Levy
2011-01-11  8:42 ` [Qemu-devel] [PATCH 5/7] ccid: add docs Alon Levy
2011-01-11  8:42 ` [Qemu-devel] [PATCH 6/7] ccid: configure: add --enable/disable and nss only disable Alon Levy
2011-01-11  8:42 ` [Qemu-devel] [PATCH 6/6] ccid: configure: add --enable-smartcard and --disable-smartcard Alon Levy
2011-01-11  9:03   ` Alon Levy
2011-01-11  8:42 ` [Qemu-devel] [PATCH 7/7] ccid: add qdev description strings Alon Levy
2011-01-17 15:56 ` [Qemu-devel] [PATCH 0/7] usb-ccid (v15) Alon Levy

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.