* [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; 24+ 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] 24+ 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; 24+ 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] 24+ 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; 24+ 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] 24+ 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; 24+ 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] 24+ 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; 24+ 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] 24+ 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; 24+ 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] 24+ 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; 24+ 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] 24+ 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; 24+ 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] 24+ 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; 24+ 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] 24+ 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; 24+ 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] 24+ 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; 24+ 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] 24+ 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; 24+ 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] 24+ 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; 24+ 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] 24+ 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; 24+ 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] 24+ 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; 24+ 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] 24+ 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; 24+ 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] 24+ 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; 24+ 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] 24+ 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; 24+ 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] 24+ 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; 24+ 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] 24+ 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; 24+ 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] 24+ 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; 24+ 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] 24+ 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; 24+ 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] 24+ 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; 24+ 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] 24+ messages in thread
* [Qemu-devel] [PATCH 0/7] usb-ccid (v15)
@ 2011-01-11 8:38 Alon Levy
0 siblings, 0 replies; 24+ messages in thread
From: Alon Levy @ 2011-01-11 8:38 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.v14
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] 24+ messages in thread
end of thread, other threads:[~2011-01-31 19:28 UTC | newest]
Thread overview: 24+ 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
-- strict thread matches above, loose matches on Subject: below --
2011-01-11 8:38 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.