From patchwork Mon Mar 21 22:07:21 2011 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Alon Levy X-Patchwork-Id: 87845 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Received: from lists.gnu.org (lists.gnu.org [199.232.76.165]) (using TLSv1 with cipher DHE-RSA-AES256-SHA (256/256 bits)) (Client did not present a certificate) by ozlabs.org (Postfix) with ESMTPS id 7FF57B6F73 for ; Tue, 22 Mar 2011 10:12:29 +1100 (EST) Received: from localhost ([127.0.0.1]:58675 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.43) id 1Q1nYw-0006DJ-R8 for incoming@patchwork.ozlabs.org; Mon, 21 Mar 2011 18:26:58 -0400 Received: from [140.186.70.92] (port=58808 helo=eggs.gnu.org) by lists.gnu.org with esmtp (Exim 4.43) id 1Q1nH9-0006KA-Db for qemu-devel@nongnu.org; Mon, 21 Mar 2011 18:08:45 -0400 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1Q1nH0-0000Q6-Lj for qemu-devel@nongnu.org; Mon, 21 Mar 2011 18:08:35 -0400 Received: from mx1.redhat.com ([209.132.183.28]:32570) by eggs.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1Q1nGz-0000Ps-Hy for qemu-devel@nongnu.org; Mon, 21 Mar 2011 18:08:26 -0400 Received: from int-mx10.intmail.prod.int.phx2.redhat.com (int-mx10.intmail.prod.int.phx2.redhat.com [10.5.11.23]) by mx1.redhat.com (8.14.4/8.14.4) with ESMTP id p2LM8Onq011937 (version=TLSv1/SSLv3 cipher=DHE-RSA-AES256-SHA bits=256 verify=OK) for ; Mon, 21 Mar 2011 18:08:24 -0400 Received: from playa.redhat.com (vpn-200-15.tlv.redhat.com [10.35.200.15]) by int-mx10.intmail.prod.int.phx2.redhat.com (8.14.4/8.14.4) with ESMTP id p2LM80VF027322 for ; Mon, 21 Mar 2011 18:08:18 -0400 From: Alon Levy To: qemu-devel@nongnu.org Date: Tue, 22 Mar 2011 00:07:21 +0200 Message-Id: <1300745246-4701-7-git-send-email-alevy@redhat.com> In-Reply-To: <1300745246-4701-1-git-send-email-alevy@redhat.com> References: <1300745246-4701-1-git-send-email-alevy@redhat.com> X-Scanned-By: MIMEDefang 2.68 on 10.5.11.23 X-detected-operating-system: by eggs.gnu.org: Genre and OS details not recognized. X-Received-From: 209.132.183.28 Subject: [Qemu-devel] [PATCH v22 06/11] libcacard: initial commit X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.5 Precedence: list List-Id: qemu-devel.nongnu.org List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Sender: qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org Errors-To: qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org From: Robert Relyea libcacard emulates a Common Access Card (CAC) which is a standard for smartcards. It is used by the emulated ccid card introduced in a following patch. Docs are available in docs/libcacard.txt Signed-off-by: Alon Levy --- changes from v21->v22: * fix configure to not link libcacard if nss not found (reported by Stefan Hajnoczi) * fix vscclient linkage with simpletrace backend (reported by Stefan Hajnoczi) * card_7816.c: add missing break in ERROR_DATA_NOT_FOUND (reported by William van de Velde) changes from v20->v21: (Jes Sorenson review) * use qemu infrastructure: qemu-thread, qemu-common (qemu_malloc and qemu_free), error_report * assert instead of ASSERT * cosmetic fixes * use strpbrk and isspace * add --disable-nss --enable-nss here, instead of in the final patch. * split vscclient, passthru and docs to following patches. changes from v19->v20: * checkpatch.pl changes from v15->v16: Build: * don't erase self with distclean * fix make clean after make distclean * Makefile: make vscclient link quiet Behavioral: * vcard_emul_nss: load coolkey in more situations * vscclient: * use hton,ntoh * send init on connect, only start vevent thread on response * read payload after header check, before type switch * remove Reconnect * update for vscard_common changes, empty Flush implementation Style/Whitespace: * fix wrong variable usage * remove unused variable * use only C style comments * add copyright header * fix tabulation Signed-off-by: Alon Levy --- Makefile | 6 +- Makefile.objs | 5 + Makefile.target | 2 + configure | 49 ++ libcacard/Makefile | 17 + libcacard/cac.c | 406 +++++++++++++++ libcacard/cac.h | 23 + libcacard/card_7816.c | 764 ++++++++++++++++++++++++++++ libcacard/card_7816.h | 62 +++ libcacard/card_7816t.h | 165 ++++++ libcacard/event.c | 108 ++++ libcacard/eventt.h | 29 ++ libcacard/link_test.c | 21 + libcacard/vcard.c | 341 +++++++++++++ libcacard/vcard.h | 86 ++++ libcacard/vcard_emul.h | 65 +++ libcacard/vcard_emul_nss.c | 1159 +++++++++++++++++++++++++++++++++++++++++++ libcacard/vcard_emul_type.c | 57 +++ libcacard/vcard_emul_type.h | 32 ++ libcacard/vcardt.h | 64 +++ libcacard/vevent.h | 27 + libcacard/vreader.c | 519 +++++++++++++++++++ libcacard/vreader.h | 55 ++ libcacard/vreadert.h | 24 + 24 files changed, 4084 insertions(+), 2 deletions(-) create mode 100644 libcacard/Makefile create mode 100644 libcacard/cac.c create mode 100644 libcacard/cac.h create mode 100644 libcacard/card_7816.c create mode 100644 libcacard/card_7816.h create mode 100644 libcacard/card_7816t.h create mode 100644 libcacard/event.c create mode 100644 libcacard/eventt.h create mode 100644 libcacard/link_test.c create mode 100644 libcacard/vcard.c create mode 100644 libcacard/vcard.h create mode 100644 libcacard/vcard_emul.h create mode 100644 libcacard/vcard_emul_nss.c create mode 100644 libcacard/vcard_emul_type.c create mode 100644 libcacard/vcard_emul_type.h create mode 100644 libcacard/vcardt.h create mode 100644 libcacard/vevent.h create mode 100644 libcacard/vreader.c create mode 100644 libcacard/vreader.h create mode 100644 libcacard/vreadert.h diff --git a/Makefile b/Makefile index 209e14d..4462df0 100644 --- a/Makefile +++ b/Makefile @@ -141,6 +141,8 @@ check-qlist: check-qlist.o qlist.o qint.o $(CHECK_PROG_DEPS) check-qfloat: check-qfloat.o qfloat.o $(CHECK_PROG_DEPS) check-qjson: check-qjson.o qfloat.o qint.o qdict.o qstring.o qlist.o qbool.o qjson.o json-streamer.o json-lexer.o json-parser.o $(CHECK_PROG_DEPS) +QEMULIBS=libhw32 libhw64 libuser libdis libdis-user + clean: # avoid old build problems by removing potentially incorrect old files rm -f config.mak op-i386.h opc-i386.h gen-op-i386.h op-arm.h opc-arm.h gen-op-arm.h @@ -152,7 +154,7 @@ clean: rm -f trace-dtrace.dtrace trace-dtrace.dtrace-timestamp rm -f trace-dtrace.h trace-dtrace.h-timestamp $(MAKE) -C tests clean - for d in $(ALL_SUBDIRS) libhw32 libhw64 libuser libdis libdis-user; do \ + for d in $(ALL_SUBDIRS) $(QEMULIBS) libcacard; do \ if test -d $$d; then $(MAKE) -C $$d $@ || exit 1; fi; \ rm -f $$d/qemu-options.def; \ done @@ -163,7 +165,7 @@ distclean: clean rm -f roms/seabios/config.mak roms/vgabios/config.mak rm -f qemu-doc.info qemu-doc.aux qemu-doc.cp qemu-doc.dvi qemu-doc.fn qemu-doc.info qemu-doc.ky qemu-doc.log qemu-doc.pdf qemu-doc.pg qemu-doc.toc qemu-doc.tp qemu-doc.vr rm -f qemu-tech.info qemu-tech.aux qemu-tech.cp qemu-tech.dvi qemu-tech.fn qemu-tech.info qemu-tech.ky qemu-tech.log qemu-tech.pdf qemu-tech.pg qemu-tech.toc qemu-tech.tp qemu-tech.vr - for d in $(TARGET_DIRS) libhw32 libhw64 libuser libdis libdis-user; do \ + for d in $(TARGET_DIRS) $(QEMULIBS); do \ rm -rf $$d || exit 1 ; \ done diff --git a/Makefile.objs b/Makefile.objs index 4a4bf0a..f703781 100644 --- a/Makefile.objs +++ b/Makefile.objs @@ -351,6 +351,11 @@ user-obj-y += qemu-timer-common.o endif endif +###################################################################### +# smartcard + +libcacard-y = cac.o event.o vcard.o vreader.o vcard_emul_nss.o vcard_emul_type.o card_7816.o + vl.o: QEMU_CFLAGS+=$(GPROF_CFLAGS) vl.o: QEMU_CFLAGS+=$(SDL_CFLAGS) diff --git a/Makefile.target b/Makefile.target index 62b102a..7f163e3 100644 --- a/Makefile.target +++ b/Makefile.target @@ -353,6 +353,8 @@ obj-y += $(addprefix $(HWDIR)/, $(hw-obj-y)) endif # CONFIG_SOFTMMU +obj-y += $(addprefix ../libcacard/, $(libcacard-$(CONFIG_SMARTCARD_NSS))) + obj-y += $(addprefix ../, $(trace-obj-y)) obj-$(CONFIG_GDBSTUB_XML) += gdbstub-xml.o diff --git a/configure b/configure index 3b0174e..8159b3c 100755 --- a/configure +++ b/configure @@ -175,6 +175,7 @@ trace_file="trace" spice="" rbd="" smartcard="" +smartcard_nss="" # parse CC options first for opt do @@ -724,6 +725,10 @@ for opt do ;; --enable-smartcard) smartcard="yes" ;; + --disable-smartcard-nss) smartcard_nss="no" + ;; + --enable-smartcard-nss) smartcard_nss="yes" + ;; *) echo "ERROR: unknown option $opt"; show_help="yes" ;; esac @@ -921,6 +926,8 @@ echo " --enable-spice enable spice" echo " --enable-rbd enable building the rados block device (rbd)" echo " --disable-smartcard disable smartcard support" echo " --enable-smartcard enable smartcard support" +echo " --disable-smartcard-nss disable smartcard nss support" +echo " --enable-smartcard-nss enable smartcard nss support" echo "" echo "NOTE: The object files are built at the place where configure is launched" exit 1 @@ -2304,6 +2311,31 @@ EOF fi fi +# check for libcacard for smartcard support +if test "$smartcard" != "no" ; then + smartcard="yes" + smartcard_cflags="" + # TODO - what's the minimal nss version we support? + if test "$smartcard_nss" != "no"; then + if $pkg_config --atleast-version=3.12.8 nss >/dev/null 2>&1 ; then + smartcard_nss="yes" + smartcard_cflags="-I\$(SRC_PATH)/libcacard" + libcacard_libs=$($pkg_config --libs nss 2>/dev/null) + libcacard_cflags=$($pkg_config --cflags nss 2>/dev/null) + QEMU_CFLAGS="$QEMU_CFLAGS $smartcard_cflags $libcacard_cflags" + LIBS="$libcacard_libs $LIBS" + else + if test "$smartcard_nss" == "yes"; then + feature_not_found "nss" + fi + smartcard_nss="no" + fi + fi +fi +if test "$smartcard" == "no" ; then + smartcard_nss="no" +fi + ########################################## ########################################## @@ -2537,6 +2569,7 @@ echo "Trace output file $trace_file-" echo "spice support $spice" echo "rbd support $rbd" echo "xfsctl support $xfs" +echo "nss used $smartcard_nss" if test $sdl_too_old = "yes"; then echo "-> Your SDL version is too old - please upgrade to have SDL support" @@ -2820,6 +2853,10 @@ if test "$smartcard" = "yes" ; then echo "CONFIG_SMARTCARD=y" >> $config_host_mak fi +if test "$smartcard_nss" = "yes" ; then + echo "CONFIG_SMARTCARD_NSS=y" >> $config_host_mak +fi + # XXX: suppress that if [ "$bsd" = "yes" ] ; then echo "CONFIG_BSD=y" >> $config_host_mak @@ -3166,6 +3203,11 @@ fi if test "$target_darwin_user" = "yes" ; then echo "CONFIG_DARWIN_USER=y" >> $config_target_mak fi +if test "$smartcard_nss" = "yes" ; then + echo "subdir-$target: subdir-libcacard" >> $config_host_mak + echo "libcacard_libs=$libcacard_libs" >> $config_host_mak + echo "libcacard_cflags=$libcacard_cflags" >> $config_host_mak +fi list="" if test ! -z "$gdb_xml_files" ; then for x in $gdb_xml_files; do @@ -3379,6 +3421,13 @@ for hwlib in 32 64; do echo "QEMU_CFLAGS+=-DTARGET_PHYS_ADDR_BITS=$hwlib" > $d/config.mak done +if [ $source_path != `pwd` ]; then + # out of tree build + mkdir -p libcacard + rm -f libcacard/Makefile + ln -s $source_path/libcacard/Makefile libcacard/Makefile +fi + d=libuser mkdir -p $d symlink $source_path/Makefile.user $d/Makefile diff --git a/libcacard/Makefile b/libcacard/Makefile new file mode 100644 index 0000000..410fa1e --- /dev/null +++ b/libcacard/Makefile @@ -0,0 +1,17 @@ +-include ../config-host.mak +-include $(SRC_PATH)/Makefile.objs +-include $(SRC_PATH)/rules.mak + +$(call set-vpath, $(SRC_PATH):$(SRC_PATH)/libcacard) + +ifeq ($(CONFIG_WIN32),y) +QEMU_THREAD=qemu-thread-win32.o +else +QEMU_THREAD=qemu-thread-posix.o +endif + +QEMU_OBJS=$(QEMU_THREAD) $(oslib-obj-y) $(trace-obj-y) qemu-malloc.o qemu-timer-common.o + +clean: + rm -f *.o */*.o *.d */*.d *.a */*.a *~ */*~ + diff --git a/libcacard/cac.c b/libcacard/cac.c new file mode 100644 index 0000000..7a910d8 --- /dev/null +++ b/libcacard/cac.c @@ -0,0 +1,406 @@ +/* + * 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 +#include + +#include "qemu-common.h" + +#include "cac.h" +#include "vcard.h" +#include "vcard_emul.h" +#include "card_7816.h" + +#define CAC_GET_PROPERTIES 0x56 +#define CAC_GET_ACR 0x4c +#define CAC_READ_BUFFER 0x52 +#define CAC_UPDATE_BUFFER 0x58 +#define CAC_SIGN_DECRYPT 0x42 +#define CAC_GET_CERTIFICATE 0x36 + +/* private data for PKI applets */ +typedef struct CACPKIAppletDataStruct { + unsigned char *cert; + int cert_len; + unsigned char *cert_buffer; + int cert_buffer_len; + unsigned char *sign_buffer; + int sign_buffer_len; + VCardKey *key; +} CACPKIAppletData; + +/* + * CAC applet private data + */ +struct VCardAppletPrivateStruct { + union { + CACPKIAppletData pki_data; + void *reserved; + } u; +}; + +/* + * handle all the APDU's that are common to all CAC applets + */ +static VCardStatus +cac_common_process_apdu(VCard *card, VCardAPDU *apdu, VCardResponse **response) +{ + int ef; + + switch (apdu->a_ins) { + case VCARD7816_INS_SELECT_FILE: + if (apdu->a_p1 != 0x02) { + /* let the 7816 code handle applet switches */ + return VCARD_NEXT; + } + /* handle file id setting */ + if (apdu->a_Lc != 2) { + *response = vcard_make_response( + VCARD7816_STATUS_ERROR_DATA_INVALID); + return VCARD_DONE; + } + /* CAC 1.0 only supports ef = 0 */ + ef = apdu->a_body[0] | (apdu->a_body[1] << 8); + if (ef != 0) { + *response = vcard_make_response( + VCARD7816_STATUS_ERROR_FILE_NOT_FOUND); + return VCARD_DONE; + } + *response = vcard_make_response(VCARD7816_STATUS_SUCCESS); + return VCARD_DONE; + case VCARD7816_INS_GET_RESPONSE: + case VCARD7816_INS_VERIFY: + /* let the 7816 code handle these */ + return VCARD_NEXT; + case CAC_GET_PROPERTIES: + case CAC_GET_ACR: + /* skip these for now, this will probably be needed */ + *response = vcard_make_response(VCARD7816_STATUS_ERROR_P1_P2_INCORRECT); + return VCARD_DONE; + } + *response = vcard_make_response( + VCARD7816_STATUS_ERROR_COMMAND_NOT_SUPPORTED); + return VCARD_DONE; +} + +/* + * reset the inter call state between applet selects + */ +static VCardStatus +cac_applet_pki_reset(VCard *card, int channel) +{ + VCardAppletPrivate *applet_private = NULL; + CACPKIAppletData *pki_applet = NULL; + applet_private = vcard_get_current_applet_private(card, channel); + assert(applet_private); + pki_applet = &(applet_private->u.pki_data); + + pki_applet->cert_buffer = NULL; + if (pki_applet->sign_buffer) { + qemu_free(pki_applet->sign_buffer); + pki_applet->sign_buffer = NULL; + } + pki_applet->cert_buffer_len = 0; + pki_applet->sign_buffer_len = 0; + return VCARD_DONE; +} + +static VCardStatus +cac_applet_pki_process_apdu(VCard *card, VCardAPDU *apdu, + VCardResponse **response) +{ + CACPKIAppletData *pki_applet = NULL; + VCardAppletPrivate *applet_private = NULL; + int size, next; + unsigned char *sign_buffer; + vcard_7816_status_t status; + + applet_private = vcard_get_current_applet_private(card, apdu->a_channel); + assert(applet_private); + pki_applet = &(applet_private->u.pki_data); + + switch (apdu->a_ins) { + case CAC_UPDATE_BUFFER: + *response = vcard_make_response( + VCARD7816_STATUS_ERROR_CONDITION_NOT_SATISFIED); + return VCARD_DONE; + case CAC_GET_CERTIFICATE: + if ((apdu->a_p2 != 0) || (apdu->a_p1 != 0)) { + *response = vcard_make_response( + VCARD7816_STATUS_ERROR_P1_P2_INCORRECT); + break; + } + assert(pki_applet->cert != NULL); + size = apdu->a_Le; + if (pki_applet->cert_buffer == NULL) { + pki_applet->cert_buffer = pki_applet->cert; + pki_applet->cert_buffer_len = pki_applet->cert_len; + } + size = MIN(size, pki_applet->cert_buffer_len); + next = MIN(255, pki_applet->cert_buffer_len - size); + *response = vcard_response_new_bytes( + card, pki_applet->cert_buffer, size, + apdu->a_Le, next ? + VCARD7816_SW1_WARNING_CHANGE : + VCARD7816_SW1_SUCCESS, + next); + pki_applet->cert_buffer += size; + pki_applet->cert_buffer_len -= size; + if ((*response == NULL) || (next == 0)) { + pki_applet->cert_buffer = NULL; + } + if (*response == NULL) { + *response = vcard_make_response( + VCARD7816_STATUS_EXC_ERROR_MEMORY_FAILURE); + } + return VCARD_DONE; + case CAC_SIGN_DECRYPT: + if (apdu->a_p2 != 0) { + *response = vcard_make_response( + VCARD7816_STATUS_ERROR_P1_P2_INCORRECT); + break; + } + size = apdu->a_Lc; + + sign_buffer = realloc(pki_applet->sign_buffer, + pki_applet->sign_buffer_len+size); + if (sign_buffer == NULL) { + qemu_free(pki_applet->sign_buffer); + pki_applet->sign_buffer = NULL; + pki_applet->sign_buffer_len = 0; + *response = vcard_make_response( + VCARD7816_STATUS_EXC_ERROR_MEMORY_FAILURE); + return VCARD_DONE; + } + memcpy(sign_buffer+pki_applet->sign_buffer_len, apdu->a_body, size); + size += pki_applet->sign_buffer_len; + switch (apdu->a_p1) { + case 0x80: + /* p1 == 0x80 means we haven't yet sent the whole buffer, wait for + * the rest */ + pki_applet->sign_buffer = sign_buffer; + pki_applet->sign_buffer_len = size; + *response = vcard_make_response(VCARD7816_STATUS_SUCCESS); + return VCARD_DONE; + case 0x00: + /* we now have the whole buffer, do the operation, result will be + * in the sign_buffer */ + status = vcard_emul_rsa_op(card, pki_applet->key, + sign_buffer, size); + if (status != VCARD7816_STATUS_SUCCESS) { + *response = vcard_make_response(status); + break; + } + *response = vcard_response_new(card, sign_buffer, size, apdu->a_Le, + VCARD7816_STATUS_SUCCESS); + if (*response == NULL) { + *response = vcard_make_response( + VCARD7816_STATUS_EXC_ERROR_MEMORY_FAILURE); + } + break; + default: + *response = vcard_make_response( + VCARD7816_STATUS_ERROR_P1_P2_INCORRECT); + break; + } + qemu_free(sign_buffer); + pki_applet->sign_buffer = NULL; + pki_applet->sign_buffer_len = 0; + return VCARD_DONE; + case CAC_READ_BUFFER: + /* new CAC call, go ahead and use the old version for now */ + /* TODO: implement */ + *response = vcard_make_response( + VCARD7816_STATUS_ERROR_COMMAND_NOT_SUPPORTED); + return VCARD_DONE; + } + return cac_common_process_apdu(card, apdu, response); +} + + +static VCardStatus +cac_applet_id_process_apdu(VCard *card, VCardAPDU *apdu, + VCardResponse **response) +{ + switch (apdu->a_ins) { + case CAC_UPDATE_BUFFER: + *response = vcard_make_response( + VCARD7816_STATUS_ERROR_CONDITION_NOT_SATISFIED); + return VCARD_DONE; + case CAC_READ_BUFFER: + /* new CAC call, go ahead and use the old version for now */ + /* TODO: implement */ + *response = vcard_make_response( + VCARD7816_STATUS_ERROR_COMMAND_NOT_SUPPORTED); + return VCARD_DONE; + } + return cac_common_process_apdu(card, apdu, response); +} + + +/* + * TODO: if we ever want to support general CAC middleware, we will need to + * implement the various containers. + */ +static VCardStatus +cac_applet_container_process_apdu(VCard *card, VCardAPDU *apdu, + VCardResponse **response) +{ + switch (apdu->a_ins) { + case CAC_READ_BUFFER: + case CAC_UPDATE_BUFFER: + *response = vcard_make_response( + VCARD7816_STATUS_ERROR_COMMAND_NOT_SUPPORTED); + return VCARD_DONE; + default: + break; + } + return cac_common_process_apdu(card, apdu, response); +} + +/* + * utilities for creating and destroying the private applet data + */ +static void +cac_delete_pki_applet_private(VCardAppletPrivate *applet_private) +{ + CACPKIAppletData *pki_applet_data = NULL; + if (pki_applet_data == NULL) { + return; + } + pki_applet_data = &(applet_private->u.pki_data); + if (pki_applet_data->cert != NULL) { + qemu_free(pki_applet_data->cert); + } + if (pki_applet_data->sign_buffer != NULL) { + qemu_free(pki_applet_data->sign_buffer); + } + if (pki_applet_data->key != NULL) { + vcard_emul_delete_key(pki_applet_data->key); + } + qemu_free(applet_private); +} + +static VCardAppletPrivate * +cac_new_pki_applet_private(const unsigned char *cert, + int cert_len, VCardKey *key) +{ + CACPKIAppletData *pki_applet_data = NULL; + VCardAppletPrivate *applet_private = NULL; + applet_private = (VCardAppletPrivate *)qemu_malloc(sizeof(VCardAppletPrivate)); + + pki_applet_data = &(applet_private->u.pki_data); + pki_applet_data->cert_buffer = NULL; + pki_applet_data->cert_buffer_len = 0; + pki_applet_data->sign_buffer = NULL; + pki_applet_data->sign_buffer_len = 0; + pki_applet_data->key = NULL; + pki_applet_data->cert = (unsigned char *)qemu_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 = NULL; + VCardApplet *applet = NULL; + unsigned char pki_aid[] = { 0xa0, 0x00, 0x00, 0x00, 0x79, 0x01, 0x00 }; + int pki_aid_len = sizeof(pki_aid); + + pki_aid[pki_aid_len-1] = i; + + applet_private = cac_new_pki_applet_private(cert, cert_len, key); + if (applet_private == NULL) { + goto failure; + } + applet = vcard_new_applet(cac_applet_pki_process_apdu, cac_applet_pki_reset, + pki_aid, pki_aid_len); + if (applet == NULL) { + goto failure; + } + vcard_set_applet_private(applet, applet_private, + cac_delete_pki_applet_private); + applet_private = NULL; + + return applet; + +failure: + if (applet_private != NULL) { + cac_delete_pki_applet_private(applet_private); + } + return NULL; +} + + +static unsigned char cac_default_container_aid[] = { + 0xa0, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00 }; +static unsigned char cac_id_aid[] = { + 0xa0, 0x00, 0x00, 0x00, 0x79, 0x03, 0x00 }; +/* + * Initialize the cac card. This is the only public function in this file. All + * the rest are connected through function pointers. + */ +VCardStatus +cac_card_init(VReader *reader, VCard *card, + const char *params, + unsigned char * const *cert, + int cert_len[], + VCardKey *key[] /* adopt the keys*/, + int cert_count) +{ + int i; + VCardApplet *applet; + + /* CAC Cards are VM Cards */ + vcard_set_type(card, VCARD_VM); + + /* create one PKI applet for each cert */ + for (i = 0; i < cert_count; i++) { + applet = cac_new_pki_applet(i, cert[i], cert_len[i], key[i]); + if (applet == NULL) { + goto failure; + } + vcard_add_applet(card, applet); + } + + /* create a default blank container applet */ + applet = vcard_new_applet(cac_applet_container_process_apdu, + NULL, cac_default_container_aid, + sizeof(cac_default_container_aid)); + if (applet == NULL) { + goto failure; + } + vcard_add_applet(card, applet); + + /* create a default blank container applet */ + applet = vcard_new_applet(cac_applet_id_process_apdu, + NULL, cac_id_aid, + sizeof(cac_id_aid)); + if (applet == NULL) { + goto failure; + } + vcard_add_applet(card, applet); + return VCARD_DONE; + +failure: + return VCARD_FAIL; +} + diff --git a/libcacard/cac.h b/libcacard/cac.h new file mode 100644 index 0000000..15a61be --- /dev/null +++ b/libcacard/cac.h @@ -0,0 +1,23 @@ +/* + * 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" +/* + * Initialize the cac card. This is the only public function in this file. All + * the rest are connected through function pointers. + */ +VCardStatus cac_card_init(VReader *reader, VCard *card, const char *params, + unsigned char * const *cert, int cert_len[], + VCardKey *key[] /* adopt the keys*/, + int cert_count); + +/* not yet implemented */ +VCardStatus cac_is_cac_card(VReader *reader); +#endif diff --git a/libcacard/card_7816.c b/libcacard/card_7816.c new file mode 100644 index 0000000..4c10cae --- /dev/null +++ b/libcacard/card_7816.c @@ -0,0 +1,764 @@ +/* + * 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 +#include +#include "qemu-common.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 = (VCardResponse *)qemu_malloc(sizeof(VCardResponse)); + new_response->b_data = qemu_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 Reponse buffer that only has a status. + */ +static VCardResponse * +vcard_response_new_status(vcard_7816_status_t status) +{ + VCardResponse *new_response; + + new_response = (VCardResponse *)qemu_malloc(sizeof(VCardResponse)); + new_response->b_data = &new_response->b_sw1; + new_response->b_len = 0; + new_response->b_total_len = 2; + new_response->b_type = VCARD_MALLOC_STRUCT; + vcard_response_set_status(new_response, status); + return new_response; +} + +/* + * same as above, but specify the status as separate bytes + */ +VCardResponse * +vcard_response_new_status_bytes(unsigned char sw1, unsigned char sw2) +{ + VCardResponse *new_response; + + new_response = (VCardResponse *)qemu_malloc(sizeof(VCardResponse)); + new_response->b_data = &new_response->b_sw1; + new_response->b_len = 0; + new_response->b_total_len = 2; + new_response->b_type = VCARD_MALLOC_STRUCT; + vcard_response_set_status_bytes(new_response, sw1, sw2); + return new_response; +} + + +/* + * free the response buffer. The Buffer has a type to handle the buffer + * allocated in other ways than through malloc. + */ +void +vcard_response_delete(VCardResponse *response) +{ + if (response == NULL) { + return; + } + switch (response->b_type) { + case VCARD_MALLOC: + /* everything was malloc'ed */ + if (response->b_data) { + qemu_free(response->b_data); + } + qemu_free(response); + break; + case VCARD_MALLOC_DATA: + /* only the data buffer was malloc'ed */ + if (response->b_data) { + qemu_free(response->b_data); + } + break; + case VCARD_MALLOC_STRUCT: + /* only the structure was malloc'ed */ + qemu_free(response); + break; + case VCARD_STATIC: + break; + } +} + +/* + * decode the class bit and set our generic type field, channel, and + * secure messaging values. + */ +static vcard_7816_status_t +vcard_apdu_set_class(VCardAPDU *apdu) { + apdu->a_channel = 0; + apdu->a_secure_messaging = 0; + apdu->a_type = apdu->a_cla & 0xf0; + apdu->a_gen_type = VCARD_7816_ISO; + + /* parse the class tables 8 & 9 of the 7816-4 Part 4 spec */ + switch (apdu->a_type) { + /* we only support the basic types */ + case 0x00: + case 0x80: + case 0x90: + case 0xa0: + apdu->a_channel = apdu->a_cla & 3; + apdu->a_secure_messaging = apdu->a_cla & 0xe; + break; + case 0xb0: + case 0xc0: + break; + + case 0x10: + case 0x20: + case 0x30: + case 0x40: + case 0x50: + case 0x60: + case 0x70: + /* Reserved for future use */ + apdu->a_gen_type = VCARD_7816_RFU; + break; + case 0xd0: + case 0xe0: + case 0xf0: + default: + apdu->a_gen_type = + (apdu->a_cla == 0xff) ? VCARD_7816_PTS : VCARD_7816_PROPIETARY; + break; + } + return VCARD7816_STATUS_SUCCESS; +} + +/* + * set the Le and Lc fiels according to table 5 of the + * 7816-4 part 4 spec + */ +static vcard_7816_status_t +vcard_apdu_set_length(VCardAPDU *apdu) +{ + int L, Le; + + /* process according to table 5 of the 7816-4 Part 4 spec. + * variable names match the variables in the spec */ + L = apdu->a_len-4; /* fixed APDU header */ + apdu->a_Lc = 0; + apdu->a_Le = 0; + apdu->a_body = NULL; + switch (L) { + case 0: + /* 1 minimal apdu */ + return VCARD7816_STATUS_SUCCESS; + case 1: + /* 2S only return values apdu */ + /* zero maps to 256 here */ + apdu->a_Le = apdu->a_header->ah_Le ? + apdu->a_header->ah_Le : 256; + return VCARD7816_STATUS_SUCCESS; + default: + /* if the ah_Le byte is zero and we have more than + * 1 byte in the header, then we must be using extended Le and Lc. + * process the extended now. */ + if (apdu->a_header->ah_Le == 0) { + if (L < 3) { + /* coding error, need at least 3 bytes */ + return VCARD7816_STATUS_ERROR_WRONG_LENGTH; + } + /* calculate the first extended value. Could be either Le or Lc */ + Le = (apdu->a_header->ah_body[0] << 8) + || apdu->a_header->ah_body[1]; + if (L == 3) { + /* 2E extended, return data only */ + /* zero maps to 65536 */ + apdu->a_Le = Le ? Le : 65536; + return VCARD7816_STATUS_SUCCESS; + } + if (Le == 0) { + /* reserved for future use, probably for next time we need + * to extend the lengths */ + return VCARD7816_STATUS_ERROR_WRONG_LENGTH; + } + /* we know that the first extended value is Lc now */ + apdu->a_Lc = Le; + apdu->a_body = &apdu->a_header->ah_body[2]; + if (L == Le+3) { + /* 3E extended, only body parameters */ + return VCARD7816_STATUS_SUCCESS; + } + if (L == Le+5) { + /* 4E extended, parameters and return data */ + Le = (apdu->a_data[apdu->a_len-2] << 8) + || apdu->a_data[apdu->a_len-1]; + apdu->a_Le = Le ? Le : 65536; + return VCARD7816_STATUS_SUCCESS; + } + return VCARD7816_STATUS_ERROR_WRONG_LENGTH; + } + /* not extended */ + apdu->a_Lc = apdu->a_header->ah_Le; + apdu->a_body = &apdu->a_header->ah_body[0]; + if (L == apdu->a_Lc + 1) { + /* 3S only body parameters */ + return VCARD7816_STATUS_SUCCESS; + } + if (L == apdu->a_Lc + 2) { + /* 4S parameters and return data */ + Le = apdu->a_data[apdu->a_len-1]; + apdu->a_Le = Le ? Le : 256; + return VCARD7816_STATUS_SUCCESS; + } + break; + } + return VCARD7816_STATUS_ERROR_WRONG_LENGTH; +} + +/* + * create a new APDU from a raw set of bytes. This will decode all the + * above fields. users of VCARDAPDU's can then depend on the already decoded + * values. + */ +VCardAPDU * +vcard_apdu_new(unsigned char *raw_apdu, int len, vcard_7816_status_t *status) +{ + VCardAPDU *new_apdu; + + *status = VCARD7816_STATUS_EXC_ERROR_MEMORY_FAILURE; + if (len < 4) { + *status = VCARD7816_STATUS_ERROR_WRONG_LENGTH; + return NULL; + } + + new_apdu = (VCardAPDU *)qemu_malloc(sizeof(VCardAPDU)); + new_apdu->a_data = qemu_malloc(len); + memcpy(new_apdu->a_data, raw_apdu, len); + new_apdu->a_len = len; + *status = vcard_apdu_set_class(new_apdu); + if (*status != VCARD7816_STATUS_SUCCESS) { + qemu_free(new_apdu); + return NULL; + } + *status = vcard_apdu_set_length(new_apdu); + if (*status != VCARD7816_STATUS_SUCCESS) { + qemu_free(new_apdu); + new_apdu = NULL; + } + return new_apdu; +} + +void +vcard_apdu_delete(VCardAPDU *apdu) +{ + if (apdu == NULL) { + return; + } + if (apdu->a_data) { + qemu_free(apdu->a_data); + } + qemu_free(apdu); +} + + +/* + * declare response buffers for all the 7816 defined error codes + */ +VCARD_RESPONSE_NEW_STATIC_STATUS(VCARD7816_STATUS_SUCCESS) +VCARD_RESPONSE_NEW_STATIC_STATUS(VCARD7816_STATUS_WARNING) +VCARD_RESPONSE_NEW_STATIC_STATUS(VCARD7816_STATUS_WARNING_RET_CORUPT) +VCARD_RESPONSE_NEW_STATIC_STATUS(VCARD7816_STATUS_WARNING_BUF_END_BEFORE_LE) +VCARD_RESPONSE_NEW_STATIC_STATUS(VCARD7816_STATUS_WARNING_INVALID_FILE_SELECTED) +VCARD_RESPONSE_NEW_STATIC_STATUS(VCARD7816_STATUS_WARNING_FCI_FORMAT_INVALID) +VCARD_RESPONSE_NEW_STATIC_STATUS(VCARD7816_STATUS_WARNING_CHANGE) +VCARD_RESPONSE_NEW_STATIC_STATUS(VCARD7816_STATUS_WARNING_FILE_FILLED) +VCARD_RESPONSE_NEW_STATIC_STATUS(VCARD7816_STATUS_EXC_ERROR) +VCARD_RESPONSE_NEW_STATIC_STATUS(VCARD7816_STATUS_EXC_ERROR_CHANGE) +VCARD_RESPONSE_NEW_STATIC_STATUS(VCARD7816_STATUS_EXC_ERROR_MEMORY_FAILURE) +VCARD_RESPONSE_NEW_STATIC_STATUS(VCARD7816_STATUS_ERROR_WRONG_LENGTH) +VCARD_RESPONSE_NEW_STATIC_STATUS(VCARD7816_STATUS_ERROR_CLA_NOT_SUPPORTED) +VCARD_RESPONSE_NEW_STATIC_STATUS(VCARD7816_STATUS_ERROR_CHANNEL_NOT_SUPPORTED) +VCARD_RESPONSE_NEW_STATIC_STATUS(VCARD7816_STATUS_ERROR_SECURE_NOT_SUPPORTED) +VCARD_RESPONSE_NEW_STATIC_STATUS(VCARD7816_STATUS_ERROR_COMMAND_NOT_SUPPORTED) +VCARD_RESPONSE_NEW_STATIC_STATUS( + VCARD7816_STATUS_ERROR_COMMAND_INCOMPATIBLE_WITH_FILE) +VCARD_RESPONSE_NEW_STATIC_STATUS(VCARD7816_STATUS_ERROR_SECURITY_NOT_SATISFIED) +VCARD_RESPONSE_NEW_STATIC_STATUS(VCARD7816_STATUS_ERROR_AUTHENTICATION_BLOCKED) +VCARD_RESPONSE_NEW_STATIC_STATUS(VCARD7816_STATUS_ERROR_DATA_INVALID) +VCARD_RESPONSE_NEW_STATIC_STATUS(VCARD7816_STATUS_ERROR_CONDITION_NOT_SATISFIED) +VCARD_RESPONSE_NEW_STATIC_STATUS(VCARD7816_STATUS_ERROR_DATA_NO_EF) +VCARD_RESPONSE_NEW_STATIC_STATUS(VCARD7816_STATUS_ERROR_SM_OBJECT_MISSING) +VCARD_RESPONSE_NEW_STATIC_STATUS(VCARD7816_STATUS_ERROR_SM_OBJECT_INCORRECT) +VCARD_RESPONSE_NEW_STATIC_STATUS(VCARD7816_STATUS_ERROR_WRONG_PARAMETERS) +VCARD_RESPONSE_NEW_STATIC_STATUS( + VCARD7816_STATUS_ERROR_WRONG_PARAMETERS_IN_DATA) +VCARD_RESPONSE_NEW_STATIC_STATUS(VCARD7816_STATUS_ERROR_FUNCTION_NOT_SUPPORTED) +VCARD_RESPONSE_NEW_STATIC_STATUS(VCARD7816_STATUS_ERROR_FILE_NOT_FOUND) +VCARD_RESPONSE_NEW_STATIC_STATUS(VCARD7816_STATUS_ERROR_RECORD_NOT_FOUND) +VCARD_RESPONSE_NEW_STATIC_STATUS(VCARD7816_STATUS_ERROR_NO_SPACE_FOR_FILE) +VCARD_RESPONSE_NEW_STATIC_STATUS(VCARD7816_STATUS_ERROR_LC_TLV_INCONSISTENT) +VCARD_RESPONSE_NEW_STATIC_STATUS(VCARD7816_STATUS_ERROR_P1_P2_INCORRECT) +VCARD_RESPONSE_NEW_STATIC_STATUS(VCARD7816_STATUS_ERROR_LC_P1_P2_INCONSISTENT) +VCARD_RESPONSE_NEW_STATIC_STATUS(VCARD7816_STATUS_ERROR_DATA_NOT_FOUND) +VCARD_RESPONSE_NEW_STATIC_STATUS(VCARD7816_STATUS_ERROR_WRONG_PARAMETERS_2) +VCARD_RESPONSE_NEW_STATIC_STATUS(VCARD7816_STATUS_ERROR_INS_CODE_INVALID) +VCARD_RESPONSE_NEW_STATIC_STATUS(VCARD7816_STATUS_ERROR_CLA_INVALID) +VCARD_RESPONSE_NEW_STATIC_STATUS(VCARD7816_STATUS_ERROR_GENERAL) + +/* + * return a single response code. This function cannot fail. It will always + * return a response. + */ +VCardResponse * +vcard_make_response(vcard_7816_status_t status) +{ + VCardResponse *response = NULL; + + switch (status) { + /* known 7816 response codes */ + case VCARD7816_STATUS_SUCCESS: + return VCARD_RESPONSE_GET_STATIC( + VCARD7816_STATUS_SUCCESS); + case VCARD7816_STATUS_WARNING: + return VCARD_RESPONSE_GET_STATIC( + VCARD7816_STATUS_WARNING); + case VCARD7816_STATUS_WARNING_RET_CORUPT: + return VCARD_RESPONSE_GET_STATIC( + VCARD7816_STATUS_WARNING_RET_CORUPT); + case VCARD7816_STATUS_WARNING_BUF_END_BEFORE_LE: + return VCARD_RESPONSE_GET_STATIC( + VCARD7816_STATUS_WARNING_BUF_END_BEFORE_LE); + case VCARD7816_STATUS_WARNING_INVALID_FILE_SELECTED: + return VCARD_RESPONSE_GET_STATIC( + VCARD7816_STATUS_WARNING_INVALID_FILE_SELECTED); + case VCARD7816_STATUS_WARNING_FCI_FORMAT_INVALID: + return VCARD_RESPONSE_GET_STATIC( + VCARD7816_STATUS_WARNING_FCI_FORMAT_INVALID); + case VCARD7816_STATUS_WARNING_CHANGE: + return VCARD_RESPONSE_GET_STATIC( + VCARD7816_STATUS_WARNING_CHANGE); + case VCARD7816_STATUS_WARNING_FILE_FILLED: + return VCARD_RESPONSE_GET_STATIC( + VCARD7816_STATUS_WARNING_FILE_FILLED); + case VCARD7816_STATUS_EXC_ERROR: + return VCARD_RESPONSE_GET_STATIC( + VCARD7816_STATUS_EXC_ERROR); + case VCARD7816_STATUS_EXC_ERROR_CHANGE: + return VCARD_RESPONSE_GET_STATIC( + VCARD7816_STATUS_EXC_ERROR_CHANGE); + case VCARD7816_STATUS_EXC_ERROR_MEMORY_FAILURE: + return VCARD_RESPONSE_GET_STATIC( + VCARD7816_STATUS_EXC_ERROR_MEMORY_FAILURE); + case VCARD7816_STATUS_ERROR_WRONG_LENGTH: + return VCARD_RESPONSE_GET_STATIC( + VCARD7816_STATUS_ERROR_WRONG_LENGTH); + case VCARD7816_STATUS_ERROR_CLA_NOT_SUPPORTED: + return VCARD_RESPONSE_GET_STATIC( + VCARD7816_STATUS_ERROR_CLA_NOT_SUPPORTED); + case VCARD7816_STATUS_ERROR_CHANNEL_NOT_SUPPORTED: + return VCARD_RESPONSE_GET_STATIC( + VCARD7816_STATUS_ERROR_CHANNEL_NOT_SUPPORTED); + case VCARD7816_STATUS_ERROR_SECURE_NOT_SUPPORTED: + return VCARD_RESPONSE_GET_STATIC( + VCARD7816_STATUS_ERROR_SECURE_NOT_SUPPORTED); + case VCARD7816_STATUS_ERROR_COMMAND_NOT_SUPPORTED: + return VCARD_RESPONSE_GET_STATIC( + VCARD7816_STATUS_ERROR_COMMAND_NOT_SUPPORTED); + case VCARD7816_STATUS_ERROR_COMMAND_INCOMPATIBLE_WITH_FILE: + return VCARD_RESPONSE_GET_STATIC( + VCARD7816_STATUS_ERROR_COMMAND_INCOMPATIBLE_WITH_FILE); + case VCARD7816_STATUS_ERROR_SECURITY_NOT_SATISFIED: + return VCARD_RESPONSE_GET_STATIC( + VCARD7816_STATUS_ERROR_SECURITY_NOT_SATISFIED); + case VCARD7816_STATUS_ERROR_AUTHENTICATION_BLOCKED: + return VCARD_RESPONSE_GET_STATIC( + VCARD7816_STATUS_ERROR_AUTHENTICATION_BLOCKED); + case VCARD7816_STATUS_ERROR_DATA_INVALID: + return VCARD_RESPONSE_GET_STATIC( + VCARD7816_STATUS_ERROR_DATA_INVALID); + case VCARD7816_STATUS_ERROR_CONDITION_NOT_SATISFIED: + return VCARD_RESPONSE_GET_STATIC( + VCARD7816_STATUS_ERROR_CONDITION_NOT_SATISFIED); + case VCARD7816_STATUS_ERROR_DATA_NO_EF: + return VCARD_RESPONSE_GET_STATIC( + VCARD7816_STATUS_ERROR_DATA_NO_EF); + case VCARD7816_STATUS_ERROR_SM_OBJECT_MISSING: + return VCARD_RESPONSE_GET_STATIC( + VCARD7816_STATUS_ERROR_SM_OBJECT_MISSING); + case VCARD7816_STATUS_ERROR_SM_OBJECT_INCORRECT: + return VCARD_RESPONSE_GET_STATIC( + VCARD7816_STATUS_ERROR_SM_OBJECT_INCORRECT); + case VCARD7816_STATUS_ERROR_WRONG_PARAMETERS: + return VCARD_RESPONSE_GET_STATIC( + VCARD7816_STATUS_ERROR_WRONG_PARAMETERS); + case VCARD7816_STATUS_ERROR_WRONG_PARAMETERS_IN_DATA: + return VCARD_RESPONSE_GET_STATIC( + VCARD7816_STATUS_ERROR_WRONG_PARAMETERS_IN_DATA); + case VCARD7816_STATUS_ERROR_FUNCTION_NOT_SUPPORTED: + return VCARD_RESPONSE_GET_STATIC( + VCARD7816_STATUS_ERROR_FUNCTION_NOT_SUPPORTED); + case VCARD7816_STATUS_ERROR_FILE_NOT_FOUND: + return VCARD_RESPONSE_GET_STATIC( + VCARD7816_STATUS_ERROR_FILE_NOT_FOUND); + case VCARD7816_STATUS_ERROR_RECORD_NOT_FOUND: + return VCARD_RESPONSE_GET_STATIC( + VCARD7816_STATUS_ERROR_RECORD_NOT_FOUND); + case VCARD7816_STATUS_ERROR_NO_SPACE_FOR_FILE: + return VCARD_RESPONSE_GET_STATIC( + VCARD7816_STATUS_ERROR_NO_SPACE_FOR_FILE); + case VCARD7816_STATUS_ERROR_LC_TLV_INCONSISTENT: + return VCARD_RESPONSE_GET_STATIC( + VCARD7816_STATUS_ERROR_LC_TLV_INCONSISTENT); + case VCARD7816_STATUS_ERROR_P1_P2_INCORRECT: + return VCARD_RESPONSE_GET_STATIC( + VCARD7816_STATUS_ERROR_P1_P2_INCORRECT); + case VCARD7816_STATUS_ERROR_LC_P1_P2_INCONSISTENT: + return VCARD_RESPONSE_GET_STATIC( + VCARD7816_STATUS_ERROR_LC_P1_P2_INCONSISTENT); + case VCARD7816_STATUS_ERROR_DATA_NOT_FOUND: + return VCARD_RESPONSE_GET_STATIC( + VCARD7816_STATUS_ERROR_DATA_NOT_FOUND); + case VCARD7816_STATUS_ERROR_WRONG_PARAMETERS_2: + return VCARD_RESPONSE_GET_STATIC( + VCARD7816_STATUS_ERROR_WRONG_PARAMETERS_2); + case VCARD7816_STATUS_ERROR_INS_CODE_INVALID: + return VCARD_RESPONSE_GET_STATIC( + VCARD7816_STATUS_ERROR_INS_CODE_INVALID); + case VCARD7816_STATUS_ERROR_CLA_INVALID: + return VCARD_RESPONSE_GET_STATIC( + VCARD7816_STATUS_ERROR_CLA_INVALID); + case VCARD7816_STATUS_ERROR_GENERAL: + return VCARD_RESPONSE_GET_STATIC( + VCARD7816_STATUS_ERROR_GENERAL); + default: + /* we don't know this status code, create a response buffer to + * hold it */ + response = vcard_response_new_status(status); + if (response == NULL) { + /* couldn't allocate the buffer, return memmory error */ + return VCARD_RESPONSE_GET_STATIC( + VCARD7816_STATUS_EXC_ERROR_MEMORY_FAILURE); + } + } + assert(response); + return response; +} + +/* + * Add File card support here if you need it. + */ +static VCardStatus +vcard7816_file_system_process_apdu(VCard *card, VCardAPDU *apdu, + VCardResponse **response) +{ + /* TODO: if we want to support a virtual file system card, we do it here. + * It would probably be a pkcs #15 card type */ + *response = vcard_make_response( + VCARD7816_STATUS_ERROR_COMMAND_NOT_SUPPORTED); + return VCARD_DONE; +} + +/* + * VM card (including java cards) + */ +static VCardStatus +vcard7816_vm_process_apdu(VCard *card, VCardAPDU *apdu, + VCardResponse **response) +{ + int bytes_to_copy, next_byte_count, count; + VCardApplet *current_applet; + VCardBufferResponse *buffer_response; + vcard_7816_status_t status; + + /* parse the class first */ + if (apdu->a_gen_type != VCARD_7816_ISO) { + *response = vcard_make_response( + VCARD7816_STATUS_ERROR_COMMAND_NOT_SUPPORTED); + return VCARD_DONE; + } + + /* use a switch so that if we need to support secure channel stuff later, + * we know where to put it */ + switch (apdu->a_secure_messaging) { + case 0x0: /* no SM */ + break; + case 0x4: /* proprietary SM */ + case 0x8: /* header not authenticated */ + case 0xc: /* header authenticated */ + default: + /* for now, don't try to support secure channel stuff in the + * virtual card. */ + *response = vcard_make_response( + VCARD7816_STATUS_ERROR_SECURE_NOT_SUPPORTED); + return VCARD_DONE; + } + + /* now parse the instruction */ + switch (apdu->a_ins) { + case VCARD7816_INS_MANAGE_CHANNEL: /* secure channel op */ + case VCARD7816_INS_EXTERNAL_AUTHENTICATE: /* secure channel op */ + case VCARD7816_INS_GET_CHALLENGE: /* secure channel op */ + case VCARD7816_INS_INTERNAL_AUTHENTICATE: /* secure channel op */ + case VCARD7816_INS_ERASE_BINARY: /* applet control op */ + case VCARD7816_INS_READ_BINARY: /* applet control op */ + case VCARD7816_INS_WRITE_BINARY: /* applet control op */ + case VCARD7816_INS_UPDATE_BINARY: /* applet control op */ + case VCARD7816_INS_READ_RECORD: /* file op */ + case VCARD7816_INS_WRITE_RECORD: /* file op */ + case VCARD7816_INS_UPDATE_RECORD: /* file op */ + case VCARD7816_INS_APPEND_RECORD: /* file op */ + case VCARD7816_INS_ENVELOPE: + case VCARD7816_INS_PUT_DATA: + *response = vcard_make_response( + VCARD7816_STATUS_ERROR_COMMAND_NOT_SUPPORTED); + break; + + case VCARD7816_INS_SELECT_FILE: + if (apdu->a_p1 != 0x04) { + *response = vcard_make_response( + VCARD7816_STATUS_ERROR_FUNCTION_NOT_SUPPORTED); + break; + } + + /* side effect, deselect the current applet if no applet has been found + * */ + current_applet = vcard_find_applet(card, apdu->a_body, apdu->a_Lc); + vcard_select_applet(card, apdu->a_channel, current_applet); + if (current_applet) { + unsigned char *aid; + int aid_len; + aid = vcard_applet_get_aid(current_applet, &aid_len); + *response = vcard_response_new(card, aid, aid_len, apdu->a_Le, + VCARD7816_STATUS_SUCCESS); + } else { + *response = vcard_make_response( + VCARD7816_STATUS_ERROR_FILE_NOT_FOUND); + } + break; + + case VCARD7816_INS_VERIFY: + if ((apdu->a_p1 != 0x00) || (apdu->a_p2 != 0x00)) { + *response = vcard_make_response( + VCARD7816_STATUS_ERROR_WRONG_PARAMETERS); + } else { + if (apdu->a_Lc == 0) { + /* handle pin count if possible */ + count = vcard_emul_get_login_count(card); + if (count < 0) { + *response = vcard_make_response( + VCARD7816_STATUS_ERROR_DATA_NOT_FOUND); + } else { + if (count > 0xf) { + count = 0xf; + } + *response = vcard_response_new_status_bytes( + VCARD7816_SW1_WARNING_CHANGE, + 0xc0 | count); + if (*response == NULL) { + *response = vcard_make_response( + VCARD7816_STATUS_EXC_ERROR_MEMORY_FAILURE); + } + } + } else { + status = vcard_emul_login(card, apdu->a_body, apdu->a_Lc); + *response = vcard_make_response(status); + } + } + break; + + case VCARD7816_INS_GET_RESPONSE: + buffer_response = vcard_get_buffer_response(card); + if (!buffer_response) { + *response = vcard_make_response( + VCARD7816_STATUS_ERROR_DATA_NOT_FOUND); + /* handle error */ + 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 0000000..2bb2a0d --- /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 alwyas return a + * valid response, if it can't allocate memory, the response will be + * VCARD7816_STATUS_EXC_ERROR_MEMORY_FAILURE */ +VCardResponse *vcard_make_response(vcard_7816_status_t status); + +/* create a raw response (status has already been encoded */ +VCardResponse *vcard_response_new_data(unsigned char *buf, int len); + + + + +/* + * destructor for VCardResponse. + * Can be called with a NULL response + */ +void vcard_response_delete(VCardResponse *response); + +/* + * constructor for VCardAPDU + */ +VCardAPDU *vcard_apdu_new(unsigned char *raw_apdu, int len, + unsigned short *status); + +/* + * destructor for VCardAPDU + * Can be called with a NULL apdu + */ +void vcard_apdu_delete(VCardAPDU *apdu); + +/* + * APDU processing starts here. This routes the card processing stuff to the + * right location. Always returns a valid response. + */ +VCardStatus vcard_process_apdu(VCard *card, VCardAPDU *apdu, + VCardResponse **response); + +#endif diff --git a/libcacard/card_7816t.h b/libcacard/card_7816t.h new file mode 100644 index 0000000..9333285 --- /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_PROPIETARY +} VCardAPDUType; + + +/* + * 7816 header. All APDU's have this header. + * They must be laid out in this order. + */ +struct VCardAPDUHeader { + unsigned char ah_cla; + unsigned char ah_ins; + unsigned char ah_p1; + unsigned char ah_p2; + unsigned char ah_Le; + unsigned char ah_body[1]; /* indefinate length */ +}; + +/* + * 7816 APDU structure. The raw bytes are stored in the union and can be + * accessed directly through u.data (which is aliased as a_data). + * + * Names of the fields match the 7816 documentation. + */ +struct VCardAPDUStruct { + int a_len; /* length of the whole buffer, including header */ + int a_Lc; /* 7816 Lc (parameter length) value */ + int a_Le; /* 7816 Le (expected result length) value */ + unsigned char *a_body; /* pointer to the parameter */ + int a_channel; /* decoded channel */ + int a_secure_messaging; /* decoded secure messaging type */ + int a_type; /* decoded type from cla (top nibble of class) */ + VCardAPDUType a_gen_type; /* generic type (7816, PROPRIETARY, RFU, etc) */ + union { + struct VCardAPDUHeader *header; + unsigned char *data; + } u; +/* give the subfields a unified look */ +#define a_header u.header +#define a_data u.data +#define a_cla a_header->ah_cla /* class */ +#define a_ins a_header->ah_ins /* instruction */ +#define a_p1 a_header->ah_p1 /* parameter 1 */ +#define a_p2 a_header->ah_p2 /* parameter 2 */ +}; + +/* 7816 status codes */ +#define VCARD7816_STATUS_SUCCESS 0x9000 +#define VCARD7816_STATUS_WARNING 0x6200 +#define VCARD7816_STATUS_WARNING_RET_CORUPT 0x6281 +#define VCARD7816_STATUS_WARNING_BUF_END_BEFORE_LE 0x6282 +#define VCARD7816_STATUS_WARNING_INVALID_FILE_SELECTED 0x6283 +#define VCARD7816_STATUS_WARNING_FCI_FORMAT_INVALID 0x6284 +#define VCARD7816_STATUS_WARNING_CHANGE 0x6300 +#define VCARD7816_STATUS_WARNING_FILE_FILLED 0x6381 +#define VCARD7816_STATUS_EXC_ERROR 0x6400 +#define VCARD7816_STATUS_EXC_ERROR_CHANGE 0x6500 +#define VCARD7816_STATUS_EXC_ERROR_MEMORY_FAILURE 0x6581 +#define VCARD7816_STATUS_ERROR_WRONG_LENGTH 0x6700 +#define VCARD7816_STATUS_ERROR_CLA_NOT_SUPPORTED 0x6800 +#define VCARD7816_STATUS_ERROR_CHANNEL_NOT_SUPPORTED 0x6881 +#define VCARD7816_STATUS_ERROR_SECURE_NOT_SUPPORTED 0x6882 +#define VCARD7816_STATUS_ERROR_COMMAND_NOT_SUPPORTED 0x6900 +#define VCARD7816_STATUS_ERROR_COMMAND_INCOMPATIBLE_WITH_FILE 0x6981 +#define VCARD7816_STATUS_ERROR_SECURITY_NOT_SATISFIED 0x6982 +#define VCARD7816_STATUS_ERROR_AUTHENTICATION_BLOCKED 0x6983 +#define VCARD7816_STATUS_ERROR_DATA_INVALID 0x6984 +#define VCARD7816_STATUS_ERROR_CONDITION_NOT_SATISFIED 0x6985 +#define VCARD7816_STATUS_ERROR_DATA_NO_EF 0x6986 +#define VCARD7816_STATUS_ERROR_SM_OBJECT_MISSING 0x6987 +#define VCARD7816_STATUS_ERROR_SM_OBJECT_INCORRECT 0x6988 +#define VCARD7816_STATUS_ERROR_WRONG_PARAMETERS 0x6a00 +#define VCARD7816_STATUS_ERROR_WRONG_PARAMETERS_IN_DATA 0x6a80 +#define VCARD7816_STATUS_ERROR_FUNCTION_NOT_SUPPORTED 0x6a81 +#define VCARD7816_STATUS_ERROR_FILE_NOT_FOUND 0x6a82 +#define VCARD7816_STATUS_ERROR_RECORD_NOT_FOUND 0x6a83 +#define VCARD7816_STATUS_ERROR_NO_SPACE_FOR_FILE 0x6a84 +#define VCARD7816_STATUS_ERROR_LC_TLV_INCONSISTENT 0x6a85 +#define VCARD7816_STATUS_ERROR_P1_P2_INCORRECT 0x6a86 +#define VCARD7816_STATUS_ERROR_LC_P1_P2_INCONSISTENT 0x6a87 +#define VCARD7816_STATUS_ERROR_DATA_NOT_FOUND 0x6a88 +#define VCARD7816_STATUS_ERROR_WRONG_PARAMETERS_2 0x6b00 +#define VCARD7816_STATUS_ERROR_INS_CODE_INVALID 0x6d00 +#define VCARD7816_STATUS_ERROR_CLA_INVALID 0x6e00 +#define VCARD7816_STATUS_ERROR_GENERAL 0x6f00 +/* 7816 sw1 codes */ +#define VCARD7816_SW1_SUCCESS 0x90 +#define VCARD7816_SW1_RESPONSE_BYTES 0x61 +#define VCARD7816_SW1_WARNING 0x62 +#define VCARD7816_SW1_WARNING_CHANGE 0x63 +#define VCARD7816_SW1_EXC_ERROR 0x64 +#define VCARD7816_SW1_EXC_ERROR_CHANGE 0x65 +#define VCARD7816_SW1_ERROR_WRONG_LENGTH 0x67 +#define VCARD7816_SW1_CLA_ERROR 0x68 +#define VCARD7816_SW1_COMMAND_ERROR 0x69 +#define VCARD7816_SW1_P1_P2_ERROR 0x6a +#define VCARD7816_SW1_LE_ERROR 0x6c +#define VCARD7816_SW1_INS_ERROR 0x6d +#define VCARD7816_SW1_CLA_NOT_SUPPORTED 0x6e + +/* 7816 Instructions */ +#define VCARD7816_INS_MANAGE_CHANNEL 0x70 +#define VCARD7816_INS_EXTERNAL_AUTHENTICATE 0x82 +#define VCARD7816_INS_GET_CHALLENGE 0x84 +#define VCARD7816_INS_INTERNAL_AUTHENTICATE 0x88 +#define VCARD7816_INS_ERASE_BINARY 0x0e +#define VCARD7816_INS_READ_BINARY 0xb0 +#define VCARD7816_INS_WRITE_BINARY 0xd0 +#define VCARD7816_INS_UPDATE_BINARY 0xd6 +#define VCARD7816_INS_READ_RECORD 0xb2 +#define VCARD7816_INS_WRITE_RECORD 0xd2 +#define VCARD7816_INS_UPDATE_RECORD 0xdc +#define VCARD7816_INS_APPEND_RECORD 0xe2 +#define VCARD7816_INS_ENVELOPE 0xc2 +#define VCARD7816_INS_PUT_DATA 0xda +#define VCARD7816_INS_GET_DATA 0xca +#define VCARD7816_INS_SELECT_FILE 0xa4 +#define VCARD7816_INS_VERIFY 0x20 +#define VCARD7816_INS_GET_RESPONSE 0xc0 + +#endif diff --git a/libcacard/event.c b/libcacard/event.c new file mode 100644 index 0000000..12722cc --- /dev/null +++ b/libcacard/event.c @@ -0,0 +1,108 @@ +/* + * 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 + +#include "qemu-thread.h" +#include "qemu-common.h" + +#include "vcard.h" +#include "vreader.h" +#include "vevent.h" + +VEvent * +vevent_new(VEventType type, VReader *reader, VCard *card) +{ + VEvent *new_vevent; + + new_vevent = (VEvent *)qemu_malloc(sizeof(VEvent)); + 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); + qemu_free(vevent); +} + +/* + * VEvent queue management + */ + +static VEvent *vevent_queue_head; +static VEvent *vevent_queue_tail; +static QemuMutex vevent_queue_lock; +static QemuCond vevent_queue_condition; + +void vevent_queue_init(void) +{ + qemu_mutex_init(&vevent_queue_lock); + qemu_cond_init(&vevent_queue_condition); + vevent_queue_head = vevent_queue_tail = NULL; +} + +void +vevent_queue_vevent(VEvent *vevent) +{ + vevent->next = NULL; + qemu_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; + qemu_cond_signal(&vevent_queue_condition); + qemu_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; + + qemu_mutex_lock(&vevent_queue_lock); + while ((vevent = vevent_dequeue_vevent()) == NULL) { + qemu_cond_wait(&vevent_queue_condition, &vevent_queue_lock); + } + qemu_mutex_unlock(&vevent_queue_lock); + return vevent; +} + +VEvent *vevent_get_next_vevent(void) +{ + VEvent *vevent; + + qemu_mutex_lock(&vevent_queue_lock); + vevent = vevent_dequeue_vevent(); + qemu_mutex_unlock(&vevent_queue_lock); + return vevent; +} + diff --git a/libcacard/eventt.h b/libcacard/eventt.h new file mode 100644 index 0000000..0dc7bd4 --- /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/link_test.c b/libcacard/link_test.c new file mode 100644 index 0000000..ca34e4f --- /dev/null +++ b/libcacard/link_test.c @@ -0,0 +1,21 @@ +/* + * 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 +#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 0000000..d7828a2 --- /dev/null +++ b/libcacard/vcard.c @@ -0,0 +1,341 @@ +/* + * 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 +#include + +#include "qemu-common.h" + +#include "vcard.h" +#include "vcard_emul.h" +#include "card_7816t.h" + +struct VCardAppletStruct { + VCardApplet *next; + VCardProcessAPDU process_apdu; + VCardResetApplet reset_applet; + unsigned char *aid; + int aid_len; + void *applet_private; + VCardAppletPrivateFree applet_private_free; +}; + +struct VCardStruct { + int reference_count; + VCardApplet *applet_list; + VCardApplet *current_applet[MAX_CHANNEL]; + VCardBufferResponse *vcard_buffer_response; + VCardType type; + VCardEmul *vcard_private; + VCardEmulFree vcard_private_free; + VCardGetAtr vcard_get_atr; +}; + +VCardBufferResponse * +vcard_buffer_response_new(unsigned char *buffer, int size) +{ + VCardBufferResponse *new_buffer; + + new_buffer = (VCardBufferResponse *)qemu_malloc(sizeof(VCardBufferResponse)); + new_buffer->buffer = (unsigned char *)qemu_malloc(size); + memcpy(new_buffer->buffer, buffer, size); + new_buffer->buffer_len = size; + new_buffer->current = new_buffer->buffer; + new_buffer->len = size; + return new_buffer; +} + +void +vcard_buffer_response_delete(VCardBufferResponse *buffer_response) +{ + if (buffer_response == NULL) { + return; + } + if (buffer_response->buffer) { + qemu_free(buffer_response->buffer); + } + qemu_free(buffer_response); +} + + +/* + * clean up state after a reset + */ +void +vcard_reset(VCard *card, VCardPower power) +{ + int i; + VCardApplet *applet = NULL; + + if (card->type == VCARD_DIRECT) { + /* select the last applet */ + VCardApplet *current_applet = NULL; + for (current_applet = card->applet_list; current_applet; + current_applet = current_applet->next) { + applet = current_applet; + } + } + for (i = 0; i < MAX_CHANNEL; i++) { + card->current_applet[i] = applet; + } + if (card->vcard_buffer_response) { + vcard_buffer_response_delete(card->vcard_buffer_response); + card->vcard_buffer_response = NULL; + } + vcard_emul_reset(card, power); + if (applet) { + applet->reset_applet(card, 0); + } +} + +/* applet utilities */ + +/* + * applet utilities + */ +/* constructor */ +VCardApplet * +vcard_new_applet(VCardProcessAPDU applet_process_function, + VCardResetApplet applet_reset_function, + unsigned char *aid, int aid_len) +{ + VCardApplet *applet; + + applet = (VCardApplet *)qemu_malloc(sizeof(VCardApplet)); + applet->next = NULL; + applet->applet_private = NULL; + applet->applet_private_free = NULL; + applet->process_apdu = applet_process_function; + applet->reset_applet = applet_reset_function; + + applet->aid = qemu_malloc(aid_len); + memcpy(applet->aid, aid, aid_len); + applet->aid_len = aid_len; + return applet; +} + +/* destructor */ +void +vcard_delete_applet(VCardApplet *applet) +{ + if (applet == NULL) { + return; + } + if (applet->applet_private_free) { + applet->applet_private_free(applet->applet_private); + applet->applet_private = NULL; + } + if (applet->aid) { + qemu_free(applet->aid); + applet->aid = NULL; + } + qemu_free(applet); +} + +/* accessor */ +void +vcard_set_applet_private(VCardApplet *applet, VCardAppletPrivate *private, + VCardAppletPrivateFree private_free) +{ + if (applet->applet_private_free) { + applet->applet_private_free(applet->applet_private); + } + applet->applet_private = private; + applet->applet_private_free = private_free; +} + +VCard * +vcard_new(VCardEmul *private, VCardEmulFree private_free) +{ + VCard *new_card; + int i; + + new_card = (VCard *)qemu_malloc(sizeof(VCard)); + new_card->applet_list = NULL; + for (i = 0; i < MAX_CHANNEL; i++) { + new_card->current_applet[i] = NULL; + } + new_card->vcard_buffer_response = NULL; + new_card->type = VCARD_VM; + new_card->vcard_private = private; + new_card->vcard_private_free = private_free; + new_card->vcard_get_atr = NULL; + new_card->reference_count = 1; + return new_card; +} + +VCard * +vcard_reference(VCard *vcard) +{ + if (vcard == NULL) { + return NULL; + } + vcard->reference_count++; + return vcard; +} + +void +vcard_free(VCard *vcard) +{ + VCardApplet *current_applet = NULL; + VCardApplet *next_applet = NULL; + + if (vcard == NULL) { + return; + } + vcard->reference_count--; + if (vcard->reference_count != 0) { + return; + } + if (vcard->vcard_private_free) { + (*vcard->vcard_private_free)(vcard->vcard_private); + vcard->vcard_private_free = 0; + vcard->vcard_private = 0; + } + for (current_applet = vcard->applet_list; current_applet; + current_applet = next_applet) { + next_applet = current_applet->next; + vcard_delete_applet(current_applet); + } + vcard_buffer_response_delete(vcard->vcard_buffer_response); + qemu_free(vcard); + return; +} + +void +vcard_get_atr(VCard *vcard, unsigned char *atr, int *atr_len) +{ + if (vcard->vcard_get_atr) { + (*vcard->vcard_get_atr)(vcard, atr, atr_len); + return; + } + vcard_emul_get_atr(vcard, atr, atr_len); +} + +void +vcard_set_atr_func(VCard *card, VCardGetAtr vcard_get_atr) +{ + card->vcard_get_atr = vcard_get_atr; +} + + +VCardStatus +vcard_add_applet(VCard *card, VCardApplet *applet) +{ + applet->next = card->applet_list; + card->applet_list = applet; + /* if our card-type is direct, always call the applet */ + if (card->type == VCARD_DIRECT) { + int i; + + for (i = 0; i < MAX_CHANNEL; i++) { + card->current_applet[i] = applet; + } + } + return VCARD_DONE; +} + +/* + * manage applets + */ +VCardApplet * +vcard_find_applet(VCard *card, unsigned char *aid, int aid_len) +{ + VCardApplet *current_applet; + + for (current_applet = card->applet_list; current_applet; + current_applet = current_applet->next) { + if (current_applet->aid_len != aid_len) { + continue; + } + if (memcmp(current_applet->aid, aid, aid_len) == 0) { + break; + } + } + return current_applet; +} + +unsigned char * +vcard_applet_get_aid(VCardApplet *applet, int *aid_len) +{ + if (applet == NULL) { + return NULL; + } + *aid_len = applet->aid_len; + return applet->aid; +} + + +void +vcard_select_applet(VCard *card, int channel, VCardApplet *applet) +{ + assert(channel < MAX_CHANNEL); + card->current_applet[channel] = applet; + /* reset the applet */ + if (applet && applet->reset_applet) { + applet->reset_applet(card, channel); + } +} + +VCardAppletPrivate * +vcard_get_current_applet_private(VCard *card, int channel) +{ + VCardApplet *applet = card->current_applet[channel]; + + if (applet == NULL) { + return NULL; + } + return applet->applet_private; +} + +VCardStatus +vcard_process_applet_apdu(VCard *card, VCardAPDU *apdu, + VCardResponse **response) +{ + if (card->current_applet[apdu->a_channel]) { + return card->current_applet[apdu->a_channel]->process_apdu( + card, apdu, response); + } + return VCARD_NEXT; +} + +/* + * Accessor functions + */ +/* accessor functions for the response buffer */ +VCardBufferResponse * +vcard_get_buffer_response(VCard *card) +{ + return card->vcard_buffer_response; +} + +void +vcard_set_buffer_response(VCard *card, VCardBufferResponse *buffer) +{ + card->vcard_buffer_response = buffer; +} + + +/* accessor functions for the type */ +VCardType +vcard_get_type(VCard *card) +{ + return card->type; +} + +void +vcard_set_type(VCard *card, VCardType type) +{ + card->type = type; +} + +/* accessor for private data */ +VCardEmul * +vcard_get_private(VCard *vcard) +{ + return vcard->vcard_private; +} + diff --git a/libcacard/vcard.h b/libcacard/vcard.h new file mode 100644 index 0000000..47dc703 --- /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 0000000..963563f --- /dev/null +++ b/libcacard/vcard_emul.h @@ -0,0 +1,65 @@ +/* + * 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); + +/* + * key functions + */ +/* delete a key */ +void vcard_emul_delete_key(VCardKey *key); +/* RSA sign/decrypt with the key, signature happens 'in place' */ +vcard_7816_status_t vcard_emul_rsa_op(VCard *card, VCardKey *key, + unsigned char *buffer, int buffer_size); + +void vcard_emul_reset(VCard *card, VCardPower power); +void vcard_emul_get_atr(VCard *card, unsigned char *atr, int *atr_len); + +/* Re-insert of a card that has been removed by force removal */ +VCardEmulError vcard_emul_force_card_insert(VReader *vreader); +/* Force a card removal even if the card is not physically removed */ +VCardEmulError vcard_emul_force_card_remove(VReader *vreader); + +VCardEmulOptions *vcard_emul_options(const char *args); +VCardEmulError vcard_emul_init(const VCardEmulOptions *options); +void vcard_emul_replay_insertion_events(void); +void vcard_emul_usage(void); +#endif diff --git a/libcacard/vcard_emul_nss.c b/libcacard/vcard_emul_nss.c new file mode 100644 index 0000000..d3ab7ea --- /dev/null +++ b/libcacard/vcard_emul_nss.c @@ -0,0 +1,1159 @@ +/* + * 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. + */ + +/* + * system headers + */ +#include +#include + +/* + * NSS headers + */ +#include +#include +#include +#include +#include +#include +#include + +#include "qemu-common.h" + +#include "vcard.h" +#include "card_7816t.h" +#include "vcard_emul.h" +#include "vreader.h" +#include "vevent.h" + +struct VCardKeyStruct { + CERTCertificate *cert; + PK11SlotInfo *slot; + SECKEYPrivateKey *key; +}; + + +typedef struct VirtualReaderOptionsStruct VirtualReaderOptions; + +struct VReaderEmulStruct { + PK11SlotInfo *slot; + VCardEmulType default_type; + char *type_params; + PRBool present; + int series; + VCard *saved_vcard; +}; + +/* + * NSS Specific options + */ +struct VirtualReaderOptionsStruct { + char *name; + char *vname; + VCardEmulType card_type; + char *type_params; + char **cert_name; + int cert_count; +}; + +struct VCardEmulOptionsStruct { + void *nss_db; + VirtualReaderOptions *vreader; + int vreader_count; + VCardEmulType hw_card_type; + const char *hw_type_params; + PRBool use_hw; +}; + +static int nss_emul_init; + +/* if we have more that just the slot, define + * VCardEmulStruct here */ + +/* + * allocate the set of arrays for certs, cert_len, key + */ +static PRBool +vcard_emul_alloc_arrays(unsigned char ***certsp, int **cert_lenp, + VCardKey ***keysp, int cert_count) +{ + *certsp = NULL; + *cert_lenp = NULL; + *keysp = NULL; + *certsp = (unsigned char **)qemu_malloc(sizeof(unsigned char *)*cert_count); + *cert_lenp = (int *)qemu_malloc(sizeof(int)*cert_count); + *keysp = (VCardKey **)qemu_malloc(sizeof(VCardKey *)*cert_count); + return PR_TRUE; +} + +/* + * Emulator specific card information + */ +typedef struct CardEmulCardStruct CardEmulPrivate; + +static VCardEmul * +vcard_emul_new_card(PK11SlotInfo *slot) +{ + PK11_ReferenceSlot(slot); + /* currently we don't need anything other than the slot */ + return (VCardEmul *)slot; +} + +static void +vcard_emul_delete_card(VCardEmul *vcard_emul) +{ + PK11SlotInfo *slot = (PK11SlotInfo *)vcard_emul; + if (slot == NULL) { + return; + } + PK11_FreeSlot(slot); +} + +static PK11SlotInfo * +vcard_emul_card_get_slot(VCard *card) +{ + /* note, the card is holding the reference, no need to get another one */ + return (PK11SlotInfo *)vcard_get_private(card); +} + + +/* + * key functions + */ +/* private constructure */ +static VCardKey * +vcard_emul_make_key(PK11SlotInfo *slot, CERTCertificate *cert) +{ + VCardKey *key; + + key = (VCardKey *)qemu_malloc(sizeof(VCardKey)); + key->slot = PK11_ReferenceSlot(slot); + key->cert = CERT_DupCertificate(cert); + /* NOTE: if we aren't logged into the token, this could return NULL */ + /* NOTE: the cert is a temp cert, not necessarily the cert in the token, + * use the DER version of this function */ + key->key = PK11_FindKeyByDERCert(slot, cert, NULL); + return key; +} + +/* destructor */ +void +vcard_emul_delete_key(VCardKey *key) +{ + if (!nss_emul_init || (key == NULL)) { + return; + } + if (key->key) { + SECKEY_DestroyPrivateKey(key->key); + key->key = NULL; + } + if (key->cert) { + CERT_DestroyCertificate(key->cert); + } + if (key->slot) { + PK11_FreeSlot(key->slot); + } + return; +} + +/* + * grab the nss key from a VCardKey. If it doesn't exist, try to look it up + */ +static SECKEYPrivateKey * +vcard_emul_get_nss_key(VCardKey *key) +{ + if (key->key) { + return key->key; + } + /* NOTE: if we aren't logged into the token, this could return NULL */ + key->key = PK11_FindPrivateKeyFromCert(key->slot, key->cert, NULL); + return key->key; +} + +/* + * Map NSS errors to 7816 errors + */ +static vcard_7816_status_t +vcard_emul_map_error(int error) +{ + switch (error) { + case SEC_ERROR_TOKEN_NOT_LOGGED_IN: + return VCARD7816_STATUS_ERROR_CONDITION_NOT_SATISFIED; + case SEC_ERROR_BAD_DATA: + case SEC_ERROR_OUTPUT_LEN: + case SEC_ERROR_INPUT_LEN: + case SEC_ERROR_INVALID_ARGS: + case SEC_ERROR_INVALID_ALGORITHM: + case SEC_ERROR_NO_KEY: + case SEC_ERROR_INVALID_KEY: + case SEC_ERROR_DECRYPTION_DISALLOWED: + return VCARD7816_STATUS_ERROR_DATA_INVALID; + case SEC_ERROR_NO_MEMORY: + return VCARD7816_STATUS_EXC_ERROR_MEMORY_FAILURE; + } + return VCARD7816_STATUS_EXC_ERROR_CHANGE; +} + +/* RSA sign/decrypt with the key, signature happens 'in place' */ +vcard_7816_status_t +vcard_emul_rsa_op(VCard *card, VCardKey *key, + unsigned char *buffer, int buffer_size) +{ + SECKEYPrivateKey *priv_key; + unsigned signature_len; + SECStatus rv; + + if ((!nss_emul_init) || (key == NULL)) { + /* couldn't get the key, indicate that we aren't logged in */ + return VCARD7816_STATUS_ERROR_CONDITION_NOT_SATISFIED; + } + priv_key = vcard_emul_get_nss_key(key); + + /* + * this is only true of the rsa signature + */ + signature_len = PK11_SignatureLen(priv_key); + if (buffer_size != signature_len) { + return VCARD7816_STATUS_ERROR_DATA_INVALID; + } + rv = PK11_PrivDecryptRaw(priv_key, buffer, &signature_len, signature_len, + buffer, buffer_size); + if (rv != SECSuccess) { + return vcard_emul_map_error(PORT_GetError()); + } + assert(buffer_size == signature_len); + return VCARD7816_STATUS_SUCCESS; +} + +/* + * Login functions + */ +/* return the number of login attempts still possible on the card. if unknown, + * return -1 */ +int +vcard_emul_get_login_count(VCard *card) +{ + return -1; +} + +/* login into the card, return the 7816 status word (sw2 || sw1) */ +vcard_7816_status_t +vcard_emul_login(VCard *card, unsigned char *pin, int pin_len) +{ + PK11SlotInfo *slot; + unsigned char *pin_string = NULL; + int i; + SECStatus rv; + + if (!nss_emul_init) { + return VCARD7816_STATUS_ERROR_CONDITION_NOT_SATISFIED; + } + slot = vcard_emul_card_get_slot(card); + /* We depend on the PKCS #11 module internal login state here because we + * create a separate process to handle each guest instance. If we needed + * to handle multiple guests from one process, then we would need to keep + * a lot of extra state in our card structure + * */ + pin_string = qemu_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 */ + qemu_free(pin_string); + if (rv == SECSuccess) { + return VCARD7816_STATUS_SUCCESS; + } + /* map the error from port get error */ + return VCARD7816_STATUS_ERROR_CONDITION_NOT_SATISFIED; +} + +void +vcard_emul_reset(VCard *card, VCardPower power) +{ + PK11SlotInfo *slot; + + if (!nss_emul_init) { + return; + } + + /* + * if we reset the card (either power on or power off), we lose our login + * state + */ + /* TODO: we may also need to send insertion/removal events? */ + slot = vcard_emul_card_get_slot(card); + PK11_Logout(slot); /* NOTE: ignoring SECStatus return value */ + return; +} + + +static VReader * +vcard_emul_find_vreader_from_slot(PK11SlotInfo *slot) +{ + VReaderList *reader_list = vreader_get_reader_list(); + VReaderListEntry *current_entry = NULL; + + if (reader_list == NULL) { + return NULL; + } + for (current_entry = vreader_list_get_first(reader_list); current_entry; + current_entry = vreader_list_get_next(current_entry)) { + VReader *reader = vreader_list_get_reader(current_entry); + VReaderEmul *reader_emul = vreader_get_private(reader); + if (reader_emul->slot == slot) { + return reader; + } + vreader_free(reader); + } + + return NULL; +} + +/* + * create a new reader emul + */ +static VReaderEmul * +vreader_emul_new(PK11SlotInfo *slot, VCardEmulType type, const char *params) +{ + VReaderEmul *new_reader_emul; + + new_reader_emul = (VReaderEmul *)qemu_malloc(sizeof(VReaderEmul)); + + new_reader_emul->slot = PK11_ReferenceSlot(slot); + new_reader_emul->default_type = type; + new_reader_emul->type_params = strdup(params); + new_reader_emul->present = PR_FALSE; + new_reader_emul->series = 0; + new_reader_emul->saved_vcard = NULL; + return new_reader_emul; +} + +static void +vreader_emul_delete(VReaderEmul *vreader_emul) +{ + if (vreader_emul == NULL) { + return; + } + if (vreader_emul->slot) { + PK11_FreeSlot(vreader_emul->slot); + } + if (vreader_emul->type_params) { + qemu_free(vreader_emul->type_params); + } + qemu_free(vreader_emul); +} + +/* + * TODO: move this to emulater non-specific file + */ +static VCardEmulType +vcard_emul_get_type(VReader *vreader) +{ + VReaderEmul *vreader_emul; + + vreader_emul = vreader_get_private(vreader); + if (vreader_emul && vreader_emul->default_type != VCARD_EMUL_NONE) { + return vreader_emul->default_type; + } + + return vcard_emul_type_select(vreader); +} +/* + * TODO: move this to emulater non-specific file + */ +static const char * +vcard_emul_get_type_params(VReader *vreader) +{ + VReaderEmul *vreader_emul; + + vreader_emul = vreader_get_private(vreader); + if (vreader_emul && vreader_emul->type_params) { + return vreader_emul->type_params; + } + + return ""; +} + +/* pull the slot out of the reader private data */ +static PK11SlotInfo * +vcard_emul_reader_get_slot(VReader *vreader) +{ + VReaderEmul *vreader_emul = vreader_get_private(vreader); + if (vreader_emul == NULL) { + return NULL; + } + return vreader_emul->slot; +} + +/* + * Card ATR's map to physical cards. VCARD_ATR_PREFIX will set appropriate + * historical bytes for any software emulated card. The remaining bytes can be + * used to indicate the actual emulator + */ +static const unsigned char nss_atr[] = { VCARD_ATR_PREFIX(3), 'N', 'S', 'S' }; + +void +vcard_emul_get_atr(VCard *card, unsigned char *atr, int *atr_len) +{ + int len = MIN(sizeof(nss_atr), *atr_len); + assert(atr != NULL); + + memcpy(atr, nss_atr, len); + *atr_len = len; + return; +} + +/* + * create a new card from certs and keys + */ +static VCard * +vcard_emul_make_card(VReader *reader, + unsigned char * const *certs, int *cert_len, + VCardKey *keys[], int cert_count) +{ + VCardEmul *vcard_emul; + VCard *vcard; + PK11SlotInfo *slot; + VCardEmulType type; + const char *params; + + type = vcard_emul_get_type(reader); + + /* ignore the inserted card */ + if (type == VCARD_EMUL_NONE) { + return NULL; + } + slot = vcard_emul_reader_get_slot(reader); + if (slot == NULL) { + return NULL; + } + + params = vcard_emul_get_type_params(reader); + /* params these can be NULL */ + + vcard_emul = vcard_emul_new_card(slot); + if (vcard_emul == NULL) { + return NULL; + } + vcard = vcard_new(vcard_emul, vcard_emul_delete_card); + if (vcard == NULL) { + vcard_emul_delete_card(vcard_emul); + return NULL; + } + vcard_init(reader, vcard, type, params, certs, cert_len, keys, cert_count); + return vcard; +} + + +/* + * 'clone' a physical card as a virtual card + */ +static VCard * +vcard_emul_mirror_card(VReader *vreader) +{ + /* + * lookup certs using the C_FindObjects. The Stan Cert handle won't give + * us the real certs until we log in. + */ + PK11GenericObject *firstObj, *thisObj; + int cert_count; + unsigned char **certs; + int *cert_len; + VCardKey **keys; + PK11SlotInfo *slot; + PRBool ret; + + slot = vcard_emul_reader_get_slot(vreader); + if (slot == NULL) { + return NULL; + } + + firstObj = PK11_FindGenericObjects(slot, CKO_CERTIFICATE); + if (firstObj == NULL) { + return NULL; + } + + /* count the certs */ + cert_count = 0; + for (thisObj = firstObj; thisObj; + thisObj = PK11_GetNextGenericObject(thisObj)) { + cert_count++; + } + + if (cert_count == 0) { + PK11_DestroyGenericObjects(firstObj); + return NULL; + } + + /* allocate the arrays */ + ret = vcard_emul_alloc_arrays(&certs, &cert_len, &keys, cert_count); + if (ret == PR_FALSE) { + return NULL; + } + + /* fill in the arrays */ + cert_count = 0; + for (thisObj = firstObj; thisObj; + thisObj = PK11_GetNextGenericObject(thisObj)) { + SECItem derCert; + CERTCertificate *cert; + SECStatus rv; + + rv = PK11_ReadRawAttribute(PK11_TypeGeneric, thisObj, + CKA_VALUE, &derCert); + if (rv != SECSuccess) { + continue; + } + /* create floating temp cert. This gives us a cert structure even if + * the token isn't logged in */ + cert = CERT_NewTempCertificate(CERT_GetDefaultCertDB(), &derCert, + NULL, PR_FALSE, PR_TRUE); + SECITEM_FreeItem(&derCert, PR_FALSE); + if (cert == NULL) { + continue; + } + + certs[cert_count] = cert->derCert.data; + cert_len[cert_count] = cert->derCert.len; + keys[cert_count] = vcard_emul_make_key(slot, cert); + cert_count++; + CERT_DestroyCertificate(cert); /* key obj still has a reference */ + } + + /* now create the card */ + return vcard_emul_make_card(vreader, certs, cert_len, keys, cert_count); +} + +static VCardEmulType default_card_type = VCARD_EMUL_NONE; +static const char *default_type_params = ""; + +/* + * This thread looks for card and reader insertions and puts events on the + * event queue + */ +static void +vcard_emul_event_thread(void *arg) +{ + PK11SlotInfo *slot; + VReader *vreader; + VReaderEmul *vreader_emul; + VCard *vcard; + SECMODModule *module = (SECMODModule *)arg; + + do { + slot = SECMOD_WaitForAnyTokenEvent(module, 0, 500); + if (slot == NULL) { + break; + } + vreader = vcard_emul_find_vreader_from_slot(slot); + if (vreader == NULL) { + /* new vreader */ + vreader_emul = vreader_emul_new(slot, default_card_type, + default_type_params); + vreader = vreader_new(PK11_GetSlotName(slot), vreader_emul, + vreader_emul_delete); + PK11_FreeSlot(slot); + slot = NULL; + vreader_add_reader(vreader); + vreader_free(vreader); + continue; + } + /* card remove/insert */ + vreader_emul = vreader_get_private(vreader); + if (PK11_IsPresent(slot)) { + int series = PK11_GetSlotSeries(slot); + if (series != vreader_emul->series) { + if (vreader_emul->present) { + vreader_insert_card(vreader, NULL); + } + vcard = vcard_emul_mirror_card(vreader); + vreader_insert_card(vreader, vcard); + vcard_free(vcard); + } + vreader_emul->series = series; + vreader_emul->present = 1; + vreader_free(vreader); + PK11_FreeSlot(slot); + continue; + } + if (vreader_emul->present) { + vreader_insert_card(vreader, NULL); + } + vreader_emul->series = 0; + vreader_emul->present = 0; + PK11_FreeSlot(slot); + vreader_free(vreader); + } while (1); +} + +/* if the card is inserted when we start up, make sure our state is correct */ +static void +vcard_emul_init_series(VReader *vreader, VCard *vcard) +{ + VReaderEmul *vreader_emul = vreader_get_private(vreader); + PK11SlotInfo *slot = vreader_emul->slot; + + vreader_emul->present = PK11_IsPresent(slot); + vreader_emul->series = PK11_GetSlotSeries(slot); + if (vreader_emul->present == 0) { + vreader_insert_card(vreader, NULL); + } +} + +/* + * each module has a separate wait call, create a thread for each module that + * we are using. + */ +static void +vcard_emul_new_event_thread(SECMODModule *module) +{ + PR_CreateThread(PR_SYSTEM_THREAD, vcard_emul_event_thread, + module, PR_PRIORITY_HIGH, PR_GLOBAL_THREAD, + PR_UNJOINABLE_THREAD, 0); +} + +static const VCardEmulOptions default_options = { + .nss_db = NULL, + .vreader = NULL, + .vreader_count = 0, + .hw_card_type = VCARD_EMUL_CAC, + .hw_type_params = "", + .use_hw = PR_TRUE +}; + + +/* + * NSS needs the app to supply a password prompt. In our case the only time + * the password is supplied is as part of the Login APDU. The actual password + * is passed in the pw_arg in that case. In all other cases pw_arg should be + * NULL. + */ +static char * +vcard_emul_get_password(PK11SlotInfo *slot, PRBool retries, void *pw_arg) +{ + /* if it didn't work the first time, don't keep trying */ + if (retries) { + return NULL; + } + /* we are looking up a password when we don't have one in hand */ + if (pw_arg == NULL) { + return NULL; + } + /* TODO: we really should verify that were are using the right slot */ + return PORT_Strdup(pw_arg); +} + +/* Force a card removal even if the card is not physically removed */ +VCardEmulError +vcard_emul_force_card_remove(VReader *vreader) +{ + if (!nss_emul_init || (vreader_card_is_present(vreader) != VREADER_OK)) { + return VCARD_EMUL_FAIL; /* card is already removed */ + } + + /* OK, remove it */ + vreader_insert_card(vreader, NULL); + return VCARD_EMUL_OK; +} + +/* Re-insert of a card that has been removed by force removal */ +VCardEmulError +vcard_emul_force_card_insert(VReader *vreader) +{ + VReaderEmul *vreader_emul; + VCard *vcard; + + if (!nss_emul_init || (vreader_card_is_present(vreader) == VREADER_OK)) { + return VCARD_EMUL_FAIL; /* card is already removed */ + } + vreader_emul = vreader_get_private(vreader); + + /* if it's a softcard, get the saved vcard from the reader emul structure */ + if (vreader_emul->saved_vcard) { + vcard = vcard_reference(vreader_emul->saved_vcard); + } else { + /* it must be a physical card, rebuild it */ + if (!PK11_IsPresent(vreader_emul->slot)) { + /* physical card has been removed, not way to reinsert it */ + return VCARD_EMUL_FAIL; + } + vcard = vcard_emul_mirror_card(vreader); + } + vreader_insert_card(vreader, vcard); + vcard_free(vcard); + + return VCARD_EMUL_OK; +} + + +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 ret, has_readers = PR_FALSE, need_coolkey_module; + VReader *vreader; + VReaderEmul *vreader_emul; + SECMODListLock *module_lock; + SECMODModuleList *module_list; + SECMODModuleList *mlp; + int i; + + if (vcard_emul_init_called) { + return VCARD_EMUL_INIT_ALREADY_INITED; + } + vcard_emul_init_called = 1; + vreader_init(); + vevent_queue_init(); + + if (options == NULL) { + options = &default_options; + } + + /* first initialize NSS */ + if (options->nss_db) { + rv = NSS_Init(options->nss_db); + } else { + rv = NSS_Init("sql:/etc/pki/nssdb"); + } + if (rv != SECSuccess) { + return VCARD_EMUL_FAIL; + } + /* Set password callback function */ + PK11_SetPasswordFunc(vcard_emul_get_password); + + /* set up soft cards emulated by software certs rather than physical cards + * */ + for (i = 0; i < options->vreader_count; i++) { + int j; + int cert_count; + unsigned char **certs; + int *cert_len; + VCardKey **keys; + PK11SlotInfo *slot; + + slot = PK11_FindSlotByName(options->vreader[i].name); + if (slot == NULL) { + continue; + } + vreader_emul = vreader_emul_new(slot, options->vreader[i].card_type, + options->vreader[i].type_params); + vreader = vreader_new(options->vreader[i].vname, vreader_emul, + vreader_emul_delete); + vreader_add_reader(vreader); + cert_count = options->vreader[i].cert_count; + + ret = vcard_emul_alloc_arrays(&certs, &cert_len, &keys, + options->vreader[i].cert_count); + if (ret == PR_FALSE) { + continue; + } + cert_count = 0; + for (j = 0; j < options->vreader[i].cert_count; j++) { + /* we should have a better way of identifying certs than by + * nickname here */ + CERTCertificate *cert = PK11_FindCertFromNickname( + options->vreader[i].cert_name[j], + NULL); + if (cert == NULL) { + continue; + } + certs[cert_count] = cert->derCert.data; + cert_len[cert_count] = cert->derCert.len; + keys[cert_count] = vcard_emul_make_key(slot, cert); + /* this is safe because the key is still holding a cert reference */ + CERT_DestroyCertificate(cert); + cert_count++; + } + if (cert_count) { + VCard *vcard = vcard_emul_make_card(vreader, certs, cert_len, + keys, cert_count); + vreader_insert_card(vreader, vcard); + vcard_emul_init_series(vreader, vcard); + /* allow insertion and removal of soft cards */ + vreader_emul->saved_vcard = vcard_reference(vcard); + vcard_free(vcard); + vreader_free(vreader); + has_readers = PR_TRUE; + } + } + + /* if we aren't suppose to use hw, skip looking up hardware tokens */ + if (!options->use_hw) { + nss_emul_init = has_readers; + return has_readers ? VCARD_EMUL_OK : VCARD_EMUL_FAIL; + } + + /* make sure we have some PKCS #11 module loaded */ + module_lock = SECMOD_GetDefaultModuleListLock(); + module_list = SECMOD_GetDefaultModuleList(); + need_coolkey_module = !has_readers; + SECMOD_GetReadLock(module_lock); + for (mlp = module_list; mlp; mlp = mlp->next) { + SECMODModule *module = mlp->module; + if (module_has_removable_hw_slots(module)) { + need_coolkey_module = PR_FALSE; + break; + } + } + SECMOD_ReleaseReadLock(module_lock); + + if (need_coolkey_module) { + SECMODModule *module; + module = SECMOD_LoadUserModule( + (char *)"library=libcoolkeypk11.so name=Coolkey", + NULL, PR_FALSE); + if (module == NULL) { + return VCARD_EMUL_FAIL; + } + SECMOD_DestroyModule(module); /* free our reference, Module will still + * be on the list. + * until we destroy it */ + } + + /* now examine all the slots, finding which should be readers */ + /* We should control this with options. For now we mirror out any + * removable hardware slot */ + default_card_type = options->hw_card_type; + default_type_params = strdup(options->hw_type_params); + + SECMOD_GetReadLock(module_lock); + for (mlp = module_list; mlp; mlp = mlp->next) { + SECMODModule *module = mlp->module; + PRBool has_emul_slots = PR_FALSE; + + if (module == NULL) { + continue; + } + + for (i = 0; i < module->slotCount; i++) { + PK11SlotInfo *slot = module->slots[i]; + + /* only map removable HW slots */ + if (slot == NULL || !PK11_IsRemovable(slot) || !PK11_IsHW(slot)) { + continue; + } + vreader_emul = vreader_emul_new(slot, options->hw_card_type, + options->hw_type_params); + vreader = vreader_new(PK11_GetSlotName(slot), vreader_emul, + vreader_emul_delete); + vreader_add_reader(vreader); + + has_readers = PR_TRUE; + has_emul_slots = PR_TRUE; + + if (PK11_IsPresent(slot)) { + VCard *vcard; + vcard = vcard_emul_mirror_card(vreader); + vreader_insert_card(vreader, vcard); + vcard_emul_init_series(vreader, vcard); + vcard_free(vcard); + } + } + if (has_emul_slots) { + vcard_emul_new_event_thread(module); + } + } + SECMOD_ReleaseReadLock(module_lock); + nss_emul_init = has_readers; + + return VCARD_EMUL_OK; +} + +/* Recreate card insert events for all readers (user should + * deduce implied reader insert. perhaps do a reader insert as well?) + */ +void +vcard_emul_replay_insertion_events(void) +{ + VReaderListEntry *current_entry; + VReaderListEntry *next_entry = NULL; + VReaderList *list = vreader_get_reader_list(); + + for (current_entry = vreader_list_get_first(list); current_entry; + current_entry = next_entry) { + VReader *vreader = vreader_list_get_reader(current_entry); + next_entry = vreader_list_get_next(current_entry); + vreader_queue_card_event(vreader); + } +} + +/* + * Silly little functions to help parsing our argument string + */ +static char * +copy_string(const char *str, int str_len) +{ + char *new_str; + + new_str = qemu_malloc(str_len+1); + memcpy(new_str, str, str_len); + new_str[str_len] = 0; + return new_str; +} + +static int +count_tokens(const char *str, char token, char token_end) +{ + int count = 0; + + for (; *str; str++) { + if (*str == token) { + count++; + } + if (*str == token_end) { + break; + } + } + return count; +} + +static const char * +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 consistant look */ +static VCardEmulOptions options; +#define READER_STEP 4 + +VCardEmulOptions * +vcard_emul_options(const char *args) +{ + int reader_count = 0; + VCardEmulOptions *opts; + char type_str[100]; + int type_len; + + /* Allow the future use of allocating the options structure on the fly */ + memcpy(&options, &default_options, sizeof(options)); + opts = &options; + + do { + args = strip(args); /* strip off the leading spaces */ + if (*args == ',') { + continue; + } + /* soft=(slot_name,virt_name,emul_type,emul_flags,cert_1, (no eol) + * cert_2,cert_3...) */ + if (strncmp(args, "soft=", 5) == 0) { + const char *name; + const char *vname; + const char *type_params; + VCardEmulType type; + int name_length, vname_length, type_params_length, count, i; + VirtualReaderOptions *vreaderOpt = NULL; + + args = strip(args + 5); + if (*args != '(') { + continue; + } + name = args; + args = strpbrk(args + 1, ",)"); + if (*args == 0) { + break; + } + if (*args == ')') { + args++; + continue; + } + args = strip(args+1); + name_length = args - name - 2; + vname = args; + args = strpbrk(args + 1, ",)"); + if (*args == 0) { + break; + } + if (*args == ')') { + args++; + continue; + } + vname_length = args - name - 2; + args = strip(args+1); + type_len = strpbrk(args, ",)") - args; + assert(sizeof(type_str) > type_len); + strncpy(type_str, args, type_len); + type_str[type_len] = 0; + type = vcard_emul_type_from_string(type_str); + args = strpbrk(args, ",)"); + if (*args == 0) { + break; + } + if (*args == ')') { + args++; + continue; + } + args = strip(args++); + type_params = args; + args = strpbrk(args + 1, ",)"); + if (*args == 0) { + break; + } + if (*args == ')') { + args++; + continue; + } + type_params_length = args - name; + args = strip(args++); + if (*args == 0) { + break; + } + + if (opts->vreader_count >= reader_count) { + reader_count += READER_STEP; + vreaderOpt = realloc(opts->vreader, + reader_count * sizeof(*vreaderOpt)); + if (vreaderOpt == NULL) { + return opts; /* we're done */ + } + } + opts->vreader = vreaderOpt; + vreaderOpt = &vreaderOpt[opts->vreader_count]; + vreaderOpt->name = copy_string(name, name_length); + vreaderOpt->vname = copy_string(vname, vname_length); + vreaderOpt->card_type = type; + vreaderOpt->type_params = + copy_string(type_params, type_params_length); + count = count_tokens(args, ',', ')'); + vreaderOpt->cert_count = count; + vreaderOpt->cert_name = (char **)qemu_malloc(count*sizeof(char *)); + for (i = 0; i < count; i++) { + const char *cert = args + 1; + args = strpbrk(args + 1, ",)"); + vreaderOpt->cert_name[i] = copy_string(cert, args - cert); + } + if (*args == ')') { + args++; + } + opts->vreader_count++; + /* use_hw= */ + } else if (strncmp(args, "use_hw=", 7) == 0) { + args = strip(args+7); + if (*args == '0' || *args == 'N' || *args == 'n' || *args == 'F') { + opts->use_hw = PR_FALSE; + } else { + opts->use_hw = PR_TRUE; + } + args = find_blank(args); + /* hw_type= */ + } else if (strncmp(args, "hw_type=", 8) == 0) { + args = strip(args+8); + opts->hw_card_type = vcard_emul_type_from_string(args); + args = find_blank(args); + /* hw_params= */ + } else if (strncmp(args, "hw_params=", 10) == 0) { + const char *params; + args = strip(args+10); + params = args; + args = find_blank(args); + opts->hw_type_params = copy_string(params, args-params); + /* db="/data/base/path" */ + } else if (strncmp(args, "db=", 3) == 0) { + const char *db; + args = strip(args+3); + if (*args != '"') { + continue; + } + args++; + db = args; + args = strpbrk(args, "\"\n"); + opts->nss_db = copy_string(db, args-db); + if (*args != 0) { + args++; + } + } else { + args = find_blank(args); + } + } while (*args != 0); + + return opts; +} + +void +vcard_emul_usage(void) +{ + fprintf(stderr, +"emul args: comma separated list of the following arguments\n" +" db={nss_database} (default sql:/etc/pki/nssdb)\n" +" use_hw=[yes|no] (default yes)\n" +" hw_type={card_type_to_emulate} (default CAC)\n" +" hw_param={param_for_card} (default \"\")\n" +" soft=({slot_name},{vreader_name},{card_type_to_emulate},{params_for_card},\n" +" {cert1},{cert2},{cert3} (default none)\n" +"\n" +" {nss_database} The location of the NSS cert & key database\n" +" {card_type_to_emulate} What card interface to present to the guest\n" +" {param_for_card} Card interface specific parameters\n" +" {slot_name} NSS slot that contains the certs\n" +" {vreader_name} Virutal reader name to present to the guest\n" +" {certN} Nickname of the certificate n on the virtual card\n" +"\n" +"These parameters come as a single string separated by blanks or newlines." +"\n" +"Unless use_hw is set to no, all tokens that look like removable hardware\n" +"tokens will be presented to the guest using the emulator specified by\n" +"hw_type, and parameters of hw_param.\n" +"\n" +"If more one or more soft= parameters are specified, these readers will be\n" +"presented to the guest\n"); +} diff --git a/libcacard/vcard_emul_type.c b/libcacard/vcard_emul_type.c new file mode 100644 index 0000000..59a1458 --- /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 +#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 0000000..0242f40 --- /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.h b/libcacard/vcardt.h new file mode 100644 index 0000000..538bdde --- /dev/null +++ b/libcacard/vcardt.h @@ -0,0 +1,64 @@ +/* + * 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 +#ifndef MIN +#define MIN(x, y) ((x) > (y) ? (y) : (x)) +#define MAX(x, y) ((x) > (y) ? (x) : (y)) +#endif + +typedef struct VCardStruct VCard; +typedef struct VCardAPDUStruct VCardAPDU; +typedef struct VCardResponseStruct VCardResponse; +typedef struct VCardBufferResponseStruct VCardBufferResponse; +typedef struct VCardAppletStruct VCardApplet; +typedef struct VCardAppletPrivateStruct VCardAppletPrivate; +typedef struct VCardKeyStruct VCardKey; /* opaque */ +typedef struct VCardEmulStruct VCardEmul; + +#define MAX_CHANNEL 4 + +/* create an ATR with appropriate historical bytes */ +#define VCARD_ATR_PREFIX(size) 0x3b, 0x66+(size), 0x00, 0xff, \ + 'V', 'C', 'A', 'R', 'D', '_' + + +typedef enum { + VCARD_DONE, + VCARD_NEXT, + VCARD_FAIL +} VCardStatus; + +typedef enum { + VCARD_FILE_SYSTEM, + VCARD_VM, + VCARD_DIRECT +} VCardType; + +typedef enum { + VCARD_POWER_ON, + VCARD_POWER_OFF +} VCardPower; + +typedef VCardStatus (*VCardProcessAPDU)(VCard *card, VCardAPDU *apdu, + VCardResponse **response); +typedef VCardStatus (*VCardResetApplet)(VCard *card, int channel); +typedef void (*VCardAppletPrivateFree) (VCardAppletPrivate *); +typedef void (*VCardEmulFree) (VCardEmul *); +typedef void (*VCardGetAtr) (VCard *, unsigned char *atr, int *atr_len); + +struct VCardBufferResponseStruct { + unsigned char *buffer; + int buffer_len; + unsigned char *current; + int len; +}; + +#endif diff --git a/libcacard/vevent.h b/libcacard/vevent.h new file mode 100644 index 0000000..38c3482 --- /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 0000000..0b67c6c --- /dev/null +++ b/libcacard/vreader.c @@ -0,0 +1,519 @@ +/* + * 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. + */ + +/* + * System includes + */ +#include +#include + +#include "qemu-thread.h" +#include "qemu-common.h" + +#include "vcard.h" +#include "vcard_emul.h" +#include "card_7816.h" +#include "vreader.h" +#include "vevent.h" + +struct VReaderStruct { + int reference_count; + VCard *card; + char *name; + vreader_id_t id; + QemuMutex lock; + VReaderEmul *reader_private; + VReaderEmulFree reader_private_free; +}; + +/* manage locking */ +static inline void +vreader_lock(VReader *reader) +{ + qemu_mutex_lock(&reader->lock); +} + +static inline void +vreader_unlock(VReader *reader) +{ + qemu_mutex_unlock(&reader->lock); +} + +/* + * vreader constructor + */ +VReader * +vreader_new(const char *name, VReaderEmul *private, + VReaderEmulFree private_free) +{ + VReader *reader; + + reader = (VReader *)qemu_malloc(sizeof(VReader)); + qemu_mutex_init(&reader->lock); + reader->reference_count = 1; + reader->name = name ? strdup(name) : NULL; + reader->card = NULL; + reader->id = (vreader_id_t)-1; + reader->reader_private = private; + reader->reader_private_free = private_free; + return reader; +} + +/* get a reference */ +VReader* +vreader_reference(VReader *reader) +{ + if (reader == NULL) { + return NULL; + } + vreader_lock(reader); + reader->reference_count++; + vreader_unlock(reader); + return reader; +} + +/* free a reference */ +void +vreader_free(VReader *reader) +{ + if (reader == NULL) { + return; + } + vreader_lock(reader); + if (reader->reference_count-- > 1) { + vreader_unlock(reader); + return; + } + vreader_unlock(reader); + if (reader->card) { + vcard_free(reader->card); + } + if (reader->name) { + qemu_free(reader->name); + } + if (reader->reader_private_free) { + reader->reader_private_free(reader->reader_private); + } + qemu_free(reader); + return; +} + +static VCard * +vreader_get_card(VReader *reader) +{ + VCard *card; + + vreader_lock(reader); + card = vcard_reference(reader->card); + vreader_unlock(reader); + return card; +} + +VReaderStatus +vreader_card_is_present(VReader *reader) +{ + VCard *card = vreader_get_card(reader); + + if (card == NULL) { + return VREADER_NO_CARD; + } + vcard_free(card); + return VREADER_OK; +} + +vreader_id_t +vreader_get_id(VReader *reader) +{ + if (reader == NULL) { + return (vreader_id_t)-1; + } + return reader->id; +} + +VReaderStatus +vreader_set_id(VReader *reader, vreader_id_t id) +{ + if (reader == NULL) { + return VREADER_NO_CARD; + } + reader->id = id; + return VREADER_OK; +} + +const char * +vreader_get_name(VReader *reader) +{ + if (reader == NULL) { + return NULL; + } + return reader->name; +} + +VReaderEmul * +vreader_get_private(VReader *reader) +{ + return reader->reader_private; +} + +static VReaderStatus +vreader_reset(VReader *reader, VCardPower power, unsigned char *atr, int *len) +{ + VCard *card = vreader_get_card(reader); + + if (card == NULL) { + return VREADER_NO_CARD; + } + /* + * clean up our state + */ + vcard_reset(card, power); + if (atr) { + vcard_get_atr(card, atr, len); + } + vcard_free(card); /* free our reference */ + return VREADER_OK; +} + +VReaderStatus +vreader_power_on(VReader *reader, unsigned char *atr, int *len) +{ + return vreader_reset(reader, VCARD_POWER_ON, atr, len); +} + +VReaderStatus +vreader_power_off(VReader *reader) +{ + return vreader_reset(reader, VCARD_POWER_OFF, NULL, 0); +} + + +VReaderStatus +vreader_xfr_bytes(VReader *reader, + unsigned char *send_buf, int send_buf_len, + unsigned char *receive_buf, int *receive_buf_len) +{ + VCardAPDU *apdu; + VCardResponse *response = NULL; + VCardStatus card_status; + unsigned short status; + VCard *card = vreader_get_card(reader); + + if (card == NULL) { + return VREADER_NO_CARD; + } + + apdu = vcard_apdu_new(send_buf, send_buf_len, &status); + if (apdu == NULL) { + response = vcard_make_response(status); + card_status = VCARD_DONE; + } else { + card_status = vcard_process_apdu(card, apdu, &response); + } + assert(card_status == VCARD_DONE); + if (card_status == VCARD_DONE) { + int size = MIN(*receive_buf_len, response->b_total_len); + memcpy(receive_buf, response->b_data, size); + *receive_buf_len = size; + } + vcard_response_delete(response); + vcard_apdu_delete(apdu); + vcard_free(card); /* free our reference */ + return VREADER_OK; +} + +struct VReaderListStruct { + VReaderListEntry *head; + VReaderListEntry *tail; +}; + +struct VReaderListEntryStruct { + VReaderListEntry *next; + VReaderListEntry *prev; + VReader *reader; +}; + + +static VReaderListEntry * +vreader_list_entry_new(VReader *reader) +{ + VReaderListEntry *new_reader_list_entry; + + new_reader_list_entry = (VReaderListEntry *) + qemu_malloc(sizeof(VReaderListEntry)); + new_reader_list_entry->next = NULL; + new_reader_list_entry->prev = NULL; + new_reader_list_entry->reader = vreader_reference(reader); + return new_reader_list_entry; +} + +static void +vreader_list_entry_delete(VReaderListEntry *entry) +{ + if (entry == NULL) { + return; + } + vreader_free(entry->reader); + qemu_free(entry); +} + + +static VReaderList * +vreader_list_new(void) +{ + VReaderList *new_reader_list; + + new_reader_list = (VReaderList *)qemu_malloc(sizeof(VReaderList)); + new_reader_list->head = NULL; + new_reader_list->tail = NULL; + return new_reader_list; +} + +void +vreader_list_delete(VReaderList *list) +{ + VReaderListEntry *current_entry; + VReaderListEntry *next_entry = NULL; + for (current_entry = vreader_list_get_first(list); current_entry; + current_entry = next_entry) { + next_entry = vreader_list_get_next(current_entry); + vreader_list_entry_delete(current_entry); + } + list->head = NULL; + list->tail = NULL; + qemu_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 QemuMutex vreader_list_mutex; + +static void +vreader_list_init(void) +{ + vreader_list = vreader_list_new(); + qemu_mutex_init(&vreader_list_mutex); +} + +static void +vreader_list_lock(void) +{ + qemu_mutex_lock(&vreader_list_mutex); +} + +static void +vreader_list_unlock(void) +{ + qemu_mutex_unlock(&vreader_list_mutex); +} + +static VReaderList * +vreader_copy_list(VReaderList *list) +{ + VReaderList *new_list = NULL; + VReaderListEntry *current_entry = NULL; + + new_list = vreader_list_new(); + if (new_list == NULL) { + return NULL; + } + for (current_entry = vreader_list_get_first(list); current_entry; + current_entry = vreader_list_get_next(current_entry)) { + VReader *reader = vreader_list_get_reader(current_entry); + VReaderListEntry *new_entry = vreader_list_entry_new(reader); + + vreader_free(reader); + vreader_queue(new_list, new_entry); + } + return new_list; +} + +VReaderList * +vreader_get_reader_list(void) +{ + VReaderList *new_reader_list; + + vreader_list_lock(); + new_reader_list = vreader_copy_list(vreader_list); + vreader_list_unlock(); + return new_reader_list; +} + +VReader * +vreader_get_reader_by_id(vreader_id_t id) +{ + VReader *reader = NULL; + VReaderListEntry *current_entry = NULL; + + if (id == (vreader_id_t) -1) { + return NULL; + } + + vreader_list_lock(); + for (current_entry = vreader_list_get_first(vreader_list); current_entry; + current_entry = vreader_list_get_next(current_entry)) { + VReader *creader = vreader_list_get_reader(current_entry); + if (creader->id == id) { + reader = creader; + break; + } + vreader_free(creader); + } + vreader_list_unlock(); + return reader; +} + +VReader * +vreader_get_reader_by_name(const char *name) +{ + VReader *reader = NULL; + VReaderListEntry *current_entry = NULL; + + vreader_list_lock(); + for (current_entry = vreader_list_get_first(vreader_list); current_entry; + current_entry = vreader_list_get_next(current_entry)) { + VReader *creader = vreader_list_get_reader(current_entry); + if (strcmp(creader->name, name) == 0) { + reader = creader; + break; + } + vreader_free(creader); + } + vreader_list_unlock(); + return reader; +} + +/* called from card_emul to initialize the readers */ +VReaderStatus +vreader_add_reader(VReader *reader) +{ + VReaderListEntry *reader_entry; + + reader_entry = vreader_list_entry_new(reader); + if (reader_entry == NULL) { + return VREADER_OUT_OF_MEMORY; + } + vreader_list_lock(); + vreader_queue(vreader_list, reader_entry); + vreader_list_unlock(); + vevent_queue_vevent(vevent_new(VEVENT_READER_INSERT, reader, NULL)); + return VREADER_OK; +} + + +VReaderStatus +vreader_remove_reader(VReader *reader) +{ + VReaderListEntry *current_entry; + + vreader_list_lock(); + for (current_entry = vreader_list_get_first(vreader_list); current_entry; + current_entry = vreader_list_get_next(current_entry)) { + if (current_entry->reader == reader) { + break; + } + } + vreader_dequeue(vreader_list, current_entry); + vreader_list_unlock(); + vreader_list_entry_delete(current_entry); + vevent_queue_vevent(vevent_new(VEVENT_READER_REMOVE, reader, NULL)); + return VREADER_OK; +} + +/* + * Generate VEVENT_CARD_INSERT or VEVENT_CARD_REMOVE based on vreader + * state. Separated from vreader_insert_card to allow replaying events + * for a given state. + */ +void +vreader_queue_card_event(VReader *reader) +{ + vevent_queue_vevent(vevent_new( + reader->card ? VEVENT_CARD_INSERT : VEVENT_CARD_REMOVE, reader, + reader->card)); +} + +/* + * insert/remove a new card. for removal, card == NULL + */ +VReaderStatus +vreader_insert_card(VReader *reader, VCard *card) +{ + vreader_lock(reader); + if (reader->card) { + /* decrement reference count */ + vcard_free(reader->card); + reader->card = NULL; + } + reader->card = vcard_reference(card); + vreader_unlock(reader); + vreader_queue_card_event(reader); + return VREADER_OK; +} + +/* + * initialize all the static reader structures + */ +void +vreader_init(void) +{ + vreader_list_init(); +} + diff --git a/libcacard/vreader.h b/libcacard/vreader.h new file mode 100644 index 0000000..ec20421 --- /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 0000000..f97e0a7 --- /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 +