aboutsummaryrefslogtreecommitdiffstats
path: root/libcacard
diff options
context:
space:
mode:
authorfishsoupisgood <github@madingley.org>2019-04-29 01:17:54 +0100
committerfishsoupisgood <github@madingley.org>2019-05-27 03:43:43 +0100
commit3f2546b2ef55b661fd8dd69682b38992225e86f6 (patch)
tree65ca85f13617aee1dce474596800950f266a456c /libcacard
downloadqemu-master.tar.gz
qemu-master.tar.bz2
qemu-master.zip
Initial import of qemu-2.4.1HEADmaster
Diffstat (limited to 'libcacard')
-rw-r--r--libcacard/Makefile45
-rw-r--r--libcacard/cac.c414
-rw-r--r--libcacard/cac.h31
-rw-r--r--libcacard/card_7816.c757
-rw-r--r--libcacard/card_7816.h62
-rw-r--r--libcacard/card_7816t.h165
-rw-r--r--libcacard/event.c103
-rw-r--r--libcacard/eventt.h29
-rw-r--r--libcacard/libcacard.pc.in13
-rw-r--r--libcacard/libcacard.syms77
-rw-r--r--libcacard/link_test.c22
-rw-r--r--libcacard/vcard.c325
-rw-r--r--libcacard/vcard.h86
-rw-r--r--libcacard/vcard_emul.h66
-rw-r--r--libcacard/vcard_emul_nss.c1274
-rw-r--r--libcacard/vcard_emul_type.c57
-rw-r--r--libcacard/vcard_emul_type.h32
-rw-r--r--libcacard/vcardt.c40
-rw-r--r--libcacard/vcardt.h59
-rw-r--r--libcacard/vcardt_internal.h6
-rw-r--r--libcacard/vevent.h27
-rw-r--r--libcacard/vreader.c578
-rw-r--r--libcacard/vreader.h55
-rw-r--r--libcacard/vreadert.h24
-rw-r--r--libcacard/vscard_common.h178
-rw-r--r--libcacard/vscclient.c785
26 files changed, 5310 insertions, 0 deletions
diff --git a/libcacard/Makefile b/libcacard/Makefile
new file mode 100644
index 00000000..b5eddff1
--- /dev/null
+++ b/libcacard/Makefile
@@ -0,0 +1,45 @@
+libcacard_includedir=$(includedir)/cacard
+
+TOOLS += vscclient$(EXESUF)
+
+# objects linked into a shared library, built with libtool with -fPIC if required
+libcacard-obj-y = $(libcacard-y)
+libcacard-lobj-y=$(patsubst %.o,%.lo,$(libcacard-obj-y))
+
+# libtool will build the .o files, too
+$(libcacard-obj-y): | $(libcacard-lobj-y)
+
+all: libcacard.la libcacard.pc
+
+vscclient$(EXESUF): libcacard/vscclient.o libcacard.la
+ $(call LINK,$^)
+
+#########################################################################
+# Rules for building libcacard standalone library
+
+libcacard.la: LDFLAGS += -rpath $(libdir) -no-undefined \
+ -export-symbols $(SRC_PATH)/libcacard/libcacard.syms
+# Prevent libcacard.so linking against the entire world of 3rd party libs
+libcacard.la: LIBS =
+libcacard.la: $(libcacard-lobj-y)
+ $(call LINK,$^)
+
+libcacard.pc: $(SRC_PATH)/libcacard/libcacard.pc.in
+ $(call quiet-command,sed -e 's|@LIBDIR@|$(libdir)|' \
+ -e 's|@INCLUDEDIR@|$(libcacard_includedir)|' \
+ -e 's|@VERSION@|$(shell cat $(SRC_PATH)/VERSION)|' \
+ -e 's|@PREFIX@|$(prefix)|' $< > libcacard.pc,\
+ " GEN $@")
+
+.PHONY: install-libcacard
+
+install: install-libcacard
+install-libcacard: libcacard.pc libcacard.la
+ $(INSTALL_DIR) "$(DESTDIR)$(libdir)"
+ $(INSTALL_DIR) "$(DESTDIR)$(libdir)/pkgconfig"
+ $(INSTALL_DIR) "$(DESTDIR)$(libcacard_includedir)"
+ $(INSTALL_LIB) libcacard.la "$(DESTDIR)$(libdir)"
+ $(INSTALL_DATA) libcacard.pc "$(DESTDIR)$(libdir)/pkgconfig"
+ for inc in $(SRC_PATH)/libcacard/*.h; do \
+ $(INSTALL_DATA) $$inc "$(DESTDIR)$(libcacard_includedir)"; \
+ done
diff --git a/libcacard/cac.c b/libcacard/cac.c
new file mode 100644
index 00000000..bc84534f
--- /dev/null
+++ b/libcacard/cac.c
@@ -0,0 +1,414 @@
+/*
+ * implement the applets for the CAC card.
+ *
+ * This code is licensed under the GNU LGPL, version 2.1 or later.
+ * See the COPYING.LIB file in the top-level directory.
+ */
+
+#include "glib-compat.h"
+
+#include <string.h>
+#include <stdbool.h>
+
+#include "cac.h"
+#include "vcard.h"
+#include "vcard_emul.h"
+#include "card_7816.h"
+
+/* 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;
+ VCardStatus ret = VCARD_FAIL;
+
+ switch (apdu->a_ins) {
+ case VCARD7816_INS_SELECT_FILE:
+ if (apdu->a_p1 != 0x02) {
+ /* let the 7816 code handle applet switches */
+ ret = VCARD_NEXT;
+ break;
+ }
+ /* handle file id setting */
+ if (apdu->a_Lc != 2) {
+ *response = vcard_make_response(
+ VCARD7816_STATUS_ERROR_DATA_INVALID);
+ ret = VCARD_DONE;
+ break;
+ }
+ /* 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);
+ ret = VCARD_DONE;
+ break;
+ }
+ *response = vcard_make_response(VCARD7816_STATUS_SUCCESS);
+ ret = VCARD_DONE;
+ break;
+ case VCARD7816_INS_GET_RESPONSE:
+ case VCARD7816_INS_VERIFY:
+ /* let the 7816 code handle these */
+ ret = VCARD_NEXT;
+ break;
+ 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);
+ ret = VCARD_DONE;
+ break;
+ default:
+ *response = vcard_make_response(
+ VCARD7816_STATUS_ERROR_COMMAND_NOT_SUPPORTED);
+ ret = VCARD_DONE;
+ break;
+ }
+ return ret;
+}
+
+/*
+ * reset the inter call state between applet selects
+ */
+static VCardStatus
+cac_applet_pki_reset(VCard *card, int channel)
+{
+ VCardAppletPrivate *applet_private;
+ CACPKIAppletData *pki_applet;
+ applet_private = vcard_get_current_applet_private(card, channel);
+ assert(applet_private);
+ pki_applet = &(applet_private->u.pki_data);
+
+ pki_applet->cert_buffer = NULL;
+ g_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;
+ VCardAppletPrivate *applet_private;
+ int size, next;
+ unsigned char *sign_buffer;
+ bool retain_sign_buffer = FALSE;
+ vcard_7816_status_t status;
+ VCardStatus ret = VCARD_FAIL;
+
+ 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);
+ ret = VCARD_DONE;
+ break;
+ 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);
+ }
+ ret = VCARD_DONE;
+ break;
+ 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 = g_realloc(pki_applet->sign_buffer,
+ pki_applet->sign_buffer_len + size);
+ 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);
+ retain_sign_buffer = TRUE;
+ break;
+ 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;
+ }
+ if (!retain_sign_buffer) {
+ g_free(sign_buffer);
+ pki_applet->sign_buffer = NULL;
+ pki_applet->sign_buffer_len = 0;
+ }
+ ret = VCARD_DONE;
+ break;
+ 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);
+ ret = VCARD_DONE;
+ break;
+ default:
+ ret = cac_common_process_apdu(card, apdu, response);
+ break;
+ }
+ return ret;
+}
+
+
+static VCardStatus
+cac_applet_id_process_apdu(VCard *card, VCardAPDU *apdu,
+ VCardResponse **response)
+{
+ VCardStatus ret = VCARD_FAIL;
+
+ switch (apdu->a_ins) {
+ case CAC_UPDATE_BUFFER:
+ *response = vcard_make_response(
+ VCARD7816_STATUS_ERROR_CONDITION_NOT_SATISFIED);
+ ret = VCARD_DONE;
+ break;
+ 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);
+ ret = VCARD_DONE;
+ break;
+ default:
+ ret = cac_common_process_apdu(card, apdu, response);
+ break;
+ }
+ return ret;
+}
+
+
+/*
+ * 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)
+{
+ VCardStatus ret = VCARD_FAIL;
+
+ switch (apdu->a_ins) {
+ case CAC_READ_BUFFER:
+ case CAC_UPDATE_BUFFER:
+ *response = vcard_make_response(
+ VCARD7816_STATUS_ERROR_COMMAND_NOT_SUPPORTED);
+ ret = VCARD_DONE;
+ break;
+ default:
+ ret = cac_common_process_apdu(card, apdu, response);
+ break;
+ }
+ return ret;
+}
+
+/*
+ * utilities for creating and destroying the private applet data
+ */
+static void
+cac_delete_pki_applet_private(VCardAppletPrivate *applet_private)
+{
+ CACPKIAppletData *pki_applet_data;
+
+ if (applet_private == NULL) {
+ return;
+ }
+ pki_applet_data = &(applet_private->u.pki_data);
+ g_free(pki_applet_data->cert);
+ g_free(pki_applet_data->sign_buffer);
+ if (pki_applet_data->key != NULL) {
+ vcard_emul_delete_key(pki_applet_data->key);
+ }
+ g_free(applet_private);
+}
+
+static VCardAppletPrivate *
+cac_new_pki_applet_private(const unsigned char *cert,
+ int cert_len, VCardKey *key)
+{
+ CACPKIAppletData *pki_applet_data;
+ VCardAppletPrivate *applet_private;
+
+ applet_private = g_new0(VCardAppletPrivate, 1);
+ pki_applet_data = &(applet_private->u.pki_data);
+ pki_applet_data->cert = (unsigned char *)g_malloc(cert_len+1);
+ /*
+ * 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;
+}
+
+
+/*
+ * 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;
+ VCardApplet *applet;
+ 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 00000000..d24a2a84
--- /dev/null
+++ b/libcacard/cac.h
@@ -0,0 +1,31 @@
+/*
+ * defines the entry point for the cac card. Only used by cac.c anc
+ * vcard_emul_type.c
+ *
+ * This work is licensed under the terms of the GNU LGPL, version 2.1 or later.
+ * See the COPYING.LIB file in the top-level directory.
+ */
+#ifndef CAC_H
+#define CAC_H 1
+#include "vcard.h"
+#include "vreader.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
+
+/*
+ * 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 00000000..22fd334d
--- /dev/null
+++ b/libcacard/card_7816.c
@@ -0,0 +1,757 @@
+/*
+ * Implement the 7816 portion of the card spec
+ *
+ * This code is licensed under the GNU LGPL, version 2.1 or later.
+ * See the COPYING.LIB file in the top-level directory.
+ */
+
+#include "glib-compat.h"
+
+#include <string.h>
+
+#include "vcard.h"
+#include "vcard_emul.h"
+#include "card_7816.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 = g_new(VCardResponse, 1);
+ new_response->b_data = g_malloc(len + 2);
+ 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 Response buffer that only has a status.
+ */
+static VCardResponse *
+vcard_response_new_status(vcard_7816_status_t status)
+{
+ VCardResponse *new_response;
+
+ new_response = g_new(VCardResponse, 1);
+ 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 = g_new(VCardResponse, 1);
+ 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 */
+ g_free(response->b_data);
+ g_free(response);
+ break;
+ case VCARD_MALLOC_DATA:
+ /* only the data buffer was malloc'ed */
+ g_free(response->b_data);
+ break;
+ case VCARD_MALLOC_STRUCT:
+ /* only the structure was malloc'ed */
+ g_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_PROPRIETARY;
+ break;
+ }
+ return VCARD7816_STATUS_SUCCESS;
+}
+
+/*
+ * set the Le and Lc fields 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 = g_new(VCardAPDU, 1);
+ new_apdu->a_data = g_memdup(raw_apdu, len);
+ new_apdu->a_len = len;
+ *status = vcard_apdu_set_class(new_apdu);
+ if (*status != VCARD7816_STATUS_SUCCESS) {
+ g_free(new_apdu);
+ return NULL;
+ }
+ *status = vcard_apdu_set_length(new_apdu);
+ if (*status != VCARD7816_STATUS_SUCCESS) {
+ g_free(new_apdu);
+ new_apdu = NULL;
+ }
+ return new_apdu;
+}
+
+void
+vcard_apdu_delete(VCardAPDU *apdu)
+{
+ if (apdu == NULL) {
+ return;
+ }
+ g_free(apdu->a_data);
+ g_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;
+
+ 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);
+ }
+ 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 */
+ break;
+ }
+ 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 00000000..4a01993d
--- /dev/null
+++ b/libcacard/card_7816.h
@@ -0,0 +1,62 @@
+/*
+ * Implement the 7816 portion of the card spec
+ *
+ * This work is licensed under the terms of the GNU LGPL, version 2.1 or later.
+ * See the COPYING.LIB file in the top-level directory.
+ */
+#ifndef 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 always 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 00000000..8eef0ce4
--- /dev/null
+++ b/libcacard/card_7816t.h
@@ -0,0 +1,165 @@
+/*
+ * Implement the 7816 portion of the card spec
+ *
+ * This work is licensed under the terms of the GNU LGPL, version 2.1 or later.
+ * See the COPYING.LIB file in the top-level directory.
+ */
+#ifndef 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_PROPRIETARY
+} 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/event.c b/libcacard/event.c
new file mode 100644
index 00000000..63f4057f
--- /dev/null
+++ b/libcacard/event.c
@@ -0,0 +1,103 @@
+/*
+ * event queue implementation.
+ *
+ * This code is licensed under the GNU LGPL, version 2.1 or later.
+ * See the COPYING.LIB file in the top-level directory.
+ */
+
+#include "glib-compat.h"
+
+#include "vcard.h"
+#include "vreader.h"
+#include "vevent.h"
+
+VEvent *
+vevent_new(VEventType type, VReader *reader, VCard *card)
+{
+ VEvent *new_vevent;
+
+ new_vevent = g_new(VEvent, 1);
+ 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);
+ g_free(vevent);
+}
+
+/*
+ * VEvent queue management
+ */
+
+static VEvent *vevent_queue_head;
+static VEvent *vevent_queue_tail;
+static CompatGMutex vevent_queue_lock;
+static CompatGCond vevent_queue_condition;
+
+void vevent_queue_init(void)
+{
+ vevent_queue_head = vevent_queue_tail = NULL;
+}
+
+void
+vevent_queue_vevent(VEvent *vevent)
+{
+ vevent->next = NULL;
+ g_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;
+ g_cond_signal(&vevent_queue_condition);
+ g_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;
+
+ g_mutex_lock(&vevent_queue_lock);
+ while ((vevent = vevent_dequeue_vevent()) == NULL) {
+ g_cond_wait(&vevent_queue_condition, &vevent_queue_lock);
+ }
+ g_mutex_unlock(&vevent_queue_lock);
+ return vevent;
+}
+
+VEvent *vevent_get_next_vevent(void)
+{
+ VEvent *vevent;
+
+ g_mutex_lock(&vevent_queue_lock);
+ vevent = vevent_dequeue_vevent();
+ g_mutex_unlock(&vevent_queue_lock);
+ return vevent;
+}
+
diff --git a/libcacard/eventt.h b/libcacard/eventt.h
new file mode 100644
index 00000000..0dc7bd46
--- /dev/null
+++ b/libcacard/eventt.h
@@ -0,0 +1,29 @@
+/*
+ * This work is licensed under the terms of the GNU LGPL, version 2.1 or later.
+ * See the COPYING.LIB file in the top-level directory.
+ */
+
+#ifndef 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/libcacard.pc.in b/libcacard/libcacard.pc.in
new file mode 100644
index 00000000..4b60023c
--- /dev/null
+++ b/libcacard/libcacard.pc.in
@@ -0,0 +1,13 @@
+prefix=@PREFIX@
+exec_prefix=${prefix}
+libdir=@LIBDIR@
+includedir=@INCLUDEDIR@
+
+Name: cacard
+Description: CA Card library
+Version: @VERSION@
+
+Requires.private: nss glib-2.0
+Libs: -L${libdir} -lcacard
+Libs.private:
+Cflags: -I${includedir}
diff --git a/libcacard/libcacard.syms b/libcacard/libcacard.syms
new file mode 100644
index 00000000..1697515a
--- /dev/null
+++ b/libcacard/libcacard.syms
@@ -0,0 +1,77 @@
+cac_card_init
+cac_is_cac_card
+vcard_add_applet
+vcard_apdu_delete
+vcard_apdu_new
+vcard_applet_get_aid
+vcard_buffer_response_delete
+vcard_buffer_response_new
+vcard_delete_applet
+vcard_emul_delete_key
+vcard_emul_force_card_insert
+vcard_emul_force_card_remove
+vcard_emul_get_atr
+vcard_emul_get_login_count
+vcard_emul_init
+vcard_emul_login
+vcard_emul_options
+vcard_emul_replay_insertion_events
+vcard_emul_reset
+vcard_emul_rsa_op
+vcard_emul_type_from_string
+vcard_emul_type_select
+vcard_emul_usage
+vcard_find_applet
+vcard_free
+vcard_get_atr
+vcard_get_buffer_response
+vcard_get_current_applet_private
+vcard_get_private
+vcard_get_type
+vcard_init
+vcard_make_response
+vcard_new
+vcard_new_applet
+vcard_process_apdu
+vcard_process_applet_apdu
+vcard_reference
+vcard_reset
+vcard_response_delete
+vcard_response_new
+vcard_response_new_bytes
+vcard_response_new_data
+vcard_response_new_status_bytes
+vcard_select_applet
+vcard_set_applet_private
+vcard_set_atr_func
+vcard_set_buffer_response
+vcard_set_type
+vevent_delete
+vevent_get_next_vevent
+vevent_new
+vevent_queue_init
+vevent_queue_vevent
+vevent_wait_next_vevent
+vreader_add_reader
+vreader_card_is_present
+vreader_free
+vreader_get_id
+vreader_get_name
+vreader_get_private
+vreader_get_reader_by_id
+vreader_get_reader_by_name
+vreader_get_reader_list
+vreader_init
+vreader_insert_card
+vreader_list_delete
+vreader_list_get_first
+vreader_list_get_next
+vreader_list_get_reader
+vreader_new
+vreader_power_off
+vreader_power_on
+vreader_queue_card_event
+vreader_reference
+vreader_remove_reader
+vreader_set_id
+vreader_xfr_bytes
diff --git a/libcacard/link_test.c b/libcacard/link_test.c
new file mode 100644
index 00000000..6f67a23d
--- /dev/null
+++ b/libcacard/link_test.c
@@ -0,0 +1,22 @@
+/*
+ * This work is licensed under the terms of the GNU LGPL, version 2.1 or later.
+ * See the COPYING.LIB file in the top-level directory.
+ */
+
+#include <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/vcard.c b/libcacard/vcard.c
new file mode 100644
index 00000000..1a87208f
--- /dev/null
+++ b/libcacard/vcard.c
@@ -0,0 +1,325 @@
+/*
+ * implement the Java card standard.
+ *
+ * This work is licensed under the terms of the GNU LGPL, version 2.1 or later.
+ * See the COPYING.LIB file in the top-level directory.
+ */
+
+#include "glib-compat.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 = g_new(VCardBufferResponse, 1);
+ new_buffer->buffer = (unsigned char *)g_memdup(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;
+ }
+ g_free(buffer_response->buffer);
+ g_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 = g_new0(VCardApplet, 1);
+ applet->process_apdu = applet_process_function;
+ applet->reset_applet = applet_reset_function;
+
+ applet->aid = g_memdup(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);
+ }
+ g_free(applet->aid);
+ g_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;
+
+ new_card = g_new0(VCard, 1);
+ new_card->type = VCARD_VM;
+ new_card->vcard_private = private;
+ new_card->vcard_private_free = private_free;
+ 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;
+ VCardApplet *next_applet;
+
+ 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);
+ }
+ 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);
+ g_free(vcard);
+}
+
+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);
+
+ /* If using an emulated card, make sure to log out of any already logged in
+ * session. */
+ vcard_emul_logout(card);
+
+ 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 00000000..47dc7038
--- /dev/null
+++ b/libcacard/vcard.h
@@ -0,0 +1,86 @@
+/*
+ * This work is licensed under the terms of the GNU LGPL, version 2.1 or later.
+ * See the COPYING.LIB file in the top-level directory.
+ */
+#ifndef 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 00000000..f09ee98d
--- /dev/null
+++ b/libcacard/vcard_emul.h
@@ -0,0 +1,66 @@
+/*
+ * 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.
+ *
+ * This work is licensed under the terms of the GNU LGPL, version 2.1 or later.
+ * See the COPYING.LIB file in the top-level directory.
+ */
+
+#ifndef 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);
+void vcard_emul_logout(VCard *card);
+
+/*
+ * 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 00000000..d9761eed
--- /dev/null
+++ b/libcacard/vcard_emul_nss.c
@@ -0,0 +1,1274 @@
+/*
+ * 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.
+ *
+ * This work is licensed under the terms of the GNU LGPL, version 2.1 or later.
+ * See the COPYING.LIB file in the top-level directory.
+ */
+
+/*
+ * NSS headers
+ */
+
+/* avoid including prototypes.h that redefines uint32 */
+#define NO_NSPR_10_SUPPORT
+
+#include <nss.h>
+#include <pk11pub.h>
+#include <cert.h>
+#include <key.h>
+#include <secmod.h>
+#include <prthread.h>
+#include <secerr.h>
+
+#include "glib-compat.h"
+
+#include "vcard.h"
+#include "card_7816t.h"
+#include "vcard_emul.h"
+#include "vreader.h"
+#include "vevent.h"
+
+#include "vcardt_internal.h"
+
+
+typedef enum {
+ VCardEmulUnknown = -1,
+ VCardEmulFalse = 0,
+ VCardEmulTrue = 1
+} VCardEmulTriState;
+
+struct VCardKeyStruct {
+ CERTCertificate *cert;
+ PK11SlotInfo *slot;
+ SECKEYPrivateKey *key;
+ VCardEmulTriState failedX509;
+};
+
+
+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;
+
+/* if we have more that just the slot, define
+ * VCardEmulStruct here */
+
+/*
+ * allocate the set of arrays for certs, cert_len, key
+ */
+static void
+vcard_emul_alloc_arrays(unsigned char ***certsp, int **cert_lenp,
+ VCardKey ***keysp, int cert_count)
+{
+ *certsp = g_new(unsigned char *, cert_count);
+ *cert_lenp = g_new(int, cert_count);
+ *keysp = g_new(VCardKey *, cert_count);
+}
+
+/*
+ * 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 = g_new(VCardKey, 1);
+ 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);
+ key->failedX509 = VCardEmulUnknown;
+ 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);
+ }
+}
+
+/*
+ * 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;
+ PK11SlotInfo *slot;
+ SECStatus rv;
+ unsigned char buf[2048];
+ unsigned char *bp = NULL;
+ int pad_len;
+ vcard_7816_status_t ret = VCARD7816_STATUS_SUCCESS;
+
+ 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);
+ if (priv_key == NULL) {
+ /* couldn't get the key, indicate that we aren't logged in */
+ return VCARD7816_STATUS_ERROR_CONDITION_NOT_SATISFIED;
+ }
+ slot = vcard_emul_card_get_slot(card);
+
+ /*
+ * 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;
+ }
+ /* be able to handle larger keys if necessariy */
+ bp = &buf[0];
+ if (sizeof(buf) < signature_len) {
+ bp = g_malloc(signature_len);
+ }
+
+ /*
+ * do the raw operations. Some tokens claim to do CKM_RSA_X_509, but then
+ * choke when they try to do the actual operations. Try to detect
+ * those cases and treat them as if the token didn't claim support for
+ * X_509.
+ */
+ if (key->failedX509 != VCardEmulTrue
+ && PK11_DoesMechanism(slot, CKM_RSA_X_509)) {
+ rv = PK11_PrivDecryptRaw(priv_key, bp, &signature_len, signature_len,
+ buffer, buffer_size);
+ if (rv == SECSuccess) {
+ assert(buffer_size == signature_len);
+ memcpy(buffer, bp, signature_len);
+ key->failedX509 = VCardEmulFalse;
+ goto cleanup;
+ }
+ /*
+ * we've had a successful X509 operation, this failure must be
+ * somethine else
+ */
+ if (key->failedX509 == VCardEmulFalse) {
+ ret = vcard_emul_map_error(PORT_GetError());
+ goto cleanup;
+ }
+ /*
+ * key->failedX509 must be Unknown at this point, try the
+ * non-x_509 case
+ */
+ }
+ /* token does not support CKM_RSA_X509, emulate that with CKM_RSA_PKCS */
+ /* is this a PKCS #1 formatted signature? */
+ if ((buffer[0] == 0) && (buffer[1] == 1)) {
+ int i;
+
+ for (i = 2; i < buffer_size; i++) {
+ /* rsa signature pad */
+ if (buffer[i] != 0xff) {
+ break;
+ }
+ }
+ if ((i < buffer_size) && (buffer[i] == 0)) {
+ /* yes, we have a properly formatted PKCS #1 signature */
+ /*
+ * NOTE: even if we accidentally got an encrypt buffer, which
+ * through sheer luck started with 00, 01, ff, 00, it won't matter
+ * because the resulting Sign operation will effectively decrypt
+ * the real buffer.
+ */
+ SECItem signature;
+ SECItem hash;
+
+ i++;
+ hash.data = &buffer[i];
+ hash.len = buffer_size - i;
+ signature.data = bp;
+ signature.len = signature_len;
+ rv = PK11_Sign(priv_key, &signature, &hash);
+ if (rv != SECSuccess) {
+ ret = vcard_emul_map_error(PORT_GetError());
+ goto cleanup;
+ }
+ assert(buffer_size == signature.len);
+ memcpy(buffer, bp, signature.len);
+ /*
+ * we got here because either the X509 attempt failed, or the
+ * token couldn't do the X509 operation, in either case stay
+ * with the PKCS version for future operations on this key
+ */
+ key->failedX509 = VCardEmulTrue;
+ goto cleanup;
+ }
+ }
+ pad_len = buffer_size - signature_len;
+ assert(pad_len < 4);
+ /*
+ * OK now we've decrypted the payload, package it up in PKCS #1 for the
+ * upper layer.
+ */
+ buffer[0] = 0;
+ buffer[1] = 2; /* RSA_encrypt */
+ pad_len -= 3; /* format is 0 || 2 || pad || 0 || data */
+ /*
+ * padding for PKCS #1 encrypted data is a string of random bytes. The
+ * random butes protect against potential decryption attacks against RSA.
+ * Since PrivDecrypt has already stripped those bytes, we can't reconstruct
+ * them. This shouldn't matter to the upper level code which should just
+ * strip this code out anyway, so We'll pad with a constant 3.
+ */
+ memset(&buffer[2], 0x03, pad_len);
+ pad_len += 2; /* index to the end of the pad */
+ buffer[pad_len] = 0;
+ pad_len++; /* index to the start of the data */
+ memcpy(&buffer[pad_len], bp, signature_len);
+ /*
+ * we got here because either the X509 attempt failed, or the
+ * token couldn't do the X509 operation, in either case stay
+ * with the PKCS version for future operations on this key
+ */
+ key->failedX509 = VCardEmulTrue;
+cleanup:
+ if (bp != buf) {
+ g_free(bp);
+ }
+ return ret;
+}
+
+/*
+ * 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;
+ 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 = g_malloc(pin_len+1);
+ 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 */
+ g_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_logout(VCard *card)
+{
+ PK11SlotInfo *slot;
+
+ if (!nss_emul_init) {
+ return;
+ }
+
+ slot = vcard_emul_card_get_slot(card);
+ if (PK11_IsLoggedIn(slot, NULL)) {
+ PK11_Logout(slot); /* NOTE: ignoring SECStatus return value */
+ }
+}
+
+void
+vcard_emul_reset(VCard *card, VCardPower power)
+{
+ /*
+ * if we reset the card (either power on or power off), we lose our login
+ * state
+ */
+ vcard_emul_logout(card);
+
+ /* TODO: we may also need to send insertion/removal events? */
+}
+
+static VReader *
+vcard_emul_find_vreader_from_slot(PK11SlotInfo *slot)
+{
+ VReaderList *reader_list = vreader_get_reader_list();
+ VReaderListEntry *current_entry;
+
+ 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) {
+ vreader_list_delete(reader_list);
+ return reader;
+ }
+ vreader_free(reader);
+ }
+
+ vreader_list_delete(reader_list);
+ 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 = g_new(VReaderEmul, 1);
+
+ new_reader_emul->slot = PK11_ReferenceSlot(slot);
+ new_reader_emul->default_type = type;
+ new_reader_emul->type_params = g_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);
+ }
+ g_free(vreader_emul->type_params);
+ g_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_alloc_atr will set appropriate
+ * historical bytes for any software emulated card. The remaining bytes can be
+ * used to indicate the actual emulator
+ */
+static unsigned char *nss_atr;
+static int nss_atr_len;
+
+void
+vcard_emul_get_atr(VCard *card, unsigned char *atr, int *atr_len)
+{
+ int len;
+ assert(atr != NULL);
+
+ if (nss_atr == NULL) {
+ nss_atr = vcard_alloc_atr("NSS", &nss_atr_len);
+ }
+ len = MIN(nss_atr_len, *atr_len);
+ memcpy(atr, nss_atr, len);
+ *atr_len = len;
+}
+
+/*
+ * 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;
+ VCard *card;
+
+ 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++;
+ }
+
+ /* allocate the arrays */
+ vcard_emul_alloc_arrays(&certs, &cert_len, &keys, cert_count);
+
+ /* 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 */
+ card = vcard_emul_make_card(vreader, certs, cert_len, keys, cert_count);
+ g_free(certs);
+ g_free(cert_len);
+ g_free(keys);
+
+ return card;
+}
+
+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 {
+ /*
+ * XXX - the latency value doesn't matter one bit. you only get no
+ * blocking (flags |= CKF_DONT_BLOCK) or PKCS11_WAIT_LATENCY (==500),
+ * hard coded in coolkey. And it isn't coolkey's fault - the timeout
+ * value we pass get's dropped on the floor before C_WaitForSlotEvent
+ * is called.
+ */
+ slot = SECMOD_WaitForAnyTokenEvent(module, 0, 500);
+ if (slot == NULL) {
+ /* this could be just a no event indication */
+ if (PORT_GetError() == SEC_ERROR_NO_EVENT) {
+ continue;
+ }
+ 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;
+}
+
+
+static PRBool
+module_has_removable_hw_slots(SECMODModule *mod)
+{
+ int i;
+ PRBool ret = PR_FALSE;
+ SECMODListLock *moduleLock = SECMOD_GetDefaultModuleListLock();
+
+ if (!moduleLock) {
+ PORT_SetError(SEC_ERROR_NOT_INITIALIZED);
+ return ret;
+ }
+ SECMOD_GetReadLock(moduleLock);
+ for (i = 0; i < mod->slotCount; i++) {
+ PK11SlotInfo *slot = mod->slots[i];
+ if (PK11_IsRemovable(slot) && PK11_IsHW(slot)) {
+ ret = PR_TRUE;
+ break;
+ }
+ }
+ SECMOD_ReleaseReadLock(moduleLock);
+ return ret;
+}
+
+/* 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;
+
+VCardEmulError
+vcard_emul_init(const VCardEmulOptions *options)
+{
+ SECStatus rv;
+ PRBool has_readers = PR_FALSE;
+ 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 {
+ gchar *path;
+#ifndef _WIN32
+ path = g_strdup("/etc/pki/nssdb");
+#else
+ if (g_get_system_config_dirs() == NULL ||
+ g_get_system_config_dirs()[0] == NULL) {
+ return VCARD_EMUL_FAIL;
+ }
+
+ path = g_build_filename(
+ g_get_system_config_dirs()[0], "pki", "nssdb", NULL);
+#endif
+
+ rv = NSS_Init(path);
+ g_free(path);
+ }
+ 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);
+
+ vcard_emul_alloc_arrays(&certs, &cert_len, &keys,
+ options->vreader[i].cert_count);
+
+ 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;
+ }
+ g_free(certs);
+ g_free(cert_len);
+ g_free(keys);
+ }
+
+ /* 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();
+ SECMOD_GetReadLock(module_lock);
+ for (mlp = module_list; mlp; mlp = mlp->next) {
+ SECMODModule *module = mlp->module;
+ if (module_has_removable_hw_slots(module)) {
+ break;
+ }
+ }
+ SECMOD_ReleaseReadLock(module_lock);
+
+ /* 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 = g_strdup(options->hw_type_params);
+
+ SECMOD_GetReadLock(module_lock);
+ for (mlp = module_list; mlp; mlp = mlp->next) {
+ SECMODModule *module = mlp->module;
+
+ /* Ignore the internal module */
+ if (module == NULL || module == SECMOD_GetInternalModule()) {
+ 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;
+ }
+ if (strcmp("E-Gate 0 0", PK11_GetSlotName(slot)) == 0) {
+ /*
+ * coolkey <= 1.1.0-20 emulates this reader if it can't find
+ * any hardware readers. This causes problems, warn user of
+ * problems.
+ */
+ fprintf(stderr, "known bad coolkey version - see "
+ "https://bugzilla.redhat.com/show_bug.cgi?id=802435\n");
+ 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);
+
+ if (PK11_IsPresent(slot)) {
+ VCard *vcard;
+ vcard = vcard_emul_mirror_card(vreader);
+ vreader_insert_card(vreader, vcard);
+ vcard_emul_init_series(vreader, vcard);
+ vcard_free(vcard);
+ }
+ }
+ vcard_emul_new_event_thread(module);
+ }
+ SECMOD_ReleaseReadLock(module_lock);
+ nss_emul_init = PR_TRUE;
+
+ 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;
+ 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);
+ }
+
+ vreader_list_delete(list);
+}
+
+/*
+ * Silly little functions to help parsing our argument string
+ */
+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 *
+strip(const char *str)
+{
+ for (; *str && isspace(*str); str++) {
+ }
+ return str;
+}
+
+static const char *
+find_blank(const char *str)
+{
+ for (; *str && !isspace(*str); str++) {
+ }
+ return str;
+}
+
+
+/*
+ * We really want to use some existing argument parsing library here. That
+ * would give us a consistent look */
+static VCardEmulOptions options;
+#define READER_STEP 4
+
+/* Expects "args" to be at the beginning of a token (ie right after the ','
+ * ending the previous token), and puts the next token start in "token",
+ * and its length in "token_length". "token" will not be nul-terminated.
+ * After calling the macro, "args" will be advanced to the beginning of
+ * the next token.
+ * This macro may call continue or break.
+ */
+#define NEXT_TOKEN(token) \
+ (token) = args; \
+ args = strpbrk(args, ",)"); \
+ if (*args == 0) { \
+ break; \
+ } \
+ if (*args == ')') { \
+ args++; \
+ continue; \
+ } \
+ (token##_length) = args - (token); \
+ args = strip(args+1);
+
+VCardEmulOptions *
+vcard_emul_options(const char *args)
+{
+ int reader_count = 0;
+ VCardEmulOptions *opts;
+
+ /* 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;
+ size_t name_length;
+ const char *vname;
+ size_t vname_length;
+ const char *type_params;
+ size_t type_params_length;
+ char type_str[100];
+ VCardEmulType type;
+ int count, i;
+ VirtualReaderOptions *vreaderOpt;
+
+ args = strip(args + 5);
+ if (*args != '(') {
+ continue;
+ }
+ args = strip(args+1);
+
+ NEXT_TOKEN(name)
+ NEXT_TOKEN(vname)
+ NEXT_TOKEN(type_params)
+ type_params_length = MIN(type_params_length, sizeof(type_str)-1);
+ memcpy(type_str, type_params, type_params_length);
+ type_str[type_params_length] = '\0';
+ type = vcard_emul_type_from_string(type_str);
+
+ NEXT_TOKEN(type_params)
+
+ if (*args == 0) {
+ break;
+ }
+
+ if (opts->vreader_count >= reader_count) {
+ reader_count += READER_STEP;
+ opts->vreader = g_renew(VirtualReaderOptions, opts->vreader,
+ reader_count);
+ }
+ vreaderOpt = &opts->vreader[opts->vreader_count];
+ vreaderOpt->name = g_strndup(name, name_length);
+ vreaderOpt->vname = g_strndup(vname, vname_length);
+ vreaderOpt->card_type = type;
+ vreaderOpt->type_params =
+ g_strndup(type_params, type_params_length);
+ count = count_tokens(args, ',', ')') + 1;
+ vreaderOpt->cert_count = count;
+ vreaderOpt->cert_name = g_new(char *, count);
+ for (i = 0; i < count; i++) {
+ const char *cert = args;
+ args = strpbrk(args, ",)");
+ vreaderOpt->cert_name[i] = g_strndup(cert, args - cert);
+ args = strip(args+1);
+ }
+ 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 = g_strndup(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 = strpbrk(args, "\"\n");
+ opts->nss_db = g_strndup(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} Virtual 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 00000000..59a14582
--- /dev/null
+++ b/libcacard/vcard_emul_type.c
@@ -0,0 +1,57 @@
+/*
+ * 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.
+ *
+ * This work is licensed under the terms of the GNU LGPL, version 2.1 or later.
+ * See the COPYING.LIB file in the top-level directory.
+ */
+
+#include <strings.h>
+#include "vcardt.h"
+#include "vcard_emul_type.h"
+#include "cac.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);
+ /* 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 00000000..0242f40e
--- /dev/null
+++ b/libcacard/vcard_emul_type.h
@@ -0,0 +1,32 @@
+/*
+ * 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.
+ *
+ * This work is licensed under the terms of the GNU LGPL, version 2.1 or later.
+ * See the COPYING.LIB file in the top-level directory.
+ */
+
+#ifndef 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.c b/libcacard/vcardt.c
new file mode 100644
index 00000000..c67de2f3
--- /dev/null
+++ b/libcacard/vcardt.c
@@ -0,0 +1,40 @@
+#include <stdlib.h>
+#include <string.h>
+#include <glib.h>
+
+#include "vcardt.h"
+
+#include "vcardt_internal.h"
+
+/* create an ATR with appropriate historical bytes */
+#define ATR_TS_DIRECT_CONVENTION 0x3b
+#define ATR_TA_PRESENT 0x10
+#define ATR_TB_PRESENT 0x20
+#define ATR_TC_PRESENT 0x40
+#define ATR_TD_PRESENT 0x80
+
+unsigned char *vcard_alloc_atr(const char *postfix, int *atr_len)
+{
+ int postfix_len;
+ const char prefix[] = "VCARD_";
+ const char default_postfix[] = "DEFAULT";
+ const int prefix_len = sizeof(prefix) - 1;
+ int total_len;
+ unsigned char *atr;
+
+ if (postfix == NULL) {
+ postfix = default_postfix;
+ }
+ postfix_len = strlen(postfix);
+ total_len = 3 + prefix_len + postfix_len;
+ atr = g_malloc(total_len);
+ atr[0] = ATR_TS_DIRECT_CONVENTION;
+ atr[1] = ATR_TD_PRESENT + prefix_len + postfix_len;
+ atr[2] = 0x00;
+ memcpy(&atr[3], prefix, prefix_len);
+ memcpy(&atr[3 + prefix_len], postfix, postfix_len);
+ if (atr_len) {
+ *atr_len = total_len;
+ }
+ return atr;
+}
diff --git a/libcacard/vcardt.h b/libcacard/vcardt.h
new file mode 100644
index 00000000..795e2653
--- /dev/null
+++ b/libcacard/vcardt.h
@@ -0,0 +1,59 @@
+/*
+ * This work is licensed under the terms of the GNU LGPL, version 2.1 or later.
+ * See the COPYING.LIB file in the top-level directory.
+ */
+#ifndef VCARDT_H
+#define VCARDT_H 1
+
+/*
+ * these should come from some common spice header file
+ */
+#include <assert.h>
+#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
+
+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/vcardt_internal.h b/libcacard/vcardt_internal.h
new file mode 100644
index 00000000..e5c8d2dd
--- /dev/null
+++ b/libcacard/vcardt_internal.h
@@ -0,0 +1,6 @@
+#ifndef VCARDT_INTERNAL_H
+#define VCARDT_INTERNAL_H
+
+unsigned char *vcard_alloc_atr(const char *postfix, int *atr_len);
+
+#endif
diff --git a/libcacard/vevent.h b/libcacard/vevent.h
new file mode 100644
index 00000000..38c3482c
--- /dev/null
+++ b/libcacard/vevent.h
@@ -0,0 +1,27 @@
+/*
+ * This work is licensed under the terms of the GNU LGPL, version 2.1 or later.
+ * See the COPYING.LIB file in the top-level directory.
+ */
+#ifndef 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 00000000..9725f46a
--- /dev/null
+++ b/libcacard/vreader.c
@@ -0,0 +1,578 @@
+/*
+ * emulate the reader
+ *
+ * This work is licensed under the terms of the GNU LGPL, version 2.1 or later.
+ * See the COPYING.LIB file in the top-level directory.
+ */
+
+#ifdef G_LOG_DOMAIN
+#undef G_LOG_DOMAIN
+#endif
+#define G_LOG_DOMAIN "libcacard"
+
+#include "glib-compat.h"
+
+#include <string.h>
+
+#include "vcard.h"
+#include "vcard_emul.h"
+#include "card_7816.h"
+#include "vreader.h"
+#include "vevent.h"
+#include "cac.h" /* just for debugging defines */
+
+#define LIBCACARD_LOG_DOMAIN "libcacard"
+
+struct VReaderStruct {
+ int reference_count;
+ VCard *card;
+ char *name;
+ vreader_id_t id;
+ CompatGMutex lock;
+ VReaderEmul *reader_private;
+ VReaderEmulFree reader_private_free;
+};
+
+/*
+ * Debug helpers
+ */
+
+static const char *
+apdu_ins_to_string(int ins)
+{
+ switch (ins) {
+ case VCARD7816_INS_MANAGE_CHANNEL:
+ return "manage channel";
+ case VCARD7816_INS_EXTERNAL_AUTHENTICATE:
+ return "external authenticate";
+ case VCARD7816_INS_GET_CHALLENGE:
+ return "get challenge";
+ case VCARD7816_INS_INTERNAL_AUTHENTICATE:
+ return "internal authenticate";
+ case VCARD7816_INS_ERASE_BINARY:
+ return "erase binary";
+ case VCARD7816_INS_READ_BINARY:
+ return "read binary";
+ case VCARD7816_INS_WRITE_BINARY:
+ return "write binary";
+ case VCARD7816_INS_UPDATE_BINARY:
+ return "update binary";
+ case VCARD7816_INS_READ_RECORD:
+ return "read record";
+ case VCARD7816_INS_WRITE_RECORD:
+ return "write record";
+ case VCARD7816_INS_UPDATE_RECORD:
+ return "update record";
+ case VCARD7816_INS_APPEND_RECORD:
+ return "append record";
+ case VCARD7816_INS_ENVELOPE:
+ return "envelope";
+ case VCARD7816_INS_PUT_DATA:
+ return "put data";
+ case VCARD7816_INS_GET_DATA:
+ return "get data";
+ case VCARD7816_INS_SELECT_FILE:
+ return "select file";
+ case VCARD7816_INS_VERIFY:
+ return "verify";
+ case VCARD7816_INS_GET_RESPONSE:
+ return "get response";
+ case CAC_GET_PROPERTIES:
+ return "get properties";
+ case CAC_GET_ACR:
+ return "get acr";
+ case CAC_READ_BUFFER:
+ return "read buffer";
+ case CAC_UPDATE_BUFFER:
+ return "update buffer";
+ case CAC_SIGN_DECRYPT:
+ return "sign decrypt";
+ case CAC_GET_CERTIFICATE:
+ return "get certificate";
+ }
+ return "unknown";
+}
+
+/* manage locking */
+static inline void
+vreader_lock(VReader *reader)
+{
+ g_mutex_lock(&reader->lock);
+}
+
+static inline void
+vreader_unlock(VReader *reader)
+{
+ g_mutex_unlock(&reader->lock);
+}
+
+/*
+ * vreader constructor
+ */
+VReader *
+vreader_new(const char *name, VReaderEmul *private,
+ VReaderEmulFree private_free)
+{
+ VReader *reader;
+
+ reader = g_new(VReader, 1);
+ g_mutex_init(&reader->lock);
+ reader->reference_count = 1;
+ reader->name = g_strdup(name);
+ 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);
+ g_mutex_clear(&reader->lock);
+ if (reader->card) {
+ vcard_free(reader->card);
+ }
+ g_free(reader->name);
+ if (reader->reader_private_free) {
+ reader->reader_private_free(reader->reader_private);
+ }
+ g_free(reader);
+}
+
+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 {
+ g_debug("%s: CLS=0x%x,INS=0x%x,P1=0x%x,P2=0x%x,Lc=%d,Le=%d %s",
+ __func__, apdu->a_cla, apdu->a_ins, apdu->a_p1, apdu->a_p2,
+ apdu->a_Lc, apdu->a_Le, apdu_ins_to_string(apdu->a_ins));
+ card_status = vcard_process_apdu(card, apdu, &response);
+ if (response) {
+ g_debug("%s: status=%d sw1=0x%x sw2=0x%x len=%d (total=%d)",
+ __func__, response->b_status, response->b_sw1,
+ response->b_sw2, response->b_len, response->b_total_len);
+ }
+ }
+ assert(card_status == VCARD_DONE && response);
+ 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 = g_new0(VReaderListEntry, 1);
+ 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);
+ g_free(entry);
+}
+
+
+static VReaderList *
+vreader_list_new(void)
+{
+ VReaderList *new_reader_list;
+
+ new_reader_list = g_new0(VReaderList, 1);
+ return new_reader_list;
+}
+
+void
+vreader_list_delete(VReaderList *list)
+{
+ VReaderListEntry *current_entry;
+ VReaderListEntry *next_entry;
+ 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);
+ }
+ g_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;
+static CompatGMutex vreader_list_mutex;
+
+static void
+vreader_list_init(void)
+{
+ vreader_list = vreader_list_new();
+}
+
+static void
+vreader_list_lock(void)
+{
+ g_mutex_lock(&vreader_list_mutex);
+}
+
+static void
+vreader_list_unlock(void)
+{
+ g_mutex_unlock(&vreader_list_mutex);
+}
+
+static VReaderList *
+vreader_copy_list(VReaderList *list)
+{
+ VReaderList *new_list;
+ VReaderListEntry *current_entry;
+
+ 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;
+
+ 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;
+
+ 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 00000000..ec204213
--- /dev/null
+++ b/libcacard/vreader.h
@@ -0,0 +1,55 @@
+/*
+ * This work is licensed under the terms of the GNU LGPL, version 2.1 or later.
+ * See the COPYING.LIB file in the top-level directory.
+ */
+
+#ifndef 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 00000000..f97e0a79
--- /dev/null
+++ b/libcacard/vreadert.h
@@ -0,0 +1,24 @@
+/*
+ * This work is licensed under the terms of the GNU LGPL, version 2.1 or later.
+ * See the COPYING.LIB file in the top-level directory.
+ */
+
+#ifndef 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/vscard_common.h b/libcacard/vscard_common.h
new file mode 100644
index 00000000..08f68e4d
--- /dev/null
+++ b/libcacard/vscard_common.h
@@ -0,0 +1,178 @@
+/* Virtual Smart Card protocol definition
+ *
+ * This protocol is between a host using virtual smart card readers,
+ * and a client providing the smart cards, perhaps by emulating them or by
+ * access to real cards.
+ *
+ * Definitions for this protocol:
+ * Host - user of the card
+ * Client - owner of the 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 via capabilities and provide
+ * for error responses.
+ *
+ * Copyright (c) 2011 Red Hat.
+ *
+ * This work is licensed under the terms of the GNU LGPL, version 2.1 or later.
+ * See the COPYING.LIB file in the top-level directory.
+ */
+
+#ifndef 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 accommodated with the existing protocol.
+ */
+
+#define VSCARD_VERSION MAKE_VERSION(0, 0, 2)
+
+typedef enum VSCMsgType {
+ VSC_Init = 1,
+ VSC_Error,
+ VSC_ReaderAdd,
+ VSC_ReaderRemove,
+ VSC_ATR,
+ VSC_CardRemove,
+ VSC_APDU,
+ VSC_Flush,
+ VSC_FlushComplete
+} VSCMsgType;
+
+typedef enum VSCErrorCode {
+ VSC_SUCCESS = 0,
+ VSC_GENERAL_ERROR = 1,
+ VSC_CANNOT_ADD_MORE_READERS,
+ VSC_CARD_ALREAY_INSERTED,
+} VSCErrorCode;
+
+#define VSCARD_UNDEFINED_READER_ID 0xffffffff
+#define VSCARD_MINIMAL_READER_ID 0
+
+#define VSCARD_MAGIC (*(uint32_t *)"VSCD")
+
+/*
+ * Header
+ * Each message starts with the header.
+ * type - message type
+ * reader_id - used by messages that are reader specific
+ * length - length of payload (not including header, i.e. zero for
+ * messages containing empty payloads)
+ */
+typedef struct VSCMsgHeader {
+ uint32_t type;
+ uint32_t reader_id;
+ uint32_t length;
+ uint8_t data[0];
+} VSCMsgHeader;
+
+/*
+ * VSCMsgInit Client <-> Host
+ * Client sends it on connection, with its own capabilities.
+ * Host replies with VSCMsgInit filling in its capabilities.
+ *
+ * It is not meant to be used for negotiation, i.e. sending more then
+ * once from any side, but could be used for that in the future.
+ */
+typedef struct VSCMsgInit {
+ uint32_t magic;
+ uint32_t version;
+ uint32_t capabilities[1]; /* receiver must check length,
+ array may grow in the future*/
+} VSCMsgInit;
+
+/*
+ * VSCMsgError Client <-> Host
+ * This message is a response to any of:
+ * Reader Add
+ * Reader Remove
+ * Card Remove
+ * If the operation was successful then VSC_SUCCESS
+ * is returned, other wise a specific error code.
+ */
+typedef struct VSCMsgError {
+ uint32_t code;
+} VSCMsgError;
+
+/*
+ * VSCMsgReaderAdd Client -> Host
+ * Host replies with allocated reader id in VSCMsgError with code==SUCCESS.
+ *
+ * name - name of the reader on client side, UTF-8 encoded. Only used
+ * for client presentation (may be translated to the device presented to the
+ * guest), protocol wise only reader_id is important.
+ */
+typedef struct VSCMsgReaderAdd {
+ uint8_t name[0];
+} VSCMsgReaderAdd;
+
+/*
+ * VSCMsgReaderRemove Client -> Host
+ * The client's reader has been removed.
+ */
+typedef struct VSCMsgReaderRemove {
+} VSCMsgReaderRemove;
+
+/*
+ * VSCMsgATR Client -> Host
+ * Answer to reset. Sent for card insertion or card reset. The reset/insertion
+ * happens on the client side, they do not require any action from the host.
+ */
+typedef struct VSCMsgATR {
+ uint8_t atr[0];
+} VSCMsgATR;
+
+/*
+ * VSCMsgCardRemove Client -> Host
+ * The client card has been removed.
+ */
+typedef struct VSCMsgCardRemove {
+} VSCMsgCardRemove;
+
+/*
+ * VSCMsgAPDU Client <-> Host
+ * Main reason of existence. Transfer a single APDU in either direction.
+ */
+typedef struct VSCMsgAPDU {
+ uint8_t data[0];
+} VSCMsgAPDU;
+
+/*
+ * VSCMsgFlush Host -> Client
+ * Request client to send a FlushComplete message when it is done
+ * servicing all outstanding APDUs
+ */
+typedef struct VSCMsgFlush {
+} VSCMsgFlush;
+
+/*
+ * VSCMsgFlush Client -> Host
+ * Client response to Flush after all APDUs have been processed and
+ * responses sent.
+ */
+typedef struct VSCMsgFlushComplete {
+} VSCMsgFlushComplete;
+
+#endif /* VSCARD_COMMON_H */
diff --git a/libcacard/vscclient.c b/libcacard/vscclient.c
new file mode 100644
index 00000000..06526844
--- /dev/null
+++ b/libcacard/vscclient.c
@@ -0,0 +1,785 @@
+/*
+ * Tester for VSCARD protocol, client side.
+ *
+ * Can be used with ccid-card-passthru.
+ *
+ * Copyright (c) 2011 Red Hat.
+ * Written by Alon Levy.
+ *
+ * This work is licensed under the terms of the GNU LGPL, version 2.1 or later.
+ * See the COPYING.LIB file in the top-level directory.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#ifndef _WIN32
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <netdb.h>
+#include <unistd.h>
+#define closesocket(x) close(x)
+#else
+#include <getopt.h>
+#endif
+
+#include "glib-compat.h"
+
+#include "vscard_common.h"
+
+#include "vreader.h"
+#include "vcard_emul.h"
+#include "vevent.h"
+
+static int verbose;
+
+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 GIOChannel *channel_socket;
+static GByteArray *socket_to_send;
+static CompatGMutex socket_to_send_lock;
+static guint socket_tag;
+
+static void
+update_socket_watch(void);
+
+static gboolean
+do_socket_send(GIOChannel *source,
+ GIOCondition condition,
+ gpointer data)
+{
+ gsize bw;
+ GError *err = NULL;
+
+ g_return_val_if_fail(socket_to_send->len != 0, FALSE);
+ g_return_val_if_fail(condition & G_IO_OUT, FALSE);
+
+ g_io_channel_write_chars(channel_socket,
+ (gchar *)socket_to_send->data, socket_to_send->len, &bw, &err);
+ if (err != NULL) {
+ g_error("Error while sending socket %s", err->message);
+ return FALSE;
+ }
+ g_byte_array_remove_range(socket_to_send, 0, bw);
+
+ if (socket_to_send->len == 0) {
+ update_socket_watch();
+ return FALSE;
+ }
+ return TRUE;
+}
+
+static gboolean
+socket_prepare_sending(gpointer user_data)
+{
+ update_socket_watch();
+
+ return FALSE;
+}
+
+static int
+send_msg(
+ VSCMsgType type,
+ uint32_t reader_id,
+ const void *msg,
+ unsigned int length
+) {
+ VSCMsgHeader mhHeader;
+
+ g_mutex_lock(&socket_to_send_lock);
+
+ if (verbose > 10) {
+ printf("sending type=%d id=%u, len =%u (0x%x)\n",
+ type, reader_id, length, length);
+ }
+
+ mhHeader.type = htonl(type);
+ mhHeader.reader_id = 0;
+ mhHeader.length = htonl(length);
+ g_byte_array_append(socket_to_send, (guint8 *)&mhHeader, sizeof(mhHeader));
+ g_byte_array_append(socket_to_send, (guint8 *)msg, length);
+ g_idle_add(socket_prepare_sending, NULL);
+
+ g_mutex_unlock(&socket_to_send_lock);
+
+ return 0;
+}
+
+static VReader *pending_reader;
+static CompatGMutex pending_reader_lock;
+static CompatGCond pending_reader_condition;
+
+#define MAX_ATR_LEN 40
+static gpointer
+event_thread(gpointer arg)
+{
+ unsigned char atr[MAX_ATR_LEN];
+ int atr_len;
+ VEvent *event;
+ 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 */
+ g_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 */
+ g_mutex_unlock(&pending_reader_lock);
+ vevent_delete(event);
+ continue;
+ }
+ /* this reader hasn't been told its status from qemu yet, wait for
+ * that status */
+ while (pending_reader != NULL) {
+ g_cond_wait(&pending_reader_condition, &pending_reader_lock);
+ }
+ g_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
+ * */
+ g_mutex_lock(&pending_reader_lock);
+ while (pending_reader != NULL) {
+ g_cond_wait(&pending_reader_condition, &pending_reader_lock);
+ }
+ pending_reader = vreader_reference(event->reader);
+ g_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 /* TODO 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: %u\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 %u: ", 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 %u:\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 int
+on_host_init(VSCMsgHeader *mhHeader, VSCMsgInit *incoming)
+{
+ uint32_t *capabilities = (incoming->capabilities);
+ int num_capabilities =
+ 1 + ((mhHeader->length - sizeof(VSCMsgInit)) / sizeof(uint32_t));
+ int i;
+
+ incoming->version = ntohl(incoming->version);
+ if (incoming->version != VSCARD_VERSION) {
+ if (verbose > 0) {
+ printf("warning: host has version %d, we have %d\n",
+ verbose, VSCARD_VERSION);
+ }
+ }
+ if (incoming->magic != VSCARD_MAGIC) {
+ printf("unexpected magic: got %d, expected %d\n",
+ incoming->magic, VSCARD_MAGIC);
+ return -1;
+ }
+ for (i = 0 ; i < num_capabilities; ++i) {
+ capabilities[i] = ntohl(capabilities[i]);
+ }
+ /* Future: check capabilities */
+ /* remove whatever reader might be left in qemu,
+ * in case of an unclean previous exit. */
+ send_msg(VSC_ReaderRemove, VSCARD_MINIMAL_READER_ID, NULL, 0);
+ /* launch the event_thread. This will trigger reader adds for all the
+ * existing readers */
+ g_thread_new("vsc/event", event_thread, NULL);
+ return 0;
+}
+
+
+enum {
+ STATE_HEADER,
+ STATE_MESSAGE,
+};
+
+#define APDUBufSize 270
+
+static gboolean
+do_socket_read(GIOChannel *source,
+ GIOCondition condition,
+ gpointer data)
+{
+ int rv;
+ int dwSendLength;
+ int dwRecvLength;
+ uint8_t pbRecvBuffer[APDUBufSize];
+ static uint8_t pbSendBuffer[APDUBufSize];
+ VReaderStatus reader_status;
+ VReader *reader = NULL;
+ static VSCMsgHeader mhHeader;
+ VSCMsgError *error_msg;
+ GError *err = NULL;
+
+ static gchar *buf;
+ static gsize br, to_read;
+ static int state = STATE_HEADER;
+
+ if (state == STATE_HEADER && to_read == 0) {
+ buf = (gchar *)&mhHeader;
+ to_read = sizeof(mhHeader);
+ }
+
+ if (to_read > 0) {
+ g_io_channel_read_chars(source, (gchar *)buf, to_read, &br, &err);
+ if (err != NULL) {
+ g_error("error while reading: %s", err->message);
+ }
+ buf += br;
+ to_read -= br;
+ if (to_read != 0) {
+ return TRUE;
+ }
+ }
+
+ if (state == STATE_HEADER) {
+ mhHeader.type = ntohl(mhHeader.type);
+ mhHeader.reader_id = ntohl(mhHeader.reader_id);
+ mhHeader.length = ntohl(mhHeader.length);
+ if (verbose) {
+ printf("Header: type=%d, reader_id=%u length=%d (0x%x)\n",
+ mhHeader.type, mhHeader.reader_id, mhHeader.length,
+ mhHeader.length);
+ }
+ switch (mhHeader.type) {
+ case VSC_APDU:
+ case VSC_Flush:
+ case VSC_Error:
+ case VSC_Init:
+ buf = (gchar *)pbSendBuffer;
+ to_read = mhHeader.length;
+ state = STATE_MESSAGE;
+ return TRUE;
+ default:
+ fprintf(stderr, "Unexpected message of type 0x%X\n", mhHeader.type);
+ return FALSE;
+ }
+ }
+
+ if (state == STATE_MESSAGE) {
+ switch (mhHeader.type) {
+ case VSC_APDU:
+ if (verbose) {
+ printf(" recv APDU: ");
+ print_byte_array(pbSendBuffer, mhHeader.length);
+ }
+ /* Transmit received 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_Flush:
+ /* TODO: actually flush */
+ send_msg(VSC_FlushComplete, mhHeader.reader_id, NULL, 0);
+ break;
+ case VSC_Error:
+ error_msg = (VSCMsgError *) pbSendBuffer;
+ if (error_msg->code == VSC_SUCCESS) {
+ g_mutex_lock(&pending_reader_lock);
+ if (pending_reader) {
+ vreader_set_id(pending_reader, mhHeader.reader_id);
+ vreader_free(pending_reader);
+ pending_reader = NULL;
+ g_cond_signal(&pending_reader_condition);
+ }
+ g_mutex_unlock(&pending_reader_lock);
+ break;
+ }
+ printf("warning: qemu refused to add reader\n");
+ if (error_msg->code == VSC_CANNOT_ADD_MORE_READERS) {
+ /* clear pending reader, qemu can't handle any more */
+ g_mutex_lock(&pending_reader_lock);
+ if (pending_reader) {
+ pending_reader = NULL;
+ /* make sure the event loop doesn't hang */
+ g_cond_signal(&pending_reader_condition);
+ }
+ g_mutex_unlock(&pending_reader_lock);
+ }
+ break;
+ case VSC_Init:
+ if (on_host_init(&mhHeader, (VSCMsgInit *)pbSendBuffer) < 0) {
+ return FALSE;
+ }
+ break;
+ default:
+ g_assert_not_reached();
+ return FALSE;
+ }
+
+ state = STATE_HEADER;
+ }
+
+
+ return TRUE;
+}
+
+static gboolean
+do_socket(GIOChannel *source,
+ GIOCondition condition,
+ gpointer data)
+{
+ /* not sure if two watches work well with a single win32 sources */
+ if (condition & G_IO_OUT) {
+ if (!do_socket_send(source, condition, data)) {
+ return FALSE;
+ }
+ }
+
+ if (condition & G_IO_IN) {
+ if (!do_socket_read(source, condition, data)) {
+ return FALSE;
+ }
+ }
+
+ return TRUE;
+}
+
+static void
+update_socket_watch(void)
+{
+ gboolean out = socket_to_send->len > 0;
+
+ if (socket_tag != 0) {
+ g_source_remove(socket_tag);
+ }
+
+ socket_tag = g_io_add_watch(channel_socket,
+ G_IO_IN | (out ? G_IO_OUT : 0), do_socket, NULL);
+}
+
+static gboolean
+do_command(GIOChannel *source,
+ GIOCondition condition,
+ gpointer data)
+{
+ char *string;
+ VCardEmulError error;
+ static unsigned int default_reader_id;
+ unsigned int reader_id;
+ VReader *reader = NULL;
+ GError *err = NULL;
+
+ g_assert(condition & G_IO_IN);
+
+ reader_id = default_reader_id;
+ g_io_channel_read_line(source, &string, NULL, NULL, &err);
+ if (err != NULL) {
+ g_error("Error while reading command: %s", err->message);
+ }
+
+ 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);
+ if (reader != NULL) {
+ error = vcard_emul_force_card_insert(reader);
+ printf("insert %s, returned %d\n",
+ vreader_get_name(reader), error);
+ } else {
+ printf("no reader by id %u found\n", reader_id);
+ }
+ } 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);
+ if (reader != NULL) {
+ error = vcard_emul_force_card_remove(reader);
+ printf("remove %s, returned %d\n",
+ vreader_get_name(reader), error);
+ } else {
+ printf("no reader by id %u found\n", reader_id);
+ }
+ } 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 %u, %s\n", reader_id,
+ vreader_get_name(reader));
+ default_reader_id = reader_id;
+ } else {
+ printf("Reader with id %u 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("%3u %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));
+ }
+ vreader_list_delete(list);
+ } 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);
+
+ return TRUE;
+}
+
+
+/* just for ease of parsing command line arguments. */
+#define MAX_CERTS 100
+
+static int
+connect_to_qemu(
+ const char *host,
+ const char *port
+) {
+ struct addrinfo hints;
+ struct addrinfo *server = NULL;
+ int ret, sock;
+
+ sock = socket(AF_INET, SOCK_STREAM, 0);
+ if (sock < 0) {
+ /* Error */
+ fprintf(stderr, "Error opening socket!\n");
+ return -1;
+ }
+
+ memset(&hints, 0, sizeof(struct addrinfo));
+ hints.ai_family = AF_UNSPEC;
+ hints.ai_socktype = SOCK_STREAM;
+ hints.ai_flags = 0;
+ hints.ai_protocol = 0; /* Any protocol */
+
+ ret = getaddrinfo(host, port, &hints, &server);
+
+ if (ret != 0) {
+ /* Error */
+ fprintf(stderr, "getaddrinfo failed\n");
+ goto cleanup_socket;
+ }
+
+ if (connect(sock, server->ai_addr, server->ai_addrlen) < 0) {
+ /* Error */
+ fprintf(stderr, "Could not connect\n");
+ goto cleanup_socket;
+ }
+ if (verbose) {
+ printf("Connected (sizeof Header=%zd)!\n", sizeof(VSCMsgHeader));
+ }
+
+ freeaddrinfo(server);
+ return sock;
+
+cleanup_socket:
+ if (server) {
+ freeaddrinfo(server);
+ }
+ closesocket(sock);
+ return -1;
+}
+
+int
+main(
+ int argc,
+ char *argv[]
+) {
+ GMainLoop *loop;
+ GIOChannel *channel_stdin;
+ char *qemu_host;
+ char *qemu_port;
+
+ VCardEmulOptions *command_line_options = NULL;
+
+ char *cert_names[MAX_CERTS];
+ char *emul_args = NULL;
+ int cert_count = 0;
+ int c, sock;
+
+#ifdef _WIN32
+ WSADATA Data;
+
+ if (WSAStartup(MAKEWORD(2, 2), &Data) != 0) {
+ c = WSAGetLastError();
+ fprintf(stderr, "WSAStartup: %d\n", c);
+ return 1;
+ }
+#endif
+#if !GLIB_CHECK_VERSION(2, 31, 0)
+ if (!g_thread_supported()) {
+ g_thread_init(NULL);
+ }
+#endif
+
+ 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':
+ print_usage();
+ exit(4);
+ break;
+ case 'd':
+ verbose = get_id_from_string(optarg, 1);
+ break;
+ }
+ }
+
+ if (argc - optind != 2) {
+ print_usage();
+ exit(4);
+ }
+
+ if (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 = g_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) {
+ command_line_options = vcard_emul_options(emul_args);
+ }
+
+ qemu_host = g_strdup(argv[argc - 2]);
+ qemu_port = g_strdup(argv[argc - 1]);
+ sock = connect_to_qemu(qemu_host, qemu_port);
+ if (sock == -1) {
+ fprintf(stderr, "error opening socket, exiting.\n");
+ exit(5);
+ }
+
+ socket_to_send = g_byte_array_new();
+ vcard_emul_init(command_line_options);
+ loop = g_main_loop_new(NULL, TRUE);
+
+ printf("> ");
+ fflush(stdout);
+
+#ifdef _WIN32
+ channel_stdin = g_io_channel_win32_new_fd(STDIN_FILENO);
+#else
+ channel_stdin = g_io_channel_unix_new(STDIN_FILENO);
+#endif
+ g_io_add_watch(channel_stdin, G_IO_IN, do_command, NULL);
+#ifdef _WIN32
+ channel_socket = g_io_channel_win32_new_socket(sock);
+#else
+ channel_socket = g_io_channel_unix_new(sock);
+#endif
+ g_io_channel_set_encoding(channel_socket, NULL, NULL);
+ /* we buffer ourself for thread safety reasons */
+ g_io_channel_set_buffered(channel_socket, FALSE);
+
+ /* Send init message, Host responds (and then we send reader attachments) */
+ VSCMsgInit init = {
+ .version = htonl(VSCARD_VERSION),
+ .magic = VSCARD_MAGIC,
+ .capabilities = {0}
+ };
+ send_msg(VSC_Init, 0, &init, sizeof(init));
+
+ g_main_loop_run(loop);
+ g_main_loop_unref(loop);
+
+ g_io_channel_unref(channel_stdin);
+ g_io_channel_unref(channel_socket);
+ g_byte_array_free(socket_to_send, TRUE);
+
+ closesocket(sock);
+ return 0;
+}